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 <andy.nichols@qt.io>
This commit is contained in:
parent
53756dcfbc
commit
8fc3bc67df
@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user