DX[11] Totally confused about the use of registers for constant buffers

Started by
1 comment, last by n3Xus 13 years, 12 months ago
Hello, I'm making a shader system that doesn't use the effect framework. I have two vertex shaders, they both have two constant buffers, each cb has a matrix, just so I can do some tests. I added the :register (cb0) to the first cb and :register (cb1) to the second constant buffer: cbuffer myCb0:regsiter(cb0) {...}; cbuffer myCb1:regsiter(cb1) {...}; Now, I use the deviceContext->VSSetConstantBuffers to set both CBs, but I do this only for the first vertex shader! The second vertex shader gets both CBs set automatically, which was quite a suprise to me, as I always though that different shaders (I mean this like: ID3D11VertexShader* vs1, ID3D11VertexShader* vs2) had this stuff totaly separate, obviously I have no idea how this really works. Since I use ID3D11ShaderReflection to get constant buffers I noticed that the order in which you declare CBs in the shader matters (no suprise here), but what about those :register (cbX) things? What is their role except for setting a CB to a specific register? I tried to change the order of CBs in one shader but keeping the CBs mapped to the same register as I thought that this will make the order of CB declaration in vertex shader2 non-important; this is how I have it set up: // vertex shader1 cbuffer myCb0 :register(cb0) {...} cbuffer myCb1 :register(cb1) {...} // vertex shader2 - note: cb order changed, but myCb0 still maps to register 0 cbuffer myCb1 :register(cb1) {...} cbuffer myCb0 :register(cb0) {...} I thought that in vertex shader2 myCb0 would have the same value as myCb0 has in vertex shader1, but it doesn't, it has the value of myCb1 from vertex shader1. So does the :register(cbX) has any other meaning than just specifing the register? EDIT: another question: how are resources actually shared between shaders, are there any articles on this?
Advertisement
The cb registers are a dx9 feature and are only respected if you compile with the backwards compatibility flag. The DX10 constant buffer register is b. I tested this myself just now and found it to work as you expected. The documentation has a register table for DX10/DX11 in the HLSL reference section under variable syntax. There are sections that discuss both packoffset and register.

There are a few powerful features there, like being able to specialize register locations per shader stage as well. The docs do have an error though, the registers cannot be specialized per shader model as the examples suggest.

With regards to your question about pipeline (settings shaders and resources). The entire directx pipeline is just one huge set of states. None of these states are affected by the setting of any other state. What this means is that your resources are still bound to the pipeline from the previous calls while you set a shader, or set a blend or render state. This behavior is exactly what you're trying to take advantage of with the register mapping... the ability to set a resource once while rendering and have all subsequent shaders use the same resource. Effects used pools, and now groups to do something similar. This reduces API calls, and reduces the effort the driver has to make to change the pipeline and should result in faster rendering.

I believe that shared variables in effects just indicated that effects should only use one reflection variable for a constant instead of several. As an example, you may want the same constant data to be used in both a vertex and pixel shader, so you would mark it shared so that effects would apply it to both shaders. If it was not shared, than effects would make it possible to set a different constant to the vertex shader and the pixel shader. (at least this is how I believe that it worked)

You won't need to worry about sharing since you'll be setting all of the resources yourself. Directx will let you bind the same constant buffer to any combination of shader stages simultaneously.

Without the register samenatics the compiler is free to bind resources to any slots and does not have to follow order of appearance. In fact some resources I know do not follow order of appearance in certain circumstances. Because the mapping cannot be trusted it is necessary to use reflection to find out what the compiler did and where your resources were placed. However if you use register mappings for all of your data, then your mappings will be fixed and you'll be able to do less reflection work if you want.
Thanks for the great explanation DieterVW, just what I needed!
Got the constant buffers to work as they should now :D

This topic is closed to new replies.

Advertisement