Files
MacroLibX/runtime/Sources/Renderer/Swapchain.cpp
T
DaemoandGitHub 2d0c02ae7e Fix freeze when resizing a window (#239)
Fixed an annoying bug where the application would freeze when resizing
the window
This pull request made it so the SDL window's resizing event now
broadcasts a new event specifically asking the swapchain to recreate
itself, which will in turn broadcast the original event when done. It
also removed the Event enum specific values, as they were irrelevant.
2026-08-07 00:18:52 +02:00

260 lines
9.3 KiB
C++

#include <PreCompiled.h>
#include <Platform/Window.h>
#include <Renderer/Swapchain.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <Core/EventBus.h>
namespace mlx
{
namespace Internal
{
struct ResizeEventBroadcast : public EventBase
{
Event What() const override { return Event::ResizeEventCode; }
};
}
std::string VulkanFormatName(VkFormat format)
{
#define STRINGIFY(x) case x: return #x
switch(format)
{
STRINGIFY(VK_FORMAT_UNDEFINED);
STRINGIFY(VK_FORMAT_R4G4_UNORM_PACK8);
STRINGIFY(VK_FORMAT_R4G4B4A4_UNORM_PACK16);
STRINGIFY(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
STRINGIFY(VK_FORMAT_R5G6B5_UNORM_PACK16);
STRINGIFY(VK_FORMAT_B5G6R5_UNORM_PACK16);
STRINGIFY(VK_FORMAT_R5G5B5A1_UNORM_PACK16);
STRINGIFY(VK_FORMAT_B5G5R5A1_UNORM_PACK16);
STRINGIFY(VK_FORMAT_A1R5G5B5_UNORM_PACK16);
STRINGIFY(VK_FORMAT_R8_UNORM);
STRINGIFY(VK_FORMAT_R8_SNORM);
STRINGIFY(VK_FORMAT_R8_USCALED);
STRINGIFY(VK_FORMAT_R8_SSCALED);
STRINGIFY(VK_FORMAT_R8_UINT);
STRINGIFY(VK_FORMAT_R8_SINT);
STRINGIFY(VK_FORMAT_R8_SRGB);
STRINGIFY(VK_FORMAT_R8G8_UNORM);
STRINGIFY(VK_FORMAT_R8G8_SNORM);
STRINGIFY(VK_FORMAT_R8G8_USCALED);
STRINGIFY(VK_FORMAT_R8G8_SSCALED);
STRINGIFY(VK_FORMAT_R8G8_UINT);
STRINGIFY(VK_FORMAT_R8G8_SINT);
STRINGIFY(VK_FORMAT_R8G8_SRGB);
STRINGIFY(VK_FORMAT_R8G8B8_UNORM);
STRINGIFY(VK_FORMAT_R8G8B8_SNORM);
STRINGIFY(VK_FORMAT_R8G8B8_USCALED);
STRINGIFY(VK_FORMAT_R8G8B8_SSCALED);
STRINGIFY(VK_FORMAT_R8G8B8_UINT);
STRINGIFY(VK_FORMAT_R8G8B8_SINT);
STRINGIFY(VK_FORMAT_R8G8B8_SRGB);
STRINGIFY(VK_FORMAT_B8G8R8_UNORM);
STRINGIFY(VK_FORMAT_B8G8R8_SNORM);
STRINGIFY(VK_FORMAT_B8G8R8_USCALED);
STRINGIFY(VK_FORMAT_B8G8R8_SSCALED);
STRINGIFY(VK_FORMAT_B8G8R8_UINT);
STRINGIFY(VK_FORMAT_B8G8R8_SINT);
STRINGIFY(VK_FORMAT_B8G8R8_SRGB);
STRINGIFY(VK_FORMAT_R8G8B8A8_UNORM);
STRINGIFY(VK_FORMAT_R8G8B8A8_SNORM);
STRINGIFY(VK_FORMAT_R8G8B8A8_USCALED);
STRINGIFY(VK_FORMAT_R8G8B8A8_SSCALED);
STRINGIFY(VK_FORMAT_R8G8B8A8_UINT);
STRINGIFY(VK_FORMAT_R8G8B8A8_SINT);
STRINGIFY(VK_FORMAT_R8G8B8A8_SRGB);
STRINGIFY(VK_FORMAT_B8G8R8A8_UNORM);
STRINGIFY(VK_FORMAT_B8G8R8A8_SNORM);
STRINGIFY(VK_FORMAT_B8G8R8A8_USCALED);
STRINGIFY(VK_FORMAT_B8G8R8A8_SSCALED);
STRINGIFY(VK_FORMAT_B8G8R8A8_UINT);
STRINGIFY(VK_FORMAT_B8G8R8A8_SINT);
STRINGIFY(VK_FORMAT_B8G8R8A8_SRGB);
STRINGIFY(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SNORM_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_USCALED_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SSCALED_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_UINT_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SINT_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SRGB_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_UNORM_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_SNORM_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_USCALED_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_SSCALED_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_UINT_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_SINT_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_SNORM_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_USCALED_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_SSCALED_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_UINT_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_SINT_PACK32);
STRINGIFY(VK_FORMAT_R16_UNORM);
STRINGIFY(VK_FORMAT_R16_SNORM);
STRINGIFY(VK_FORMAT_R16_USCALED);
STRINGIFY(VK_FORMAT_R16_SSCALED);
STRINGIFY(VK_FORMAT_R16_UINT);
STRINGIFY(VK_FORMAT_R16_SINT);
STRINGIFY(VK_FORMAT_R16_SFLOAT);
STRINGIFY(VK_FORMAT_R16G16_UNORM);
STRINGIFY(VK_FORMAT_R16G16_SNORM);
STRINGIFY(VK_FORMAT_R16G16_USCALED);
STRINGIFY(VK_FORMAT_R16G16_SSCALED);
STRINGIFY(VK_FORMAT_R16G16_UINT);
STRINGIFY(VK_FORMAT_R16G16_SINT);
STRINGIFY(VK_FORMAT_R16G16_SFLOAT);
STRINGIFY(VK_FORMAT_R16G16B16_UNORM);
STRINGIFY(VK_FORMAT_R16G16B16_SNORM);
STRINGIFY(VK_FORMAT_R16G16B16_USCALED);
STRINGIFY(VK_FORMAT_R16G16B16_SSCALED);
STRINGIFY(VK_FORMAT_R16G16B16_UINT);
STRINGIFY(VK_FORMAT_R16G16B16_SINT);
STRINGIFY(VK_FORMAT_R16G16B16_SFLOAT);
STRINGIFY(VK_FORMAT_R16G16B16A16_UNORM);
STRINGIFY(VK_FORMAT_R16G16B16A16_SNORM);
STRINGIFY(VK_FORMAT_R16G16B16A16_USCALED);
STRINGIFY(VK_FORMAT_R16G16B16A16_SSCALED);
STRINGIFY(VK_FORMAT_R16G16B16A16_UINT);
STRINGIFY(VK_FORMAT_R16G16B16A16_SINT);
STRINGIFY(VK_FORMAT_R16G16B16A16_SFLOAT);
STRINGIFY(VK_FORMAT_R32_UINT);
STRINGIFY(VK_FORMAT_R32_SINT);
STRINGIFY(VK_FORMAT_R32_SFLOAT);
STRINGIFY(VK_FORMAT_R32G32_UINT);
STRINGIFY(VK_FORMAT_R32G32_SINT);
STRINGIFY(VK_FORMAT_R32G32_SFLOAT);
STRINGIFY(VK_FORMAT_R32G32B32_UINT);
STRINGIFY(VK_FORMAT_R32G32B32_SINT);
STRINGIFY(VK_FORMAT_R32G32B32_SFLOAT);
STRINGIFY(VK_FORMAT_R32G32B32A32_UINT);
STRINGIFY(VK_FORMAT_R32G32B32A32_SINT);
STRINGIFY(VK_FORMAT_R32G32B32A32_SFLOAT);
STRINGIFY(VK_FORMAT_R64_UINT);
STRINGIFY(VK_FORMAT_R64_SINT);
STRINGIFY(VK_FORMAT_R64_SFLOAT);
STRINGIFY(VK_FORMAT_R64G64_UINT);
STRINGIFY(VK_FORMAT_R64G64_SINT);
STRINGIFY(VK_FORMAT_R64G64_SFLOAT);
STRINGIFY(VK_FORMAT_R64G64B64_UINT);
STRINGIFY(VK_FORMAT_R64G64B64_SINT);
STRINGIFY(VK_FORMAT_R64G64B64_SFLOAT);
STRINGIFY(VK_FORMAT_R64G64B64A64_UINT);
STRINGIFY(VK_FORMAT_R64G64B64A64_SINT);
STRINGIFY(VK_FORMAT_R64G64B64A64_SFLOAT);
STRINGIFY(VK_FORMAT_B10G11R11_UFLOAT_PACK32);
STRINGIFY(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32);
default: return "Unknown format";
}
#undef STRINGIFY
return "Unknown format"; // To avoid warnings
}
void Swapchain::Init(NonOwningPtr<Window> window)
{
p_window = window;
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::SwapchainResizeEventCode && !m_resize)
m_resize = true;
};
EventBus::RegisterListener({ functor, "mlx_swapchain_" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });
CreateSwapchain();
m_resize = false;
}
void Swapchain::AquireFrame(VkSemaphore signal)
{
if(m_resize)
{
RenderCore::Get().WaitDeviceIdle();
Destroy();
CreateSwapchain();
EventBus::SendBroadcast(Internal::ResizeEventBroadcast{});
m_resize = false;
}
VkResult result = RenderCore::Get().vkAcquireNextImageKHR(RenderCore::Get().GetDevice(), m_swapchain, UINT64_MAX, signal, VK_NULL_HANDLE, &m_current_image_index);
if(result == VK_SUBOPTIMAL_KHR)
m_resize = true; // Recreate Swapchain next time
else if(result == VK_ERROR_OUT_OF_DATE_KHR)
{
m_resize = true;
AquireFrame(signal);
}
else if(result != VK_SUCCESS)
FatalError("Vulkan: failed to acquire swapchain image, %", kvfVerbaliseVkResult(result));
}
void Swapchain::Present(VkSemaphore wait) noexcept
{
if(!kvfQueuePresentKHR(RenderCore::Get().GetDevice(), wait, m_swapchain, m_current_image_index))
m_resize = true;
}
void Swapchain::Destroy()
{
if(m_swapchain == VK_NULL_HANDLE)
return;
RenderCore::Get().WaitDeviceIdle();
for(Image& img : m_swapchain_images)
img.DestroyImageView();
m_swapchain_images.clear();
kvfDestroySwapchainKHR(RenderCore::Get().GetDevice(), m_swapchain);
m_swapchain = VK_NULL_HANDLE;
DebugLog("Vulkan: swapchain destroyed");
RenderCore::Get().vkDestroySurfaceKHR(RenderCore::Get().GetInstance(), m_surface, nullptr);
m_surface = VK_NULL_HANDLE;
DebugLog("Vulkan: surface destroyed");
}
void Swapchain::CreateSwapchain()
{
VkExtent2D extent;
do
{
Vec2ui size = p_window->GetVulkanDrawableSize();
extent = { size.x, size.y };
} while(extent.width == 0 || extent.height == 0);
m_surface = p_window->CreateVulkanSurface(RenderCore::Get().GetInstance());
DebugLog("Vulkan: surface created");
m_swapchain = kvfCreateSwapchainKHR(RenderCore::Get().GetDevice(), RenderCore::Get().GetPhysicalDevice(), m_surface, extent, m_swapchain, false, true);
m_images_count = kvfGetSwapchainImagesCount(m_swapchain);
m_min_images_count = kvfGetSwapchainMinImagesCount(m_swapchain);
std::vector<VkImage> tmp(m_images_count);
m_swapchain_images.resize(m_images_count);
RenderCore::Get().vkGetSwapchainImagesKHR(RenderCore::Get().GetDevice(), m_swapchain, &m_images_count, tmp.data());
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
extent = kvfGetSwapchainImagesSize(m_swapchain); // fix the extent
VkFormat format = kvfGetSwapchainImagesFormat(m_swapchain);
for(std::size_t i = 0; i < m_images_count; i++)
{
#ifdef DEBUG
m_swapchain_images[i].Init(tmp[i], format, extent.width, extent.height, VK_IMAGE_LAYOUT_UNDEFINED, "mlx_swapchain_image_" + std::to_string(i));
#else
m_swapchain_images[i].Init(tmp[i], format, extent.width, extent.height, VK_IMAGE_LAYOUT_UNDEFINED, {});
#endif
m_swapchain_images[i].TransitionLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, cmd);
m_swapchain_images[i].CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
}
kvfEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
DebugLog("Vulkan: swapchain created with format %", VulkanFormatName(format));
}
}