原文链接:https://vulkan.lunarg.com/doc/sdk/1.2.131.2/windows/tutorial/html/08-init_pipeline_layout.html
Descriptor Set Layouts 和 Pipeline Layouts
这一章节的代码文件是 08-init_pipeline_layout.cpp
在之前的示例中,你创建过一个 uniform buffer,但是你还没有做任何事情来描述它如何被 shader 使用。你是知道的,该缓冲区里包含了MVP变换的 uniform 变量,并且它将被顶点 shader 使用,但是 Vulkan 还不知道这些。
我们通过使用 descriptor 来完成这个任务。
Descriptors 和 Descriptor Sets
descriptor 是特殊的不透明的 shader 变量,用来以间接方式访问缓冲区和图像资源。它可以被认为是指向某个资源的“指针”。Vulkan API 允许这些变量在绘制操作之间被改变,所以 shader 每次绘制时可以访问不同的资源。
在示例中,你仅有一个 uniform buffer。但你可以创建两个,每个带有不同的MVP,给场景不同的视野。然后你可以轻松地改变 descriptor 来指向任一个 uniform buffer,好在MVP矩阵间来回切换。
descriptor set 之所以称为“集合”,是因为它可以指向被相同 layout binding 描述的相同性质资源的数组。(“layout binding”稍后解释)
在本示例中你没有用到 texture,但是使用多重描述符的一种可能方式是,用两个 descriptor 构造一个 descriptor set,每个引用一个单独的 texture。两个 texture 因此都是在绘制中可用的。command buffer 里的命令可以通过指定所需 texture 的索引来选择使用。
重点注意,这里你只是致力于描述 descriptor set,并没有实际地分配和创建 descriptor set 本身,这些你之后会在 descriptor_set 示例中来做。
为了描述一个 descriptor set,你要使用 descriptor set layout。
Descriptor Set Layouts
descriptor set layout 是用来描述某个 descriptor set 列表的内容。对应每一个 descriptor set,你还需要一个 layout binding 用来描述它:
VkDescriptorSetLayoutBinding layout_binding = {};
layout_binding.binding = 0;
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layout_binding.descriptorCount = 1;
layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layout_binding.pImmutableSamplers = NULL;
- 碰巧你只创建了一个 descriptor set,所以 binding 数值唯一的选择是 0。
- 因为这个 descriptor 是引用了一个 uniform buffer,你得设置相应的 descriptorType。
- 在这个 descriptor set 里你仅有一个 descriptor,这些通过 descriptorCount 数值来声明。
- 你声明了这个 uniform buffer 资源是被绑定到着色顶点阶段。
完成了我们这一个 descriptor set 的 binding 定义,你就已经准备好了创建 descriptor set layout:
#define NUM_DESCRIPTOR_SETS 1
VkDescriptorSetLayoutCreateInfo descriptor_layout = {};
descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_layout.pNext = NULL;
descriptor_layout.bindingCount = 1;
descriptor_layout.pBindings = &layout_binding;
info.desc_layout.resize(NUM_DESCRIPTOR_SETS);
res = vkCreateDescriptorSetLayout(info.device, &descriptor_layout, NULL,
info.desc_layout.data());
Pipeline Layouts
一个pipleline layout 包含了一列 descriptor set layout。它也可以包含一列 push constant ranges,这是给 shader 传递常量的另一种方式,这里不再涉及。
和 descriptor set 一样,你只是在定义 layout。后面会给真正的 descriptor set 分配并填入 uniform buffer 引用。
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.pNext = NULL;
pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
pipelineLayoutCreateInfo.pPushConstantRanges = NULL;
pipelineLayoutCreateInfo.setLayoutCount = NUM_DESCRIPTOR_SETS;
pipelineLayoutCreateInfo.pSetLayouts = info.desc_layout.data();
res = vkCreatePipelineLayout(info.device, &pipelineLayoutCreateInfo, NULL,
&info.pipeline_layout);
之后你将会使用该 pipeline layout 来创建 graphics pipeline。
Descriptors 的 Shader 引用
值得指出的是,shader 用着色器语言显式地引用这些 descriptor。
例如,在GLSL里:
layout (set=M, binding=N) uniform sampler2D variableNameArray[I];
- M 指在 pipeline layout 的 pSetLayouts 成员里的第 M 个descriptor set layout
- N 指在第 M 个 descriptor set layout 的 pBinding 成员里的第 N 个 descriptor set(binding)
- I 是在第 N 个 descriptor set 里的数组索引
你将在顶点着色器里使用的 uniform buffer 的布局代码像这样:
layout (std140, binding = 0) uniform bufferVals {
mat4 mvp;
} myBufferVals;
这映射 uniform buffer 内容到 myBufferVals 结构体。"set=M" 没有指定,默认是0。
"std140" 是一个标准,描述数据如何被包装成 uniform blocks。如果你想要放更多数据到一个 uniform block里,可能会希望参考它。查看 这份文档 获得更多信息。