我一直在通过 NVidia 的 vk_raytrace 示例研究 Vulkan 和光线追踪,我对管道布局、GLSL 着色器“布局”和描述符集之间的协调方式感到困惑。
初级问题:
我真的很想创建一个单一的“布局”文件,无论管道如何,它都可以在所有着色器文件之间共享。如果管道布局仅匹配着色器中使用的描述符集,这是否可能?将着色器编译为 SPIR-V 时,是否会优化掉未使用的布局?
其他问题:
与主要问题相关的代码部分:
使用自己的描述符集创建 Post 管道
void RenderOutput::createPostPipeline(const VkRenderPass& renderPass)
{
// This descriptor is passed to the RTX pipeline
// Ray tracing will write to the binding 1, but the fragment shader will be using binding 0, so it can use a sampler too.
bind.addBinding({OutputBindings::eSampler, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT});
bind.addBinding({OutputBindings::eStore, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1,
VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_RAYGEN_BIT_KHR});
m_postDescSetLayout = bind.createLayout(m_device);
m_postDescPool = bind.createPool(m_device);
m_postDescSet = nvvk::allocateDescriptorSet(m_device, m_postDescPool, m_postDescSetLayout);
//...
pipelineLayoutCreateInfo.pSetLayouts =
&m_postDescSetLayout;
//...
vkCreatePipelineLayout(m_device, &pipelineLayoutCreateInfo, nullptr, &m_postPipelineLayout);
使用共享描述符集 m_offscreen.getDescSet 的 Raygen 管道的创建:
void SampleExample::createRender(RndMethod method)
{
//...
m_pRender[m_rndMethod]->create(
m_size, {m_accelStruct.getDescLayout(),
m_offscreen.getDescLayout(), // This is the shared desclayout
m_scene.getDescLayout(),
m_descSetLayout}, &m_scene);
}
这里是相应的着色器布局 - 对于 Post 管道,唯一相关的文件是“post.frag”
//...
layout(location = 0) in vec2 uvCoords;
layout(location = 0) out vec4 fragColor;
layout(set = 0, binding = 0) uniform sampler2D inImage;
layout(push_constant) uniform _Tonemapper
{
Tonemapper tm;
};
//...
其余布局包含在 most? raygen 着色器布局中 - “layouts.glsl”:
// clang-format off
layout(set = 0, binding = 0) uniform accelerationStructureEXT topLevelAS;
// Shared layout
layout(set = 1, binding = 1) uniform image2D resultImage;
//...
为了确保我们在同一页面上,让我们从一些术语开始。
当我说“管道布局”时,我的意思是一个
VkPipelineLayout
对象和用于构建它的各种组件(描述符集布局等)。着色器中也有描述符定义,它们是……该着色器可能使用的描述符资源的定义。这些是not“管道布局”(忽略关键字layout
用于声明描述符和设置索引的事实;这只是方便的语法)。最后,还有您在运行时创建和绑定的实际 VkDescriptorSet
对象。
创建管道布局时不指定实际的“描述符集”编号
是的。集合X指的是数组
VkPipelineLayoutCreateInfo::pSetLayouts
的第X个索引。集合 0 是描述符集合pSetLayouts[0]
.
如果 NVIDIA 示例在不同的管道布局中使用相同的描述符集布局和不同的集合索引,那么绑定在不同集合索引中使用描述符集布局的管道将扰乱绑定的描述符集。所以它还必须将描述符集重新绑定到适当的集索引。
也就是说,有问题的代码并没有试图保持管道布局之间的绑定兼容性;它只是希望能够使用相同的描述符集。
着色器模块可以不定义管线布局中指定的所有描述符。事实上,如果管线布局中的某些描述符在构建管线时由着色器模块的none 定义就可以了。描述符集布局很重要;只要着色器模块不 contradict 管道布局,针对特定布局构建的
VkDescriptorSet
将使用包含该描述符集布局的 VkPipeline
对象与 VkPipelineLayout
对象一起工作。