Descriptor sets

Hi all,

I am in the middle of porting a DX11 game to Vulkan and I have hit a problem with descriptor sets

I am getting a lot of these…

Type mismatch on descriptor slot 0.0 (expected VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) but descriptor of type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC

And the error is totally correct.

The code was already started by some other person and they just hacked in 2 descriptor sets for the 2 shaders used by the GUI. They totally ignored the 30,000 + other shaders the game uses.

I have written a tool that converted all the original shaders to Spir-v and they all get precompiled and packed into archives, so I don’t have the source code for the shaders available at run time.

So now I have to create descriptor set layout bindings for all the shaders

The DX11 version of the renderer doesn’t have to do this, it just treats everything as a block of memory that gets written to the command buffer and if it is in the wrong layout, well, tough

SO …

  1. Can you query a compiled shader to get the expected layout?

If you can it would make things a lot easier, read the layout, hash it, get/create matching VkDescriptorSetLayoutBinding, store/retrieve

  1. I don’t really see the point of layout bindings

Since they have to match what the shader is expecting, the layout must be encoded into the shader already , so defining something in code that is already existing in the compiled shader seems redundant

Cheers guys

Well, if those shaders weren’t Vulkan shaders, then the resources they use are not defined in a way Vulkan can understand. Which means your conversion tool would have to define those resources within the descriptor model, yes?

Which means that your tool must know what resources each shader uses, since it has to figure out how to pack them into descriptor sets. So right there, you have all the information you need to build a layout.

Also, you really need to work on reducing the number of distinct shaders. Employ Uber-shader techniques with (uniform) conditional branching in the shader.

Not exactly. If you have one shader that uses, say, a diffuse texture, and another shader that uses a diffuse texture and a specular texture, it is entirely reasonable for both shaders to use the same layout. They would use the same set and binding for the resources they have in common, and they would use different bindings for resources they don’t share. So the diffuse texture would be binding 0, while the specular texture might be binding 1.

Your single layout would have both the diffuse and specular textures in it, even though the first shader doesn’t use the second binding. This way, all of your pipelines can be layout compatible. And if you make your binding zero an array texture or an array of samplers, then you won’t even have to change descriptor sets between different objects. You just have each one set with an array containing all of the diffuse textures, and you send each object an index it uses to pick its diffuse texture from that array.

Once you’ve culled your number of shaders down to a reasonable count, you then need to start working on making the layout used by groups of those shaders more uniform. Look at the frequency of pipelines changes in your application and divide particular resources into different sets based on that frequency (higher frequency should mean a higher set index). Then try to build layouts that can be the same across large numbers of your remaining shaders.

I cannot reduce the number of shaders. It’s an existing product with an existing bespoke engine.

Shaders are created in a windows tool and then pass through a parser to produce targeted platform specific shader code

Sadly the tool does not support Vulkan and is no longer in development, so I had to parse the DX11 HLSL into something Vulkan could work with.

A single original shader may be compiled up to 5 times depending on settings ( light only pass, depth only pass, colour pass , etc.)

So you end up with a lot of compiled shaders which are then packed into archives

Neither am I going to sit down and recode 10’s of thousands of shaders just to make setting bindings easier. Not an option.

If the information is stored in the compiled shader, and it must be for the validation layer to function, then why would you make coders store a second copy of that information?

It’s basic code design.

Store data once , not twice, then use it as many times as you can

VkDescriptorSetLayout is also used for creation of the actual Descriptor Set. If the Layout was not a thing, you would have to pass it the whole VkPipeline, which would be less flexible (the Pipeline is a concrete instance, and also needs to already be created when making descriptors).

Additionally in some cases it might be better to have more stuff in Layout than is actually in the Shaders in order to make efficient use of Pipeline Layout Compatibility and Descriptor Set Binding.

As a bonus, driver does not have to inspect the Shaders specifically for this information.

I don’t see how the latter statement necessitates the former. It might require significant effort, but it is certainly possible.

Validation layers are layers; they’re not part of the Vulkan implementation itself. The implementation doesn’t store this information. Because it doesn’t need it.

Layouts are ultimately a tool to inform the driver about where pipelines get resources and how to construct descriptor sets that provide those resources. The layouts don’t need to be stored internally in a way that looks anything like the data you provided Vulkan. A set layout could just be a series of byte offsets for different resources. That would be most efficient for building descriptor sets, but you can’t convert that back into something like a VkDescriptorSetLayout.

The pipeline compiler looks at the layout and adjust the pipeline’s compilation accordingly. It sees that set 0, descriptor 2 uses byte offset Y to access its data. And that’s it.

Validation layers generally exist to help you catch errors, not to add functionality to the implementation.

That would mean that this particular version of the game would be completely different than every other version of the game and should anything be changed on the base version, applying the change to this version would be a major effort.

Okay so since this information that is stored in the compiled shader is only used in the validation layer, can I get rid of it for release builds?

Ahh now it makes sense, this is to make things easier for driver developers and hardware manufacturers , sod the rest of us.

Anyway, the answer to my original question is obviously no so I will just have to get the information another way.

Cheers