以下是Vulkan渲染设置:
- 提交CB1-包含具有X个绘制调用的渲染过程A
- 提交CB2-包含具有Y绘制调用的渲染过程B,其中一个绘制调用来自作为渲染过程A的渲染目标的图像的样本
- 在提交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.