SharpDX.Toolkit Constant Buffer

You should not expect a lot of documentation on ShaprDX. You may argue that it does not need it as it is a wrapper around DirectX so everything could be found on MSDN. That is true, as long as you use pure DirectX. I wanted to make some use of the SharpDX.Toolkit, and that is where all ‘problems’ come from.

Working with DirectX, like with any graphics API, is about memory management. But not only assigning and disposing. You have to use different types of buffers, and get everything into the rendering pipeline. I managed to create a simple form with a graph (I will not go much into details), but I also wanted to be able to write in the same window. The easiest way I found is to use SharpDX.Toolkit.

My most recent problem, described also on gamedev.stackexchange.com, was related to setting up a constant buffer that Geometry Shader could use. Basically those were some constants used in geometry transformations. I wrote the code in C# using SharpDX without a Toolkit, and everything worked fine. When I switched to the Toolkit then problems appeared. It looked like the constant buffer was first created, then some values were assigned, and after assigning it to the shader it was cleared. It is nicely visible on the screen below, taken from the Visual Studio 2013 (I like the dark theme it provides). So, on the right hand side there is a Graphics Event List which shows the order in which actions took place. The ones with a small clock/arrow are the ones that were created before the captured frame was drawn, on the screen below that would be for example event 72. Below that is Graphics Object Table listing all the objects created before and during the time a captured frame was created. From the creation of the device to buffers. In the left part of the picture there are properties of selected object from the table. In this case object obj:9, which is my constant buffer of length 16 (Bytes). The problem is that Graphics Event List after setting up a shader, GSSetShader, the constant buffer is the object obj:9, which consist of zeros. Not something I would expect.

When you select an event from Graphics Event List some of the objects on Graphics Object Table are in white, some are in grey, and some of them have * next to their names. Stars indicate which of them are active. So as in the screen below I have selected the Draw event it showed that active constant buffer of length 16 is obj:9. The constant buffer I wanted to be active, which contains non-zero values, is obj:16, also of length 16.

Visual Studio Graphics Debuger

Clean Constant Buffer

In my effect file, I defined the buffer as

cbuffer ConstantBuffer : register(b0)
{
    float slideX;
    float sinPi075;
    float cosPi075;
    float scaleK;
}

In the main program body I have defined a structure that would store the data for the constant buffer:

[StructLayout(LayoutKind.Explicit, Size = 16)]
struct GS_CONSTANT_BUF_DATA
{
    [FieldOffset(0)]
    public float slideX;
    [FieldOffset(4)]
    public float sinPi075;
    [FieldOffset(8)]
    public float cosPi075;
    [FieldOffset(12)]
    public float scaleK;
};

Followed by the first attempt to create a constant buffer, which worked in non-toolkit based version of the program:

private SharpDX.Direct3D11.Buffer _constantBuffer;
private GS_CONSTANT_BUF_DATA constData;
private int sizeOfconstData;
private SharpDX.Direct3D11.Device _device;
private void InitializeConstantBuffer()
{
    System.Diagnostics.Debug.WriteLine("InitializeConstantBuffer");
    constData = new GS_CONSTANT_BUF_DATA
    {
        slideX = 0.0f,
        sinPi075 = (float)Math.Sin(Math.PI * 0.75),
        cosPi075 = (float)Math.Cos(Math.PI * 0.75),
        scaleK = 0.003f
    };
    _device = (SharpDX.Direct3D11.Device)GraphicsDevice.MainDevice;
    sizeOfconstData = Utilities.SizeOf<GS_CONSTANT_BUF_DATA>();
    _constantBuffer = new SharpDX.Direct3D11.Buffer(
                                       _device
                                     , sizeOfconstData
                                     , ResourceUsage.Default
                                     , BindFlags.ConstantBuffer
                                     , CpuAccessFlags.None
                                     , ResourceOptionFlags.None
                                     , 0);
    _device.ImmediateContext.GeometryShader.SetConstantBuffer(0, _constantBuffer);
    _device.ImmediateContext.UpdateSubresource(ref constData, _constantBuffer);
}

Finally a drawing method:

public void Draw(GraphicsDevice graphicsDevice, PrimitiveType primitiveType, EffectPass pass)
{
    if (pass != null)
        pass.Apply();

    // Setup the Vertex Buffer
    graphicsDevice.SetVertexBuffer(0, vertexBuffer);

    // Setup the Vertex Buffer Input layout
    graphicsDevice.SetVertexInputLayout(InputLayout);

    graphicsDevice.Draw(primitiveType, 4, 0);
}

And the main draw method:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    for (int i = 0; i < technique.Passes.Count; ++i)
    {
       _device.ImmediateContext.GeometryShader.SetConstantBuffer(0, _constantBuffer);
       _chartGeometry.Draw(
                        GraphicsDevice
                      , SharpDX.Toolkit.Graphics.PrimitiveType.LineList
                      , technique.Passes[i]);
    }
}

My idea was, that

I have also tried changing buffer to dynamic, and hence, changing UpdateSubresourceto Map/Unmap. The result is the same, see creen below, this time obj:16 (has data) and obj:8 (does not).

Clean Constant Buffer

Clean constant buffer afer map/unmap.

If you have not forgotten to set up debug option while compiling the effect, like

compiledEffect = (new EffectCompiler()).CompileFromFile(@"Content\MiniTri - Copy.fx", EffectCompilerFlags.Debug | EffectCompilerFlags.SkipOptimization | EffectCompilerFlags.WarningsAreErrors);

then you should be able to debug the HLSL code. Then you may also observe the values of variables from the buffer in the Locals window, see the screen below.

List of local variables during HLSL debug.

List of local variables during HLSL debug.

Everything looked like if something in the background was cleaning the buffer, or not even cleaning but creating new one as in the Graphics Object Table you could notice two constant buffers of size 16B with two differen obj: numbers. I decided to investigate the Toolkit source.

I found out that there is a way to update the constant buffer through effects, later on gamedev.stackexchange.com I was told that it I could set up constant buffer “using an Effect in the Toolkit, which is similar to the XNA Effect or the native D3D11 Effect API”.

In the Toolkit’s EffectConstantBuffer.cs there is a the most important seems to be the function internal EffectConstantBuffer, which reads a structure of a constant buffer form the hlsl effect sets a corresponding buffer, and by default clears it setting all values to zeroes. And that was what caused my problem.

The solution was simple, to use an effect to update the buffer. It can be done twofold by creating a buffer and making it ditry (my solution):

compiledEffect = (new EffectCompiler()).CompileFromFile(
                                 @"Content\MiniTri - Copy.fx"
                                 , EffectCompilerFlags.Debug
                                  |EffectCompilerFlags.SkipOptimization
                                  |EffectCompilerFlags.WarningsAreErrors
                                 );
_effect = new Effect(GraphicsDevice, compiledEffect.EffectData);

constData = new GS_CONSTANT_BUF_DATA
{
    slideX = 0.0f,
    sinPi075 = (float)Math.Sin(Math.PI * 0.75),
    cosPi075 = (float)Math.Cos(Math.PI * 0.75),
    scaleK = 0.003f
};

_effect.ConstantBuffers[0].Set<GS_CONSTANT_BUF_DATA>(0, constData);
_effect.ConstantBuffers[0].IsDirty = true;

or by changing values of allocated buffer and applying the changes (solution suggested by xoofx):

_effect.Parameters["slideX"].SetValue(0.0f);
_effect.Parameters["sinPi075"].SetValue((float)Math.Sin(Math.PI * 0.75));
_effect.Parameters["cosPi075"].SetValue((float)Math.Cos(Math.PI * 0.75));
_effect.Parameters["scaleK"].SetValue(0.003f);
_effect.Apply();

The original post on gamedev.stackexchange.com can be found here.

Leave a Reply