Using multiple constant buffer

Started by
9 comments, last by caballero 12 years, 2 months ago
Hi,
I've started to learn dx11 with the slimdx framework.
Currently i tried to use two constant buffer to update informations more frequent then other.

But with my current shader code I get some strange behaviour using the two constant buffers:
// cbuffer definition
struct ShaderLightBuffer
{
public Vector3 Ambient;
public float Alpha;
public Vector3 Diffuse;
public float Shininess;
public Vector3 Specular;
public float padding;
public Vector2 SpecularTextured;
public Vector2 padding2;
}

struct ShaderMatrixBuffer
{
public Matrix World;
public Matrix WorldViewProjection;
public Vector3 CameraPosition;
public float padding1;
}

// buffer initialization
_light_constant_buffer = new Buffer(Device, Marshal.SizeOf(typeof(ShaderLightBuffer)), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
_matrix_constant_buffer = new Buffer(Device, Marshal.SizeOf(typeof(ShaderMatrixBuffer)), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

// render logic
ImmediateContext.InputAssembler.InputLayout = _input_layout;
ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

ImmediateContext.InputAssembler.SetVertexBuffers(0, _buffer_bindings.ToArray());
ImmediateContext.VertexShader.Set(_vertex_shader);

ImmediateContext.VertexShader.SetConstantBuffer(_matrix_constant_buffer, 0);
ImmediateContext.VertexShader.SetConstantBuffer(_light_constant_buffer, 1);

ImmediateContext.Rasterizer.SetViewports(Engine.DeviceManager.Viewport);

ImmediateContext.PixelShader.Set(_pixel_shader);
ImmediateContext.PixelShader.SetConstantBuffer(_light_constant_buffer, 2);

ImmediateContext.OutputMerger.SetTargets(DepthBuffer, RenderTarget);

// .. set wvp, world, camera position to matrix buffer..
ShaderMatrixBuffer matrix_buffer = new ShaderMatrixBuffer();
matrix_buffer.WorldViewProjection = view_proj;
matrix_buffer.World = world_matrix;
matrix_buffer.CameraPosition = Engine.Camera.Eye;

// update matrix constant buffer
var matrix_stream = new DataStream(Marshal.SizeOf(typeof(ShaderMatrixBuffer)), true, true);
matrix_stream.Write(matrix_buffer);
matrix_stream.Position = 0;

ImmediateContext.UpdateSubresource(new DataBox(0, 0, matrix_stream), _matrix_constant_buffer, 0);
ImmediateContext.VertexShader.SetConstantBuffer(_matrix_constant_buffer, 0);

foreach (var material in Materials)
{
ShaderLightBuffer light_buffer = new ShaderLightBuffer();
light_buffer.Alpha = 1f;
light_buffer.Ambient = material.Ambient;
light_buffer.Diffuse = material.Diffuse;
light_buffer.Specular = material.Specular;
light_buffer.Shininess = (int)material.Shininess;
light_buffer.SpecularTextured = new Vector2 (0);

if (!string.IsNullOrEmpty(material.TextureFilename))
{
light_buffer.SpecularTextured = new Vector2(1);
if (_shader_resource == null)
{
var texture = Texture2D.FromFile(Device, Path.Combine(material.Path, material.TextureFilename));
_shader_resource = new ShaderResourceView(Device, texture);
}

ImmediateContext.PixelShader.SetShaderResource(_shader_resource, 0);
}

ImmediateContext.PixelShader.SetSampler(_sampler_state, 0);

var light_stream = new DataStream(Marshal.SizeOf(typeof(ShaderLightBuffer)), true, true);
light_stream.Write(light_buffer);
light_stream.Position = 0;

ImmediateContext.UpdateSubresource(new DataBox(0, 0, light_stream), _light_constant_buffer, 0);

ImmediateContext.VertexShader.SetConstantBuffer(_light_constant_buffer, 1);
ImmediateContext.VertexShader.SetConstantBuffer(_light_constant_buffer, 2);

ImmediateContext.Draw(_vertices_count[i], 0);
}
// end render logic
Alright, I hope this code is straight forward, just setting all infos for the pipeline stages, updating the general constant buffer and the cbuffer
for each material used for the geometry.

Now the odd part - shader code
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output;

// Transform the position into world space for lighting, and projected space
float4 vPosWorld = mul( float4(input.position,1), World );
output.position = mul( float4(input.position,1), WorldViewProjection );

// pass texture coordinate
output.texcoord = input.texcoord;

// transform normal into world space for lighting
float3 normal_world = mul( input.normal, (float3x3) World);
float3 light_vector = normalize( LightPosition - vPosWorld.xyz );

// compute the ambient and diffuse components of illumination
output.color.rgb = LightColor * MaterialAmbient;
output.color.rgb += LightColor * MaterialDiffuse * saturate( dot( light_vector, normal_world ) );

if( SpecularTextured.x > 0)
{
float3 camera = normalize( vPosWorld.xyz - CameraPosition );
float3 reflection = reflect ( light_vector, normal_world );
float phone_value = saturate( dot( reflection, camera ) );
output.color.rgb += MaterialSpecular * pow( phone_value, MaterialShininess );
}

// odd things happens if i comment the next line -> [7480] D3D11: WARNING: ID3D11DeviceContext::Draw: The size of the Constant Buffer at slot 1 of the Vertex Shader unit is too small (64 bytes provided, 144 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects. [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL ]
output.color.rgb = MaterialDiffuse;
output.color.a = MaterialShininess;

return output;
}

float4 PS( PS_INPUT input ) : SV_Target
{
float4 output = input.color;
//// Sample and modulate the texture
if ( SpecularTextured.y > 0 )
output.rgb *= MeshTexture.Sample( samLinear, input.texcoord );
return output;
}
If I set the color values to diffuse the in the vertex shader it draws my geometry correctly with the defined color.
But commenting out the color assignment (since this line was used for debugging), will give me the following warning:
[7480] D3D11: WARNING: ID3D11DeviceContext::Draw: The size of the Constant Buffer at slot 1 of the Vertex Shader unit is too small (64 bytes provided, 144 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects. [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL ]

This happens also if I use UpdateSubResource method to update both constant buffers.
It seems that the second constant buffer update overwrites also the first constant buffer.

I wanted to ask how to handle multiple constant buffer updates? Am I missing something?
Advertisement
When you have multiple constant buffers, you have to put them in separate slots. i don't know how to use slimdx, but in regular dx11, you can set the vertex's constant buffer by calling VSSetConstantBuffers of the device context, and setting the second parameter as 0 for the first constant buffer, and 1 for the second. If the vertex and pixel shader share a constant buffer, you will have to bind the same constant buffer separately to each shader, using VSSetConstantBuffers and VSSetConstantBuffers.
Thanks for the quick response!
I think you meant VSSetConstantBuffer and PSSetConstantBuffer in the last sentence.

I've replaced setting both cbuffer at the same time

[color=#660066]ImmediateContext[color=#666600].[color=#660066]VertexShader[color=#666600].[color=#660066]SetConstantBuffers[color=#666600]([color=#000088]new[color=#666600][][color=#000000] [color=#666600]{[color=#000000] _matrix_constant_buffer[color=#666600],[color=#000000] _light_constant_buffer [color=#666600]},[color=#000000] [color=#006666]0[color=#666600],[color=#006666]2[color=#666600]);

with

ImmediateContext.VertexShader.SetConstantBuffer(_matrix_constant_buffer, 0);
ImmediateContext.VertexShader.SetConstantBuffer(_light_constant_buffer, 1);

and also set the cbuffer to the pixel shader
ImmediateContext.PixelShader.SetConstantBuffer(_light_constant_buffer, 1);

I also tried to attach - : register(cbx) to the cbuffer declaration in the shader code.
but the result is the same error message sad.png

Alright, I tried the same code with the sharpdx framework with the same ouput..
sorry it didn't work for you, but one other thing, iis (as far as i know) you have to put each buffer in it's own slot, even if two shaders use the same buffer, they should go into separate slots, so when you bind your pixel shaders buffer, you should try changing the '1' to a '2'

also, can you show the structure of your constant buffers?
The structure of the constant buffer is


cbuffer Matrixbuffer : register(cb0)
{
matrix World;
matrix WorldViewProjection;
float3 CameraPosition;
float padding;
};

and

cbuffer LightBuffer : register(cb1)
{
float3 MaterialAmbient; // Material's ambient color
float MaterialAlpha;
float3 MaterialDiffuse; // Material's diffuse color
float MaterialShininess;
float3 MaterialSpecular; // Material's specular color
float padding2;
float2 SpecularTextured;
float2 padding3;
};


It seems to me that updating the constant buffer in the foreach loop overwrites the first "global" constant buffer.
Could there be something that I am missing?

Would a pix run file help you to get more information out of it?
actually, could you post the constant buffers in your main app?

This might not be the problem, but just check it out. Are you making sure you are packaging your constant buffers correctly in your app? the shader takes 16 byte chunks for the constant buffer, so if you have a variable that's split between two chunks, it will cause problems. float variables are 4 bytes, float3's are 12 bytes, matrices are 16 bytes. I see you have it aligned correctly in the shader, so i'm guessing it's probably the same for your app, but double check.
I just noticed you are not setting your constant buffers after you change them. I think you have to reset them every time they change. also, try using update subresource instead:
ImmediateContext->UpdateSubresource( light_buffer, 0, NULL, &_light_constant_buffer, 0, 0 );
Somethng like that anyway

[7480] D3D11: WARNING: ID3D11DeviceContext::Draw: The size of the Constant Buffer at slot 1 of the Vertex Shader unit is too small (64 bytes provided, 144 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects. [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL ]


The error message has all the information about the problem. Your constant buffer bound at slot 1 has size of 64 bytes, but the shader is reading data from area outside of the 64 bytes area.

This means that you have the light buffer bound to slot 1, but on the shader expects the matrix buffer to be at slot 1. You have to have same registers at the program side and at the shader side. Remember that the register indices are 0-based.



Cheers!

Hi, I've updated all code parts in my previous posts.

Thx kauna for the hint since after changing the slots in the code no warnings or errors occur.

// I've set the following for slots for the buffers to be comprehensible
vs.setconstantbuffer(matrix_buffer, 1);
vs.setconstantbuffer(light_buffer, 0);
ps.setconstantbuffer(light_buffer, 2);

I thought that the cbuffers in the shader will be declared in sequential order since I'm defining first the Matrix cbuffer then the Light cbuffer. I tried to fixate that by setting : register(cb#).

Obviously i was wrong with this assumption.

Now it's all black (of course it is black, I think I am really learning dx the hard way) without any warnings thanks for your help!

Theoretically, if I define more cbuffers, how can I find out which slots they were assigned to?

// I've set the following for slots for the buffers to be comprehensible
vs.setconstantbuffer(matrix_buffer, 1);
vs.setconstantbuffer(light_buffer, 0);
ps.setconstantbuffer(light_buffer, 2);


Hi, you may set the light_buffer to slot 0 for vertex shader and pixel shader. I assume that in the shader code you have probably slot 0 (both vs and ps) for the light_buffer?

You may use shader reflection API to extract information from the shaders (including which constant buffer is expected at which register ...)

Best regards!

This topic is closed to new replies.

Advertisement