Search code examples
c#slimdx

Trying to pass struct with Vector4 array to cbuffer


I'm playing around with SlimDX (with DX10/11) a bit and I got some nice results so far, but I'm stuck on passing an array of Vector4s to the shaders.

If I try the following struct:

[StructLayout(LayoutKind.Sequential)]
        public struct PerFrameBuffer
        {
            public Vector4 Position;
            public Matrix World;
            public Matrix View;
            public Matrix Projection;

            [MarshalAs( UnmanagedType.ByValArray, SizeConst=2)]
            public Vector4[] Lights;
        }

and try to store it in the following cbuffer:

cbuffer cPerFrame : register( b0 )
{

    float4 xPosition;
    matrix xWorld;
    matrix xView;
    matrix xProjection;

    float4 pointLights[2];
}

I don't get any errors, but the values in the matrices are broken.

However when I declare my struct in c# as follows:

[StructLayout(LayoutKind.Sequential)]
        public struct PerFrameBuffer
        {
            public Vector4 Position;
            public Matrix World;
            public Matrix View;
            public Matrix Projection;
            public Vector4 Light1;
            public Vector4 Light2;
        }

Then everything works fine as expected.

I don't really want to make 64 fields in the struct and fill them up like that (I could with reflection but still). How can I use an array to pass the Vector4's to my constantbuffer?

If by any means this is a dumb idea and should be done in another way, please let me know. It's my first time programming directx and shader related stuff.

To be complete:

Declaration of the buffer in C#

 cPerFrameBuffer = new SlimDX.Direct3D11.Buffer(device, new BufferDescription
            {
                Usage = ResourceUsage.Default,
                SizeInBytes = Marshal.SizeOf(typeof(PerFrameBuffer)),
                BindFlags = BindFlags.ConstantBuffer
            });
            context.VertexShader.SetConstantBuffer(cPerFrameBuffer, 0);

How I'm updating the buffer:

var cb = new AbstractDXManager.PerFrameBuffer()
   {
       Position = new Vector4(camera.Position, 1.0f),
       Projection = Matrix.Transpose(camera.ProjectionMatrix),
       View = Matrix.Transpose(camera.ViewMatrix),
       World = Matrix.Transpose(Matrix.Identity),

   };
   cb.Lights = new Vector4[2];
   cb.Lights[0] = cb.Position;
   cb.Lights[1] = new Vector4(20, 20, 20, 1);
   var context = device.ImmediateContext;

   using (DataStream data = new DataStream(Marshal.SizeOf(typeof(PerFrameBuffer)), true, true))
   {
       data.Write(cb);
       data.Position = 0;
       context.UpdateSubresource(new DataBox(0, 0, data), cPerFrameBuffer, 0);
   }

Thanks in advance


Solution

  • I had the same problem. I changed buffer writing. Now it works

    This is my struct

    [StructLayout(LayoutKind.Sequential,Pack=1)]
    public struct WorldConstants
    {
        public Matrix View;
        public Matrix Projection;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public Color4 [] DiffuseColor;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public Color4[] AmbientColor;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public Vector4[] LightDirection;
        public Vector4 ViewPosition;
    }
    

    I used StructToPtr to get the alligned data.

            using (DataStream stream = new DataStream(streamSize, true, true))
            {
                int rawsize = Marshal.SizeOf(typeof(ElementT));
                IntPtr buffer = Marshal.AllocHGlobal(rawsize);
                foreach (var element in elements)
                {
                    Marshal.StructureToPtr(element, buffer, false);
                    stream.WriteRange(buffer,rawsize);
                }
                Marshal.FreeHGlobal(buffer);
    

    Unfortunately this causes an additional copy of the data. In my case I didn't notice any performance problems.