2
\$\begingroup\$

I have a question about constant buffers in directX 11. They really confuse me and after searching on google most sites simply supply sample code without explaining how it works. I am probably overlooking something but, as of now, they appear to me to work by sheer magic. Say I have two constant buffers in an hlsl file:

struct Light
{
    float3 dir;
    float4 ambient;
    float4 diffuse;
};

cbuffer cbPerFram
{
    Light light;
};

cbuffer cbPerObject
{
    float4x4 WVP;
    float4x4 World;
};

And I create an equivalent buffer in my C++ program like so:

//Create the buffer to send to the cbuffer in effect file
D3D11_BUFFER_DESC cbbd; 
ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC));

cbbd.Usage = D3D11_USAGE_DEFAULT;
//cbPerObject is a struct with same layout as cbPerObject in HLSL file
cbbd.ByteWidth = sizeof(cbPerObject);
cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbbd.CPUAccessFlags = 0;
cbbd.MiscFlags = 0;

hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerObjectBuffer);
//Create the buffer to send to the cbuffer per frame in effect file
ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC));

cbbd.Usage = D3D11_USAGE_DEFAULT;
//cbPerFrame is struct with same layout as in cbPerFrame in effect file
cbbd.ByteWidth = sizeof(cbPerFrame);
cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbbd.CPUAccessFlags = 0;
cbbd.MiscFlags = 0;

And then I update one like this:

d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerFrameBuffer);

How does directX know which constant buffers in my shader are going to be used for VS and which for PS and which buffers in my codes correspond to which buffers in my hlsl? For example I call VSSetConstantBuffers above and pass the arguments and it automatically knows to throw that stuff in the per object buffer as opposed to trying to put it in the perFrame buffer. As far as I can tell when creating cbPerObjectBuffer, I never explicitly bound it to the cbPerObject buffer in my hlsl. Is slot number dependent on the order the buffers appear in hlsl? The only way I can figure is that the data structures simply define an interface to the buffer memory on the card and it is up to the programmer to use the correct one. For example, I COULD attempt to use cbPerFrame in my vertex shader and indeed the same data can be accessed that way but in order to get correct results you would have to access it using offsets into the light object. Does what I am asking make any sense? lol

\$\endgroup\$
6
\$\begingroup\$

The key step that you're missing is the implicit slot assignment that occurs when you compile a shader. When you compile an HLSL shader that contains a bindable object (be it a Texture2D, RWStructuredBuffer, or cbuffer), each object must be assigned a slot number. This corresponds to the UINT StartSlot parameter to e.g. VSSetConstantBuffers. You can optionally declare a slot explicitly using e.g. : register(c0) as a declaration suffix, but if you omit the slot, the compiler will assign the lowest free slot available.

As for your original question, how DidrectX "knows" what cbuffer to bind, well it doesn't. What's actually happening is that since in your vertex shader, you only refer to one cbuffer, that gets assigned slot 0 for the vertex shader when you compile it, and the other is ignored. Likewise, the same occurs for the pixel shader, except the other cbuffer is assigned to slot 0. This means that when you assign buffers to the 0 StartSlot for both the vertex and pixel shader stages, it just so happens that those are the right buffers for those shaders. Basically, it just happens to work because slot 0 is always the right slot. Once you get to more advanced shaders that reference multiple cbuffers, you'll want to get into the habit of assigning slots explicitly.

\$\endgroup\$
  • \$\begingroup\$ Good answer! Now I am wondering though how directX knows, when I make the function call d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ), that the vertex shader is using cbPerObject? Why doesn't it put that in cbPerFrame instead? I never explicitly state which buffer is going to be used for which shader. \$\endgroup\$ – user3355098 Nov 4 '14 at 2:05
  • \$\begingroup\$ @user3355098 If the only cbuffer you use in your vertex shader is cbPerObject, then that gets assigned slot 0. If you used both cbPerFrame and cbPerObject, they would be implicitly assigned slots 0 and 1, respectively. This is because if you don't use one of the buffers, it's eliminated entirely and not assigned a slot. \$\endgroup\$ – MooseBoys Nov 4 '14 at 7:32
5
\$\begingroup\$

VSSetConstantBuffers stands for VertexShaderSetConstantBuffers. Hence you also got PSSetConstantBuffers for the pixel shader.

\$\endgroup\$
  • \$\begingroup\$ yes, this is what i was looking for, somehow I missed that VSSetConstantBuffer only sets cbuffer in Vertex shader. In OpenGL its a bit different, a single call to setUniform will affect across all the shaders used in that particular shader program. \$\endgroup\$ – 2am Oct 23 '15 at 4:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.