不同渲染过程之间的渲染目标写入/着色器读取同步



以下是Vulkan渲染设置:

  1. 提交CB1-包含具有X个绘制调用的渲染过程A
  2. 提交CB2-包含具有Y绘制调用的渲染过程B,其中一个绘制调用来自作为渲染过程A的渲染目标的图像的样本
  3. 在提交CB2期间,插入与某些外部GPU API共享的信号量信号,以确保在进一步使用渲染过程B的渲染目标之前完成CB2执行(在这种情况下由CUDA执行(。这个步骤设置正确,我很清楚它是如何工作的

所有这些都发生在同一个队列上,并按上述指定顺序进行。渲染过程A和B共享MSAA图像,但每个过程都具有唯一的颜色附件,MSAA将解析到其中。

我的问题是,在CB1完成对渲染过程A RT的写入和在渲染过程B执行期间从着色器中的RT采样的CB2中的一个或多个绘制调用之间,同步的最佳方式是什么?

根据我在Khronos Vulkan Slack小组上收到的一些建议,我尝试通过子路径依赖关系进行同步。

渲染过程A依赖项设置:

VkSubpassDependency dependencies[2] = {};
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkAttachmentReference colorReferences[2] = {};
colorReferences[0] = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; //this one for MSAA attachment
colorReferences[1] = { 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };

渲染过程B依赖项设置:

VkSubpassDependency dependencies[2] = {};
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkAttachmentReference colorReferences[2] = {};
colorReferences[0] = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; 
colorReferences[1] = { 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };

上述解决方案似乎正在发挥作用。但我不确定我是否有关于它的隐含同步保证

唯一不是这种情况的图像是渲染的依赖项目标。不管怎样,这些都是通过子通道进行特殊处理的。每个subpass说明了它在写什么以及写到哪里,因此系统具有明确构建这些内存依赖关系的信息。

在这篇文章中,作者还写道:

注意:渲染过程中的帧缓冲区操作发生在当然是API订单。这是规范调用的一个特殊异常出来

但在这个SO问题中,答案是必须在命令缓冲区之间进行同步,在这种情况下必须同步事件。

因此,我尝试过的另一个选项是在CB2记录期间为先前提交的CB(渲染过程A(中的渲染目标图像插入管道内存屏障,然后再开始渲染过程B记录:

CB2记录

BeginCommandBuffer(commandBuffer);
...
if (vulkanTex->isRenderTarget)//for each sampler in this pass
{
VkImageMemoryBarrier imageMemoryBarrier = CreateImageBarrier();
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageMemoryBarrier.image = vulkanTex->image;
VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
imageMemoryBarrier.subresourceRange = range;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
VkRenderPassBeginInfo renderPassBeginInfo = {};
...
.....

当然,在这种情况下,我可以将所有渲染过程的子过程依赖关系设置为相同,就像渲染过程B依赖关系设置一样。这个也行。但这需要我将更多的命令记录到CB中。那么,就硬件效率而言,哪种方式是正确的(给定的子通道依赖选项是有效的(和最优化的?

上述解决方案似乎正在运行。

您的依赖关系毫无意义。考虑到您对实际依赖关系的描述,renderpass B依赖关系尤其奇怪:"其中一个绘制调用来自渲染过程A的渲染目标图像的样本。"这将表示A中的帧缓冲区写入和B中的纹理读取之间的依赖关系。您在示例中的依赖关系在a中的framebuffer写入和B中的framebuffer写之间创建了依赖关系。这毫无意义,与您所说的实际操作无关。

此外,当您试图与写入内存的东西同步时,您的srcAccessMask没有意义,因为您声明先前的源正在读取内存。

现在,可能是您的信号量依赖性恰好覆盖了它,或者信号量干扰了Vulkan层检测依赖性问题的尝试(您正在使用层,对吧?(。但是您所展示的代码根本没有意义。

对renderpass B的外部依赖是正确的做法(目前尚不清楚为什么render pass A需要外部依赖(,但它需要真正有意义。如果renderpass B确实是从renderpass A写入的图像中采样,那么它看起来像这样:

dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; //Assuming you're reading the image in the fragment shader. Insert other shader stage(s) if otherwise.
dependencies[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; //renderpass A wrote to the image as an attachment.
dependencies[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; //renderpass B is reading from the image in a shader.
dependencies[0].dependencyFlags = 0; //By region dependencies make no sense here, since they're not part of the same renderpass.

最新更新