summaryrefslogtreecommitdiff
path: root/hk21/game/gpu.cpp
diff options
context:
space:
mode:
authorSteven Le Rouzic <steven.lerouzic@gmail.com>2025-02-01 23:48:01 +0100
committerSteven Le Rouzic <steven.lerouzic@gmail.com>2025-02-01 23:48:01 +0100
commit41336fb9d421868fcc1b56f39232a665cabd2ef7 (patch)
treed8a04c1b1f060095e07c248b86943d23c3ac7cdd /hk21/game/gpu.cpp
parent093d424eb48b2fae917547b48deb2e0a45efbed3 (diff)
Swapchain creation
Diffstat (limited to 'hk21/game/gpu.cpp')
-rw-r--r--hk21/game/gpu.cpp216
1 files changed, 173 insertions, 43 deletions
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 <asl/buffer.hpp>
-#include <asl/log/log.hpp>
#include <asl/format.hpp>
+#include <asl/option.hpp>
+#include <asl/log/log.hpp>
#include <SDL3/SDL_vulkan.h>
@@ -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<PhysicalDeviceInfo> 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<VkDevice> 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<VkSwapchainKHR> m_swapchain;
+ asl::buffer<VkImage> 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<VkSurfaceFormatKHR> 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<VkPresentModeKHR> 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<VkSurfaceFormatKHR> 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<asl::box<Gpu>> init(SDL_Window* window)
{
auto instance = create_instance();
@@ -313,12 +434,21 @@ asl::status_or<asl::box<Gpu>> init(SDL_Window* window)
VkQueue queue{};
vkGetDeviceQueue(device.value(), physical_device_info.value().queue_family_index, 0, &queue);
- return asl::make_box<GpuImpl>(
+ auto gpu = asl::make_box<GpuImpl>(
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<uint32_t>(window_width), static_cast<uint32_t>(window_height));
+ ASL_TRY(s);
+
+ return gpu;
};
} // namespace gpu