此问题带有C++
标签,因为DirectX
中使用C++
的开发人员比C#
中的更多。我不认为这个问题与任何一种语言都直接相关,而是与所使用的类型(据我所知完全相同)或DirectX
本身及其如何编译着色器有关。如果在C++
工作的人知道更好,更具描述性的答案,那么我宁愿选择这个答案而不是自己的答案。我了解两种语言,但主要使用C#
。
在HLSL
着色器中,设置常量缓冲区时遇到了一个非常奇怪的问题。有问题的原始常量缓冲区设置如下:
cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}
cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}
如果我交换b0
和b1
寄存器,则渲染不再起作用( e1 )。如果我不理会这些寄存器,并再次在World
和WorldViewProjection
之间交换顺序,则渲染不再起作用( e2 )。但是,只需将ViewBuffer
文件中的ObjectBuffer
移动到HLSL
上方而不进行其他修改,就可以了。
现在,我希望寄存器的放置非常重要,并且第一个寄存器b0
需要该缓冲区中给出的三个属性,并且我知道HLSL
常量缓冲区必须位于16个字节中大块。但是,这让我有些疑问。
考虑到HLSL
期望常量缓冲区为16字节块的事实;
float4x4
类型与Matrix
类型实际上不是数组一样吗?
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ TOTAL ] = 64 bytes
由于float
本身是4个字节,因此这意味着float4
是16个字节,因此float4x4
是64个字节。那么,如果大小保持不变,为什么顺序很重要?
ObjectBuffer
分配给b0
而不是其他任何b
寄存器?答案 0 :(得分:0)
我目前正在对问题进行进一步的分析,以便可以给出更详细和准确的答案。我将更新问题和答案,以在发现更多细节时尽可能多地反映出准确性。
上述问题的确切问题(发布时尚不知道)是HLSL
缓冲区与它们的C#
表示形式不匹配;因此,变量的重新排序导致着色器失败。但是,我仍然不确定为什么类型相同。我在沿途中学到了其他一些东西,希望得到答案,因此决定将其发布在这里。
经过一些进一步的研究和测试,我仍然不确定100%都是相同类型的背后原因。总体而言,我认为这可能是由于cbuffer
中期望的类型和struct
中类型的顺序造成的。在这种情况下,如果您的cbuffer
首先期望bool
,然后期望float
,则重新排列会导致问题。
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
我投入了一些研究工作来测试类型的字节大小的差异,但这似乎并没有太大改变,但是我将在这里发布常见基本类型的发现:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
您必须意识到用于构造更复杂类型的基本类型。假设您有一个Vector2
,该属性带有X
属性和一个Y
属性。如果这些由float
类型表示,则在下一个属性之前需要8字节的填充,除非您有其他帮助达到16字节的填充。但是,如果这些类型由double
类型或decimal
类型表示,则大小会有所不同,您需要注意这一点。
我能够解决注册问题;设置缓冲区时,它也对应于C#
端。设置缓冲区时,需要为这些缓冲区分配索引,并且HLSL
应该使用相同的索引。
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
由于将缓冲区分配给正确的寄存器,因此上面的代码将按预期工作。但是,例如,如果将相机的缓冲区更改为8,则必须将cbuffer
分配给寄存器b8
才能正常工作。出于确切的原因,下面的代码不起作用。
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);