From 8279acc1a4754bedaad0ba8bf00541c7b2a043e9 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic <steven.lerouzic@gmail.com> Date: Wed, 12 Jun 2024 23:57:24 +0200 Subject: Swapchain creation --- deimos/core/allocator.h | 15 ++++ deimos/core/base.h | 14 +++- deimos/render/backend.h | 6 ++ deimos/vulkan/vulkan_backend.cpp | 109 ++++++++++++++++++++++++++++ deimos/vulkan/vulkan_device_functions.inc | 9 ++- deimos/vulkan/vulkan_instance_functions.inc | 3 + 6 files changed, 153 insertions(+), 3 deletions(-) (limited to 'deimos') diff --git a/deimos/core/allocator.h b/deimos/core/allocator.h index 9e8dd87..f6adea2 100644 --- a/deimos/core/allocator.h +++ b/deimos/core/allocator.h @@ -143,6 +143,21 @@ public: std::forward<A5>(arg5)); } + template<typename T, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> + constexpr gsl::owner<T*> New( + A0&& arg0, A1&& arg1, A2&& arg2, A3&& arg3, A4&& arg4, A5&& arg5, A6&& arg6, + const SourceLocation& source_location = {}) + { + return NewInner<T>(source_location, + std::forward<A0>(arg0), + std::forward<A1>(arg1), + std::forward<A2>(arg2), + std::forward<A3>(arg3), + std::forward<A4>(arg4), + std::forward<A5>(arg5), + std::forward<A6>(arg6)); + } + template<typename T> void Delete(gsl::owner<T*> t, const SourceLocation& source_location = {}) { diff --git a/deimos/core/base.h b/deimos/core/base.h index 0c15ccd..4c74b33 100644 --- a/deimos/core/base.h +++ b/deimos/core/base.h @@ -64,8 +64,9 @@ struct SourceLocation {} }; -template<typename T> T Min(T a, T b) { return (a < b) ? a : b; } -template<typename T> T Max(T a, T b) { return (a > b) ? a : b; } +template<typename T> constexpr T Min(T a, T b) { return (a < b) ? a : b; } +template<typename T> constexpr T Max(T a, T b) { return (a > b) ? a : b; } +template<typename T> constexpr T Clamp(T x, T min, T max) { return Min(Max(x, min), max); } [[maybe_unused]] static constexpr int64_t Kilobytes = 1024; [[maybe_unused]] static constexpr int64_t Megabytes = 1024 * 1024; @@ -151,6 +152,15 @@ public: Expects(offset + size <= m_size); return Span(m_begin + offset, size); } + + bool Contains(const T& v) const + { + for (const T& p: *this) + { + if (p == v) { return true; } + } + return false; + } }; template<typename T> diff --git a/deimos/render/backend.h b/deimos/render/backend.h index cba8d90..bb94cb9 100644 --- a/deimos/render/backend.h +++ b/deimos/render/backend.h @@ -1,10 +1,13 @@ #pragma once #include <deimos/core/base.h> +#include <deimos/core/status.h> namespace deimos { +struct RenderSwapchain{}; + class IRenderBackend { public: @@ -14,6 +17,9 @@ public: virtual void BeginFrame() = 0; virtual void EndFrame() = 0; + + virtual StatusOr<gsl::owner<RenderSwapchain*>> CreateSwapchain() = 0; + virtual void DestroySwapchain(gsl::owner<RenderSwapchain*>) = 0; }; } // namespace deimos diff --git a/deimos/vulkan/vulkan_backend.cpp b/deimos/vulkan/vulkan_backend.cpp index 87e4451..cc54d4d 100644 --- a/deimos/vulkan/vulkan_backend.cpp +++ b/deimos/vulkan/vulkan_backend.cpp @@ -22,8 +22,14 @@ VulkanLoaderApi* vulkan_loader_api; const VkAllocationCallbacks* kVkAlloc = nullptr; +struct VulkanSwapchain: public RenderSwapchain +{ + VkSwapchainKHR swapchain{}; +}; + class VulkanBackendImpl : public IVulkanBackend, public IRenderBackend { + Allocator* m_allocator; VulkanApi* m_vk; VkInstance m_instance; VkPhysicalDevice m_physical_device; @@ -41,12 +47,14 @@ class VulkanBackendImpl : public IVulkanBackend, public IRenderBackend public: VulkanBackendImpl( + Allocator* allocator, VulkanApi* vk, VkInstance instance, VkPhysicalDevice physical_device, uint32_t queue_family, VkSurfaceKHR surface, VkDevice device) : + m_allocator{allocator}, m_vk{vk}, m_instance{instance}, m_physical_device{physical_device}, m_queue_family{queue_family}, m_surface{surface}, m_device{device} { @@ -80,6 +88,106 @@ public: m_vk->DeviceWaitIdle(m_device); m_vk->DestroyCommandPool(m_device, m_frame_resources.cmd_pool, kVkAlloc); } + + StatusOr<gsl::owner<RenderSwapchain*>> CreateSwapchain() override + { + auto temp_alloc = temp_api->Acquire(); + + VkSurfaceCapabilitiesKHR caps; + VkResult res = m_vk->GetPhysicalDeviceSurfaceCapabilitiesKHR(m_physical_device, m_surface, &caps); + if (res != VK_SUCCESS) + { + return RuntimeError("Couldn't retrieve Vulkan surface capabitilies"); + } + + uint32_t format_count = 0; + uint32_t present_mode_count = 0; + + m_vk->GetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, m_surface, &format_count, nullptr); + m_vk->GetPhysicalDeviceSurfacePresentModesKHR(m_physical_device, m_surface, &present_mode_count, nullptr); + + auto formats = temp_alloc.allocator().NewArray<VkSurfaceFormatKHR>(format_count); + auto present_modes = temp_alloc.allocator().NewArray<VkPresentModeKHR>(present_mode_count); + + m_vk->GetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, m_surface, &format_count, formats.data()); + m_vk->GetPhysicalDeviceSurfacePresentModesKHR(m_physical_device, m_surface, &present_mode_count, present_modes.data()); + + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; + if (present_modes.Contains(VK_PRESENT_MODE_MAILBOX_KHR)) + { + present_mode = VK_PRESENT_MODE_MAILBOX_KHR; + } + + static constexpr VkFormat kAcceptableFormats[] = { + VK_FORMAT_R8G8B8_SRGB, + VK_FORMAT_B8G8R8_SRGB, + VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_B8G8R8A8_SRGB, + }; + + bool has_format = false; + VkSurfaceFormatKHR format; + for (const auto& candidate: kAcceptableFormats) + { + for (const auto& f: formats) + { + if (f.format == candidate && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + { + has_format = true; + format = f; + break; + } + } + + if (has_format) { break; } + } + + + if (!has_format) + { + return RuntimeError("No acceptable Vulkan swapchain format found"); + } + + VkSwapchainCreateInfoKHR create_info = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = m_surface, + .minImageCount = Clamp<uint32_t>(2, caps.minImageCount, caps.maxImageCount), + .imageFormat = format.format, + .imageColorSpace = format.colorSpace, + .imageExtent = caps.currentExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .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 vk_swapchain{}; + res = m_vk->CreateSwapchainKHR(m_device, &create_info, kVkAlloc, &vk_swapchain); + if (res != VK_SUCCESS) + { + return RuntimeError("Error when creating Vulkan swapchain"); + } + + auto* swapchain = m_allocator->New<VulkanSwapchain>(); + swapchain->swapchain = vk_swapchain; + + return swapchain; + } + + void DestroySwapchain(gsl::owner<RenderSwapchain*> swapchain) override + { + auto* vk_swapchain = (VulkanSwapchain*)swapchain; + m_vk->DestroySwapchainKHR(m_device, vk_swapchain->swapchain, kVkAlloc); + m_allocator->Delete(swapchain); + } }; StatusOr<VkInstance> CreateInstance(VulkanApi* vk) @@ -315,6 +423,7 @@ StatusOr<gsl::owner<IVulkanBackend*>> InitializeVulkan(Allocator* allocator, OsW vulkan_loader_api->LoadDevice(vk, device); return allocator->New<VulkanBackendImpl>( + allocator, vk, instance, physical_device, queue_family, surface, device); } diff --git a/deimos/vulkan/vulkan_device_functions.inc b/deimos/vulkan/vulkan_device_functions.inc index f83ebb7..f6e6a30 100644 --- a/deimos/vulkan/vulkan_device_functions.inc +++ b/deimos/vulkan/vulkan_device_functions.inc @@ -1,8 +1,15 @@ // NOLINTBEGIN FN(DestroyDevice) +FN(DeviceWaitIdle) + FN(GetDeviceQueue) FN(CreateCommandPool) FN(ResetCommandPool) FN(DestroyCommandPool) -FN(DeviceWaitIdle) + +FN(CreateSwapchainKHR) +FN(DestroySwapchainKHR) +FN(GetSwapchainImagesKHR) +FN(AcquireNextImageKHR) +FN(QueuePresentKHR) // NOLINTEND diff --git a/deimos/vulkan/vulkan_instance_functions.inc b/deimos/vulkan/vulkan_instance_functions.inc index 0a46455..89ace12 100644 --- a/deimos/vulkan/vulkan_instance_functions.inc +++ b/deimos/vulkan/vulkan_instance_functions.inc @@ -10,4 +10,7 @@ FN(GetDeviceProcAddr) FN(CreateWin32SurfaceKHR) FN(DestroySurfaceKHR) FN(GetPhysicalDeviceSurfaceSupportKHR) +FN(GetPhysicalDeviceSurfaceCapabilitiesKHR) +FN(GetPhysicalDeviceSurfaceFormatsKHR) +FN(GetPhysicalDeviceSurfacePresentModesKHR) // NOLINTEND -- cgit