Feature: VkDevice Creation (#5)
* update contributing with goals * add required extensions to vulkan instance * add validation layers + debug callback * add device class which enumerates availble gpu devices * improve device selection logic * add logical device creation
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -83,10 +83,18 @@ This seems like a WSL specific error and causes real issues with relaying graphi
|
|||||||
$ sudo add-apt-repository ppa:kisak/kisak-mesa
|
$ sudo add-apt-repository ppa:kisak/kisak-mesa
|
||||||
$ sudo apt-get update && sudo apt upgrade
|
$ 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.
|
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)
|
(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 };
|
||||||
|
|
||||||
|
};
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user