c# - 为什么顺序在着色器中很重要?
问题描述
速记
这个问题有C++
标签,因为与DirectX
in合作的开发人员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
andb1
寄存器,渲染不再有效(e1)。如果我不理会这些寄存器,World
并WorldViewProjection
一次又一次地交换顺序,则渲染不再起作用(e2)。但是,只需在文件中移动ViewBuffer
上述内容而不进行其他修改,它就可以正常工作。ObjectBuffer
HLSL
现在,我希望寄存器放置非常重要,并且第一个寄存器b0
需要该缓冲区中给定的三个属性,并且我知道HLSL
常量缓冲区需要在 16 字节块中。然而,这给我留下了一些问题。
问题
鉴于HLSL
期望常量缓冲区位于 16 字节块中的事实;
- 为什么排序在e2中的排序如此重要?
类型与本质上是数组数组的类型不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
由于 afloat
本身是 4 个字节,这意味着 afloat4
是 16 个字节,因此 afloat4x4
是 64 个字节。那么,如果尺寸保持不变,为什么顺序很重要呢?
- 为什么在这种情况下
ObjectBuffer
必须分配到b0
而不是任何其他b
寄存器?
解决方案
速记
我目前正在进一步分析这个问题,以便我可以给出更详细和准确的答案。当我发现更多细节时,我将更新问题和答案以尽可能准确地反映。
基本答案
上述问题的确切问题(在发布时未知)是HLSL
缓冲区与其表示不匹配C#
;因此,变量的重新排序导致着色器失败。但是,我仍然不确定为什么类型相同。我在路上学到了一些其他的东西来寻求答案,并决定把它们贴在这里。
为什么订单很重要
经过一些进一步的研究和测试,我仍然不能 100% 确定类型都相同的原因。总的来说,我相信这可能是由于预期cbuffer
的类型和struct
. 在这种情况下,如果您cbuffer
期望 a bool
first 然后 a 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);
推荐阅读
- python-3.x - 如何使用 python 中的 Paramiko 库将文件从 Windows 复制到 linux
- javascript - 在 html/js 中加密 src 的值
- python - 如何在python的where子句中使用变量来访问sql
- android - 在android中使用套接字发送和接收字符串
- python - 如果将光标悬停在小部件的特定区域上,则更改光标
- flutter - sms_autofill 无法检测到 OTP
- python - 如何在 Python 中找到以下问题的最小值?
- python - Tkinter 回调中的异常 - TypeError:字符串格式化期间并非所有参数都转换
- python - Python 流中的第一个非重复字符
- java - 集群标记显示不准确