1. 程式人生 > >hlsl shader編譯中遇到的一個坑

hlsl shader編譯中遇到的一個坑

        最近在修改我們引擎中shader編譯相關的邏輯的時候遇到了一個坑。寫這個引擎的前輩為了讓vertex shader和fragmentshader名稱相同的暫存器常量對於的暫存器索引也是一樣的。在編寫shader程式碼的時候手動指定了每個常量的暫存器索引並人工保證索引不會衝突,如此一來就可以在邏輯程式碼中對於每個名稱的uniform只記錄一個索引號並利用這個索引號來填充暫存器常量。我個人認為這樣寫不太好,一方面由於shader程式碼中存在著很多預編譯巨集,這回導致很多在巨集內部的索引號沒有利用起來使得整個暫存器的可用空間極大的浪費。另一方面每次修改一個效果或者增減一些效果的時候需要增加或減少一些uniform。由於要保證vertex shader 和fragment shader中索引相同,需要調整兩個shader中暫存器的索引以便達到索引相同的目的,這是一個非常頭疼並且沒什麼價值的工作。因此我就把shader的邏輯程式碼中記錄暫存器索引的地方改為針對每個shader都單獨記錄一個索引ID並分別使用各自的ID來設定暫存器常量的資料,這樣在寫shader的時候就不需要考慮索引衝突問題。

        然而悲劇就這樣發生了,編譯後的shader不知道為什麼索引號好像都出問題了。按照編譯生成的索引號去填充資料結果有時候表現就不對。一開始以為是設定資料的時候出問題了,找了好久都沒有發現問題的所在。然後就使用了shader版的列印大發(把每個資料單獨分離出來最終當做顏色輸出)一個一個數據看,矩陣一行一行看。最終發現有些暫存器資料好像被寫壞了,然後再使用註釋大法找到了相關的程式碼片段。根據這一點就開始跟蹤整個shader的編譯過程包括暫存器索引的儲存和使用到最後資料的設定,終於皇天不負有心人讓我發現了問題的所在。然來shader編譯後整個shader都被編譯器個優化了以便導致好多東西面目全非。

        看看具體的程式碼吧

struct VertexShaderInput
{
	float4 g_Color : COLOR0;
	float4 g_Position : POSITION0;
	float3 g_Normal : NORMAL0;
	float2 g_TexCoord0 : TEXCOORD0;
};

struct VertexShaderOutput
{
	float4 pos : POSITION;
	float4 v_TexCoord0 : TEXCOORD0;
	float3 v_WorldPos : TEXCOORD1;
	float4 v_ProjPos : TEXCOORD2;
	float3 v_VertexNormal : TEXCOORD3;
    float3 v_ViewPos : TEXCOORD6;
};

//float4x4 g_WorldViewProjMatrix ;
//float4x4 g_WorldMatrix ;
//float4x4 g_WorldViewMatrix ;
float4x4 g_WorldMatrix;
float time;


VertexShaderOutput main(VertexShaderInput input)
{
	VertexShaderOutput output;
	float x = time;
    output.pos = float4(1.0, 1.0, 1.0, 1.0);
  //  output.pos = mul(float4(1.0, 1.0, 1.0, 1.0), g_WorldViewProjMatrix);
    output.v_TexCoord0 = float4(1.0, 1.0, 1.0, 1.0) ;//mul(float4(1.0, 1.0, 1.0, 1.0), g_WorldMatrix);
	output.v_WorldPos =float3(1.0, 1.0, 1.0);
	output.v_ProjPos = float4(1.0, 0.0, 0.0, 0.0);
	//output.v_ProjPos = mul(float4(1.0, 0.0, 0.0, 0.0), g_WorldViewMatrix);
	float4 ttt=  mul(float4(1.0, 1.0, 1.0, 1.0), g_WorldMatrix);
	output.v_VertexNormal =ttt.xyz;
    output.v_ViewPos = float3(1.0, 1.0, 1.0);
    return output;
}
上面是我精簡到最後的程式碼片段。這段程式碼中聲明瞭兩個暫存器常量float4x4 g_WorldMatrix和float time。由於time在程式碼中只對x進行了賦值帶並沒有輸出。因此這個常量就沒有了相與沒有宣告,這一點還比較好理解。一般這個最後設定shader資料的時候也沒有填。因此的更深的是g_WorldMatrix。明明宣告的是float4x4應該是4x4的矩陣佔用4個暫存器。然而並不是這樣。
float4 ttt=  mul(float4(1.0, 1.0, 1.0, 1.0), g_WorldMatrix);
	output.v_VertexNormal =ttt.xyz;
由於在輸出結果中outpu.v_VertexNormal 只取了ttt的xyz分量。因此g_WorldMatrix中最後一行並不影響輸出的結果因此g_worldmatrix只被分配了3個暫存器因為最後一行不影響計算結果。這優化做的。。。只能改程式碼了