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 cbufferfor 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?