簡體   English   中英

為什么在着色器中順序很重要?

[英]Why does order matter in shaders?

快速說明

這個問題帶有C++標記,因為使用C++ DirectX開發人員比使用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;
}

如果我在周圍交換b0b1寄存器,則渲染不再起作用( e1 )。 如果我不理會這些寄存器,並再次在WorldWorldViewProjection之間交換順序,則渲染不再起作用( e2 )。 但是,只需將ViewBuffer ObjectBuffer HLSL文件中的ViewBuffer上方,而不進行其他修改,就可以正常工作。

現在,我希望寄存器的放置非常重要,並且第一個寄存器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

由於float本身是4個字節,因此這意味着float4是16個字節,因此float4x4是64個字節。 那么,如果大小保持不變,為什么順序很重要呢?

  • 為什么在這種情況下必須將ObjectBuffer分配給b0而不是其他b寄存器?

快速說明

我目前正在對問題進行進一步的分析,以便可以給出更詳細和准確的答案。 我將更新問題和答案,以反映我發現更多細節時盡可能多的准確性。


基本答案

上面問題的確切問題(發布HLSL不知道)是HLSL緩沖區不匹配其C#表示形式。 因此,變量的重新排序導致着色器失敗。 但是,我仍然不確定為什么類型相同。 我在沿途中學到了其他一些問題,希望將其發布在這里。


為什么要訂購

經過一些進一步的研究和測試之后,對於類型完全相同的背后原因,我仍然不確定100%。 總的來說,我認為這可能是由於cbuffer預期的類型以及struct類型的順序cbuffer 在這種情況下,如果您的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

您確實必須意識到用於構造更復雜類型的基本類型。 假設您有一個帶有X屬性和Y屬性的Vector2 如果這些內容由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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM