到目前为止,根据我的读物,单个命令缓冲区中的命令可能会混乱,而无需显式同步。这是vulkan规范所说的内容(https://vulkan.lunarg.com/doc/view/1.0.26.0/linux/vkspec.chunked/ch02s02.html#fundamentals-queueoperation-commandorder)
“执行动作命令所涉及的工作通常被允许重叠或重新排序,但是这样做一定不能改变每个动作命令要使用的状态。通常,动作命令是那些会更改帧缓冲区附件(读/写)的命令缓冲区或图像内存,或写入查询池。”
编辑:起初,我认为设置状态命令将充当某种障碍,以确保绘制命令井然有序。我已经被解释说这是错误的。所以我看一下Vulkan中的绽放效果示例 https://github.com/SaschaWillems/Vulkan/blob/master/examples/bloom/bloom.cpp
/*First render pass: Render glow parts of the model (separate mesh) to an offscreen frame buffer*/
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.glowPass);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufoGlow.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufoGlow.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufoGlow.indexCount, 1, 0, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
/*Second render pass: Vertical blur
Render contents of the first pass into a second framebuffer and apply a vertical blur
This is the first blur pass, the horizontal blur is applied when rendering on top of the scene*/
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[1].framebuffer;
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurVert, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurVert);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
这是两个渲染过程都使用的2个子过程依赖项
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
然后,我的理解是这两个子通道依赖关系负责渲染过程的执行顺序,但是由于我仍然对子通道依赖关系尚不清楚,因此我不确定如何执行。如果我的理解是正确的,您可以向我解释为什么子传递依赖项有助于命令draw命令吗?如果我错了,那么确保draw命令顺序的是什么?
因此,正在发生的事情是渲染了某种东西img1
(作为颜色附件)。然后
img1
进行采样,然后将内容写入img2
(作为颜色附件)。然后img2
被采样并写入交换链映像。
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstSubpass = 0;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
对于第一个和第二个渲染遍历实例,这可能会阻止对资源的某些先前采样。可能来自前一帧。假设后续帧之间没有其他同步。
dependencies[1].srcSubpass = 0;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
现在已经写入了颜色附件VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
,更重要的是(又方便),存储操作在同一阶段进行颜色附件。VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
无论是STORE
还是都不总是无关紧要的DONT_CARE
。
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
并且VK_ACCESS_SHADER_READ_BIT
再次非常适合图像采样(在片段着色器中)。
因此,这意味着img1
在第二个渲染过程实例对它进行采样之前,它已从第一个渲染过程实例完全渲染并存储。
而且这也意味着img2
在第二个渲染过程实例对它进行采样之前,它已从第二个渲染过程实例中完全渲染并存储。
这是一个高级示例,希望您已经了解同步。
状态命令不受同步限制。它们仅在引入后续动作命令后立即更改它们的上下文,并且通常持续到命令缓冲区结束或状态再次更改为止。
子传递依赖关系和障碍以这种方式定义了依赖关系:src
同步作用域在dst
同步作用域开始执行之前完成执行。
子通道的依赖性和障碍几乎相同。屏障通常在渲染过程之外使用,而子过程依赖于它。子通道彼此无序,因此子通道依赖项还具有*Subpass
参数,并且同步作用域仅限于所述子通道。VK_SUBPASS_EXTERNAL
表示vkCmdBeginRenderPass
\之后的内容vkCmdEndRenderPass
是同步范围的一部分。
了解同步系统需要花费一些时间,因此我无法在此处进行适当介绍。在使用管道壁垒而不是信号灯时,我对壁垒有更多扩展的答案,否则互联网上也将充满资源。
首先,感谢您的详细答复。我想我终于有点了解这里发生了什么。第一个子通道依赖项:您想写入image1,但是有可能在上一帧的另一个渲染过程中读取image1(这在片段着色器中发生),因此您等待在STAGE_COLOR_ATTACHMENT_OUTPUT中完成读取。完成后,您就可以开始写作了。
第二个子通道依赖关系:您正在STAGE_COLOR_ATTACHMENT_OUTPUT中写入image1,但是第二个渲染通道可能已经启动。第二个渲染通道想在其片段着色器中读取/采样image1,因此您要等待它的STAGE_FRAGMENT_SHADER_BIT读取,直到第一个渲染通道的STAGE_COLOR_ATTACHMENT_OUTPUT完成写入为止。
由于image2和image3的关系与image1和image2的关系相同,因此在第二次渲染通道中会发生相同的情况。我现在的理解正确吗?
在第一个子传递依赖项中只剩下一个混淆点。也就是说,如果不是读取图像,而是在另一个渲染过程的STAGE_COLOR_ATTACHMENT_OUTPUT中写入了image1,该怎么办?然后,您可能会有2个竞争的写操作(1个来自先前的renderpass1,1个来自当前renderpass1),结果可能不正确,对吧?
@HenryWise是的,如果用法不同,那么作者将选择不同的参数。否则可能会导致数据危险。执行顺序和操作结果可能不正确。