Remapping bindings and descriptor sets in SPIRV

Started by
3 comments, last by DiligentDev 5 years, 11 months ago

Hello everyone!

For my engine, I want to be able to automatically generate pipeline layouts based on shader resources. That works perfectly well in D3D12 as shader resources are not required to specify descriptor tables, so I use reflection system and map different shader registers to tables as I need. In Vulkan, however, looks like descriptor sets must be specified in both SPIRV bytecode and when creating pipeline layout (why is that?). So it looks like I will have to mess around with the bytecode to tweak bindings and descriptor sets. I looked at SPIRV-cross but it seems like it can't emit SPIRV (funny enough). I also use glslang to compile GLSL to SPIRV and for some reason, binding decoration is only present for these resources that I explicitly defined.

Does anybody know if there is a tool to change bindings in SPIRV bytecode?

 

Advertisement

I don't know if this is something you can use but maybe it will give you some ideas. I wrote a preprocessor that is run before the actual shader compilation is performed. I let the preprocessor generate all the bindings for all shader inputs, according to some rules I have set up. This system is completely deterministic and I can decide exactly what I want and where it goes. I compile HLSL to both DX bytecode for DX11/12 and to SPIR-V for Vulkan. I might have something like this:


#autobind Texture2D gTexture;
#autobind cbuffer gConstantBuffer
#autobind sampler gSampler;

where #autobind is a preprocessor directive that gets expanded to something like this:


layout(set=0,binding=0) Texture2D gTexture : register(t0);
layout(set=0,binding=1) cbuffer gConstantBuffer : register(b0)
layout(set=0,binding=2) sampler gSampler : register(s0);

Using this system I have full control over where my inputs get bound, eg. I can always bind shadow map inputs to certain slots etc. I then use D3DReflect() on DX and SPIRV-cross on Vulkan to verify that the compiler generated what I want. Eg. in certain cases the compiler optimizes away an input in DXBC but not in SPIR-V or vice versa.

This does not let you "re-bind" stuff in the already generated SPIR-V but it does let you decide where they are put in the first place.

 

@GuyWithBeard Thank you for the suggestion! My problem is little bit different though as I want to be able to work with SPIRV directly. As a gross workaround I can use SPIRV-Cross to emit GLSL after assigning bindings and then use glslang to compile it back to SPIRV. This is obviously far from ideal solution...

So for reference here is the solution that seems to work for me.

1. First of all, SPIRV code was missing binding and descriptor set decorations because I was not using glslang properly. I called Shader.setAutoMapBindings(true) and was expecting the compiler to assign decorations automatically. In reality this call only tells the compiler that decorations will be explicitly assigned later by the app. The actual assignment is done by Program.mapIO() function. Since I do not care about values at SPIRV compilation stage, but only need decorations to be present in the byte code, I simply return 0 for all resources.

2. SPIRV-Cross has get_binary_offset_for_decoration() method that returns offset in the original bytecode to the specific decoration of a certain resource. I use this method to find and modify decorations in the compiled SPIRV.

This topic is closed to new replies.

Advertisement