Bagikan melalui


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 Model Shader 5.1, sehingga dukungan untuk model ini tidak bergantung pada tingkat fitur perangkat keras.

Jenis dan array sumber daya

Sintaks sumber daya Shader Model 5 (SM5.0) menggunakan register kata kunci untuk menyampaikan informasi penting tentang sumber daya ke pengkompilasi HLSL. Misalnya, pernyataan berikut menyatakan array empat tekstur yang terikat pada slot t3, t4, t5, dan t6. t3 adalah satu-satunya slot register yang muncul dalam pernyataan , hanya menjadi yang pertama dalam array empat.

Texture2D<float4> tex1[4] : register(t3)

Sintaks sumber daya Shader Model 5.1 (SM5.1) di 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 sampler
  • u – untuk tampilan akses tidak berurut (UAV)
  • b – untuk tampilan buffer konstan (CBV)

Tanda tangan akar 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 menggambarkan tabel deskriptor dengan slot t0 hingga t98.

DescriptorTable( CBV(b1), SRV(t0,numDescriptors=99), CBV(b2) )

Deklarasi sumber daya mungkin skalar, array 1D, atau array multidimensi:

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 SM5.0. Batas deklarasi SM5.1 lebih fleksibel, dan hanya dibatasi oleh batas runtime/perangkat keras. Kata space kunci menentukan ruang register logis mana variabel yang dideklarasikan terikat. Jika kata kunci dihilangkan space , maka indeks spasi default 0 secara implisit ditetapkan ke rentang (sehingga tex2 rentang 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 dinyatakan 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 angka tetap yang diatur dengan dalam tabel deskriptor, dan ukuran tetap di HLSL memang cocok dengan numDescriptors deklarasi yang tidak terbatas dalam tabel deskriptor.

Array multidimensi diperbolehkan, termasuk ukuran yang tidak terbatas. Array multidimensi ini diratakan di ruang register.

Texture2D<float4> tex2[3000][10] : register(t0, space0); // t0-t29999 in space0
Texture2D<float4> tex3[0][5][3] : register(t5, space1)

Alias 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 juga termasuk rentang yang tidak terbatas. Rentang yang dideklarasikan dalam ruang register yang berbeda tidak pernah tumpang tindih. Perhatikan bahwa unbounded tex2 (di atas) berada di space0, sementara unbounded tex3 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 memvariasikan indeks diperlukan, maka tentukan NonUniformResourceIndex kualifikasi pada indeks, misalnya:

tex1[NonUniformResourceIndex(myMaterialID)].Sample(samp[NonUniformResourceIndex(samplerID)], texCoords);

Pada beberapa perangkat keras, penggunaan kualifikasi ini menghasilkan kode tambahan untuk memberlakukan kebenaran (termasuk di seluruh utas) tetapi dengan biaya performa kecil. Jika indeks diubah tanpa kualifikasi ini dan dalam gambar/pengiriman, 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 berikut menunjukkan contoh mengakses array tekstur dari 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 secara bebas, tanpa perlu kualifikasi 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 penggunaan float yang canggung untuk indeks array diganti dengan myArrayOfTex2D[2]. Array deskriptor juga menawarkan lebih banyak fleksibilitas dengan dimensi. Jenisnya, Texture2D adalah contoh ini, tidak dapat bervariasi, tetapi format, lebar, tinggi, dan jumlah mip semuanya dapat bervariasi dengan setiap deskriptor.

Sah memiliki array deskriptor array tekstur:

Texture2DArray<float4> myArrayOfTex2DArrays[2] : register(t0);

Tidak sah untuk mendeklarasikan array struktur, setiap struktur yang berisi deskriptor, misalnya 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 adalah aaa... Bbb... ccc....

Texture2D                     a[10000] : register(t0);
Texture2D                     b[10000] : register(t10000);
ConstantBuffer<myConstants>   c[10000] : register(b0);

Untuk mencapai tata letak memori abcabcabc.... , gunakan tabel deskriptor tanpa menggunakan myStruct struktur.

Alias sumber daya

Rentang sumber daya yang ditentukan dalam shader HLSL adalah rentang logis. Mereka terikat pada rentang timbunan beton pada runtime melalui mekanisme tanda tangan akar. Biasanya, rentang logis memetakan ke rentang timbunan yang tidak tumpang tindih dengan rentang timbunan lainnya. Namun, mekanisme tanda tangan akar memungkinkan rentang timbunan alias (tumpang tindih) dari jenis yang kompatibel. Misalnya, tex2 dan tex3 rentang dari contoh di atas dapat dipetakan ke rentang tumpukan yang sama (atau tumpang tindih), yang memiliki efek alias 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 turunannya

SM5.1 tidak memberlakukan batasan pada indeks sumber daya; yaitu, tex2[idx].Sample(…) – idx indeks dapat menjadi konstanta harfiah, konstanta cbuffer, atau nilai terinterpolasi. Meskipun model pemrograman memberikan fleksibilitas yang begitu besar, ada masalah yang perlu diperhatikan:

  • Jika indeks menyimpang di seluruh quad, jumlah turunan dan turunan yang dihitung perangkat keras seperti LOD mungkin tidak ditentukan. Kompilator HLSL melakukan upaya terbaik untuk mengeluarkan peringatan dalam hal ini, tetapi tidak akan mencegah shader dikompilasi. Perilaku ini mirip dengan komputasi turunan dalam alur kontrol yang berbeda.
  • Jika indeks sumber daya berbeda, performa 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 NonUniformResourceIndex fungsi dalam kode HLSL. Jika tidak, hasil tidak ditentukan.

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 (cbuffer) SM5.1 telah berubah dari SM5.0 untuk memungkinkan pengembang mengindeks buffer konstanta. Untuk mengaktifkan buffer konstanta yang dapat diindeks, SM5.1 memperkenalkan ConstantBuffer konstruksi "templat":

struct Foo
{
    float4 a;
    int2 b;
};
ConstantBuffer<Foo> myCB1[2][3] : register(b2, space1);
ConstantBuffer<Foo> myCB2 : register(b0, space1);

Kode sebelumnya mendeklarasikan variabel myCB1 buffer konstan jenis Foo dan ukuran 6, dan variabel myCB2buffer skalar dan konstanta . 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 mundur, SM5.1 mendukung konsep cbuffer lama untuk cbuffer skalar. Pernyataan berikut membuat variabel 'a' dan 'b' global, baca-saja seperti pada SM5.0. Namun, cbuffer gaya lama seperti itu tidak dapat diindeks.

cbuffer : register(b1)
{
    float4 a;
    int2 b;
};

Saat ini, pengkompilasi shader hanya mendukung ConstantBuffer templat untuk struktur yang ditentukan pengguna.

Untuk alasan kompatibilitas, pengkompilasi HLSL dapat secara otomatis menetapkan pendaftaran sumber daya untuk rentang yang dideklarasikan dalam space0. Jika 'spasi' dihilangkan dalam klausa register, default space0 akan digunakan. Kompilator menggunakan heuristik pertama yang cocok untuk menetapkan register. Penugasan dapat diambil melalui API pantulan, yang telah diperluas untuk menambahkan bidang Spasi 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 membongkar ke:

// 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 rentang tidak terkait dengan deklarasi terikat HLSL yang lebih rendah.

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 juga dapat dipancarkan 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 ada dalam instruksi deklarasi. Di SM5.0, sumber daya pengindeksan instruksi memerlukan atribut sumber daya untuk dijelaskan dalam token opcode yang diperluas, karena pengindeksan mengaburkan asosiasi ke deklarasi. Dalam SM5.1, setiap ID (seperti 't1') secara tidak ambigu dikaitkan dengan satu deklarasi yang menjelaskan informasi sumber daya yang diperlukan. Oleh karena itu, token opcode yang diperluas yang digunakan pada instruksi untuk menjelaskan informasi sumber daya tidak lagi dipancarkan.

Dalam instruksi non-deklarasi, operand sumber daya untuk sampler, SRV, dan UAV adalah operand 2D. Indeks pertama adalah konstanta harfiah yang menentukan ID rentang. Indeks kedua mewakili nilai linearisasi indeks. Nilai dihitung relatif terhadap awal ruang register yang sesuai (tidak relatif terhadap awal rentang logis) untuk lebih berkorelasi dengan tanda tangan akar dan untuk mengurangi beban pengkompilasi driver untuk menyesuaikan indeks.

Operand sumber daya untuk CBV adalah operand 3D, yang berisi: ID harfiah dari rentang, indeks buffer konstanta, offset ke dalam instans buffer konstanta tertentu.

Contoh Deklarasi HLSL

Program HLSL tidak perlu tahu apa-apa tentang tanda tangan root. Mereka dapat menetapkan pengikatan ke ruang pengikatan "register" virtual, t# untuk SRV, u# untuk UAV, b# untuk CBV, s# untuk sampler, atau mengandalkan kompilator untuk memilih tugas (dan mengkueri pemetaan yang dihasilkan menggunakan bayangan 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 oleh shader HLSL. Perhatikan bahwa tidak ada referensi ke tanda tangan akar 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)