From 093d424eb48b2fae917547b48deb2e0a45efbed3 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sat, 1 Feb 2025 00:05:19 +0100 Subject: Vulkan device creation --- hk21/game/gpu.cpp | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 8 deletions(-) (limited to 'hk21/game') diff --git a/hk21/game/gpu.cpp b/hk21/game/gpu.cpp index c18093f..9122f83 100644 --- a/hk21/game/gpu.cpp +++ b/hk21/game/gpu.cpp @@ -74,13 +74,24 @@ class GpuImpl : public Gpu { bool m_destroyed{}; - VkInstance m_instance; - VkSurfaceKHR m_surface; + VkInstance m_instance; + VkSurfaceKHR m_surface; + uint32_t m_queue_family_index; + VkDevice m_device; + VkQueue m_queue; public: - GpuImpl(VkInstance instance, VkSurfaceKHR surface) + GpuImpl( + VkInstance instance, + VkSurfaceKHR surface, + uint32_t queue_family_index, + VkDevice device, + VkQueue queue) : m_instance{instance} , m_surface{surface} + , m_queue_family_index{queue_family_index} + , m_device{device} + , m_queue{queue} {} ASL_DELETE_COPY_MOVE(GpuImpl); @@ -94,11 +105,16 @@ public: { ASL_ASSERT(!m_destroyed); m_destroyed = true; + vkDestroyDevice(m_device, VK_ALLOCATOR); vkDestroySurfaceKHR(m_instance, m_surface, VK_ALLOCATOR); vkDestroyInstance(m_instance, VK_ALLOCATOR); } }; +static uint32_t kTargetVersionMajor = 1; +static uint32_t kTargetVersionMinor = 3; +static uint32_t kTargetVersion = VK_MAKE_API_VERSION(0, kTargetVersionMajor, kTargetVersionMinor, 0); // NOLINT + static asl::status_or create_instance() { asl::buffer instance_extensions; @@ -127,7 +143,7 @@ static asl::status_or create_instance() uint32_t vk_major = VK_API_VERSION_MAJOR(version); // NOLINT uint32_t vk_minor = VK_API_VERSION_MINOR(version); // NOLINT - if (vk_major != 1 || vk_minor < 3) + if (vk_major != kTargetVersionMajor || vk_minor < kTargetVersionMinor) { return asl::runtime_error("Incompatible Vulkan version: {}.{}", vk_major, vk_minor); } @@ -139,7 +155,7 @@ static asl::status_or create_instance() .applicationVersion = 0, .pEngineName = "Custom", .engineVersion = 0, - .apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), // NOLINT + .apiVersion = kTargetVersion, }; VkInstanceCreateInfo instance_create_info{ @@ -163,6 +179,8 @@ static asl::status_or create_instance() status = vulkan_loader::load_instance(vkGetInstanceProcAddr, instance); ASL_TRY(status); + ASL_LOG_INFO("Vulkan instance created"); + return instance; } @@ -177,6 +195,105 @@ static asl::status_or create_surface(SDL_Window* window, VkInstanc return surface; } +struct PhysicalDeviceInfo +{ + VkPhysicalDevice physical_device; + uint32_t queue_family_index; +}; + +static asl::status_or find_physical_device(VkInstance instance) +{ + uint32_t count{}; + asl::buffer physical_devices; + asl::buffer queue_family_prps; + + vkEnumeratePhysicalDevices(instance, &count, nullptr); + physical_devices.resize_zero(count); + VkResult res = vkEnumeratePhysicalDevices(instance, &count, physical_devices.data()); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't enumerate Vulkan physical devices: {}", res); + } + + for (auto* physical_device: physical_devices) + { + VkPhysicalDeviceProperties prps; + vkGetPhysicalDeviceProperties(physical_device, &prps); + + asl::string_view name{prps.deviceName, asl::strlen(prps.deviceName)}; + + if (prps.apiVersion < kTargetVersion) + { + ASL_LOG_INFO("Device {}: invalid supported Vulkan API version", name); + continue; + } + + uint32_t queue_family_count{}; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr); + queue_family_prps.resize_zero(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_family_prps.data()); + + static constexpr VkQueueFlags kWantedQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT; + + for (uint32_t queue_family_index = 0; queue_family_index < queue_family_count; ++queue_family_index) + { + if ((queue_family_prps[queue_family_index].queueFlags & kWantedQueueFlags) != kWantedQueueFlags) { continue; } + if (!SDL_Vulkan_GetPresentationSupport(instance, physical_device, queue_family_index)) { continue; } + + ASL_LOG_INFO("Chosing device \"{}\" with queue family {}", name, queue_family_index); + return PhysicalDeviceInfo{ physical_device, queue_family_index }; + } + + ASL_LOG_INFO("Device {}: no valid queue found", name); + } + + return asl::runtime_error("Couldn't find a valid Vulkan physical device"); +} + +static asl::status_or create_device(VkPhysicalDevice physical_device, uint32_t queue_family_index) +{ + asl::buffer device_extensions; + device_extensions.push(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + const float queue_priority = 1.0F; + + VkDeviceQueueCreateInfo queue_create_info{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = queue_family_index, + .queueCount = 1, + .pQueuePriorities = &queue_priority, + }; + + VkDeviceCreateInfo create_info{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queue_create_info, + .enabledLayerCount = 0, + .ppEnabledLayerNames = nullptr, + .enabledExtensionCount = static_cast(device_extensions.size()), + .ppEnabledExtensionNames = device_extensions.data(), + .pEnabledFeatures = nullptr, + }; + + VkDevice device{}; + VkResult res = vkCreateDevice(physical_device, &create_info, VK_ALLOCATOR, &device); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't create Vulkan device: {}", res); + } + + ASL_LOG_INFO("Vulkan device created"); + + auto status = vulkan_loader::load_device(device); + ASL_TRY(status); + + return device; +} + asl::status_or> init(SDL_Window* window) { auto instance = create_instance(); @@ -185,10 +302,23 @@ asl::status_or> init(SDL_Window* window) auto surface = create_surface(window, instance.value()); ASL_TRY(surface); - // asl::buffer device_extensions; - // device_extensions.push(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + auto physical_device_info = find_physical_device(instance.value()); + ASL_TRY(physical_device_info); + + auto device = create_device( + physical_device_info.value().physical_device, + physical_device_info.value().queue_family_index); + ASL_TRY(device); + + VkQueue queue{}; + vkGetDeviceQueue(device.value(), physical_device_info.value().queue_family_index, 0, &queue); - return asl::make_box(instance.value(), surface.value()); + return asl::make_box( + instance.value(), + surface.value(), + physical_device_info.value().queue_family_index, + device.value(), + queue); }; } // namespace gpu -- cgit