1

For example, instead of:

VertexShader.hlsl

cbuffer VSPerInstance : register(b0){
    matrix World, View, Projection;
};

PixelShader.hlsl

cbuffer PSPerInstance : register(b0){
    float4 AmbientColor;
    float4 DiffuseColor;
    float4 SpecularColor;
    float4 EmissiveColor;
};

I could have:

MyInclude.hlsl

cbuffer PerInstance : register(b0){
    matrix World, View, Projection;
    float4 AmbientColor;
    float4 DiffuseColor;
    float4 SpecularColor;
    float4 EmissiveColor;
};

In a include file, when updating the constant buffer this would reduce the number of calls to ID3D11DeviceContext::Map, although I would still have to copy the same amount of bytes and set the constant buffer for each stage, like: ID3D11DeviceContext::VSSetShader, ID3D11DeviceContext::PSSetShader, etc.

So my question is, is it even legal to set the same constant buffer in multiple shader stages? And is there anything negative about it that I should reconsider? Since I started learning Direct3D programming, all the examples that I have seem use individual buffers for each stage as in the first example, so I don't know if this is a good practice.

Just to make things clearer, I'm still using more than one constant buffer anyway, I have one constant buffer for each frequency of update, one for each instance, for each partition, for each draw call...

Sol Sol
  • 25
  • 5
  • 1
    It is legal, but I don't see how view and projection matrices would be considered per instance – mateeeeeee Sep 06 '22 at 14:59
  • I would personally use different cbuffer for colors which you might call Material cbuffer and that could potentially be shared between some instances, but you know your use case the best – mateeeeeee Sep 06 '22 at 15:01
  • @mateeeeeee Hi, thanks for the answer, I've run some tests yesterday, and it seemed to work as expected, it's true that the view and projection matrices aren't per instance, but I don't have a per frame buffer yet so I had to put it in the per instance one. Still, it's odd because I don't find any example that does this. – Sol Sol Sep 06 '22 at 18:30
  • Maybe tutorials never go that much in depth to actually have good use of it. fwiw I used that approach in my toy engine :) – mateeeeeee Sep 06 '22 at 18:39
  • @mateeeeeee Yes, I agree. I think it's also because they want to keep the implementation simple, so they avoid optmizing the shader in order to make it easier to understand. – Sol Sol Sep 08 '22 at 01:18

1 Answers1

1

You generally want to organize the constant buffers by "frequency of update". So in your case, you'd probably want:

cbuffer VSPerInstance : register(b0){
    matrix World, View, Projection;
};

which is updated per render frame and a different.

cbuffer PerInstance : register(b1){
    float4 AmbientColor;
    float4 DiffuseColor;
    float4 SpecularColor;
    float4 EmissiveColor;
};

which is updated per material.

You can reuse the same cbuffers at different stages of the render pipeline, and it's generally a good idea to minimize binding and update costs. The only real restriction is you can't have the same resource bound as an input and output at the same time (such as a texture bound for reading by the pixel shader and as render target in the same Draw).

See this venerable presentation from Gamefest 2007) for general recommendations on constant buffer usage for Direct3D 10 and 11.

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81
  • Thanks for the answer, I was afraid that it would be somehow forbidden to do that, but it's great that I was wrong! :) Currently, I'm using different transformations and materials for each instance that I draw, so I would update both the buffers at the same frequency, this is why I thought about joining them in a single buffer that is bind to both Vertex and Pixel shader stages. I followed [this](https://learn.microsoft.com/en-us/windows/win32/dxtecharts/direct3d10-frequently-asked-questions#what-is-the-best-way-to-organize-my-constant-buffers) article to organize my buffers. – Sol Sol Sep 08 '22 at 01:11