From 0598a89d0c5dc233ebb448652ad435592c8c6d2c Mon Sep 17 00:00:00 2001 From: Riley King-Saunders Date: Mon, 7 Jul 2025 22:54:19 +1000 Subject: [PATCH] Created swapchain object. Manages most of the present-related stuff. Still need to move some queue submits inside of it though --- include/vulkan/basalt_swapchain.h | 40 ++++++++ src/vulkan/basalt_swapchain.cpp | 149 ++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 include/vulkan/basalt_swapchain.h create mode 100644 src/vulkan/basalt_swapchain.cpp diff --git a/include/vulkan/basalt_swapchain.h b/include/vulkan/basalt_swapchain.h new file mode 100644 index 0000000..26e6b83 --- /dev/null +++ b/include/vulkan/basalt_swapchain.h @@ -0,0 +1,40 @@ +#pragma once +#include "vulkan/basalt_device.h" + + +namespace basalt +{ + class Swapchain + { + public: + Swapchain(const Swapchain& src) = delete; + Swapchain& operator =(const Swapchain& src) = delete; + Swapchain(Swapchain&& src) = delete; + Swapchain& operator =(Swapchain&& src) = delete; + + + Swapchain(basalt::Device& device, basalt::Window& window, + const std::initializer_list& allowed_formats, + const std::initializer_list allowed_present_modes); + ~Swapchain(); + + void create_framebuffers(VkRenderPass render_pass); + void wait_and_reset(bool wait_all=VK_TRUE, u64 timeout=UINT64_MAX); + u32 acquire_next_image(VkFence fence=VK_NULL_HANDLE, u64 timeout=UINT64_MAX); + + basalt::darray semaphores_image_available; + basalt::darray semaphores_render_finished; + basalt::darray fences_in_flight; + basalt::SwapchainSupportDetails details; + basalt::darray swapchain_images; + basalt::darray swapchain_image_views; + basalt::darray framebuffers; + basalt::Device* device = nullptr; + basalt::Window* window = nullptr; + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + VkExtent2D swapchain_extent; + u32 image_count = -1; + u32 max_frames_in_flight = 2; + u32 current_frame = 0; + }; +} \ No newline at end of file diff --git a/src/vulkan/basalt_swapchain.cpp b/src/vulkan/basalt_swapchain.cpp new file mode 100644 index 0000000..d04d048 --- /dev/null +++ b/src/vulkan/basalt_swapchain.cpp @@ -0,0 +1,149 @@ +#include "vulkan/basalt_swapchain.h" + +basalt::Swapchain::Swapchain(basalt::Device& device, basalt::Window& window, + const std::initializer_list& allowed_formats, + const std::initializer_list allowed_present_modes) : + details(device, window), device(&device), window(&window), + swapchain_images(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + swapchain_image_views(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + framebuffers(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + semaphores_image_available(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + semaphores_render_finished(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), + fences_in_flight(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY) +{ + this->image_count = details.surface_capabilities.minImageCount + 1; + BCLAMP(this->image_count, details.surface_capabilities.minImageCount, details.surface_capabilities.maxImageCount); + if (this->image_count == 0) + this->image_count = details.surface_capabilities.minImageCount + 1; + + VkSwapchainCreateInfoKHR ci = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; + ci.surface = window; + ci.minImageCount = this->image_count; + ci.imageFormat = details.get_surface_format(allowed_formats).format; + ci.imageColorSpace = details.get_surface_format(allowed_formats).colorSpace; + ci.imageExtent = details.get_framebuffer_extent(window); + ci.imageArrayLayers = 1; + ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + ci.presentMode = details.get_present_mode(allowed_present_modes); + ci.clipped = VK_TRUE; + ci.oldSwapchain = VK_NULL_HANDLE; + + ci.preTransform = details.surface_capabilities.currentTransform; + ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + u32 queue_family_indicies[] = { device.phys->indicies.graphics, device.phys->indicies.present }; + if (device.phys->indicies.graphics == device.phys->indicies.present) + { + ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + ci.queueFamilyIndexCount = 0; + ci.pQueueFamilyIndices = 0; + } + else + { + ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + ci.queueFamilyIndexCount = sizeof(queue_family_indicies)/sizeof(queue_family_indicies[0]); + ci.pQueueFamilyIndices = queue_family_indicies; + } + + VkResult err = VK_SUCCESS; + this->swapchain_extent = ci.imageExtent; + VK_ASSERT(vkCreateSwapchainKHR(device, &ci, device.ctx->vk_alloc, &this->swapchain), "Failed to create swapchain\n\tAt %s:%d\n\tError %s\n"); + + u32 n_images = 0; + vkGetSwapchainImagesKHR(device, this->swapchain, &n_images, nullptr); + this->swapchain_images.resize(n_images); + vkGetSwapchainImagesKHR(device, this->swapchain, &n_images, this->swapchain_images.m_pdata); + this->swapchain_images.resize(n_images); + + this->swapchain_image_views.reserve(n_images); + for (u32 i = 0; i < n_images; ++i) + { + VkImageViewCreateInfo view_ci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + view_ci.image = swapchain_images[i]; + view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_ci.format = this->details.cached_format.format; + view_ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_ci.subresourceRange.baseMipLevel = 0; + view_ci.subresourceRange.levelCount = 1; + view_ci.subresourceRange.baseArrayLayer = 0; + view_ci.subresourceRange.layerCount = 1; + + VK_ASSERT(vkCreateImageView(device, &view_ci, device.ctx->vk_alloc, &swapchain_image_views[i]), "Failed to create image view\n\tAt %s:%d\n\tError %s\n\tImage index: %u\n", i); + } + swapchain_image_views.m_nelements = n_images; + + VkSemaphoreCreateInfo semaphore_ci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + VkFenceCreateInfo fence_ci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; + fence_ci.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + semaphores_image_available.resize(max_frames_in_flight); + fences_in_flight.resize(max_frames_in_flight); + for (u32 i = 0; i < max_frames_in_flight; ++i) + { + VK_ASSERT(vkCreateSemaphore(device, &semaphore_ci, device.ctx->vk_alloc, semaphores_image_available.m_pdata+i), "Failed to create image available semaphore\n\tAt %s:%d\n\tError %s\n"); + VK_ASSERT(vkCreateFence(device, &fence_ci, device.ctx->vk_alloc, fences_in_flight.m_pdata+i), "Failed to create in-flight fence\n\tAt %s:%d\n\tError %s\n"); + } + semaphores_render_finished.resize(image_count); + for (u32 i = 0; i < image_count; ++i) + VK_ASSERT(vkCreateSemaphore(device, &semaphore_ci, device.ctx->vk_alloc, semaphores_render_finished.m_pdata+i), "Failed to create render finished semaphore\n\tAt %s:%d\n\tError %s\n"); +} + +basalt::Swapchain::~Swapchain() +{ + vkDeviceWaitIdle(device->logical); + + for (u32 i = 0; i < max_frames_in_flight; ++i) + { + vkDestroySemaphore(device->logical, semaphores_image_available.m_pdata[i], device->ctx->vk_alloc); + vkDestroyFence(device->logical, fences_in_flight.m_pdata[i], device->ctx->vk_alloc); + } + for (u32 i = 0; i < image_count; ++i) + vkDestroySemaphore(device->logical, semaphores_render_finished.m_pdata[i], device->ctx->vk_alloc); + + for (VkFramebuffer& buffer : framebuffers) + vkDestroyFramebuffer(*device, buffer, device->ctx->vk_alloc); + for (VkImageView& view : this->swapchain_image_views) + vkDestroyImageView(*this->device, view, this->device->ctx->vk_alloc); + vkDestroySwapchainKHR(*device, this->swapchain, device->ctx->vk_alloc); +} + +void basalt::Swapchain::create_framebuffers(VkRenderPass render_pass) +{ + if (framebuffers.m_nelements != 0) + { + for (VkFramebuffer& buffer : framebuffers) + vkDestroyFramebuffer(*device, buffer, device->ctx->vk_alloc); + } + VkResult err = VK_SUCCESS; + framebuffers.reserve(swapchain_image_views.m_nelements); + for (size_t i = 0; i < swapchain_image_views.m_nelements; ++i) + { + VkFramebufferCreateInfo ci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; + ci.pNext = VK_NULL_HANDLE; + ci.flags = 0; + ci.renderPass = render_pass; + ci.attachmentCount = 1; + ci.pAttachments = swapchain_image_views.m_pdata + i; + ci.width = swapchain_extent.width; + ci.height = swapchain_extent.height; + ci.layers = 1; + VK_ASSERT(vkCreateFramebuffer(*device, &ci, device->ctx->vk_alloc, framebuffers.m_pdata+i), "Failed to create framebuffer\n\tAt %s:%d\n\tError %s\n\tIndex: %llu\n", i); + } + framebuffers.m_nelements = swapchain_image_views.m_nelements; +} + +void basalt::Swapchain::wait_and_reset(bool wait_all, u64 timeout) +{ + vkWaitForFences(device->logical, 1, &fences_in_flight.m_pdata[current_frame], wait_all, timeout); + vkResetFences(device->logical, 1, &fences_in_flight.m_pdata[current_frame]); +} + +u32 basalt::Swapchain::acquire_next_image(VkFence fence, u64 timeout) +{ + u32 img = -1; + vkAcquireNextImageKHR(device->logical, swapchain, timeout, semaphores_image_available[current_frame], fence, &img); + return img; +}