描述符集合布局与管道布局
创建完Uniform Buffer后,接下来就是要告诉着色器如何使用。程序员知道Uniform Buffer里面存储的是MVP数据,以及MVP数据会被顶点着色器使用,但是Vulkan目前还不知道。这些都是通过描述符告诉Vulkan的。
描述符与描述符集合
描述符是一个特殊的不透明着色器变量,着色器使用它以间接的方式访问缓冲区和图像资源。描述符可以被看做是一个纸箱资源的指针。Vulkan运行描述符在绘制期间被改变,因此着色器在每次绘制操作时都可以访问到不同的资源。
可以创建两个Uniform Buffer,每一个buffer都存储不同的MVP,分别用于不同的绘制场景。然后可以轻易的指定描述符指向人一个MVP,以实现来回切换。
描述符集合可以引用一组同类型资源,这些资源可以用相同的布局绑定来描述。
在这个例子里面不会用到纹理,但是有一个可能的途径使用多个描述符是使用两个描述符构建一个描述符集,每一个描述符都应用一个单独的纹理。因此,两个纹理在绘制中可以被使用。然后,命令缓冲区中的命令可以通过指定所需纹理的索引来选择要使用的纹理。
注意,此处仅仅是描述描述符集合,并不是实际创建或使用描述符集合本身。
为描述一个描述符集合,需要用到描述符集合布局。
描述符集合布局
描述符集合布局用于描述一个描述符集合里面包含的内容(有点拗口)。还需要为每个描述符集合设置一个布局绑定,用于描述每个描述符集:
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;
binding是此条目的绑定号,对应于着色器阶段中相同绑定号的资源。此处只有一个描述符集合,因此将binding设置为0。
因为这个描述符引用了Uniform Buffer,因此此处设置为VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER。
描述符集合里面只有一个描述符,因此设置descriptorCount为1。
此处指定Uniform Buffer被绑定到顶点着色器阶段。
除非descriptorType被指定为VK_DESCRIPTOR_TYPE_SAMPLER或者VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,否者忽略此成员。
通过定义一个描述符集的绑定,可以创建描述符集布局:
#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());
管道布局
管道布局包含一个描述符集合布局的列表,还可以包含push常亮范围列表,这是一种将常量传递给着色器的替代方法,在这里不讨论。
与描述符集一样,只是定义布局。分配实际的描述符集,并在稍后用统一缓冲区引用填充。
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {};
pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pPipelineLayoutCreateInfo.pNext = NULL;
pPipelineLayoutCreateInfo.pushConstantRangeCount = 0;
pPipelineLayoutCreateInfo.pPushConstantRanges = NULL;
pPipelineLayoutCreateInfo.setLayoutCount = NUM_DESCRIPTOR_SETS;
pPipelineLayoutCreateInfo.pSetLayouts = info.desc_layout.data();
res = vkCreatePipelineLayout(info.device, &pPipelineLayoutCreateInfo, NULL, &info.pipeline_layout);
之后可以使用管道布局来创建一个图形管道。
描述符的着色器引用
值得指出的是,着色器在着色器语言中显式引用这些描述符。
例如,在GLSL里面:
layout (set=M, binding=N) uniform sampler2D variableNameArray[I];
M指的是管道布局的pSetLayouts成员中的第M个描述符集布局。
N指代描述符集布局的M的pBindings成员中的第N个描述符集(绑定)。
I是N的描述符集中描述符数组的索引。
顶点着色器中的统一缓冲区的布局代码如下所示:
layout (std140, binding = 0) uniform bufferVals {
mat4 mvp;
} myBufferVals;
这将统一缓冲区内容映射到myBufferVals结构。 未指定“set = M”且默认为0。
“std140”是描述如何将数据打包在统一块中的标准。 如果希望将更多数据放入统一块中,可能希望参考它。 有关更多信息,请参阅此文档。