inexorgame / vulkan-renderer

A new 3D game engine for Linux and Windows using C++20 and Vulkan API 1.3, in very early but ongoing development
https://inexor.org
MIT License
757 stars 33 forks source link

[swapchain] Fix destruction bug #523

Closed IAmNotHanni closed 1 year ago

IAmNotHanni commented 1 year ago

Closes #517

IAmNotHanni commented 1 year ago

Maybe I can fix this bug as well:

2023-03-06 09:31:19.788123 error 6766 [vulkan-renderer] Validation Error: [ VUID-VkFramebufferCreateInfo-flags-04533 ] Object 0: handle = 0x559c1038b590, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xfe6b2428 | vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #0 mip level 0 has width (1411) smaller than the corresponding framebuffer width (1426). The Vulkan spec states: If flags does not include VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each element of pAttachments that is used as an input, color, resolve, or depth/stencil attachment by renderPass must have been created with a VkImageCreateInfo::extent.width greater than or equal to width (https://vulkan.lunarg.com/doc/view/1.3.239.0/linux/1.3-extensions/vkspec.html#VUID-VkFramebufferCreateInfo-flags-04533)

IAmNotHanni commented 1 year ago

I fixed the swapchain bug! :partying_face: So on Linux, the following is probably happening if you resize the window very fast:

1) The window is resized 2) The framebuffer resize callback is called 3) Swapchain recreation code VulkanRenderer::recreate_swapchain() is called 4) Window is resized again, but framebuffer resize callback isn't polled yet 5) Swapchain is recreated with the old and incorrect framebuffer size

So I changed the code to this:

void VulkanRenderer::recreate_swapchain() {
    m_window->wait_for_focus();
    m_device->wait_idle();

    // Query the framebuffer size here again although the window width is set during framebuffer resize callback
    // The reason for this is that the framebuffer size could already be different again because we missed a poll
    // This seems to be an issue on Linux only though
    int window_width = 0;
    int window_height = 0;
    glfwGetFramebufferSize(m_window->get(), &window_width, &window_height);

    m_render_graph.reset();
    m_swapchain->setup_swapchain(window_width, window_height, m_vsync_enabled);
IAmNotHanni commented 1 year ago

What really confuses me a little though is that Window::wait_for_idle should already do this?

void Window::wait_for_focus() {
    int current_width = 0;
    int current_height = 0;

    do {
        glfwWaitEvents();
        glfwGetFramebufferSize(m_window, &current_width, &current_height);
    } while (current_width == 0 || current_height == 0);

    m_width = current_width;
    m_height = current_height;
}