Pengikatan sumber daya di HLSL
Topik ini menjelaskan beberapa fitur spesifik menggunakan High Level Shader Language (HLSL) Shader Model 5.1 dengan Direct3D 12. Semua perangkat keras Direct3D 12 mendukung Shader Model 5.1, sehingga dukungan untuk model ini tidak bergantung pada tingkat fitur perangkat keras.
Jenis sumber daya dan array
Sintaks sumber daya Shader Model 5 (SM5.0) menggunakan kata kunci register
untuk menyampaikan informasi penting tentang sumber daya ke kompilator HLSL. Misalnya, pernyataan berikut mendeklarasikan array empat tekstur yang terikat pada slot t3, t4, t5, dan t6. t3 adalah satu-satunya slot register yang muncul dalam pernyataan, sekadar sebagai yang pertama dari empat dalam array.
Texture2D<float4> tex1[4] : register(t3)
Sintaks sumber daya Shader Model 5.1 (SM5.1) dalam HLSL didasarkan pada sintaks sumber daya register yang ada, untuk memungkinkan porting yang lebih mudah. Sumber daya Direct3D 12 di HLSL terikat ke register virtual dalam ruang register logis:
- t – untuk tampilan sumber daya shader (SRV)
- s – untuk pengambil sampel
- u – untuk tampilan akses yang tidak diurutkan (UAV)
- b – untuk tampilan buffer konstan (CBV)
Tanda tangan root yang mereferensikan shader harus kompatibel dengan slot register yang dideklarasikan. Misalnya, bagian berikut dari tanda tangan akar akan kompatibel dengan penggunaan slot tekstur t3 hingga t6, karena menjelaskan tabel deskriptor dengan slot t0 hingga t98.
DescriptorTable( CBV(b1), SRV(t0,numDescriptors=99), CBV(b2) )
Deklarasi sumber daya mungkin berupa skalar, array 1D, atau array multidmensional:
Texture2D<float4> tex1 : register(t3, space0)
Texture2D<float4> tex2[4] : register(t10)
Texture2D<float4> tex3[7][5][3] : register(t20, space1)
SM5.1 menggunakan jenis sumber daya dan jenis elemen yang sama seperti yang dilakukan SM5.0. Batas deklarasi SM5.1 lebih fleksibel, dan hanya dibatasi oleh batas runtime/perangkat keras. Kata kunci space
menentukan ruang register logis mana variabel yang dideklarasikan terikat. Jika kata kunci space
dihilangkan, maka indeks ruang default 0 secara implisit ditetapkan ke rentang (sehingga rentang tex2
di atas berada di space0
).
register(t3, space0)
tidak akan pernah berkonflik dengan register(t3, space1)
, atau dengan array apa pun di ruang lain yang mungkin menyertakan t3.
Sumber daya array mungkin memiliki ukuran yang tidak terbatas, yang dideklarasikan dengan menentukan dimensi pertama yang kosong, atau 0:
Texture2D<float4> tex1[] : register(t0)
Tabel deskriptor yang cocok dapat berupa:
DescriptorTable( CBV(b1), UAV(u0, numDescriptors = 4), SRV(t0, numDescriptors=unbounded) )
Array tidak terbatas di HLSL memang cocok dengan kumpulan angka tetap dengan numDescriptors
dalam tabel deskriptor, dan ukuran tetap dalam HLSL memang cocok dengan deklarasi yang tidak terbatas dalam tabel deskriptor.
Array multidimensi diizinkan, termasuk ukuran yang tidak terbatas. Array multidimensional ini diratakan di ruang pendaftaran.
Texture2D<float4> tex2[3000][10] : register(t0, space0); // t0-t29999 in space0
Texture2D<float4> tex3[0][5][3] : register(t5, space1)
Pengaliasan rentang sumber daya tidak diperbolehkan. Dengan kata lain, untuk setiap jenis sumber daya (t, s, u, b), rentang register yang dinyatakan tidak boleh tumpang tindih. Itu termasuk rentang yang tidak terbatas juga. Rentang yang dideklarasikan dalam ruang register yang berbeda tidak pernah tumpang tindih. Perhatikan bahwa tex2
yang tidak terikat (di atas) berada di space0
, sementara tex3
yang tidak terbatas berada di space1
, sehingga tidak tumpang tindih.
Mengakses sumber daya yang telah dinyatakan sebagai array semederhana mengindeksnya.
Texture2D<float4> tex1[400] : register(t3);
sampler samp[7] : register(s0);
tex1[myMaterialID].Sample(samp[samplerID], texCoords);
Ada pembatasan default penting pada penggunaan indeks (myMaterialID
dan samplerID
dalam kode di atas) karena mereka tidak diizinkan untuk bervariasi dalam gelombang . Bahkan mengubah indeks berdasarkan jumlah instancing yang bervariasi.
Jika variasi indeks diperlukan, tentukan kualifikasi NonUniformResourceIndex
pada indeks, misalnya:
tex1[NonUniformResourceIndex(myMaterialID)].Sample(samp[NonUniformResourceIndex(samplerID)], texCoords);
Pada beberapa perangkat keras, penggunaan kualifikasi ini menghasilkan kode tambahan untuk menjamin keakuratan (termasuk di seluruh thread) meskipun dengan sedikit biaya pada performa. Jika suatu indeks diubah tanpa penentu ini dan dalam proses menggambar/mendistribusikan, hasilnya tidak terdefinisi.
Array deskriptor dan array tekstur
Array tekstur telah tersedia sejak DirectX 10. Array tekstur memerlukan satu deskriptor, namun semua irisan array harus berbagi format, lebar, tinggi, dan jumlah mip yang sama. Selain itu, array harus menempati rentang yang berdampingan di ruang alamat virtual. Kode sumber berikut menunjukkan contoh mengakses array tekstur dari sebuah shader.
Texture2DArray<float4> myTex2DArray : register(t0); // t0
float3 myCoord(1.0f,1.4f,2.2f); // 2.2f is array index (rounded to int)
color = myTex2DArray.Sample(mySampler, myCoord);
Dalam array tekstur, indeks dapat bervariasi dengan bebas, tanpa memerlukan penanda seperti NonUniformResourceIndex
.
Array deskriptor yang setara adalah:
Texture2D<float4> myArrayOfTex2D[] : register(t0); // t0+
float2 myCoord(1.0f, 1.4f);
color = myArrayOfTex2D[2].Sample(mySampler,myCoord); // 2 is index
Perhatikan bahwa penggunaan angka pecahan yang canggung untuk indeks array telah digantikan dengan myArrayOfTex2D[2]
. Array deskriptor juga menawarkan lebih banyak fleksibilitas dalam hal dimensi. Jenis Texture2D
dalam contoh ini tidak dapat bervariasi, tetapi format, lebar, tinggi, dan jumlah mip semuanya dapat bervariasi pada setiap deskriptor.
Sah-sah saja memiliki sebuah array deskriptor dari array tekstur.
Texture2DArray<float4> myArrayOfTex2DArrays[2] : register(t0);
Tidak sah mendeklarasikan array struktur di mana setiap struktur berisi deskriptor; sebagai contoh, kode berikut tidak didukung.
struct myStruct {
Texture2D a;
Texture2D b;
ConstantBuffer<myConstants> c;
};
myStruct foo[10000] : register(....);
Ini akan memungkinkan tata letak memori abcabcabc...., tetapi merupakan batasan bahasa dan tidak didukung. Salah satu metode yang didukung untuk melakukan ini adalah sebagai berikut, meskipun tata letak memori dalam hal ini aaa... Bbb... ccc....
Texture2D a[10000] : register(t0);
Texture2D b[10000] : register(t10000);
ConstantBuffer<myConstants> c[10000] : register(b0);
Untuk mencapai abcabcabc.... tata letak memori, gunakan tabel deskriptor tanpa menggunakan struktur myStruct
.
Alias sumber daya
Rentang sumber daya yang ditentukan dalam shader HLSL adalah rentang logis. Mereka terikat ke rentang timbunan beton pada runtime melalui mekanisme tanda tangan akar. Biasanya, rentang logis dipetakan ke rentang timbunan yang tidak tumpang tindih dengan rentang timbunan yang lain. Namun, mekanisme root signature memungkinkan untuk alias (tumpang tindih) rentang tumpukan dari jenis yang kompatibel. Misalnya, rentang tex2
dan tex3
dari contoh di atas dapat dipetakan ke rentang heap yang sama (atau tumpang tindih), yang memiliki efek terhadap pen aliasan tekstur dalam program HLSL. Jika alias tersebut diinginkan, shader harus dikompilasi dengan opsi D3D10_SHADER_RESOURCES_MAY_ALIAS, yang diatur dengan menggunakan opsi /res_may_alias untuk Effect-Compiler Tool (FXC). Opsi ini membuat pengkompilasi menghasilkan kode yang benar dengan mencegah pengoptimalan beban/penyimpanan tertentu dengan asumsi bahwa sumber daya mungkin alias.
Divergensi dan turunan
SM5.1 tidak memberlakukan batasan pada indeks sumber daya; yaitu, tex2[idx].Sample(…)
– idx indeks dapat berupa konstanta harfiah, konstanta cbuffer, atau nilai terinterpolasi. Meskipun model pemrograman memberikan fleksibilitas yang begitu besar, ada masalah yang perlu diperhatikan:
- Jika indeks menyimpang dalam satu quad, derivatif dan kuantitas turunan yang dihitung perangkat keras seperti LOD mungkin tidak terdefinisi. Kompilator HLSL berusaha sebaik mungkin untuk mengeluarkan peringatan dalam hal ini, tetapi tidak akan mencegah shader mengompilasi. Perilaku ini mirip dengan komputasi turunan dalam alur kontrol yang berbeda.
- Jika indeks sumber daya berbeda, performanya berkurang dibandingkan dengan kasus indeks seragam, karena perangkat keras perlu melakukan operasi pada beberapa sumber daya. Indeks sumber daya yang mungkin berbeda harus ditandai dengan fungsi
NonUniformResourceIndex
dalam kode HLSL. Jika tidak, hasil tidak terdefinisi.
UAV dalam shader piksel
SM5.1 tidak memberlakukan batasan pada rentang UAV dalam pemecah piksel seperti halnya untuk SM5.0.
Buffer Konstanta
Sintaks buffer konstanta SM5.1 (cbuffer) telah berubah dari SM5.0 untuk memungkinkan pengembang mengindeks buffer konstan. Untuk mengaktifkan buffer konstanta yang dapat diindeks, SM5.1 memperkenalkan konstruksi "templat" ConstantBuffer
:
struct Foo
{
float4 a;
int2 b;
};
ConstantBuffer<Foo> myCB1[2][3] : register(b2, space1);
ConstantBuffer<Foo> myCB2 : register(b0, space1);
Kode sebelumnya mendeklarasikan variabel buffer konstan myCB1
jenis Foo
dan ukuran 6, dan variabel buffer skalar konstan myCB2
. Variabel buffer konstan sekarang dapat diindeks dalam shader sebagai:
myCB1[i][j].a.xyzw
myCB2.b.yy
Bidang 'a' dan 'b' tidak menjadi variabel global, melainkan harus diperlakukan sebagai bidang. Untuk kompatibilitas ke belakang, SM5.1 mendukung konsep cbuffer yang lama untuk cbuffer skalar. Pernyataan berikut membuat variabel global 'a' dan 'b' menjadi variabel hanya-baca seperti dalam SM5.0. Namun, cbuffer gaya lama seperti itu tidak dapat diindeks.
cbuffer : register(b1)
{
float4 a;
int2 b;
};
Saat ini, pengkompilasi shader hanya mendukung templat ConstantBuffer
untuk struktur yang ditentukan pengguna.
Untuk alasan kompatibilitas, kompilator HLSL dapat secara otomatis menetapkan register sumber daya untuk rentang yang dideklarasikan dalam space0
. Jika 'spasi' dihilangkan dalam klausa register, space0
digunakan sebagai default. Kompiler menggunakan heuristik lubang pertama yang cocok untuk menetapkan register. Penugasan dapat diambil melalui API refleksi, yang telah diperluas untuk menambahkan bidang Ruang untuk ruang, sementara bidang BindPoint menunjukkan batas bawah rentang register sumber daya.
Perubahan bytecode di SM5.1
SM5.1 mengubah cara pendaftaran sumber daya dideklarasikan dan direferensikan dalam instruksi. Sintaks melibatkan mendeklarasikan register "variabel", mirip dengan bagaimana hal itu dilakukan untuk register memori bersama grup:
Texture2D<float4> tex0 : register(t5, space0);
Texture2D<float4> tex1[][5][3] : register(t10, space0);
Texture2D<float4> tex2[8] : register(t0, space1);
SamplerState samp0 : register(s5, space0);
float4 main(float4 coord : COORD) : SV_TARGET
{
float4 r = coord;
r += tex0.Sample(samp0, r.xy);
r += tex2[r.x].Sample(samp0, r.xy);
r += tex1[r.x][r.y][r.z].Sample(samp0, r.xy);
return r;
}
Ini akan terbongkar menjadi:
// Resource Bindings:
//
// Name Type Format Dim ID HLSL Bind Count
// ------------------------------ ---------- ------- ----------- ----- --------- ---------
// samp0 sampler NA NA S0 a5 1
// tex0 texture float4 2d T0 t5 1
// tex1[0][5][3] texture float4 2d T1 t10 unbounded
// tex2[8] texture float4 2d T2 t0.space1 8
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// COORD 0 xyzw 0 NONE float xyzw
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_TARGET 0 xyzw 0 TARGET float xyzw
//
ps_5_1
dcl_globalFlags refactoringAllowed
dcl_sampler s0[5:5], mode_default, space=0
dcl_resource_texture2d (float,float,float,float) t0[5:5], space=0
dcl_resource_texture2d (float,float,float,float) t1[10:*], space=0
dcl_resource_texture2d (float,float,float,float) t2[0:7], space=1
dcl_input_ps linear v0.xyzw
dcl_output o0.xyzw
dcl_temps 2
sample r0.xyzw, v0.xyxx, t0[0].xyzw, s0[5]
add r0.xyzw, r0.xyzw, v0.xyzw
ftou r1.x, r0.x
sample r1.xyzw, r0.xyxx, t2[r1.x + 0].xyzw, s0[5]
add r0.xyzw, r0.xyzw, r1.xyzw
ftou r1.xyz, r0.zyxz
imul null, r1.yz, r1.zzyz, l(0, 15, 3, 0)
iadd r1.y, r1.z, r1.y
iadd r1.x, r1.x, r1.y
sample r1.xyzw, r0.xyxx, t1[r1.x + 10].xyzw, s0[5]
add o0.xyzw, r0.xyzw, r1.xyzw
ret
// Approximately 12 instruction slots are used.
Setiap rentang sumber daya shader sekarang memiliki ID (nama) yang unik untuk bytecode shader. Misalnya, array tekstur tex1 (t10) menjadi 'T1' dalam bytecode shader. Memberikan ID unik untuk setiap rentang sumber daya memungkinkan dua hal:
- Secara tidak ambigu mengidentifikasi rentang sumber daya mana (lihat dcl_resource_texture2d) yang sedang diindeks dalam instruksi (lihat instruksi sampel).
- Melampirkan sekumpulan atribut ke deklarasi, misalnya, jenis elemen, ukuran langkah, mode operasi raster, dll.
Perhatikan bahwa ID dari rentang tidak terkait dengan deklarasi batas bawah HLSL.
Urutan pengikatan sumber daya refleksi (daftar di bagian atas) dan instruksi deklarasi shader (dcl_*) sama untuk membantu mengidentifikasi korespondensi antara variabel HLSL dan ID bytecode.
Setiap instruksi deklarasi di SM5.1 menggunakan operand 3D untuk menentukan: ID rentang, batas bawah dan atas. Token tambahan dipancarkan untuk menentukan ruang register. Token lain dapat dipancarkan juga untuk menyampaikan properti tambahan dari rentang, misalnya, cbuffer atau instruksi deklarasi buffer terstruktur memancarkan ukuran cbuffer atau struktur. Detail pengodean yang tepat dapat ditemukan di d3d12TokenizedProgramFormat.h dan D3D10ShaderBinary::CShaderCodeParser.
Instruksi SM5.1 tidak akan memancarkan informasi operand sumber daya tambahan sebagai bagian dari instruksi (seperti dalam SM5.0). Informasi ini sekarang dalam instruksi deklarasi. Di SM5.0, instruksi yang mengindeks sumber daya memerlukan atribut sumber daya untuk digambarkan dalam token opcode yang diperluas, karena pengindeksan dapat mengaburkan asosiasi dengan deklarasi. Dalam SM5.1, setiap ID (seperti 't1') dikaitkan secara jelas dengan satu deklarasi yang menjelaskan informasi sumber daya yang dibutuhkan. Oleh karena itu, token opcode yang diperluas yang digunakan pada instruksi untuk menjelaskan informasi sumber daya tidak lagi dipancarkan.
Dalam instruksi tanpa deklarasi, operand sumber daya untuk sampler, SRV, dan UAV adalah operand dua dimensi. Indeks pertama adalah konstanta harfiah yang menentukan ID rentang. Indeks kedua mewakili nilai indeks linier. Nilai dihitung relatif terhadap awal ruang register yang sesuai (tidak relatif terhadap awal rentang logis) untuk lebih berkorelasi dengan signature root serta guna mengurangi beban pada driver compiler dalam menyesuaikan indeks.
Operand sumber daya untuk CBV adalah operan 3D, yang berisi: ID unik dari rentang, indeks buffer konstanta, dan offset ke instans buffer konstanta yang spesifik.
Contoh Deklarasi HLSL
Program HLSL tidak perlu tahu apa-apa tentang tanda tangan root. Mereka dapat menetapkan pengikatan ke ruang pengikatan "daftar" virtual, t# untuk SRV, u# untuk UAV, b# untuk CBV, s# untuk sampler, atau mengandalkan pengkompilasi untuk memilih tugas (dan mengkueri pemetaan yang dihasilkan menggunakan pantulan shader setelahnya). Tanda tangan akar memetakan tabel deskriptor, deskriptor akar, dan konstanta akar ke ruang register virtual ini.
Berikut ini adalah beberapa contoh deklarasi yang mungkin dimiliki shader HLSL. Perhatikan bahwa tidak ada referensi ke tanda tangan root atau tabel deskriptor.
Texture2D foo[5] : register(t2);
Buffer bar : register(t7);
RWBuffer dataLog : register(u1);
Sampler samp : register(s0);
struct Data
{
UINT index;
float4 color;
};
ConstantBuffer<Data> myData : register(b0);
Texture2D terrain[] : register(t8); // Unbounded array
Texture2D misc[] : register(t0,space1); // Another unbounded array
// space1 avoids overlap with above t#
struct MoreData
{
float4x4 xform;
};
ConstantBuffer<MoreData> myMoreData : register(b1);
struct Stuff
{
float2 factor;
UINT drawID;
};
ConstantBuffer<Stuff> myStuff[][3][8] : register(b2, space3)