所以我想在Vulkan中渲染两个独立的网格。我涉足纹理,第一个网格使用4个纹理,而第二个网格使用5个纹理。我正在执行索引绘制。
为简单起见,每个网格都将自己的统一缓冲区和采样器数组打包到单独的描述符集中,每个描述符带一个UBO绑定和另一个采样器绑定。为每个网格运行以下代码,其中descriptorSet
是与单个网格关联的描述符集。 filepaths
是具有特定用途的图像路径的向量。
std::vector<VkWriteDescriptorSet> descriptorWrites;
descriptorWrites.resize(2);
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = buffers[i];
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject);
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSet;
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &bufferInfo;
std::vector<VkDescriptorImageInfo> imageInfos;
imageInfos.resize(filepaths.size());
for (size_t j = 0; j < filepaths.size(); j++) {
imageInfos[j].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfos[j].imageView = imageViews[j];
imageInfos[j].sampler = samplers[j];
}
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = descriptorSet;
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrites[1].descriptorCount = imageInfos.size();
descriptorWrites[1].pImageInfo = imageInfos.data();
vkUpdateDescriptorSets(devicesHandler->device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr);
因此,为了告诉Vulkan如何布置这些描述符集,我当然需要两个描述符集布局,即每个网格一个,由于filepaths
的大小不同,它们在采样器的绑定上也不同: / p>
// <Stuff for binding 0 for UBO here>
// ...
VkDescriptorSetLayoutBinding layoutBinding = {};
layoutBinding.binding = 1;
layoutBinding.descriptorCount = filepaths.size();
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
现在,当我创建管道时,我需要提供管道布局。我这样做如下,其中layouts
是填充到向量中的网格的描述符集布局。
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = layouts.size();
pipelineLayoutInfo.pSetLayouts = layouts.data();
最后在渲染之前,我绑定了适当的描述符集。
天真的,我认为定义管道布局的方法就是这样做的方法(简单地采用所有涉及的布局并将它们传递到pSetLayouts
上),但是它不起作用。我得到的错误是:
descriptorSet #0 being bound is not compatible with overlapping descriptorSetLayout at index 0 of pipelineLayout 0x6e due to: DescriptorSetLayout 87 has 5 descriptors, but DescriptorSetLayout 88, which comes from pipelineLayout, has 6 descriptors.. The Vulkan spec states: Each element of pDescriptorSets must have been allocated with a VKDescriptorSetLayout that matches (is the same as, or identically defined as) the VkDescriptorSetLayout at set n in layout, where n is the sum of firstSet and the index into pDescriptorSets.
我还注意到,如果我将第二个网格中使用的纹理数量从5个减少到4个,以使它们与第一个网格中的4个匹配,那么它将起作用。所以我想知道是否需要为布局的每个可能配置创建管道?也就是说,将setLayoutCount
设置为4并将另一个设置为5的一个管道,并在我要绘制一个网格或另一个网格时绑定相应的管道?那是愚蠢的吗?我想念什么吗?
值得一提的是,如果我单独渲染每个网格,一切都会顺利进行。当我将它们都放在场景中时,就会出现问题。
此外,我知道应该连续分配缓冲区并考虑到对齐方式,并且我在做的是一个不好的做法-但我还没有解决这个问题。
答案 0 :(得分:3)
将多个集合布局传递给管道意味着您希望管道能够同时访问两个集合中的所有绑定,例如着色器可以访问(set = 0,binding = 0)和(set = 1,binding = 0)的两个UBO,(set = 0,binding = 1)的四个纹理,以及(set = 1,绑定= 1)。
然后,当将第二个网格的集合绑定为唯一集合时,您将获得不兼容性,因为它的布局(5个纹理)不同于管道对集合0(4个纹理)的期望。
是的,当您具有不同的描述符集布局时,您需要不同的管道。如果您使用管道缓存,则实际上很多编译都可以在两个管道之间重用。
如果您试图对两个网格使用相同的管线,那么假设您的着色器中访问第五个纹理的代码是有条件的,基于制服还是其他?另一种方法是在绘制4纹理网格时绑定虚拟纹理。由于将不会访问它,所以它的内容无关紧要,它可以是1x1,依此类推。然后,您可以对两个网格使用相同的5纹理集布局和相同的管线。