KhronosGroup / Vulkan-Docs

The Vulkan API Specification and related tools
Other
2.77k stars 465 forks source link

Weird validation err when recreating swapchain with VkSwapchainPresentScalingCreateInfoEXT #2376

Closed Farouk123456 closed 4 months ago

Farouk123456 commented 4 months ago

[VK_EXT_swapchain_maintenance1] @syoussefi

Hello, I'm starting to learn Vulkan API as of a few months now. And since window resizing is so glitchy on xorg I tried VkSwapchainPresentScalingCreateInfoEXT, and it worked wonderfully until I decided that I also want to recreate the swap chain when the Window becomes too big/small and the scaling makes the image to pixelated — basically if the window size is bigger than 50 + original window size recreate the swap chain.

When I do this, I get the error : validation: Failed to initialize scaled swapchain images (-2)

It's also of Note that the error only occurs after a few swap chain recreations, making the issue more frustrating Im Vaguely following this guide https://docs.vulkan.org/tutorial/latest/00_Introduction.html although i used SDL2

Also if the pNext of VkSwapchainPresentScalingCreateInfoEXT isnt nullptr or something else it will segfault

Here's my function for recreating the swap chain:

VkSwapchainKHR createSwapChainFromOld(int width, int height, VkSwapchainKHR oldSwapchain)
    {
        SwapChainSupportDetails swapChainSupport;
        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &swapChainSupport.capabilities);

        uint32_t formatCount;
        vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);

        if (formatCount != 0)
        {
            swapChainSupport.formats.resize(formatCount);
            vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, swapChainSupport.formats.data());
        }

        uint32_t presentModeCount;
        vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr);

        if (presentModeCount != 0)
        {
            swapChainSupport.presentModes.resize(presentModeCount);
            vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, swapChainSupport.presentModes.data());
        }

        VkSurfaceFormatKHR surfaceFormat = swapChainSupport.formats[0];

        for (const auto &availableFormat : swapChainSupport.formats)
        {
            if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
            {
                surfaceFormat = availableFormat;
            }
        }

        VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
        for (const auto &availablePresentMode : swapChainSupport.presentModes)
        {
            if (availablePresentMode == ((DISPLAY_FPS_LOCK) ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR))
            {
                presentMode = availablePresentMode;
            }
        }

        VkExtent2D extent;

        if (swapChainSupport.capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
        {
            extent = swapChainSupport.capabilities.currentExtent;
        }
        else
        {
            VkExtent2D actualExtent = {
                static_cast<uint32_t>(width),
                static_cast<uint32_t>(height)
            };

            actualExtent.width = std::clamp(actualExtent.width, swapChainSupport.capabilities.minImageExtent.width, swapChainSupport.capabilities.maxImageExtent.width);
            actualExtent.height = std::clamp(actualExtent.height, swapChainSupport.capabilities.minImageExtent.height, swapChainSupport.capabilities.maxImageExtent.height);

            extent = actualExtent;
        }

        uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;

        if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
        {
            imageCount = swapChainSupport.capabilities.maxImageCount;
        }

        VkSwapchainCreateInfoKHR createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
        createInfo.surface = surface;
        createInfo.minImageCount = imageCount;
        createInfo.imageFormat = surfaceFormat.format;
        createInfo.imageColorSpace = surfaceFormat.colorSpace;
        createInfo.imageExtent = extent;
        createInfo.imageArrayLayers = 1;
        createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

        QueueFamilyIndices indices;

        uint32_t queueFamilyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);

        std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
        vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());

        int i = 0;
        for (const auto &queueFamily : queueFamilies)
        {
            if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
            {
                indices.graphicsFamily = i;
            }

            VkBool32 presentSupport = false;
            vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);

            if (presentSupport)
            {
                indices.presentFamily = i;
            }

            if (indices.isComplete())
                break;

            i++;
        }

        uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};

        if (indices.graphicsFamily != indices.presentFamily)
        {
            createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
            createInfo.queueFamilyIndexCount = 2;
            createInfo.pQueueFamilyIndices = queueFamilyIndices;
        }
        else
        {
            createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
            createInfo.queueFamilyIndexCount = 0;     // Optional
            createInfo.pQueueFamilyIndices = nullptr; // Optional
        }

        createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
        createInfo.presentMode = presentMode;
        createInfo.clipped = VK_TRUE;
        createInfo.oldSwapchain = oldSwapchain;

        VkSwapchainPresentScalingCreateInfoEXT cI;
        cI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT;
        cI.pNext = nullptr;
        cI.presentGravityX = VK_PRESENT_GRAVITY_CENTERED_BIT_EXT;
        cI.presentGravityY = VK_PRESENT_GRAVITY_CENTERED_BIT_EXT;
        cI.scalingBehavior = VK_PRESENT_SCALING_STRETCH_BIT_EXT;

        createInfo.pNext = &cI;

        VkSwapchainKHR retSwap;

        if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &retSwap) != VK_SUCCESS)
        {
            LOG_FATAL("failed to create swap chain!");
        }

        if (oldSwapchain != VK_NULL_HANDLE)
            vkDestroySwapchainKHR(device, oldSwapchain, nullptr);

        vkGetSwapchainImagesKHR(device, retSwap, &imageCount, nullptr);
        swapChainImages.resize(imageCount);

        vkGetSwapchainImagesKHR(device, retSwap, &imageCount, swapChainImages.data());
        swapChainImageFormat = surfaceFormat.format;
        swapChainExtent = extent;

        return retSwap;
    }

Any Help would be appreciated

Farouk123456 commented 4 months ago

The error also only triggers when resizing the window of the window is left alone the errors stop

Farouk123456 commented 4 months ago

The problem got solved by not passing the oldSwapChain parameter wich is not documented anywhere in the spec

oddhack commented 4 months ago

Also assigning @versalinyaa whom I think? is currently chairing the SI TSG.

ShabbyX commented 4 months ago

While I haven't read the entire code, I can see a bug with oldSwapchain right away. But first, a high level comment:

And since window resizing is so glitchy on xorg I tried VkSwapchainPresentScalingCreateInfoEXT, and it worked wonderfully until I decided that I also want to recreate the swap chain when the Window becomes too big/small and the scaling makes the image to pixelated — basically if the window size is bigger than 50 + original window size recreate the swap chain.

I strongly suggest not doing that. Scaling by even just one pixel will visibly affect the quality of the output (making it blurry). If you intend to recreate the swapchain, my suggestion is to do it on every window size change instead of mixing it with scaling.

And since window resizing is so glitchy on xorg

It's not glitchy at all if you get it right. I wrote this sample + doc to explain how one can deal with swapchain recreation with and without VK_EXT_swapchain_maintenance1, which would hopefully help you understand why this extension helps and also shed light on what you still need to take care of: https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/swapchain_recreation

(If you run that samples, you can verify that window resizing can be buttery smooth!)

In particular, you need to pay attention to the fact that the lifetime of the objects are managed by the application, which means it is an error to destroy objects while they are still in use by the GPU or the present engine. The bug I mentioned is this:

        if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &retSwap) != VK_SUCCESS)
        {
            LOG_FATAL("failed to create swap chain!");
        }

        if (oldSwapchain != VK_NULL_HANDLE)
            vkDestroySwapchainKHR(device, oldSwapchain, nullptr);

The application is not waiting for the present engine to be done with the old swapchain before destroying it.

I'm going to close this bug as there is nothing to fix in the spec about this. If you believe there is a validation bug, please file an issue with https://github.com/KhronosGroup/Vulkan-ValidationLayers. My suggestion is to closely study the Vulkan sample I linked to as dealing with the window system is quite tricky :)

Farouk123456 commented 4 months ago

@ShabbyX hi thanks for the info i implemented some of it (old swapchain issue) and it helped somewhat. im pretty sure it must be on SDL# or xorg becaus in wayland resizing is always smooth but in xorg resizing is only smooth when using window decorations when usin borderless mode its not smooth at all even though its running at around 15k fps

Farouk123456 commented 4 months ago

Could you also tell me what the validation error means i dont quite understand it

ShabbyX commented 3 months ago

Could you also tell me what the validation error means i dont quite understand it

Sorry, what validation error?

Farouk123456 commented 3 months ago

This one:

Failed to initialize scaled swapchain images (-2)

ShabbyX commented 3 months ago

I don't know why that says "validation: ..." is that your app's log? The error itself (VK_ERROR_OUT_OF_DEVICE_MEMORY == -2) is a driver error, nothing about validation layers here.

Your best option is to enable VVL (Vulkan Validation Layers) and make sure your program is error free before pursuing driver-returned failures. If the application is error-free (according to VVL), you could consider filing an issue with your vendor (mesa or nvidia for example).