Vulkan交换链,范围为0

时间:2017-02-08 06:57:28

标签: qt vulkan

我在我的Vulkan应用程序中偶然发现了一个奇怪的行为(基于Qt,但它没关系)。

当我的Vulkan窗口调整大小时,我需要重新创建交换链。我使用vkGetPhysicalDeviceSurfaceCapabilitiesKHR()来获取有关窗口表面的新信息(未重新创建,可能会或可能不会出现问题)并接收新的表面范围:currentExtent = { 46, 0 } minImageExtent = { 1, 1 }。换句话说,Vulkan希望我创建一个0高度的交换链,同时1x1是允许的最小值。

假设驱动程序知道它正在做什么,我继续进行,之后我得到一个调试报告,说明某些内部“vkCreateImage失败”,之后Vulkan崩溃只留下死亡和破坏。怎么处理?我应该将currentExtent限制为min和max吗?或者我错过了什么?

编辑:

正如评论所示,我正在复制粘贴部分代码。我注意到的是,由于某些原因,操作系统实际上在某个时刻将窗口大小调整为46x0。

void QtWindow::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    scheduleSwapchainRecreation();
}

void QtWindow::scheduleSwapchainRecreation()
{
    std::lock_guard<std::mutex> lock{mSwapchainRecreationMutex};
    if (mSwapchainRecreationScheduled)
        return;

    mSwapchainRecreationScheduled = true;
    QMetaObject::invokeMethod(this, "recreateSwapchain", Qt::QueuedConnection);
}

void QtWindow::recreateSwapchain()
{
    std::lock_guard<std::mutex> lock{mStateMutex};

    checkVkResult(vkDeviceWaitIdle(mDevice));
    createSwapchain();

    // state mutex should also cover swapchain recreation
    mSwapchainRecreationScheduled = false;
}

void QtWindow::createSwapchain()
{
    mSwapchain = makeUnique<VulkanSwapchain>(mMemoryManager,
                                             width(),
                                             height(),
                                             mInstance,
                                             mPhysicalDevice,
                                             mDevice,
                                             mSurface,
                                             mPresentationQueueFamily,
                                             mPresentationQueue,
                                             mMemoryManager,
                                             mSwapchain.get());

    const auto min = mSwapchain->getMinExtent();
    const auto max = mSwapchain->getMaxExtent();

    setMinimumSize({ static_cast<int>(min.width), static_cast<int>(min.height) });
    setMaximumSize({ static_cast<int>(max.width), static_cast<int>(max.height) });
}

交换链类:

VulkanSwapchain::VulkanSwapchain(uint32_t width,
                                 uint32_t height,
                                 VkInstance instance,
                                 VkPhysicalDevice physicalDevice,
                                 const VulkanDevice &device,
                                 VkSurfaceKHR surface,
                                 uint32_t presentationQueueFamily,
                                 VkQueue presentationQueue,
                                 const MemoryManagerPtr &memoryManager,
                                 VulkanSwapchain *oldSwapchain)
    : mInstance{instance}
    , mDevice{device}
    , mSurface{surface}
    , mPresentQueue{presentationQueue}
    , mDetails{{},{{}, memoryManager},{{}, memoryManager}}
    , mSwapchainImages{memoryManager}
    , mSwapchainImageViews{memoryManager}
    , mMainCommandBuffers{memoryManager}
{
    createSwapchain(width, height, physicalDevice, presentationQueueFamily, memoryManager, oldSwapchain);
    createSwapchainImageViews();
    createSemaphores();
    createMainCommandPool();
}

VulkanSwapchain::~VulkanSwapchain()
{
    if (mMainCommandPool != VK_NULL_HANDLE)
        vkDestroyCommandPool(mDevice, mMainCommandPool, nullptr);

    vkDestroySemaphore(mDevice, mAcquireSemaphore, nullptr);

    for (const auto view : mSwapchainImageViews)
        vkDestroyImageView(mDevice, view, nullptr);

    if (mSwapchain != VK_NULL_HANDLE)
        vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr);

    if (mSurface != VK_NULL_HANDLE)
        vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
}

VkResult VulkanSwapchain::acquire() noexcept
{
    return vkAcquireNextImageKHR(mDevice, mSwapchain, UINT64_MAX, mAcquireSemaphore, VK_NULL_HANDLE, &mAcquiredImageIndex);
}

VkResult VulkanSwapchain::present(VkSemaphore renderingFinishedSemaphore) noexcept
{
    VkPresentInfoKHR presentInfo = {};
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = &renderingFinishedSemaphore;
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = &mSwapchain;
    presentInfo.pImageIndices = &mAcquiredImageIndex;

    return vkQueuePresentKHR(mPresentQueue, &presentInfo);
}

void VulkanSwapchain::createSwapchain(uint32_t width,
                                      uint32_t height,
                                      VkPhysicalDevice physicalDevice,
                                      uint32_t presentationQueueFamily,
                                      const MemoryManagerPtr &memoryManager,
                                      VulkanSwapchain *oldSwapchain)
{
    GE_LOG << L"Creating swapchain..." << std::endl;

    querySwapchainDetails(physicalDevice);
    chooseSwapchainSurfaceFormat();
    const auto chosenPresentMode = chooseSwapchainPresentMode();
    chooseSwapchainExtents(width, height);

    // triple or double buffering
    auto imageCount = (chosenPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) ? (3u) : (2u);
    if (imageCount < mDetails.mCapabilities.minImageCount)
        imageCount = mDetails.mCapabilities.minImageCount;
    else if (mDetails.mCapabilities.maxImageCount > 0 && imageCount > mDetails.mCapabilities.maxImageCount)
        imageCount = mDetails.mCapabilities.maxImageCount;

    GE_LOG << L"Chosen surface format: " << mSwapchainFormat.format << L' ' << mSwapchainFormat.colorSpace << std::endl;
    GE_LOG << L"Chosen present mode: " << chosenPresentMode << std::endl;
    GE_LOG << L"Chosen extents: " << mExtent.width << L'x' << mExtent.height << std::endl;
    GE_LOG << L"Image count: " << imageCount << std::endl;

    VkSwapchainCreateInfoKHR createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    createInfo.surface = mSurface;
    createInfo.minImageCount = imageCount;
    createInfo.imageFormat = mSwapchainFormat.format;
    createInfo.imageColorSpace = mSwapchainFormat.colorSpace;
    createInfo.imageExtent = mExtent;
    createInfo.imageArrayLayers = 1;
    createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    createInfo.preTransform = mDetails.mCapabilities.currentTransform;
    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    createInfo.presentMode = chosenPresentMode;
    createInfo.clipped = VK_TRUE;

    if (oldSwapchain != nullptr)
        createInfo.oldSwapchain = *oldSwapchain;

    const auto graphicsQueueFamily = mDevice.getGraphicsQueueFamily();
    if (graphicsQueueFamily != presentationQueueFamily)
    {
        uint32_t queueFamilyIndices[] = { graphicsQueueFamily, presentationQueueFamily };

        createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
        createInfo.queueFamilyIndexCount = 2;
        createInfo.pQueueFamilyIndices = queueFamilyIndices;
    }
    else
    {
        createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
    }

    checkVkResult(vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapchain));

    uint32_t swapchainImageCount = 0;
    checkVkResult(vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount, nullptr));

    mSwapchainImages.resize(swapchainImageCount);
    checkVkResult(vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount, mSwapchainImages.data()));
}

void VulkanSwapchain::querySwapchainDetails(VkPhysicalDevice physicalDevice)
{
    checkVkResult(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface, &mDetails.mCapabilities));

    uint32_t numSurfaceFormats = 0;
    checkVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &numSurfaceFormats, nullptr));

    if (numSurfaceFormats == 0)
        BOOST_THROW_EXCEPTION(RenderingModule::Exception{"Cannot find surface formats."});

    mDetails.mSurfaceFormats.resize(numSurfaceFormats);
    checkVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &numSurfaceFormats, mDetails.mSurfaceFormats.data()));

    uint32_t numPresentModes = 0;
    checkVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface, &numPresentModes, nullptr));

    if (numPresentModes == 0)
        BOOST_THROW_EXCEPTION(RenderingModule::Exception{"Cannot find presentation modes."});

    mDetails.mPresentModes.resize(numPresentModes);
    checkVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface, &numPresentModes, mDetails.mPresentModes.data()));
}

void VulkanSwapchain::chooseSwapchainExtents(uint32_t width, uint32_t height)
{
    if (mDetails.mCapabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
        mExtent.width = mDetails.mCapabilities.currentExtent.width;
    else
        mExtent.width = std::min(std::max(mDetails.mCapabilities.minImageExtent.width, width), mDetails.mCapabilities.maxImageExtent.width);

    if (mDetails.mCapabilities.currentExtent.height != std::numeric_limits<uint32_t>::max())
        mExtent.height = mDetails.mCapabilities.currentExtent.height;
    else
        mExtent.height = std::min(std::max(mDetails.mCapabilities.minImageExtent.height, height), mDetails.mCapabilities.maxImageExtent.height);
}

void VulkanSwapchain::chooseSwapchainSurfaceFormat()
{
    VkSurfaceFormatKHR desiredSurfaceFormat = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };

    if (mDetails.mSurfaceFormats.size() == 1 && mDetails.mSurfaceFormats[0].format == VK_FORMAT_UNDEFINED)
    {
        mSwapchainFormat = desiredSurfaceFormat;
    }
    else
    {
        auto found = false;
        for (const auto &format : mDetails.mSurfaceFormats)
        {
            if (format.format == desiredSurfaceFormat.format && format.colorSpace == desiredSurfaceFormat.colorSpace)
            {
                mSwapchainFormat = format;
                found = true;
                break;
            }
        }

        if (!found)
        {
            // try without color space
            for (const auto &format : mDetails.mSurfaceFormats)
            {
                if (format.format == desiredSurfaceFormat.format)
                {
                    mSwapchainFormat = format;
                    found = true;
                    break;
                }
            }

            if (!found)
                mSwapchainFormat = mDetails.mSurfaceFormats.front();
        }
    }
}

void VulkanSwapchain::createSwapchainImageViews()
{
    mSwapchainImageViews.resize(mSwapchainImages.size());

    VkImageViewCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    createInfo.format = mSwapchainFormat.format;
    createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
    createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
    createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
    createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
    createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    createInfo.subresourceRange.levelCount = 1;
    createInfo.subresourceRange.layerCount = 1;

    for (auto i = 0u; i < mSwapchainImages.size(); ++i)
    {
        createInfo.image = mSwapchainImages[i];
        checkVkResult(vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapchainImageViews[i]));
    }
}

void VulkanSwapchain::createSemaphores()
{
    VkSemaphoreCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;

    checkVkResult(vkCreateSemaphore(mDevice, &createInfo, nullptr, &mAcquireSemaphore));
}

void VulkanSwapchain::createMainCommandPool()
{
    VkCommandPoolCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
    createInfo.queueFamilyIndex = mDevice.getGraphicsQueueFamily();

    checkVkResult(vkCreateCommandPool(mDevice, &createInfo, nullptr, &mMainCommandPool));

    mMainCommandBuffers.resize(mSwapchainImageViews.size());

    VkCommandBufferAllocateInfo allocateInfo = {};
    allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocateInfo.commandPool = mMainCommandPool;
    allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocateInfo.commandBufferCount = static_cast<uint32_t>(mMainCommandBuffers.size());

    checkVkResult(vkAllocateCommandBuffers(mDevice, &allocateInfo, mMainCommandBuffers.data()));
}

VkPresentModeKHR VulkanSwapchain::chooseSwapchainPresentMode() const
{
    VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR;

    QSettings settings;
    if (!settings.value(swapIntervalSettingName, true).toBool())
    {
        for (const auto mode : mDetails.mPresentModes)
        {
            if (mode == VK_PRESENT_MODE_IMMEDIATE_KHR)
            {
                chosenPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
                break;
            }
        }
    }
#if (defined(Q_OS_WIN) && !defined(Q_OS_WINPHONE)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
    else if (settings.value(tripleBufferingSettingName, true).toBool())
    {
        // reduce latency on desktops, but preserve battery life on mobile
        for (const auto mode : mDetails.mPresentModes)
        {
            if (mode == VK_PRESENT_MODE_MAILBOX_KHR)
            {
                chosenPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
                break;
            }
        }
    }
#endif

    return chosenPresentMode;
}

0 个答案:

没有答案