将数组编组到像素缓冲区的常量缓冲区中,无法按预期工作

时间:2018-07-01 19:28:11

标签: c# directx-11 hlsl sharpdx packing

我正在尝试使用SharpDX学习Direct3d编程,并且正在互联网上研究各种示例,以及Frank Luna的DirectX 11 3D游戏编程简介。目前,我正在尝试传递多个光源(Direction,Point和Spotlight)使用数组作为像素着色器。现在,据我所知,HLSL中的打包是如何工作的,所有的东西都应该工作,但是,当然不是,而且我似乎找不到问题所在。

在我的HLSL着色器代码中,我的照明结构具有以下结构定义:

struct A {
// ...
template<typename T> bool f(uint8_t _i1, T& _val);
template<typename T> bool f(E1 _i1, T& _val);
};

template<> bool f<is_PoD<T> && not_like_gsl_span<T>>(uint8_t /*...*/}
template<> bool f<is_PoD<T> && not_like_gsl_span<T>>(E1 /*...*/}
template<> bool f<is_like_gsl_span<T>>(uint8_t /*...*/}
template<> bool f<is_like_gsl_span<T>>(E1 /*...*/}

正如您所看到的,填充变量都在那里,以确保所有内容都被整齐地打包到float4中,并遵守16字节的边界。

然后在我的C#代码中,我具有以下对应的结构':

#define NUM_LIGHTS 1 //Max number of lights in scene

struct DirectionalLight {
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Direction;
    float pad;
};

struct PointLight {
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Position;
    float Range;
    float3 Attentuation;
    float pad;
};

struct SpotLight {
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Position;
    float Range;
    float3 Direction;
    float Spot;
    float3 Attentuation;
    float pad;
};

在HLSL中,我的常量缓冲区的设置如下:

[StructLayout(LayoutKind.Sequential, Size=64)]
    public struct DirectionalLight
    {
        public Color4 Ambient;
        public Color4 Diffuse;
        public Color4 Specular;
        public Vector3 Direction;
        public float pad;
    }

[StructLayout(LayoutKind.Sequential, Size=96)]
    public struct Spotlight
    {
        public Color4 Ambient;
        public Color4 Diffuse;
        public Color4 Specular;
        public Vector3 Position;
        public float Range;
        public Vector3 Direction;
        public float Spot;
        public Vector3 Attentuation;
        public float pad;
    }

[StructLayout(LayoutKind.Sequential, Size=80)]
    public struct PointLight
    {
        public Color4 Ambient;
        public Color4 Diffuse;
        public Color4 Specular;
        public Vector3 Position;
        public float Range;
        public Vector3 Attentuation;
        public float pad;
    }

再次在C#中:

cbuffer cbPerFrame : register(b1) //register b0 used for cbuffer cbPerObject //(world, view, projection matrices)
{
    DirectionalLight gDirLight[NUM_LIGHTS];
    SpotLight gSpotLight[NUM_LIGHTS];
    PointLight gPointLight[NUM_LIGHTS];
    float3 cameraPosition;
    float fogStart;
    float fogEnd;
    float3 pad;
};

请注意,目前我正尝试使它尽可能简单,只传递每种类型的1个光(这就是为什么SizeConst = 1)

然后,按如下方式在C#中创建我的常量缓冲区:

[StructLayout(LayoutKind.Sequential, Size=272)]
    public struct ConstantBufferPerFrame
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public DirectionalLight[] DirectionalLight;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public Spotlight[] SpotLight;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public PointLight[] PointLight;
        public Vector3 CameraPosition;
        public float FogStart;
        public float FogEnd;
        public Vector3 pad;
    }

然后我按如下所示写入缓冲区:

...
int size = Marshal.SizeOf(typeof(ConstantBufferPerFrame));

_constantBufferPerFrame = new SharpDX.Direct3D11.Buffer(device, size,
                ResourceUsage.Dynamic, BindFlags.ConstantBuffer, 
                CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
...
_context.PixelShader.SetConstantBuffer(1, _constantBufferPerFrame);
...

现在所有这些看起来都应该可以工作,但是当我运行程序时,常量缓冲区是完全错误的。

例如,如果C#ConstantBuffer对象包含以下值:

...

 DataStream mappedResource;
            context.MapSubresource(_constantBufferPerFrame, 0, MapMode.WriteDiscard, MapFlags.None, out mappedResource);

 mappedResource.Write(_perFrameBuffer);

 context.UnmapSubresource(_constantBufferPerFrame, 0);

 context.PixelShader.SetShaderResource(0, texture);

 context.DrawIndexed(indexCount, 0, 0);
...

在图形分析器中,我得到以下编译器输出:

ConstantBuffer
{
  DirectionalLight:
  {
     Ambient: {Alpha:1, Red:100, Green:100, Blue:100},
     Diffuse: {Alpha:1, Red:0, Green:0, Blue:0},
     Specular: {Alpha:1, Red:0, Green:0, Blue:0},
     Direction: {X:0, Y:0, Z:0}
  },
  PointLight:
  {
    Ambient: {Alpha:1, Red:10.7, Green:0.7, Blue:0.7},
    Diffuse: {Alpha:1, Red:0, Green:0, Blue:0},
    Specular: {Alpha:1, Red:0, Green:0, Blue:0},
    Position: {X:201.0751 Y:6.075078 Z:2145},
    Range: 100
    Attentuation: {X:0 Y:1 Z:0}
    Pad: 0
  },
  Spotlight:
  {
    Ambient: {Alpha:1, Red:10.7, Green:0.7, Blue:0.7},
    Diffuse: {Alpha:1, Red:0, Green:0, Blue:0},
    Specular: {Alpha:1, Red:0, Green:0, Blue:0},
    Direction: {X:0, Y:0, Z:0}
    Position: {X:0, Y:0, Z:0}
    Range: 0,
    Spot: 0,
    Attentuation: {x:0, Y:0, Z:0},
    Pad: 0
  },
  CameraPosition: {X:195, Y:16, Z:2145},
  FogStart: 50,
  FogEnd: 1000,
  Pad: {X:0, Y:0, Z:0}
}

但是,常量缓冲区具有以下值:

// cbuffer cbPerFrame
// {
//
//   struct DirectionalLight
//   {
//       
//       float4 Ambient;                // Offset:    0
//       float4 Diffuse;                // Offset:   16
//       float4 Specular;               // Offset:   32
//       float3 Direction;              // Offset:   48
//       float pad;                     // Offset:   60
//
//   } gDirLight;                       // Offset:    0 Size:    64
//   
//   struct SpotLight
//   {
//       
//       float4 Ambient;                // Offset:   64
//       float4 Diffuse;                // Offset:   80
//       float4 Specular;               // Offset:   96
//       float3 Position;               // Offset:  112
//       float Range;                   // Offset:  124
//       float3 Direction;              // Offset:  128
//       float Spot;                    // Offset:  140
//       float3 Attentuation;           // Offset:  144
//       float pad;                     // Offset:  156
//
//   } gSpotLight;                      // Offset:   64 Size:    96
//   
//   struct PointLight
//   {
//       
//       float4 Ambient;                // Offset:  160
//       float4 Diffuse;                // Offset:  176
//       float4 Specular;               // Offset:  192
//       float3 Position;               // Offset:  208
//       float Range;                   // Offset:  220
//       float3 Attentuation;           // Offset:  224
//       float pad;                     // Offset:  236
//
//   } gPointLight;                     // Offset:  160 Size:    80
//   float3 cameraPosition;             // Offset:  240 Size:    12 [unused]
//   float fogStart;                    // Offset:  252 Size:     4 [unused]
//   float fogEnd;                      // Offset:  256 Size:     4 [unused]
//   float3 pad;                        // Offset:  260 Size:    12 [unused]
//
// }

如您所见,CameraPosition和Fog End / Start值正确编组了(或者是?当我再次查看它时,看到位置被切换了,我期望浮动6,7中的CameraPosition变量和8以及2个雾漂浮在位置9和10上),似乎只是阵列灯光结构看上去有点儿摇摇欲坠。

我一定在某处缺少某物,有人可以帮助我吗?

1 个答案:

答案 0 :(得分:0)

使用Marshal属性和DataStream确实不能做到这一点。 DataStream不提供Marshal / PInvoke层,但直接转储到内存中而中间没有Marshal层。我将在结构中使用固定数组,或者如果NUM_LIGHTS必须以某种方式自适应,则将自定义序列化为DataStream / buffer。