From 41336fb9d421868fcc1b56f39232a665cabd2ef7 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sat, 1 Feb 2025 23:48:01 +0100 Subject: Swapchain creation --- hk21/game/gpu.cpp | 216 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 43 deletions(-) (limited to 'hk21/game') diff --git a/hk21/game/gpu.cpp b/hk21/game/gpu.cpp index 9122f83..60aa6c2 100644 --- a/hk21/game/gpu.cpp +++ b/hk21/game/gpu.cpp @@ -1,8 +1,9 @@ #include "hk21/game/gpu.hpp" #include -#include #include +#include +#include #include @@ -70,47 +71,6 @@ namespace gpu { -class GpuImpl : public Gpu -{ - bool m_destroyed{}; - - VkInstance m_instance; - VkSurfaceKHR m_surface; - uint32_t m_queue_family_index; - VkDevice m_device; - VkQueue m_queue; - -public: - 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); - - ~GpuImpl() override - { - ASL_ASSERT(m_destroyed); - } - - void destroy() override - { - 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 @@ -220,6 +180,7 @@ static asl::status_or find_physical_device(VkInstance instan VkPhysicalDeviceProperties prps; vkGetPhysicalDeviceProperties(physical_device, &prps); + // @Todo Add from_zstr to asl::string_view asl::string_view name{prps.deviceName, asl::strlen(prps.deviceName)}; if (prps.apiVersion < kTargetVersion) @@ -294,6 +255,166 @@ static asl::status_or create_device(VkPhysicalDevice physical_device, return device; } +class GpuImpl : public Gpu +{ + bool m_destroyed{}; + + VkInstance m_instance; + VkPhysicalDevice m_physical_device; + VkSurfaceKHR m_surface; + uint32_t m_queue_family_index; + VkDevice m_device; + VkQueue m_queue; + + asl::option m_swapchain; + asl::buffer m_swapchain_images; + +public: + GpuImpl( + VkInstance instance, + VkPhysicalDevice physical_device, + VkSurfaceKHR surface, + uint32_t queue_family_index, + VkDevice device, + VkQueue queue) + : m_instance{instance} + , m_physical_device{physical_device} + , m_surface{surface} + , m_queue_family_index{queue_family_index} + , m_device{device} + , m_queue{queue} + { + } + + ASL_DELETE_COPY_MOVE(GpuImpl); + + ~GpuImpl() override + { + ASL_ASSERT(m_destroyed); + } + + void destroy() override + { + ASL_ASSERT(!m_destroyed); + m_destroyed = true; + + if (m_swapchain.has_value()) + { + vkDestroySwapchainKHR(m_device, m_swapchain.value(), VK_ALLOCATOR); + } + + vkDestroyDevice(m_device, VK_ALLOCATOR); + vkDestroySurfaceKHR(m_instance, m_surface, VK_ALLOCATOR); + vkDestroyInstance(m_instance, VK_ALLOCATOR); + } + + asl::status create_swapchain(uint32_t width, uint32_t height) + { + uint32_t count{}; + VkSurfaceCapabilitiesKHR caps{}; + VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physical_device, m_surface, &caps); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't retrieve Vulkan surface capabitilies: {}", res); + } + + asl::buffer formats; + vkGetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, m_surface, &count, nullptr); + formats.resize_zero(count); + res = vkGetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, m_surface, &count, formats.data()); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't retrieve Vulkan surface formats: {}", res); + } + + asl::buffer present_modes; + vkGetPhysicalDeviceSurfacePresentModesKHR(m_physical_device, m_surface, &count, nullptr); + present_modes.resize_zero(count); + res = vkGetPhysicalDeviceSurfacePresentModesKHR(m_physical_device, m_surface, &count, present_modes.data()); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't retrieve Vulkan surface present modes: {}", res); + } + + asl::option format; + for (const auto& f: formats) + { + if (f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR && + (f.format == VK_FORMAT_B8G8R8_UNORM || + f.format == VK_FORMAT_B8G8R8A8_UNORM || + f.format == VK_FORMAT_R8G8B8_UNORM || + f.format == VK_FORMAT_R8G8B8A8_UNORM)) + { + format = f; + break; + } + } + + if (!format.has_value()) + { + return asl::runtime_error("Couldn't find suitable Vulkan surface format"); + } + + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; + for (const auto& p: present_modes) + { + if (p == VK_PRESENT_MODE_MAILBOX_KHR) + { + present_mode = p; + break; + } + } + + VkSwapchainCreateInfoKHR create_info{ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = m_surface, + .minImageCount = asl::min(asl::max(2U, caps.minImageCount), caps.maxImageCount), + .imageFormat = format.value().format, + .imageColorSpace = format.value().colorSpace, + .imageExtent = { + .width = asl::min(asl::max(width, caps.minImageExtent.width), caps.maxImageExtent.width), + .height = asl::min(asl::max(height, caps.minImageExtent.height), caps.maxImageExtent.height), + }, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = &m_queue_family_index, + .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = present_mode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + VkSwapchainKHR swapchain{}; + res = vkCreateSwapchainKHR(m_device, &create_info, VK_ALLOCATOR, &swapchain); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't create Vulkan swapchain: {}", res); + } + + m_swapchain = swapchain; + + vkGetSwapchainImagesKHR(m_device, m_swapchain.value(), &count, nullptr); + m_swapchain_images.resize_zero(count); + res = vkGetSwapchainImagesKHR(m_device, m_swapchain.value(), &count, m_swapchain_images.data()); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't retrieve Vulkan swapchain images: {}", res); + } + + ASL_LOG_INFO("Vulkan swapchain created ({}x{} with {} images)", + create_info.imageExtent.width, + create_info.imageExtent.height, + m_swapchain_images.size()); + + return asl::ok(); + } +}; + asl::status_or> init(SDL_Window* window) { auto instance = create_instance(); @@ -313,12 +434,21 @@ asl::status_or> init(SDL_Window* window) VkQueue queue{}; vkGetDeviceQueue(device.value(), physical_device_info.value().queue_family_index, 0, &queue); - return asl::make_box( + auto gpu = asl::make_box( instance.value(), + physical_device_info.value().physical_device, surface.value(), physical_device_info.value().queue_family_index, device.value(), queue); + + int window_width{}; + int window_height{}; + SDL_GetWindowSizeInPixels(window, &window_width, &window_height); + auto s = gpu->create_swapchain(static_cast(window_width), static_cast(window_height)); + ASL_TRY(s); + + return gpu; }; } // namespace gpu -- cgit