summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <[email protected]>2025-04-09 15:00:19 +0200
committerLaszlo Agocs <[email protected]>2025-05-12 15:27:00 +0200
commit8fc3bc67df2baf23ccd70802cfafc4d2153282e9 (patch)
tree15aa9145504461fd715cdb78b4ced004f2a50403
parent53756dcfbcb717327e26ee65cea995c2794e01fe (diff)
Modernize and fix frame sync in QVulkanWindow
Follow 24d62ffd720b5bec5d07b07b8d2c9dda7635f3c0 for QVulkanWindow. The Vulkan backend of QRhi has diverged from QVulkanWindow even before that recent fix, having many changes over the years that are not present in QVulkanWindow. Introduce some of these now, to make things work with the newer validation layer bundled with the Vulkan 1.4 SDKs. Task-number: QTBUG-135645 Change-Id: Ic630e9a57c344843c171871b5a0386ed18027b22 Reviewed-by: Andy Nichols <[email protected]>
-rw-r--r--src/gui/vulkan/qvulkanwindow.cpp123
-rw-r--r--src/gui/vulkan/qvulkanwindow_p.h8
2 files changed, 55 insertions, 76 deletions
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, &currentImage);
+ frame.imageSem, VK_NULL_HANDLE, &currentImage);
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, &copyInfo);
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;