Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vulkan - when should I create a new pipeline?

Tags:

c++

3d

vulkan

So I want to render two independent meshes in Vulkan. I'm dabbling in textures and the 1st mesh uses 4 of them while the 2nd uses 5. I'm doing indexed draws.

Each mesh has its own uniform buffer and sampler array packed into separate descriptor sets for simplicity, each one with a binding for the UBO and another binding for the samplers. The following code is run for each mesh, where descriptorSet is the descriptor set associated to a single mesh. filepaths is the vector of image paths that mesh in particular uses.

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);

So in order to tell Vulkan how these descriptor sets are laid out I need of course two descriptor set layouts i.e. one per mesh, which differ in the binding for the samplers due to the different size of filepaths:

// <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;

Now, when I create the pipeline I need to provide the pipeline layout. I'm doing it as follows, where layouts are the descriptor set layouts of the meshes stuffed into a vector.:

VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = layouts.size();
pipelineLayoutInfo.pSetLayouts = layouts.data();

Finally before rendering I bind the aproppriate descriptor set.

Naively I would think that way to define the pipeline layout this is the way to go (simply taking all the involved layouts and passing them on pSetLayouts) but it's not working. The error I get is:

 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.

I also noticed that if I reduce the number of textures used from 5 to 4 in the second mesh so they match the 4 from the first mesh, then it works. So I'm wondering if I need to create a pipeline for every possible configuration of the layouts? That is, one pipeline with setLayoutCount set to 4 and another set to 5, and bind the corresponding one when I'm going to draw one mesh or the other? Is that stupid? Am I missing something?

Worth noting is that if I render each mesh alone everything runs smoothly. The problem arises when I put both of them in the scene.

Also, I know buffers should be allocated consecutively and taking into account alignments and that what I'm doing there is a bad practice - but I am just not dealing with that yet.

like image 303
Carlos Romero Avatar asked Nov 14 '18 18:11

Carlos Romero


People also ask

What is a Vulkan pipeline?

Vulkan pipelines are a massive object with many different configuration structs, some of them even running pointers and being arrays. For this reason, we are going to create a class specifically for building pipelines, that will simplify the process.

What is pipeline layout Vulkan?

A pipeline layout describes the layout of descriptors and push constants used by a graphics pipeline or a compute pipeline.

What is descriptor set in Vulkan?

A descriptor is a special opaque shader variable that shaders use to access buffer and image resources in an indirect fashion. It can be thought of as a "pointer" to a resource. The Vulkan API allows these variables to be changed between draw operations so that the shaders can access different resources for each draw.


1 Answers

Passing multiple set layouts to the pipeline means that you want the pipeline to be able to access all the bindings in both sets simultaneously, e.g. the shaders have access to two UBOs at (set=0, binding=0) and (set=1, binding=0), four textures at (set=0, binding=1), and five textures as (set=1, binding=1).

Then when you bind the set for the second mesh as the only set, you get the incompatibility because it has a different layout (5 textures) than the pipeline expects for set 0 (4 textures).

So yes, when you have different descriptor set layouts, you need different pipelines. If you use the pipeline cache, much of the compilation may actually be reused between the two pipelines.

If you're trying to use the same pipeline for both meshes, then presumably the code in your shader that accesses the fifth texture is conditional, based on a uniform or something? The alternative is to bind a dummy texture when drawing the 4-texture mesh; since it won't be accessed, it doesn't matter what its contents are, it can be 1x1, etc. Then you can use the same 5-texture set layout and same pipeline for both meshes.

like image 125
Jesse Hall Avatar answered Oct 12 '22 11:10

Jesse Hall