Direct3D 12 轉譯階段

Windows 10 版本 1809 (10.0 的轉譯階段功能是新功能;組建 17763) ,並引進 Direct3D 12 轉譯階段的概念。 轉譯傳遞是由您記錄到命令清單中的命令子集所組成。

若要宣告每個轉譯階段的開始和結束位置,請將屬於 的命令巢狀置於 ID3D12GraphicsCommandList4::BeginRenderPassEndRenderPass的呼叫內。 因此,任何命令清單都包含零、一或多個轉譯階段。

案例

如果轉譯器是以Tile-Based延遲轉譯 (TBDR) 為基礎,則轉譯階段可以改善轉譯器的效能。 更具體來說,這項技術可協助您的轉譯器藉由讓應用程式更清楚地識別資源轉譯順序需求和資料相依性,藉以降低晶片記憶體之間的記憶體流量,以改善 GPU 效率。

以明確方式撰寫的顯示驅動程式利用轉譯傳遞功能可提供最佳結果。 但轉譯傳遞 API 甚至可以在預先存在的驅動程式上執行 (,但不一定有效能改善) 。

這些是轉譯階段的設計目的是要提供值的案例。

允許您的應用程式避免在Tile-Based延遲轉譯 (TBDR) 架構上,從/到主要記憶體不必要的資源載入/儲存

轉譯階段的價值主張之一,就是它提供一個中央位置,以指出一組轉譯作業的應用程式資料相依性。 這些資料相依性可讓顯示驅動程式在系結/屏障時間檢查此資料,併發出將資源載入/儲存從/儲存到主要記憶體的指示。

允許 TBDR 架構在晶片上快取中,跨轉譯傳遞 (,甚至在不同的命令清單)

注意

具體來說,此案例僅限於您在多個命令清單中寫入相同轉譯目標 () 的情況。

常見的轉譯模式是讓應用程式序列化成相同轉譯目標 (多個命令清單) ,即使轉譯命令會以平行方式產生。 在此案例中使用轉譯傳遞可讓這些傳遞以這種方式結合 (,因為應用程式知道它會在立即成功的命令清單上繼續轉譯,) 顯示驅動程式可以避免排清命令清單界限上的主記憶體。

您的應用程式責任

即使轉譯通過功能,Direct3D 12 執行時間或顯示驅動程式都不會負責重新排序/避免載入和存放區。 若要正確運用轉譯階段功能,您的應用程式會負責這些責任。

  • 正確識別其作業的資料/排序相依性。
  • 以最小化排清 (的方式排序其提交,因此,請將 _PRESERVE 旗標的使用降到最低) 。
  • 正確利用資源屏障,並追蹤資源狀態。
  • 避免不必要的複製/清除。 為了協助識別這些情況,您可以使用PIX on Windows 工具的自動化效能警告。

使用轉譯傳遞功能

什麼是 轉譯階段

轉譯階段是由這些元素所定義。

  • 一組在轉譯階段期間固定的輸出系結。 這些系結位於一或多個轉譯目標檢視 (RTV) 和/或深度樣板檢視 (DSV) 。
  • 以該組輸出系結為目標的 GPU 作業清單。
  • 中繼資料,描述轉譯階段目標之所有輸出系結的載入/儲存相依性。

宣告輸出系結

在轉譯階段的開頭,您會在轉譯目標 () 和/或宣告至深度/樣板緩衝區的系結。 您可以選擇系結至轉譯目標 () ,並選擇性地系結至深度/樣板緩衝區。 但您必須系結至至少兩者之一,而在下列程式碼範例中,我們會系結至這兩者。

您會在 對 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).
}

您可以將 D3D12_RENDER_PASS_RENDER_TARGET_DESC 結構的第一個欄位設定為對應至一或多個轉譯目標檢視的 CPU 描述元控制碼, (RTV) 。 同樣地, D3D12_RENDER_PASS_DEPTH_STENCIL_DESC 包含對應至 DSV () 深度樣板檢視的 CPU 描述元控制碼。 這些 CPU 描述元控制碼與您傳遞至 ID3D12GraphicsCommandList::OMSetRenderTargets相同。 而且,就像OMSetRenderTargets一樣,CPU 描述元會在呼叫BeginRenderPass時,從各自的 (CPU 描述元) 堆積來貼齊

RTV 和 DSV 不會繼承至轉譯階段。 而是必須設定它們。 BeginRenderPass中宣告的 RTV 和 DSV 也不會傳播至命令清單。 而是在轉譯階段之後處於未定義的狀態。

轉譯傳遞和工作負載

您無法巢狀轉譯傳遞,而且您無法在錄製到單一命令清單時,將轉譯階段跨越多個命令清單, (它們必須在錄製成單一命令清單時開始和結束) 。 以下的轉譯階段旗標一節會討論設計來啟用有效率的多執行緒轉譯 階段產生優化。

您從轉譯階段內執行的 寫入,在 後續轉譯階段之前,無法從中讀取。 這可排除轉譯階段內某些類型的屏障,例如,從RENDER_TARGET 阻礙到目前 系結轉譯目標上的 SHADER_RESOURCE 。 如需詳細資訊,請參閱下面的轉譯 傳遞和資源屏障一節。

剛才提及的寫入讀取條件約束的其中一個例外狀況牽涉到在深度測試和轉譯目標混合過程中發生的隱含讀取。 因此,在轉譯階段內不允許這些 API, (核心執行時間會在錄製期間呼叫任何 API 時移除命令清單) 。

轉譯傳遞和資源屏障

您可能不會讀取或取用相同轉譯階段內發生的寫入。 某些屏障不符合此條件約束,例如,從 D3D12_RESOURCE_STATE_RENDER_TARGET 到目前系結轉譯目標 (的 *_SHADER_RESOURCE ,偵錯層將會錯誤到該效果) 。 但是,在目前轉譯階段 外部 寫入的轉譯目標上,該相同的屏障是一致的,因為寫入會在目前的轉譯階段開始之前完成。 您可能會受益于瞭解顯示驅動程式可以在此方面進行的特定優化。 假設有一致性的工作負載,顯示驅動程式可能會將轉譯階段中遇到的任何障礙移至轉譯階段的開頭。 您可以在該處聯合 (,而不會干擾任何磚/量化作業) 。 這是有效的優化,前提是所有寫入在目前的轉譯階段開始之前都已完成。

以下是更完整的驅動程式優化範例,假設您的轉譯引擎具有預先 Direct3D 12 樣式的資源系結設計,根據資源系結的方式 依需求 執行屏障。 寫入未排序的存取檢視時, (UAV) 在下列畫面格結尾 (取用下列畫面格) 時,引擎可能會在畫面結束時讓資源處於 D3D12_RESOURCE_STATE_UNORDERED_ACCESS 狀態。 在後續框架中,當引擎將資源系結為著色器資源檢視 (SRV) 時,它會發現資源處於正確的狀態,而且會發出屏障,使其無法D3D12_RESOURCE_STATE_UNORDERED_ACCESSD3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE。 如果轉譯階段內發生該屏障,則顯示驅動程式會在假設所有寫入都已經在這個目前的轉譯階段 之外 發生,因此 (,而以下是顯示驅動程式) 顯示驅動程式可能會將屏障向上 移動 至轉譯階段開頭的位置。 同樣地,只要您的程式碼符合本節和最後一個章節中所述的寫入讀取條件約束,這一點就有效。

這些是一致性障礙的範例。

  • D3D12_RESOURCE_STATE_UNORDERED_ACCESSD3D12_RESOURCE_STATE_INDIRECT_ARGUMENT。
  • D3D12_RESOURCE_STATE_COPY_DEST*_SHADER_RESOURCE

這些是不符合規範障礙的範例。

  • D3D12_RESOURCE_STATE_RENDER_TARGET 目前系結 RTV/DSV 上的任何讀取狀態。
  • D3D12_RESOURCE_STATE_DEPTH_WRITE 目前系結 RTV/DSV 上的任何讀取狀態。
  • 任何別名屏障。
  • 未排序的存取檢視 (UAV) 障礙。 

資源存取宣告

BeginRenderPass 時間,以及宣告作為該階段內 RTV 和/或 DSV 的所有資源,您也必須指定其開始和結束 存取 特性。 如您在上述宣告 輸出 系結一節的程式碼範例中所見,您可以使用 D3D12_RENDER_PASS_RENDER_TARGET_DESCD3D12_RENDER_PASS_DEPTH_STENCIL_DESC結構來執行 此動作。

如需詳細資訊,請參閱 D3D12_RENDER_PASS_BEGINNING_ACCESSD3D12_RENDER_PASS_ENDING_ACCESS 結構,以及 D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPED3D12_RENDER_PASS_ENDING_ACCESS_TYPE 列舉。

轉譯傳遞旗標

傳遞至 BeginRenderPass 的最後一個參數是轉譯傳遞旗標, (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 寫入

在轉譯階段內允許未排序的存取檢視 (UAV) 寫入,但您必須特別指出您將透過指定 D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES在轉譯階段內發出 UAV 寫入,讓顯示驅動程式視需要退出宣告圖格。

UAV 存取必須遵循上述的寫入讀取條件約束, (轉譯階段中的寫入無效,直到後續轉譯階段) 為止。 轉譯階段內不允許 UAV 屏障。

UAV 系結 (透過根資料表或根描述元) 繼承至轉譯階段,並傳播到轉譯階段。

暫止傳遞和繼續傳遞

您可以將整個轉譯階段表示為暫止階段和/或繼續傳遞。 暫止後接的繼續配對在傳遞之間必須有相同的檢視/存取旗標,而且在暫停轉譯階段和繼續轉譯階段之間可能沒有任何交錯的 GPU 作業 (,例如,繪製、分派、捨棄、清除、複製、更新磚對應、write-buffer-immediates、查詢、查詢會解析) 暫停轉譯階段和繼續轉譯階段。

預期的使用案例是多執行緒轉譯,其中假設有四個命令清單 (每個都有自己的轉譯階段,) 可以將目標設為相同的轉譯目標。 當轉譯階段在命令清單中暫停/繼續時,命令清單必須在對 ID3D12CommandQueue::ExecuteCommandLists的相同呼叫中執行。

轉譯階段可以是繼續和暫停。 在剛才指定的多執行緒範例中,命令清單 2 和 3 會分別從 1 和 2 繼續。 同時,2 和 3 會分別暫停至 3 和 4。

查詢轉譯傳遞功能支援

您可以呼叫 ID3D12Device::CheckFeatureSupport 來查詢設備磁碟機和/或硬體有效率地支援轉譯傳遞的範圍。

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) };

由於執行時間的對應邏輯,轉譯會一律會運作。 但是,視功能支援而定,它們不一定會提供好處。 您可以使用類似上述程式碼範例的程式碼來判斷/何時值得您發出命令作為轉譯階段,以及當它絕對不是優點 (,也就是執行時間只對應至現有的 API 介面時) 。 如果您使用 D3D11On12) ,則執行這項檢查特別重要。

如需三層支援的描述,請參閱 D3D12_RENDER_PASS_TIER 列舉。