From 425c226793da806538222c520fea182f401200f7 Mon Sep 17 00:00:00 2001 From: Riley King-Saunders Date: Mon, 7 Jul 2025 22:52:25 +1000 Subject: [PATCH] Contains convenient function to design a device based on the requirements for the physical device it is based around NOTE: To get the appropriate index into the basalt::Device queue list, track the order of request_queue(s) calls in the designer --- include/vulkan/basalt_device.h | 55 ++++++++++ src/vulkan/basalt_device.cpp | 182 +++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 include/vulkan/basalt_device.h create mode 100644 src/vulkan/basalt_device.cpp diff --git a/include/vulkan/basalt_device.h b/include/vulkan/basalt_device.h new file mode 100644 index 0000000..4bd54ae --- /dev/null +++ b/include/vulkan/basalt_device.h @@ -0,0 +1,55 @@ +#pragma once +#include "basalt_physical_device.h" +#include "basalt_queue.h" + +namespace basalt +{ + class Device + { + public: + Device(const Device& src); + Device& operator =(const Device& src); + Device(Device&& other) noexcept; + Device& operator =(Device&& other) noexcept; + + Device(basalt::Context& ctx, basalt::PhysicalDevice& physical, VkDevice logical); + ~Device(); + + operator VkPhysicalDevice(); + operator VkDevice(); + + basalt::darray queues; + VkDevice logical = VK_NULL_HANDLE; + basalt::Context* ctx; + basalt::PhysicalDevice* phys; + bool should_free = true; + }; + class DeviceDesigner + { + public: + DeviceDesigner(basalt::Context& ctx, basalt::PhysicalDevice& phy); + + DeviceDesigner& request_features(VkPhysicalDeviceFeatures features); + + DeviceDesigner& request_extensions(const basalt::darray& extensions); + DeviceDesigner& request_extensions(const char** extensions, const u32 num_extensions); + DeviceDesigner& request_extension(const char* extension); + + DeviceDesigner& request_layers(const basalt::darray& layers); + DeviceDesigner& request_layers(const char** layers, const u32 num_layers); + DeviceDesigner& request_layer(const char* layer); + + DeviceDesigner& request_queue(u32 family_index, const float priority=1.0f, VkDeviceQueueCreateFlags flags=0); + DeviceDesigner& request_queues(u32 family_index, const basalt::darray& queue_priorities, VkDeviceQueueCreateFlags flags=0); + + basalt::Device design(void); + + VkPhysicalDeviceFeatures req_features; + basalt::darray req_queues; + basalt::darray req_extensions; + basalt::darray req_layers; + basalt::darray queue_priorities; + basalt::Context& ctx; + basalt::PhysicalDevice& phys; + }; +} diff --git a/src/vulkan/basalt_device.cpp b/src/vulkan/basalt_device.cpp new file mode 100644 index 0000000..d4b8f63 --- /dev/null +++ b/src/vulkan/basalt_device.cpp @@ -0,0 +1,182 @@ +#include "vulkan/basalt_device.h" + +basalt::DeviceDesigner::DeviceDesigner(basalt::Context& ctx, basalt::PhysicalDevice& phy) : + ctx(ctx), phys(phy), req_features(VkPhysicalDeviceFeatures{}), + req_queues(MEMORY_TAG_CLASS_DYNARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + req_extensions(phy.enabled_extensions), + req_layers(MEMORY_TAG_CLASS_DYNARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + queue_priorities(MEMORY_TAG_CLASS_DYNARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY) +{} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_features(VkPhysicalDeviceFeatures features) +{ + VkBool32* cur = (VkBool32*)(&this->req_features); + const VkBool32* req = (VkBool32*)(&features); + for (u16 i = 0; i < sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); ++i) + cur[i] |= req[i]; + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_extensions(const basalt::darray& extension) +{ + this->req_extensions.push_back(extension); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_extensions(const char** extensions, const u32 num_extensions) +{ + this->req_extensions.push_back(extensions, num_extensions); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_extension(const char* extension) +{ + this->req_extensions.push_back(extension); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_layers(const basalt::darray&layers) +{ + this->req_layers.push_back(layers); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_layers(const char** layers, const u32 num_layers) +{ + this->req_layers.push_back(layers, num_layers); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_layer(const char* layer) +{ + this->req_layers.push_back(layer); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_queue(u32 family_index, const float priority, VkDeviceQueueCreateFlags flags) +{ + const u32 priority_ref = this->queue_priorities.m_nelements; + this->queue_priorities.push_back(priority); + VkDeviceQueueCreateInfo ci = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + ci.flags = flags; + ci.pNext = VK_NULL_HANDLE; + ci.pQueuePriorities = (float*)priority_ref; + ci.queueCount = 1; + ci.queueFamilyIndex = family_index; + this->req_queues.push_back(ci); + return *this; +} + +basalt::DeviceDesigner& basalt::DeviceDesigner::request_queues(u32 family_index, const basalt::darray& queue_priorities, VkDeviceQueueCreateFlags flags) +{ + const u32 priority_ref = this->queue_priorities.m_nelements; + this->queue_priorities.push_back(queue_priorities); + VkDeviceQueueCreateInfo ci = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + ci.flags = flags; + ci.pNext = VK_NULL_HANDLE; + ci.pQueuePriorities = (float*)priority_ref; + ci.queueCount = queue_priorities.size(); + ci.queueFamilyIndex = family_index; + this->req_queues.push_back(ci); + return *this; +} + +basalt::Device basalt::DeviceDesigner::design(void) +{ + VkDevice logical = VK_NULL_HANDLE; + + // Resolve the relative references for queue priorities in the queue create infos + for (VkDeviceQueueCreateInfo& queue_ci : this->req_queues) + queue_ci.pQueuePriorities = ((u32)queue_ci.pQueuePriorities) + this->queue_priorities.begin(); + + VkDeviceCreateInfo ci = {VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; + ci.flags = 0; + ci.queueCreateInfoCount = this->req_queues.m_nelements; + ci.pQueueCreateInfos = this->req_queues.m_pdata; + ci.pEnabledFeatures = &this->req_features; + ci.enabledExtensionCount = this->req_extensions.m_nelements; + ci.ppEnabledExtensionNames = this->req_extensions.m_pdata; + ci.enabledLayerCount = this->req_layers.m_nelements; + ci.ppEnabledLayerNames = this->req_layers.m_pdata; + + VkResult err = VK_SUCCESS; + VK_ASSERT(vkCreateDevice(this->phys, &ci, this->ctx.vk_alloc, &logical), "Failed to create logical device\n\tAt %s:%d\n\tError %s\n"); + basalt::Device dev(this->ctx, this->phys, logical); + + u32 num_total_queues = 0; + for (u32 i = 0; i < this->req_queues.m_nelements; ++i) + num_total_queues += this->req_queues[i].queueCount; + dev.queues.reserve(num_total_queues); + + u32 running_total = 0; + for (u32 i = 0; i < this->req_queues.m_nelements; ++i) + for (u32 j = 0; j < this->req_queues[i].queueCount; ++j) + vkGetDeviceQueue(logical, this->req_queues[i].queueFamilyIndex, j, &dev.queues[running_total++].queue); + dev.queues.m_nelements = num_total_queues; + + return std::move(dev); +} + +basalt::Device::Device(const Device& src) : + ctx(src.ctx), logical(src.logical), phys(src.phys), queues(src.queues), should_free(false) +{} + +basalt::Device& basalt::Device::operator=(const Device & src) +{ + if (&src == this) + return *this; + if (src.logical != this->logical && this->logical != VK_NULL_HANDLE && this->should_free) + this->~Device(); + this->ctx = src.ctx; + this->logical = src.logical; + this->phys = src.phys; + this->queues = darray(src.queues); + this->should_free = false; + return *this; +} + +basalt::Device::Device(Device&& other) noexcept : + ctx(other.ctx), logical(other.logical), phys(other.phys), queues(std::move(other.queues)), should_free(other.should_free) +{ + other.ctx = nullptr; + other.logical = VK_NULL_HANDLE; + other.phys = nullptr; + other.should_free = false; +} + +basalt::Device& basalt::Device::operator=(Device&& other) noexcept +{ + if (&other == this) + return *this; + if (this->logical != other.logical && this->logical != VK_NULL_HANDLE && this->should_free) + this->~Device(); + + this->ctx = other.ctx; + this->logical = other.logical; + this->phys = other.phys; + this->should_free = other.should_free; + this->queues = std::move(other.queues); + + other.ctx = nullptr; + other.logical = VK_NULL_HANDLE; + other.phys = nullptr; + other.should_free = false; + + return *this; +} + +basalt::Device::Device(basalt::Context& ctx, basalt::PhysicalDevice& physical, VkDevice logical) : + ctx(&ctx), phys(&physical), logical(logical), queues(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), should_free(true) +{} + +basalt::Device::~Device() +{ + if (this->should_free && this->logical != VK_NULL_HANDLE) + vkDestroyDevice(this->logical, this->ctx->vk_alloc); +} + +basalt::Device::operator VkPhysicalDevice() +{ return this->phys->phys; } + +basalt::Device::operator VkDevice() +{ return this->logical; }