Direct3D 12 render passes

Fitur render passes baru untuk Windows 10, versi 1809 (10.0; Build 17763), dan memperkenalkan konsep direct3D 12 render pass. Pass render terdiri dari subset perintah yang Anda rekam ke dalam daftar perintah.

Untuk mendeklarasikan di mana setiap pass render dimulai dan berakhir, Anda menumpuk perintah milik panggilan tersebut ke ID3D12GraphicsCommandList4::BeginRenderPass dan EndRenderPass. Akibatnya, daftar perintah apa pun berisi nol, satu, atau beberapa lolos render.

Skenario

Render pass dapat meningkatkan performa perender Anda jika didasarkan pada Tile-Based Deferred Rendering (TBDR), di antara teknik lainnya. Lebih khusus lagi, teknik ini membantu perender Anda meningkatkan efisiensi GPU dengan mengurangi lalu lintas memori ke/dari memori off-chip dengan memungkinkan aplikasi Anda mengidentifikasi persyaratan pemesanan dan dependensi data penyajian sumber daya dengan lebih baik.

Driver tampilan yang ditulis secara tegas untuk memanfaatkan fitur render passes memberikan hasil terbaik. Tetapi API render pass dapat berjalan bahkan pada driver yang sudah ada sebelumnya (meskipun, belum tentu dengan peningkatan performa).

Ini adalah skenario di mana render pass dirancang untuk memberikan nilai.

Izinkan aplikasi Anda menghindari beban/penyimpanan sumber daya yang tidak perlu dari/ke memori utama pada arsitektur Tile-Based Deferred Rendering (TBDR)

Salah satu proposisi nilai dari render pass adalah bahwa ia memberi Anda lokasi pusat untuk menunjukkan dependensi data aplikasi Anda untuk serangkaian operasi penyajian. Dependensi data ini memungkinkan driver tampilan untuk memeriksa data ini pada waktu pengikatan/pembatas, dan untuk mengeluarkan instruksi yang meminimalkan beban/penyimpanan sumber daya dari/ke memori utama.

Izinkan arsitektur TBDR Anda untuk sumber daya persisten secara oportunistik dalam cache on-chip di seluruh lintasan render (bahkan dalam Daftar Perintah terpisah)

Catatan

Secara khusus, skenario ini terbatas pada kasus di mana Anda menulis ke target render yang sama di beberapa daftar perintah.

Pola penyajian umum adalah agar aplikasi Anda merender ke target render yang sama di beberapa daftar perintah secara serial, meskipun perintah penyajian dihasilkan secara paralel. Penggunaan Render pass Anda dalam skenario ini memungkinkan pass ini digabungkan sedemikian rupa (karena aplikasi tahu bahwa ia akan melanjutkan penyajian pada daftar perintah yang segera berhasil) bahwa driver tampilan dapat menghindari flush ke memori utama pada batas daftar perintah.

Tanggung jawab aplikasi Anda

Bahkan dengan fitur render passes, baik runtime Direct3D 12 maupun driver display tidak bertanggung jawab untuk mengurangi peluang untuk memesan ulang/menghindari beban dan toko. Untuk memanfaatkan fitur render passes dengan benar, aplikasi Anda memiliki tanggung jawab ini.

  • Identifikasi dependensi data/pengurutan dengan benar untuk operasinya.
  • Pesan pengirimannya dengan cara yang meminimalkan flush (jadi, minimalkan penggunaan bendera _PRESERVE ).
  • Gunakan penghalang sumber daya dengan benar, dan lacak status sumber daya.
  • Hindari salinan/penghapusan yang tidak perlu. Untuk membantu mengidentifikasi ini, Anda dapat menggunakan peringatan performa otomatis dari PIX pada alat Windows.

Menggunakan fitur render pass

Apa itu lulus render?

Lulus render didefinisikan oleh elemen-elemen ini.

  • Satu set pengikatan output yang diperbaiki selama durasi render pass. Pengikatan ini adalah untuk satu atau beberapa tampilan target render (RTV), dan/atau ke tampilan stensil kedalaman (DSV).
  • Daftar operasi GPU yang menargetkan kumpulan pengikatan output tersebut.
  • Metadata yang menjelaskan dependensi beban/penyimpanan untuk semua pengikatan output yang ditargetkan oleh render pass.

Mendeklarasikan pengikatan output Anda

Pada awal lulus render, Anda menyatakan pengikatan ke target render Anda dan/atau ke buffer kedalaman/stensil Anda. Ini opsional untuk mengikat untuk merender target, dan opsional untuk mengikat buffer kedalaman/stensil. Tetapi Anda harus mengikat setidaknya salah satu dari keduanya, dan dalam contoh kode di bawah ini kami mengikat keduanya.

Anda menyatakan pengikatan ini dalam panggilan ke ID3D12GraphicsCommandList4::BeginRenderPass.

void render_passes(::ID3D12GraphicsCommandList4 * pIGCL4,
    D3D12_CPU_DESCRIPTOR_HANDLE const& rtvCPUDescriptorHandle,
    D3D12_CPU_DESCRIPTOR_HANDLE const& dsvCPUDescriptorHandle)
{
    const float clearColor4[]{ 0.f, 0.f, 0.f, 0.f };
    CD3DX12_CLEAR_VALUE clearValue{ DXGI_FORMAT_R32G32B32_FLOAT, clearColor4 };

    D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessClear{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, { clearValue } };
    D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessPreserve{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {} };
    D3D12_RENDER_PASS_RENDER_TARGET_DESC renderPassRenderTargetDesc{ rtvCPUDescriptorHandle, renderPassBeginningAccessClear, renderPassEndingAccessPreserve };

    D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessNoAccess{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, {} };
    D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessNoAccess{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, {} };
    D3D12_RENDER_PASS_DEPTH_STENCIL_DESC renderPassDepthStencilDesc{ dsvCPUDescriptorHandle, renderPassBeginningAccessNoAccess, renderPassBeginningAccessNoAccess, renderPassEndingAccessNoAccess, renderPassEndingAccessNoAccess };

    pIGCL4->BeginRenderPass(1, &renderPassRenderTargetDesc, &renderPassDepthStencilDesc, D3D12_RENDER_PASS_FLAG_NONE);
    // Record command list.
    pIGCL4->EndRenderPass();
    // Begin/End further render passes and then execute the command list(s).
}

Anda mengatur bidang pertama dari struktur D3D12_RENDER_PASS_RENDER_TARGET_DESC ke handel deskriptor CPU yang sesuai dengan satu atau beberapa tampilan target render (RTV). Demikian pula, D3D12_RENDER_PASS_DEPTH_STENCIL_DESC berisi handel deskriptor CPU yang sesuai dengan tampilan stensil kedalaman (DSV). Handel deskriptor CPU tersebut sama dengan yang akan Anda berikan ke ID3D12GraphicsCommandList::OMSetRenderTargets. Dan, sama seperti OMSetRenderTargets, deskriptor CPU di-snap dari tumpukan masing-masing (deskriptor CPU) pada saat panggilan ke BeginRenderPass.

RTV dan DSV tidak diwarisi ke render pass. Sebaliknya, mereka harus diatur. RTV dan DSV juga tidak dideklarasikan dalam BeginRenderPass yang disebarluaskan ke daftar perintah. Sebaliknya, mereka berada dalam status tidak terdefinisi setelah lulus render.

Merender pass dan beban kerja

Anda tidak dapat melakukan nest render pass, dan Anda tidak dapat memiliki render pass straddle lebih dari satu daftar perintah (mereka harus dimulai dan berakhir saat merekam ke dalam satu daftar perintah). Pengoptimalan yang dirancang untuk memungkinkan pembuatan render pass multi-rangkaian yang efisien dibahas di bagian render pass Flags, di bawah ini.

Tulisan yang Anda lakukan dari dalam render pass tidak valid bagi Anda untuk membaca dari sampai lulus render berikutnya. Itu menghalangi beberapa jenis hambatan dari dalam kode render—misalnya, penghalang dari RENDER_TARGET ke SHADER_RESOURCE pada target render yang saat ini terikat. Untuk informasi selengkapnya, lihat bagian Merender pass dan penghalang sumber daya, di bawah ini.

Satu pengecualian untuk batasan tulis-baca yang baru saja disebutkan melibatkan pembacaan implisit yang terjadi sebagai bagian dari pengujian kedalaman dan penpaduan target render. Jadi, API ini tidak diizinkan dalam render pass (runtime inti menghapus daftar perintah jika salah satunya dipanggil selama perekaman).

Merender pass dan penghalang sumber daya

Anda tidak boleh membaca dari, atau mengonsumsi, tulisan yang terjadi dalam render pass yang sama. Hambatan tertentu tidak sesuai dengan batasan ini, misalnya dari D3D12_RESOURCE_STATE_RENDER_TARGET ke *_SHADER_RESOURCE pada target render yang saat ini terikat (dan lapisan debug akan mengalami kesalahan pada efek tersebut). Tapi, hambatan yang sama pada target render yang ditulis di luar render pass saat ini sesuai, karena tulisan akan selesai sebelum render pass saat ini dimulai. Anda mungkin mendapat manfaat dari mengetahui tentang pengoptimalan tertentu yang dapat dilakukan driver tampilan dalam hal ini. Mengingat beban kerja yang sesuai, driver tampilan mungkin memindahkan hambatan apa pun yang ditemui di render pass Anda ke awal render pass. Di sana, mereka dapat dikoalesse (dan tidak mengganggu operasi pemisah/pengikatan). Ini adalah pengoptimalan yang valid asalkan semua tulisan Anda telah selesai sebelum render pass saat ini dimulai.

Berikut adalah contoh pengoptimalan driver yang lebih lengkap, yang mengasumsikan bahwa Anda memiliki mesin penyajian yang memiliki desain pengikatan sumber daya pra-Direct3D 12 gaya—melakukan penghalang sesuai permintaan berdasarkan bagaimana sumber daya terikat. Saat menulis ke dalam tampilan akses yang tidak berurut (UAV) menuju akhir bingkai (untuk digunakan dalam bingkai berikut), mesin mungkin terjadi untuk meninggalkan sumber daya dalam status D3D12_RESOURCE_STATE_UNORDERED_ACCESS pada kesimpulan bingkai. Dalam bingkai yang mengikuti, ketika mesin pergi untuk mengikat sumber daya sebagai tampilan sumber daya shader (SRV), akan menemukan bahwa sumber daya tidak dalam keadaan yang benar, dan itu akan mengeluarkan penghambat dari D3D12_RESOURCE_STATE_UNORDERED_ACCESS ke D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE. Jika hambatan itu terjadi dalam render pass, maka driver tampilan dibenarkan dalam asumsi bahwa semua tulisan telah terjadi di luar pass render ini saat ini, dan akibatnya (dan di sinilah pengoptimalan masuk) driver tampilan mungkin memindahkan hambatan ke awal render pass. Sekali lagi, ini valid, selama kode Anda sesuai dengan batasan baca-tulis yang dijelaskan di bagian ini dan yang terakhir.

Ini adalah contoh hambatan yang sesuai.

  • D3D12_RESOURCE_STATE_UNORDERED_ACCESS ke D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT.
  • D3D12_RESOURCE_STATE_COPY_DEST ke *_SHADER_RESOURCE.

Dan ini adalah contoh hambatan yang tidak sesuai.

  • D3D12_RESOURCE_STATE_RENDER_TARGET ke status baca apa pun pada RTV/DSV yang saat ini terikat.
  • D3D12_RESOURCE_STATE_DEPTH_WRITE ke status baca pada RTV/DSV yang saat ini terikat.
  • Setiap hambatan alias.
  • Penghalang tampilan akses tidak berurut (UAV). 

Deklarasi akses sumber daya

Pada waktu BeginRenderPass , serta mendeklarasikan semua sumber daya yang berfungsi sebagai RTV dan/atau DSV dalam pass tersebut, Anda juga harus menentukan karakteristik akses awal dan akhir. Seperti yang Anda lihat dalam contoh kode di bagian Nyatakan pengikatan output Anda di atas, Anda melakukan ini dengan struktur D3D12_RENDER_PASS_RENDER_TARGET_DESC dan D3D12_RENDER_PASS_DEPTH_STENCIL_DESC .

Untuk detail selengkapnya, lihat struktur D3D12_RENDER_PASS_BEGINNING_ACCESS dan D3D12_RENDER_PASS_ENDING_ACCESS , serta enumerasi D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE dan D3D12_RENDER_PASS_ENDING_ACCESS_TYPE .

Merender bendera pass

Parameter terakhir yang diteruskan ke BeginRenderPass adalah bendera pass render (nilai dari enumerasi D3D12_RENDER_PASS_FLAGS ).

enum D3D12_RENDER_PASS_FLAGS
{
    D3D12_RENDER_PASS_FLAG_NONE = 0,
    D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES = 0x1,
    D3D12_RENDER_PASS_FLAG_SUSPENDING_PASS = 0x2,
    D3D12_RENDER_PASS_FLAG_RESUMING_PASS = 0x4
};

UAV menulis dalam kode render

Penulisan tampilan akses yang tidak diurutkan (UAV) diizinkan dalam kode render, tetapi Anda harus secara khusus menunjukkan bahwa Anda akan mengeluarkan tulisan UAV dalam pass render dengan menentukan D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES, sehingga driver tampilan dapat menolak ubin jika perlu.

Akses UAV harus mengikuti batasan baca-tulis yang dijelaskan di atas (menulis dalam kode render tidak valid untuk dibaca hingga lulus render berikutnya). Hambatan UAV tidak diizinkan dalam render pass.

Pengikatan UAV (melalui tabel akar atau deskriptor akar) diwariskan ke dalam pass render, dan disebarluaskan dari pass render.

Menangguhkan-lolos, dan melanjutkan-lulus

Anda dapat menunjukkan seluruh pass render sebagai suspending-pass dan/atau resuming-pass. Pasangan yang ditangguhkan-diikuti oleh-lanjutan harus memiliki bendera tampilan/akses yang identik di antara pass, dan mungkin tidak memiliki operasi GPU yang mengintervensi (misalnya, menggambar, mengirimkan, membuang, menghapus, menyalin, memperbarui pemetaan petak peta, write-buffer-immediates, kueri, penyelesaian kueri) antara pass render yang ditangguhkan dan meneruskan lulus render.

Kasus penggunaan yang dimaksudkan adalah penyajian multi-utas, di mana katakanlah empat daftar perintah (masing-masing dengan pass render mereka sendiri) dapat menargetkan target render yang sama. Saat pass render ditangguhkan/dilanjutkan di seluruh daftar perintah, daftar perintah harus dijalankan dalam panggilan yang sama ke ID3D12CommandQueue::ExecuteCommandLists.

Pass render dapat berupa melanjutkan dan menangguhkan. Dalam contoh multi-utas yang baru saja diberikan, perintah masing-masing mencantumkan 2 dan 3 akan dilanjutkan dari 1 dan 2. Dan pada saat yang sama 2 dan 3 masing-masing akan ditangguhkan ke 3 dan 4.

Kueri untuk dukungan fitur lolos render

Anda dapat memanggil ID3D12Device::CheckFeatureSupport untuk mengkueri sejauh mana driver perangkat dan/atau perangkat keras secara efisien mendukung render pass.

D3D12_RENDER_PASS_TIER get_render_passes_tier(::ID3D12Device * pIDevice)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureSupport{};
    winrt::check_hresult(
        pIDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &featureSupport, sizeof(featureSupport))
    );
    return featureSupport.RenderPassesTier;
}
...
    D3D12_RENDER_PASS_TIER renderPassesTier{ get_render_passes_tier(pIDevice) };

Karena logika pemetaan runtime, render lulus selalu berfungsi. Tetapi, tergantung pada dukungan fitur, mereka tidak akan selalu memberikan manfaat. Anda dapat menggunakan kode yang mirip dengan contoh kode di atas untuk menentukan apakah/kapan layak bagi Anda saat mengeluarkan perintah sebagai lulus render, dan ketika itu jelas bukan manfaat (yaitu, ketika runtime hanya memetakan ke permukaan API yang ada). Melakukan pemeriksaan ini sangat penting jika Anda menggunakan D3D11On12).

Untuk deskripsi tiga tingkat dukungan, lihat enumerasi D3D12_RENDER_PASS_TIER .