From bcd19f1e60b614423f6d7f946b6e5bd2b561638d Mon Sep 17 00:00:00 2001 From: Preston McGee <106772059+Blitblank@users.noreply.github.com> Date: Fri, 22 May 2026 19:48:51 -0700 Subject: [PATCH] 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 --- CMakeLists.txt | 1 + README.md | 8 +++ src/Device.cpp | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Device.hpp | 32 ++++++++++ src/Engine.cpp | 8 ++- 5 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 src/Device.cpp create mode 100644 src/Device.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e7c3d1..8dae3ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(maiden_core STATIC src/App.cpp src/Window.cpp src/Engine.cpp + src/Device.cpp # include extra source files here ) diff --git a/README.md b/README.md index 98f0210..75fa95b 100644 --- a/README.md +++ b/README.md @@ -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 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 ### lots of todo here diff --git a/src/Device.cpp b/src/Device.cpp new file mode 100644 index 0000000..41d2cdd --- /dev/null +++ b/src/Device.cpp @@ -0,0 +1,155 @@ + +#include "Device.hpp" + +#include + +Device::Device(vk::raii::Instance* instance): instance_(instance) { + +} + +Device::~Device() { + +} + +bool Device::selectPhysicalDevice() { + + std::vector 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(); + if(features.template get().dynamicRendering && features.template get().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 queueFamilyProperties = physicalDevice_.getQueueFamilyProperties(); + auto graphicsQueueFamilyProperty = std::ranges::find_if(queueFamilyProperties, [](auto const &qfp) { return (qfp.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast(0); }); + auto graphicsIndex = static_cast(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 featureChain = { + {}, // empty for now + deviceFeatures, + deviceStateFeatures + }; + + // create logical device + vk::DeviceCreateInfo deviceCreateInfo { + .pNext = &featureChain.get(), + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &deviceQueueCreateInfo, + .enabledExtensionCount = static_cast(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; + } + +} diff --git a/src/Device.hpp b/src/Device.hpp new file mode 100644 index 0000000..61bd35b --- /dev/null +++ b/src/Device.hpp @@ -0,0 +1,32 @@ + +#pragma once + +#include + +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 requiredDeviceExtensions_ = { vk::KHRSwapchainExtensionName }; + +}; diff --git a/src/Engine.cpp b/src/Engine.cpp index 35afc4f..ad7ca36 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -3,6 +3,8 @@ #include +#include "Device.hpp" + Engine::Engine(Window* window): window_(window) { // cleans up this constructor @@ -19,8 +21,12 @@ void Engine::init() { std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Error creating Vulkan instance." << std::endl; } - // next steps: // device selection and setup + Device device(&instance_); + (void)device.selectPhysicalDevice(); + (void)device.createLogicalDevice(); + + // next steps: // queue creation // vulkan memory allocator // create vulkan surface