Created swapchain object.

Manages most of the present-related stuff.
Still need to move some queue submits inside of it though
This commit is contained in:
2025-07-07 22:54:19 +10:00
parent 425c226793
commit 0598a89d0c
2 changed files with 189 additions and 0 deletions

View File

@@ -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<VkSurfaceFormatKHR>& allowed_formats,
const std::initializer_list<VkPresentModeKHR> 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<VkSemaphore> semaphores_image_available;
basalt::darray<VkSemaphore> semaphores_render_finished;
basalt::darray<VkFence> fences_in_flight;
basalt::SwapchainSupportDetails details;
basalt::darray<VkImage> swapchain_images;
basalt::darray<VkImageView> swapchain_image_views;
basalt::darray<VkFramebuffer> 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;
};
}

View File

@@ -0,0 +1,149 @@
#include "vulkan/basalt_swapchain.h"
basalt::Swapchain::Swapchain(basalt::Device& device, basalt::Window& window,
const std::initializer_list<VkSurfaceFormatKHR>& allowed_formats,
const std::initializer_list<VkPresentModeKHR> 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;
}