diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp index 91095b23ea8..4d9bbe21b59 100644 --- a/src/gui/vulkan/qvulkanwindow.cpp +++ b/src/gui/vulkan/qvulkanwindow.cpp @@ -1196,13 +1196,6 @@ void QVulkanWindowPrivate::recreateSwapChain() return; } - err = devFuncs->vkCreateFence(dev, &fenceInfo, nullptr, &image.cmdFence); - if (err != VK_SUCCESS) { - qWarning("QVulkanWindow: Failed to create command buffer fence: %d", err); - return; - } - image.cmdFenceWaitable = true; // fence was created in signaled state - VkImageView views[3] = { image.imageView, dsView, msaa ? image.msaaImageView : VK_NULL_HANDLE }; @@ -1270,13 +1263,17 @@ void QVulkanWindowPrivate::recreateSwapChain() frame.imageAcquired = false; frame.imageSemWaitable = false; - devFuncs->vkCreateFence(dev, &fenceInfo, nullptr, &frame.fence); - frame.fenceWaitable = true; // fence was created in signaled state - devFuncs->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem); devFuncs->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem); if (gfxQueueFamilyIdx != presQueueFamilyIdx) devFuncs->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.presTransSem); + + err = devFuncs->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence); + if (err != VK_SUCCESS) { + qWarning("QVulkanWindow: Failed to create command buffer fence: %d", err); + return; + } + frame.cmdFenceWaitable = true; // fence was created in signaled state } currentFrame = 0; @@ -1432,12 +1429,9 @@ void QVulkanWindowPrivate::releaseSwapChain() for (int i = 0; i < frameLag; ++i) { FrameResources &frame(frameRes[i]); - if (frame.fence) { - if (frame.fenceWaitable) - devFuncs->vkWaitForFences(dev, 1, &frame.fence, VK_TRUE, UINT64_MAX); - devFuncs->vkDestroyFence(dev, frame.fence, nullptr); - frame.fence = VK_NULL_HANDLE; - frame.fenceWaitable = false; + if (frame.cmdBuf) { + devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf); + frame.cmdBuf = VK_NULL_HANDLE; } if (frame.imageSem) { devFuncs->vkDestroySemaphore(dev, frame.imageSem, nullptr); @@ -1451,17 +1445,17 @@ void QVulkanWindowPrivate::releaseSwapChain() devFuncs->vkDestroySemaphore(dev, frame.presTransSem, nullptr); frame.presTransSem = VK_NULL_HANDLE; } + if (frame.cmdFence) { + if (frame.cmdFenceWaitable) + devFuncs->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX); + devFuncs->vkDestroyFence(dev, frame.cmdFence, nullptr); + frame.cmdFence = VK_NULL_HANDLE; + frame.cmdFenceWaitable = false; + } } for (int i = 0; i < swapChainBufferCount; ++i) { ImageResources &image(imageRes[i]); - if (image.cmdFence) { - if (image.cmdFenceWaitable) - devFuncs->vkWaitForFences(dev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX); - devFuncs->vkDestroyFence(dev, image.cmdFence, nullptr); - image.cmdFence = VK_NULL_HANDLE; - image.cmdFenceWaitable = false; - } if (image.fb) { devFuncs->vkDestroyFramebuffer(dev, image.fb, nullptr); image.fb = VK_NULL_HANDLE; @@ -1470,10 +1464,6 @@ void QVulkanWindowPrivate::releaseSwapChain() devFuncs->vkDestroyImageView(dev, image.imageView, nullptr); image.imageView = VK_NULL_HANDLE; } - if (image.cmdBuf) { - devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &image.cmdBuf); - image.cmdBuf = VK_NULL_HANDLE; - } if (image.presTransCmdBuf) { devFuncs->vkFreeCommandBuffers(dev, presCmdPool, 1, &image.presTransCmdBuf); image.presTransCmdBuf = VK_NULL_HANDLE; @@ -1920,24 +1910,21 @@ void QVulkanWindowPrivate::beginFrame() return; } + // wait if we are too far ahead FrameResources &frame(frameRes[currentFrame]); + if (frame.cmdFenceWaitable) { + devFuncs->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX); + devFuncs->vkResetFences(dev, 1, &frame.cmdFence); + frame.cmdFenceWaitable = false; + } + // move on to next swapchain image if (!frame.imageAcquired) { - // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate - // (note that we are using FIFO mode -> vsync) - if (frame.fenceWaitable) { - devFuncs->vkWaitForFences(dev, 1, &frame.fence, VK_TRUE, UINT64_MAX); - devFuncs->vkResetFences(dev, 1, &frame.fence); - frame.fenceWaitable = false; - } - - // move on to next swapchain image VkResult err = vkAcquireNextImageKHR(dev, swapChain, UINT64_MAX, - frame.imageSem, frame.fence, ¤tImage); + frame.imageSem, VK_NULL_HANDLE, ¤tImage); if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) { frame.imageSemWaitable = true; frame.imageAcquired = true; - frame.fenceWaitable = true; } else if (err == VK_ERROR_OUT_OF_DATE_KHR) { recreateSwapChain(); q->requestUpdate(); @@ -1950,23 +1937,15 @@ void QVulkanWindowPrivate::beginFrame() } } - // make sure the previous draw for the same image has finished - ImageResources &image(imageRes[currentImage]); - if (image.cmdFenceWaitable) { - devFuncs->vkWaitForFences(dev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX); - devFuncs->vkResetFences(dev, 1, &image.cmdFence); - image.cmdFenceWaitable = false; - } - // build new draw command buffer - if (image.cmdBuf) { - devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &image.cmdBuf); - image.cmdBuf = nullptr; + if (frame.cmdBuf) { + devFuncs->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf); + frame.cmdBuf = nullptr; } VkCommandBufferAllocateInfo cmdBufInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, nullptr, cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 }; - VkResult err = devFuncs->vkAllocateCommandBuffers(dev, &cmdBufInfo, &image.cmdBuf); + VkResult err = devFuncs->vkAllocateCommandBuffers(dev, &cmdBufInfo, &frame.cmdBuf); if (err != VK_SUCCESS) { if (!checkDeviceLost(err)) qWarning("QVulkanWindow: Failed to allocate frame command buffer: %d", err); @@ -1975,7 +1954,7 @@ void QVulkanWindowPrivate::beginFrame() VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, 0, nullptr }; - err = devFuncs->vkBeginCommandBuffer(image.cmdBuf, &cmdBufBeginInfo); + err = devFuncs->vkBeginCommandBuffer(frame.cmdBuf, &cmdBufBeginInfo); if (err != VK_SUCCESS) { if (!checkDeviceLost(err)) qWarning("QVulkanWindow: Failed to begin frame command buffer: %d", err); @@ -1985,6 +1964,7 @@ void QVulkanWindowPrivate::beginFrame() if (frameGrabbing) frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888); // the format is as documented + ImageResources &image(imageRes[currentImage]); if (renderer) { framePending = true; renderer->startNextFrame(); @@ -2006,8 +1986,8 @@ void QVulkanWindowPrivate::beginFrame() rpBeginInfo.renderArea.extent.height = swapChainImageSize.height(); rpBeginInfo.clearValueCount = sampleCount > VK_SAMPLE_COUNT_1_BIT ? 3 : 2; rpBeginInfo.pClearValues = clearValues; - devFuncs->vkCmdBeginRenderPass(image.cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - devFuncs->vkCmdEndRenderPass(image.cmdBuf); + devFuncs->vkCmdBeginRenderPass(frame.cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + devFuncs->vkCmdEndRenderPass(frame.cmdBuf); endFrame(); } @@ -2033,7 +2013,7 @@ void QVulkanWindowPrivate::endFrame() presTrans.image = image.image; presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1; - devFuncs->vkCmdPipelineBarrier(image.cmdBuf, + devFuncs->vkCmdPipelineBarrier(frame.cmdBuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, @@ -2044,7 +2024,7 @@ void QVulkanWindowPrivate::endFrame() if (frameGrabbing) addReadback(); - VkResult err = devFuncs->vkEndCommandBuffer(image.cmdBuf); + VkResult err = devFuncs->vkEndCommandBuffer(frame.cmdBuf); if (err != VK_SUCCESS) { if (!checkDeviceLost(err)) qWarning("QVulkanWindow: Failed to end frame command buffer: %d", err); @@ -2056,7 +2036,7 @@ void QVulkanWindowPrivate::endFrame() memset(&submitInfo, 0, sizeof(submitInfo)); submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &image.cmdBuf; + submitInfo.pCommandBuffers = &frame.cmdBuf; if (frame.imageSemWaitable) { submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = &frame.imageSem; @@ -2068,12 +2048,12 @@ void QVulkanWindowPrivate::endFrame() VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submitInfo.pWaitDstStageMask = &psf; - Q_ASSERT(!image.cmdFenceWaitable); + Q_ASSERT(!frame.cmdFenceWaitable); - err = devFuncs->vkQueueSubmit(gfxQueue, 1, &submitInfo, image.cmdFence); + err = devFuncs->vkQueueSubmit(gfxQueue, 1, &submitInfo, frame.cmdFence); if (err == VK_SUCCESS) { frame.imageSemWaitable = false; - image.cmdFenceWaitable = true; + frame.cmdFenceWaitable = true; } else { if (!checkDeviceLost(err)) qWarning("QVulkanWindow: Failed to submit to graphics queue: %d", err); @@ -2228,6 +2208,7 @@ void QVulkanWindowPrivate::addReadback() return; } + FrameResources &frame(frameRes[currentFrame]); ImageResources &image(imageRes[currentImage]); VkImageMemoryBarrier barrier; @@ -2242,7 +2223,7 @@ void QVulkanWindowPrivate::addReadback() barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.image = image.image; - devFuncs->vkCmdPipelineBarrier(image.cmdBuf, + devFuncs->vkCmdPipelineBarrier(frame.cmdBuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, @@ -2254,7 +2235,7 @@ void QVulkanWindowPrivate::addReadback() barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.image = frameGrabImage; - devFuncs->vkCmdPipelineBarrier(image.cmdBuf, + devFuncs->vkCmdPipelineBarrier(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, @@ -2268,7 +2249,7 @@ void QVulkanWindowPrivate::addReadback() copyInfo.extent.height = frameGrabTargetImage.height(); copyInfo.extent.depth = 1; - devFuncs->vkCmdCopyImage(image.cmdBuf, image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + devFuncs->vkCmdCopyImage(frame.cmdBuf, image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, frameGrabImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info); barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; @@ -2277,7 +2258,7 @@ void QVulkanWindowPrivate::addReadback() barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT; barrier.image = frameGrabImage; - devFuncs->vkCmdPipelineBarrier(image.cmdBuf, + devFuncs->vkCmdPipelineBarrier(frame.cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 0, nullptr, @@ -2286,14 +2267,14 @@ void QVulkanWindowPrivate::addReadback() void QVulkanWindowPrivate::finishBlockingReadback() { - ImageResources &image(imageRes[currentImage]); - // Block until the current frame is done. Normally this wait would only be // done in current + concurrentFrameCount(). - devFuncs->vkWaitForFences(dev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX); - devFuncs->vkResetFences(dev, 1, &image.cmdFence); - // will reuse the same image for the next "real" frame, do not wait then - image.cmdFenceWaitable = false; + FrameResources &frame(frameRes[currentFrame]); + if (frame.cmdFenceWaitable) { + devFuncs->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX); + devFuncs->vkResetFences(dev, 1, &frame.cmdFence); + frame.cmdFenceWaitable = false; + } VkImageSubresource subres = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; VkSubresourceLayout layout; @@ -2523,7 +2504,7 @@ QSize QVulkanWindow::swapChainImageSize() const } /*! - Returns The active command buffer for the current swap chain image. + Returns The active command buffer for the current swap chain frame. Implementations of QVulkanWindowRenderer::startNextFrame() are expected to add commands to this command buffer. @@ -2537,7 +2518,7 @@ VkCommandBuffer QVulkanWindow::currentCommandBuffer() const qWarning("QVulkanWindow: Attempted to call currentCommandBuffer() without an active frame"); return VK_NULL_HANDLE; } - return d->imageRes[d->currentImage].cmdBuf; + return d->frameRes[d->currentFrame].cmdBuf; } /*! diff --git a/src/gui/vulkan/qvulkanwindow_p.h b/src/gui/vulkan/qvulkanwindow_p.h index 7a0ee091e62..8c32461fe2e 100644 --- a/src/gui/vulkan/qvulkanwindow_p.h +++ b/src/gui/vulkan/qvulkanwindow_p.h @@ -109,9 +109,6 @@ public: struct ImageResources { VkImage image = VK_NULL_HANDLE; VkImageView imageView = VK_NULL_HANDLE; - VkCommandBuffer cmdBuf = VK_NULL_HANDLE; - VkFence cmdFence = VK_NULL_HANDLE; - bool cmdFenceWaitable = false; VkFramebuffer fb = VK_NULL_HANDLE; VkCommandBuffer presTransCmdBuf = VK_NULL_HANDLE; VkImage msaaImage = VK_NULL_HANDLE; @@ -123,13 +120,14 @@ public: uint32_t currentImage; struct FrameResources { - VkFence fence = VK_NULL_HANDLE; - bool fenceWaitable = false; VkSemaphore imageSem = VK_NULL_HANDLE; VkSemaphore drawSem = VK_NULL_HANDLE; VkSemaphore presTransSem = VK_NULL_HANDLE; + VkFence cmdFence = VK_NULL_HANDLE; + VkCommandBuffer cmdBuf = VK_NULL_HANDLE; bool imageAcquired = false; bool imageSemWaitable = false; + bool cmdFenceWaitable = false; } frameRes[MAX_FRAME_LAG]; uint32_t currentFrame;