Compare commits
6 Commits
6835359510
...
feature/vk
| Author | SHA1 | Date | |
|---|---|---|---|
| b95bc3101f | |||
| a94f2da6b2 | |||
| 9bcb8385fe | |||
| a0b3325e1d | |||
| e3c4267fc2 | |||
| cedb80cb03 |
@@ -33,6 +33,7 @@ add_library(maiden_core STATIC
|
|||||||
src/App.cpp
|
src/App.cpp
|
||||||
src/Window.cpp
|
src/Window.cpp
|
||||||
src/Engine.cpp
|
src/Engine.cpp
|
||||||
|
src/Device.cpp
|
||||||
# include extra source files here
|
# include extra source files here
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -10,10 +10,13 @@ The maiden project is a GPU accelerated 3D rendering engine built with C++ based
|
|||||||
|
|
||||||
### Clone Repository
|
### Clone Repository
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://git.vxbard.net/homeburger/maiden.git
|
# ssh recommended for contribution
|
||||||
|
$ git clone git@github.com:Blitblank/maiden.git
|
||||||
|
# http if you don't like ssh:
|
||||||
|
$ git clone https://github.com/Blitblank/maiden.git
|
||||||
|
|
||||||
# If there's any necessary submodules then:
|
# If there's any necessary submodules then:
|
||||||
$ git clone --recurse-submodules https://git.vxbard.net/homeburger/maiden.git
|
$ git clone --recurse-submodules git@github.com:Blitblank/maiden.git
|
||||||
|
|
||||||
# If you have already cloned the repository and you need its submodules:
|
# If you have already cloned the repository and you need its submodules:
|
||||||
$ git submodule update --init --recursive
|
$ git submodule update --init --recursive
|
||||||
@@ -74,6 +77,24 @@ $ gcovr -r .. --filter "../src"
|
|||||||
Basically these are some tricky situations that I encountered when trying to execute this app throughout this development phase. If you are running on WSL Ubuntu 26.04 like me, then you mightr run into these too, hopefully my steps help fix.
|
Basically these are some tricky situations that I encountered when trying to execute this app throughout this development phase. If you are running on WSL Ubuntu 26.04 like me, then you mightr run into these too, hopefully my steps help fix.
|
||||||
note: I am running an x86_64 system with an Nvidia GPU so some things may be slightly different if your system doesn't match.
|
note: I am running an x86_64 system with an Nvidia GPU so some things may be slightly different if your system doesn't match.
|
||||||
|
|
||||||
|
### [WARN: COPY MODE]
|
||||||
|
This seems like a WSL specific error and causes real issues with relaying graphics from linux to windows. I fixed this by installing new mesa drivers as reccommended by https://github.com/microsoft/wslg/discussions/312:
|
||||||
|
```bash
|
||||||
|
$ sudo add-apt-repository ppa:kisak/kisak-mesa
|
||||||
|
$ sudo apt-get update && sudo apt upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: this resulted in the following erre "WARNING: dzn is not a conformant Vulkan implementation, testing use only." Running `$ vkcube` showed that this indeed was just a warning.
|
||||||
|
|
||||||
|
(for those curious, dzn is a compaitibility layer between DirectX12 and Vulkan for that WSL conformity)
|
||||||
|
|
||||||
|
### Could not locate a Nvidia GPU
|
||||||
|
```bash
|
||||||
|
$ sudo add-apt-repository ppa:kisak/turtle
|
||||||
|
$ sudo apt update
|
||||||
|
$ sudo apt upgrade
|
||||||
|
```
|
||||||
|
verify with `$ vulkaninfo --summary` to ensure your GPU is shown.
|
||||||
|
|
||||||
## Development Roadmap
|
## Development Roadmap
|
||||||
### lots of todo here
|
### lots of todo here
|
||||||
|
|||||||
155
src/Device.cpp
Normal file
155
src/Device.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
|
||||||
|
#include "Device.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
Device::Device(vk::raii::Instance* instance): instance_(instance) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device::selectPhysicalDevice() {
|
||||||
|
|
||||||
|
std::vector<vk::raii::PhysicalDevice> physicalDevices = instance_->enumeratePhysicalDevices();
|
||||||
|
|
||||||
|
if(physicalDevices.empty()) {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error: no physical devices with Vulkan support found." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate found devices
|
||||||
|
uint32_t maxScore = 0;
|
||||||
|
for(vk::raii::PhysicalDevice& physicalDevice : physicalDevices) {
|
||||||
|
uint32_t capabilityScore = evaluatePhysicalDevice(physicalDevice);
|
||||||
|
if(capabilityScore > maxScore) {
|
||||||
|
maxScore = capabilityScore;
|
||||||
|
physicalDevice_ = physicalDevice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(maxScore = 0) {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error: physical devices found, but none capable for this engine." << std::endl;
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
vk::PhysicalDeviceProperties deviceProperties = physicalDevice_.getProperties();
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Physical device selected: " << deviceProperties.deviceName << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Device::evaluatePhysicalDevice(vk::raii::PhysicalDevice& device) {
|
||||||
|
|
||||||
|
vk::PhysicalDeviceProperties deviceProperties = device.getProperties();
|
||||||
|
vk::PhysicalDeviceFeatures deviceFeatures = device.getFeatures();
|
||||||
|
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Physical device found: " << deviceProperties.deviceName << std::endl;
|
||||||
|
|
||||||
|
uint32_t score = 0;
|
||||||
|
|
||||||
|
// TODO: this is very basic and can be improved
|
||||||
|
|
||||||
|
// prefer discrete graphics to integrated graphics
|
||||||
|
if(deviceProperties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
|
||||||
|
score += 2;
|
||||||
|
} else {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Warning: physical device " << deviceProperties.deviceName << " is not a discrete device!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefer devices that support vulkan 1.3
|
||||||
|
if(deviceProperties.apiVersion >= vk::ApiVersion14) {
|
||||||
|
score++;
|
||||||
|
} else {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Warning: physical device " << deviceProperties.deviceName << " does not support Vulkan 1.3! (" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefer devices that support graphics queues
|
||||||
|
auto queueFamilies = device.getQueueFamilyProperties();
|
||||||
|
if(std::ranges::any_of( queueFamilies, []( auto const & qfp ) { return !!( qfp.queueFlags & vk::QueueFlagBits::eGraphics ); } )) {
|
||||||
|
score++;
|
||||||
|
} else {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Warning: physical device " << deviceProperties.deviceName << " does not support graphics queue families!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefer devices that support all required extensions
|
||||||
|
auto availableDeviceExtensions = device.enumerateDeviceExtensionProperties();
|
||||||
|
bool found = false;
|
||||||
|
uint32_t missingExtensions = 0;
|
||||||
|
for(auto& requiredExtension : requiredDeviceExtensions_) {
|
||||||
|
for(auto& availableExtension: availableDeviceExtensions) {
|
||||||
|
if(strcmp(availableExtension.extensionName, requiredExtension) == 0) {
|
||||||
|
found = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(found == false) {
|
||||||
|
missingExtensions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(missingExtensions == 0) {
|
||||||
|
score++;
|
||||||
|
} else {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Warning: physical device " << deviceProperties.deviceName << " is missing extensions!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefer devices that support all required features
|
||||||
|
auto features = device.template getFeatures2<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>();
|
||||||
|
if(features.template get<vk::PhysicalDeviceVulkan13Features>().dynamicRendering && features.template get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>().extendedDynamicState) {
|
||||||
|
score++;
|
||||||
|
} else {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Warning: physical device " << deviceProperties.deviceName << " is missing features!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device::createLogicalDevice() {
|
||||||
|
|
||||||
|
if(physicalDevice_ == nullptr) {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error: cannot create logical device without a valid physical device." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify queue family requirements
|
||||||
|
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice_.getQueueFamilyProperties();
|
||||||
|
auto graphicsQueueFamilyProperty = std::ranges::find_if(queueFamilyProperties, [](auto const &qfp) { return (qfp.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlags>(0); });
|
||||||
|
auto graphicsIndex = static_cast<uint32_t>(std::distance(queueFamilyProperties.begin(), graphicsQueueFamilyProperty));
|
||||||
|
float queuePriority = 1.0f;
|
||||||
|
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||||
|
.queueFamilyIndex = graphicsIndex,
|
||||||
|
.queueCount = 1,
|
||||||
|
.pQueuePriorities = &queuePriority
|
||||||
|
};
|
||||||
|
|
||||||
|
// specify device feature requirements
|
||||||
|
vk::PhysicalDeviceVulkan13Features deviceFeatures = { .dynamicRendering = true };
|
||||||
|
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT deviceStateFeatures = { . extendedDynamicState = true };
|
||||||
|
vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
|
||||||
|
{}, // empty for now
|
||||||
|
deviceFeatures,
|
||||||
|
deviceStateFeatures
|
||||||
|
};
|
||||||
|
|
||||||
|
// create logical device
|
||||||
|
vk::DeviceCreateInfo deviceCreateInfo {
|
||||||
|
.pNext = &featureChain.get<vk::PhysicalDeviceFeatures2>(),
|
||||||
|
.queueCreateInfoCount = 1,
|
||||||
|
.pQueueCreateInfos = &deviceQueueCreateInfo,
|
||||||
|
.enabledExtensionCount = static_cast<uint32_t>(requiredDeviceExtensions_.size()),
|
||||||
|
.ppEnabledExtensionNames = requiredDeviceExtensions_.data()
|
||||||
|
};
|
||||||
|
logicalDevice_ = vk::raii::Device(physicalDevice_, deviceCreateInfo);
|
||||||
|
|
||||||
|
// initialize the graphics queue
|
||||||
|
graphicsQueue_ = vk::raii::Queue(logicalDevice_, graphicsIndex, 0);
|
||||||
|
|
||||||
|
if(logicalDevice_ != nullptr) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error: could not create a valid logical device." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
src/Device.hpp
Normal file
32
src/Device.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan_raii.hpp>
|
||||||
|
|
||||||
|
class Device {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Device(vk::raii::Instance* instance);
|
||||||
|
~Device();
|
||||||
|
|
||||||
|
// assigns a capable gpu vkdevice to physicalDevice
|
||||||
|
bool selectPhysicalDevice();
|
||||||
|
|
||||||
|
// initializes the logical device
|
||||||
|
bool createLogicalDevice();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// gives a device a score to attempt to select the most capable device
|
||||||
|
uint32_t evaluatePhysicalDevice(vk::raii::PhysicalDevice& device);
|
||||||
|
|
||||||
|
vk::raii::Instance* instance_ = nullptr;
|
||||||
|
vk::raii::PhysicalDevice physicalDevice_ = nullptr;
|
||||||
|
vk::raii::Device logicalDevice_ = nullptr;
|
||||||
|
vk::raii::Queue graphicsQueue_ = nullptr;
|
||||||
|
|
||||||
|
// required extensions for the physical device
|
||||||
|
std::vector<const char*> requiredDeviceExtensions_ = { vk::KHRSwapchainExtensionName };
|
||||||
|
|
||||||
|
};
|
||||||
143
src/Engine.cpp
143
src/Engine.cpp
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "Device.hpp"
|
||||||
|
|
||||||
Engine::Engine(Window* window): window_(window) {
|
Engine::Engine(Window* window): window_(window) {
|
||||||
|
|
||||||
// cleans up this constructor
|
// cleans up this constructor
|
||||||
@@ -19,8 +21,12 @@ void Engine::init() {
|
|||||||
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error creating Vulkan instance." << std::endl;
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error creating Vulkan instance." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// next steps:
|
|
||||||
// device selection and setup
|
// device selection and setup
|
||||||
|
Device device(&instance_);
|
||||||
|
(void)device.selectPhysicalDevice();
|
||||||
|
(void)device.createLogicalDevice();
|
||||||
|
|
||||||
|
// next steps:
|
||||||
// queue creation
|
// queue creation
|
||||||
// vulkan memory allocator
|
// vulkan memory allocator
|
||||||
// create vulkan surface
|
// create vulkan surface
|
||||||
@@ -45,24 +51,7 @@ bool Engine::createInstance() {
|
|||||||
.apiVersion = VK_API_VERSION_1_4 // this one is most important
|
.apiVersion = VK_API_VERSION_1_4 // this one is most important
|
||||||
};
|
};
|
||||||
|
|
||||||
// get necessary extensions that our windowing library requires
|
std::vector<const char*> requiredInstanceExtensions = getRequiredInstanceExtensions();
|
||||||
uint32_t instanceExtensionsCount = 0;
|
|
||||||
char const* const* instanceExtensions{ SDL_Vulkan_GetInstanceExtensions(&instanceExtensionsCount) };
|
|
||||||
|
|
||||||
// get required validation layers as specified by our app
|
|
||||||
std::vector<const char*> requiredValidationLayers;
|
|
||||||
if(enableValidationLayers) requiredValidationLayers.assign(validationLayers.begin(), validationLayers.end());
|
|
||||||
|
|
||||||
auto validationLayerProperties = context_.enumerateInstanceLayerProperties();
|
|
||||||
auto unsupportedLayer = std::ranges::find_if(requiredValidationLayers, [&validationLayerProperties](const auto& requiredLayer) {
|
|
||||||
return std::ranges::none_of(validationLayerProperties, [requiredLayer](const auto& layerProperty) {
|
|
||||||
return strcmp(layerProperty.layerName, requiredLayer) == 0;
|
|
||||||
});
|
|
||||||
}); // TODO: what black magic even is this
|
|
||||||
if(unsupportedLayer != requiredValidationLayers.end()) {
|
|
||||||
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required validation layer not supported: " << *unsupportedLayer << std::endl;
|
|
||||||
errorCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all available extensions
|
// get all available extensions
|
||||||
auto extensionProperties = context_.enumerateInstanceExtensionProperties();
|
auto extensionProperties = context_.enumerateInstanceExtensionProperties();
|
||||||
@@ -71,35 +60,129 @@ bool Engine::createInstance() {
|
|||||||
std::cout << "Available Vulkan Extensions: " << std::endl;
|
std::cout << "Available Vulkan Extensions: " << std::endl;
|
||||||
for(const auto& extensionProperty : extensionProperties) {
|
for(const auto& extensionProperty : extensionProperties) {
|
||||||
std::cout << "\t" << extensionProperty.extensionName << std::endl;
|
std::cout << "\t" << extensionProperty.extensionName << std::endl;
|
||||||
}
|
} // this would be a logger.debug(...)
|
||||||
|
|
||||||
for(uint32_t i = 0; i < instanceExtensionsCount; i++) { // for each extension that we require
|
// check that all required extensions are available
|
||||||
|
for(uint32_t i = 0; i < requiredInstanceExtensions.size(); i++) { // for each extension that we require
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for(const auto& extensionProperty : extensionProperties) { // see if it matches any extensions that are provided
|
for(const auto& extensionProperty : extensionProperties) { // see if it matches any extensions that are provided
|
||||||
if(strcmp(extensionProperty.extensionName, instanceExtensions[i]) == 0) {
|
if(strcmp(extensionProperty.extensionName, requiredInstanceExtensions[i]) == 0) {
|
||||||
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required SDL3 extension not supported: " << instanceExtensions[i] << std::endl;
|
|
||||||
} else {
|
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!found) {
|
if(!found) {
|
||||||
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required SDL3 extension not supported: " << instanceExtensions[i] << std::endl;
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required SDL3 extension not supported: " << requiredInstanceExtensions[i] << std::endl;
|
||||||
errorCount++;
|
errorCount++;
|
||||||
} else {
|
} else {
|
||||||
// in case you're curious
|
// in case you're curious
|
||||||
//std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] SDL3 extension located: " << instanceExtensions[i] << std::endl;
|
//std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] SDL3 extension located: " << requiredInstanceExtensions[i] << std::endl;
|
||||||
}
|
}
|
||||||
} // if any weren't then we must exit
|
}
|
||||||
if(errorCount != 0) return false;
|
|
||||||
|
// get required validation layers as specified by our app
|
||||||
|
std::vector<const char*> requiredValidationLayers;
|
||||||
|
if(enableValidationLayers) requiredValidationLayers.assign(validationLayers.begin(), validationLayers.end());
|
||||||
|
|
||||||
|
// get available validation layers
|
||||||
|
auto validationLayerProperties = context_.enumerateInstanceLayerProperties();
|
||||||
|
|
||||||
|
// again print if we feel like it
|
||||||
|
std::cout << "Available Vulkan Validation Layers: " << std::endl;
|
||||||
|
for(const auto& validationLayer : validationLayerProperties) {
|
||||||
|
std::cout << "\t" << validationLayer.layerName << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all required validation layers are avilable
|
||||||
|
for(int i = 0; i < requiredValidationLayers.size(); i++) {
|
||||||
|
bool found = false;
|
||||||
|
for(const auto& validationLayer : validationLayerProperties) {
|
||||||
|
if(strcmp(requiredValidationLayers[i], validationLayer.layerName) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
errorCount++;
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required validation layer not supported: " << requiredValidationLayers[i] << std::endl;
|
||||||
|
} else { // in case you're curious
|
||||||
|
//std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] VkValidation layer located: " << requiredValidationLayers[i] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if any we had errors then we must exit
|
||||||
|
if(errorCount != 0) {
|
||||||
|
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Unable to create Vulkan instance. Error count: " << errorCount << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
vk::InstanceCreateInfo instanceCreateInfo {
|
vk::InstanceCreateInfo instanceCreateInfo {
|
||||||
.pApplicationInfo = &appInfo,
|
.pApplicationInfo = &appInfo,
|
||||||
.enabledExtensionCount = instanceExtensionsCount,
|
.enabledLayerCount = static_cast<uint32_t>(requiredValidationLayers.size()),
|
||||||
.ppEnabledExtensionNames = instanceExtensions,
|
.ppEnabledLayerNames = requiredValidationLayers.data(),
|
||||||
|
.enabledExtensionCount = static_cast<uint32_t>(requiredInstanceExtensions.size()),
|
||||||
|
.ppEnabledExtensionNames = requiredInstanceExtensions.data()
|
||||||
};
|
};
|
||||||
|
|
||||||
instance_ = vk::raii::Instance(context_, instanceCreateInfo);
|
instance_ = vk::raii::Instance(context_, instanceCreateInfo);
|
||||||
return (instance_ != nullptr);
|
return (instance_ != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<const char*> Engine::getRequiredInstanceExtensions() {
|
||||||
|
|
||||||
|
// get extensions that our windowing library requires
|
||||||
|
uint32_t sdlExtensionsCount = 0;
|
||||||
|
const char* const* sdlExtensions{ SDL_Vulkan_GetInstanceExtensions(&sdlExtensionsCount) };
|
||||||
|
// what in the world is this kind of pointer btw
|
||||||
|
|
||||||
|
std::vector<const char*> requiredExtensions(sdlExtensions, sdlExtensions + static_cast<size_t>(sdlExtensionsCount));
|
||||||
|
|
||||||
|
// manually add an extension for handling validation layers
|
||||||
|
if(enableValidationLayers) {
|
||||||
|
requiredExtensions.push_back(vk::EXTDebugUtilsExtensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return requiredExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::initDebugMessenger() {
|
||||||
|
|
||||||
|
if(!enableValidationLayers) return false;
|
||||||
|
|
||||||
|
// masks for which debug messages we want to see
|
||||||
|
vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError);
|
||||||
|
vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags( vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation);
|
||||||
|
vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfoEXT{.messageSeverity = severityFlags,
|
||||||
|
.messageType = messageTypeFlags,
|
||||||
|
.pfnUserCallback = &debugCallback};
|
||||||
|
debugMessenger_ = instance_.createDebugUtilsMessengerEXT( debugUtilsMessengerCreateInfoEXT );
|
||||||
|
// we could get rid of this and just pass all the control to the logger
|
||||||
|
// like: "treat all messages of severity vk::eVerbose as our own DebugVerbosity::Info"
|
||||||
|
|
||||||
|
return (debugMessenger_ != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
VKAPI_ATTR vk::Bool32 VKAPI_CALL Engine::debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||||
|
vk::DebugUtilsMessageTypeFlagsEXT type,
|
||||||
|
const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData) {
|
||||||
|
|
||||||
|
// this will eventually go through our logger
|
||||||
|
std::cout << "[ Validation Layer ] [Type: " << to_string(type) << "] " << pCallbackData->pMessage << std::endl;
|
||||||
|
/*
|
||||||
|
vk severity types:
|
||||||
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose
|
||||||
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo
|
||||||
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning
|
||||||
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError
|
||||||
|
|
||||||
|
vk message types:
|
||||||
|
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral
|
||||||
|
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation
|
||||||
|
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance
|
||||||
|
*/
|
||||||
|
|
||||||
|
// returns whether or not we should abort, we'll always say no
|
||||||
|
return vk::False;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public:
|
|||||||
Engine(Window* window);
|
Engine(Window* window);
|
||||||
~Engine() = default;
|
~Engine() = default;
|
||||||
|
|
||||||
|
// initializes and sets up the vulkan instance. outside of constructor to allow control of order the order of initialization
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
// draw is called every render iteration in that while loop
|
// draw is called every render iteration in that while loop
|
||||||
@@ -20,14 +21,29 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Window* window_;
|
// returns a list of the required extensions needed by our engine
|
||||||
|
std::vector<const char*> getRequiredInstanceExtensions();
|
||||||
|
|
||||||
|
// helper for organizing the creation of the vulkan instance. returns success or failure
|
||||||
bool createInstance();
|
bool createInstance();
|
||||||
|
|
||||||
// Vulkan specific
|
// helper for attaching callbacks to the instance
|
||||||
|
bool initDebugMessenger();
|
||||||
|
|
||||||
|
// callback function for the vulkan debug extension: routes debug messages from validation layers to our app
|
||||||
|
static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||||
|
vk::DebugUtilsMessageTypeFlagsEXT type,
|
||||||
|
const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData);
|
||||||
|
|
||||||
|
Window* window_;
|
||||||
|
|
||||||
|
// Vulkan specific instance members
|
||||||
vk::raii::Context context_;
|
vk::raii::Context context_;
|
||||||
vk::raii::Instance instance_ = nullptr;
|
vk::raii::Instance instance_ = nullptr;
|
||||||
|
vk::raii::DebugUtilsMessengerEXT debugMessenger_ = nullptr;
|
||||||
|
|
||||||
|
// members for control over validation layers
|
||||||
static constexpr bool enableValidationLayers = true; // TODO: only true in debug mode
|
static constexpr bool enableValidationLayers = true; // TODO: only true in debug mode
|
||||||
const std::vector<const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" };
|
const std::vector<const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user