Hello,
I’m building a little game (using a homemade engine, using LWJGL), which :
- prepare an image with some compute shaders
- blit it into a swapchain image
- execute a render pass to draw the UI
1 is executed separately, with a compute queue. 2 and 3 in the same execute, by a graphic queue.
The image is perfectly blit. Unfortunately, the UI is not draw every frames, and appears to blink. Generally, it’s barely/not visible, but on my linux laptop (intel gpu), it’s annoying.
I fought with this problem for a while, try many synchronizations, with no success. No validation errors.
I tried to isolate the problem, if I just draw the UI: no problems.
I added a fence between the computation and graphic execution, no changes.
My feeling is that the blit is sometime made after/during the draw of the UI. I tried to add some barrier after the blit, and wire the subpass dependencies accordingly, but I still miss something.
My question is: what is the proper way to synchronize a blit into a swapchain, followed by a render pass ?
But maybe I miss something else. The code is abstract, but I’ll try to sum up what could be (maybe?) relevant:
for (int i = 0; i < commandBuffers.size(); i++)
{
RenderCommandBuffer commandBuffer = commandBuffers.get(i);
ImageView imageView = configuration.imageViewManager.getImageViews().get(i);
commandBuffer.startCommand();
blitToSwapchainImage(commandBuffer, imageView);
commandBuffer.startRenderPass();
ui.drawFrame(commandBuffer.getVkCommandBuffer());
commandBuffer.endRenderPass();
commandBuffer.endCommand();
}
The blitToSwapchainImage method:
srcImage is the image built by the compute shaders.
dstImage is the target swapchain image.
[CODE
// Prepare transfer from Image to Frambuffer
ImageBarrier barrier = new ImageBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// From VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL to VK_ACCESS_TRANSFER_READ_BIT
barrier.addImageBarrier(srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT);
// From UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
barrier.addImageBarrier(dstImage, dstImageFormat, 1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
barrier.execute(commandBuffer.getVkCommandBuffer());
VkImageBlit.Buffer region = VkImageBlit.calloc(1);
region.srcSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
region.srcSubresource().mipLevel(0);
region.srcSubresource().baseArrayLayer(0);
region.srcSubresource().layerCount(1);
region.srcOffsets(0).x(0);
region.srcOffsets(0).y(0);
region.srcOffsets(0).z(0);
region.srcOffsets(1).x(srcImage.getWidth());
region.srcOffsets(1).y(srcImage.getHeight());
region.srcOffsets(1).z(1);
region.dstSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
region.dstSubresource().mipLevel(0);
region.dstSubresource().baseArrayLayer(0);
region.dstSubresource().layerCount(1);
region.dstOffsets(0).x(0);
region.dstOffsets(0).y(0);
region.dstOffsets(0).z(0);
region.dstOffsets(1).x(extent.getWidth());
region.dstOffsets(1).y(extent.getHeight());
region.dstOffsets(1).z(1);
vkCmdBlitImage(commandBuffer.getVkCommandBuffer(), srcImage,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region, VK_FILTER_NEAREST);
// Change layout again before render pass.
ImageBarrier barrierEnd = new ImageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// From VK_IMAGE_LAYOUT_GENERAL to VK_ACCESS_SHADER_WRITE_BIT
barrierEnd.addImageBarrier(srcImage, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT);
// From VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL to VK_IMAGE_COLOR_ATTACHMENT_OPTIMAL
barrierEnd.addImageBarrier(dstImage, dstImageFormat, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_COLOR_ATTACHMENT_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_WRITE_BIT);
barrierEnd.execute(commandBuffer.getVkCommandBuffer());
[b]The creation of the render pass:[/b]
VkAttachmentDescription colorAttachment = VkAttachmentDescription.calloc();
colorAttachment.format(context.swapChainManager.getColorDomain().getColorFormat());
colorAttachment.samples(VK_SAMPLE_COUNT_1_BIT);
colorAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_LOAD);
colorAttachment.storeOp(VK_ATTACHMENT_STORE_OP_STORE);
colorAttachment.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
colorAttachment.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
colorAttachment.initialLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
colorAttachment.finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
VkAttachmentReference.Buffer colorAttachmentRef = VkAttachmentReference.calloc(1);
colorAttachmentRef.attachment(0);
colorAttachmentRef.layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkSubpassDescription.Buffer subpass = VkSubpassDescription.calloc(1);
subpass.pipelineBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS);
subpass.colorAttachmentCount(1);
subpass.pColorAttachments(colorAttachmentRef);
VkSubpassDependency.Buffer dependency = VkSubpassDependency.calloc(1);
dependency.srcSubpass(VK_SUBPASS_EXTERNAL);
dependency.dstSubpass(0);
dependency.srcStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
dependency.dstStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
dependency.srcAccessMask(VK_SHADER_WRITE_BIT);
dependency.dstAccessMask(VK_SHADER_WRITE_BIT);
int attachmentCount = 1;
VkAttachmentDescription.Buffer attachments = VkAttachmentDescription.calloc(attachmentCount);
attachments.put(colorAttachment);
attachments.flip();
VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.calloc();
renderPassInfo.sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO);
renderPassInfo.pAttachments(attachments);
renderPassInfo.pSubpasses(subpass);
renderPassInfo.pDependencies(dependency);