Direct3D 12 轉譯階段

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

若要宣告每個轉譯階段開始和結束的位置,您可以巢狀屬於該傳遞的命令,該命令會傳入 ID3D12GraphicsCommandList4::BeginRenderPassEndRenderPass。 因此,任何命令清單都包含零、一或多個轉譯階段。

案例

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

以快速方式撰寫的顯示驅動程式,利用轉譯階段功能可提供最佳結果。 但是,轉譯傳遞 API 即使在既有的驅動程式上仍可執行 (,但不一定有效能改善) 。

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

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

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

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

注意

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

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

您的應用程式責任

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

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

使用轉譯階段功能

什麼是 轉譯階段

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

  • 在轉譯階段期間固定的一組輸出系結。 這些系結是一或多個轉譯目標檢視, (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 包含對應至深度樣板檢視的 CPU 描述項控制碼, (DSV) 。 這些 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_ACCESS到D3D12_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 列舉。