Direct3D 12 轉譯階段
轉譯傳遞功能是 Windows 10 版本 1809 (10.0) 的新功能;組建 17763),並引進 Direct3D 12 轉譯階段的概念。 轉譯階段是由您記錄到命令清單中的命令子集所組成。
若要宣告每個轉譯階段開始和結束的位置,請將屬於 的命令巢狀化,該命令會傳遞至 id3D12GraphicsCommandList4::BeginRenderPass,並 EndRenderPass。 因此,任何命令清單都包含零、一或多個轉譯階段。
場景
如果轉譯階段是以 Tile-Based 延遲轉譯(TBDR)為基礎,則轉譯器可以改善轉譯器的效能以及其他技術。 更具體來說,這項技術可藉由讓應用程式更清楚地識別資源轉譯順序需求和數據相依性,藉以降低記憶體對晶元外記憶體的記憶體流量,以協助轉譯器提升 GPU 效率。
以明確方式撰寫以利用轉譯傳遞功能所撰寫的顯示驅動程式可提供最佳結果。 但轉譯傳遞 API 甚至可以在既有的驅動程式上執行(雖然不一定具有效能改善)。
這些是轉譯階段設計來提供值的案例。
允許您的應用程式避免在 Tile-Based 延遲轉譯架構上,從/到主要記憶體不必要的資源載入/儲存
轉譯階段的其中一個價值主張是,它提供您一個中央位置,以指出應用程式的一組轉譯作業的數據相依性。 這些數據相依性可讓顯示驅動程式在系結/屏障時間檢查此數據,併發出將資源載入/儲存從/儲存到主要記憶體的指示。
允許 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 包含對應至深度樣板檢視 (DSV) 的 CPU 描述元句柄。 這些 CPU 描述元句柄與您傳遞至 ID3D12GraphicsCommandList::OMSetRenderTargets相同的句柄。 而且,就像 OMSetRenderTargets一樣,CPU 描述元 會在呼叫 beginRenderPass 時,從各自的 (CPU 描述元) 堆積 擷取 CPU 描述項。
RTV 和 DSV 不會繼承至轉譯階段。 相反地,必須設定它們。 BeginRenderPass 中宣告的 RTV 和 DSV 也不會 傳播至命令清單。 而是在轉譯階段之後處於未定義的狀態。
轉譯傳遞和工作負載
您無法巢狀轉譯傳遞,而且不能有一個以上的轉譯傳遞命令清單(它們必須在錄製到單一命令清單中時開始和結束)。 以下的轉譯傳遞 轉譯傳遞旗標一節將討論設計為啟用有效率的多線程轉譯階段產生優化。
您從轉譯階段內執行的寫入不會 有效的,讓您在後續轉譯階段之前讀取。 這排除了轉譯階段內某些類型的屏障,例如,從 RENDER_TARGET 到目前系結轉譯目標上的 SHADER_RESOURCE 障礙。 如需詳細資訊,請參閱下方 轉譯傳遞和資源屏障一節,。
剛才提及的寫入讀取條件約束的其中一個例外狀況牽涉到在深度測試和轉譯目標混合時發生的隱含讀取。 因此,在轉譯階段內不允許這些 API(如果錄製期間呼叫任何 API,核心運行時間會移除命令清單)。
- ID3D12GraphicsCommandList1::AtomicCopyBufferUINT
- ID3D12GraphicsCommandList1::AtomicCopyBufferUINT64
- ID3D12GraphicsCommandList4::BeginRenderPass
- ID3D12GraphicsCommandList::ClearDepthStencilView
- ID3D12GraphicsCommandList::ClearRenderTargetView
- ID3D12GraphicsCommandList::ClearState
- ID3D12GraphicsCommandList::ClearUnorderedAccessViewFloat
- ID3D12GraphicsCommandList::ClearUnorderedAccessViewUint
- ID3D12GraphicsCommandList::CopyBufferRegion
- ID3D12GraphicsCommandList::CopyResource
- ID3D12GraphicsCommandList::CopyTextureRegion
- ID3D12GraphicsCommandList::CopyTiles
- ID3D12GraphicsCommandList::D iscardResource
- ID3D12GraphicsCommandList::D ispatch
- ID3D12GraphicsCommandList::OMSetRenderTargets
- ID3D12GraphicsCommandList::ResolveQueryData
- ID3D12GraphicsCommandList::ResolveSubresource
- ID3D12GraphicsCommandList1::ResolveSubresourceRegion
- ID3D12GraphicsCommandList3::SetProtectedResourceSession
轉譯傳遞和資源屏障
您可能不會讀取或取用相同轉譯階段內發生的寫入。 某些障礙不符合此條件約束,例如從 D3D12_RESOURCE_STATE_RENDER_TARGET 到目前系結轉譯目標上的 *_SHADER_RESOURCE #,而偵錯層將錯誤到該效果)。 但是,在 目前轉譯階段外寫入 轉譯目標的相同屏障是一致的,因為寫入會在目前的轉譯階段開始之前完成。 您可能會受益於了解顯示驅動程式在這方面可以進行的特定優化。 假設工作負載一致,顯示驅動程式可能會將轉譯階段中遇到的任何障礙移至轉譯階段的開頭。 在那裡,它們可以聯合(且不會干擾任何並排/量化作業)。 這是一個有效的優化,前提是所有寫入在目前的轉譯階段開始之前都已完成。
以下是更完整的驅動程式優化範例,假設您有一個轉譯引擎具有預先 Direct3D 12 樣式的資源系結設計,請根據資源系結方式,依需求執行障礙 。 當寫入框架結尾的未排序存取檢視 (UAV)時,引擎可能會在框架結束時讓資源處於 D3D12_RESOURCE_STATE_UNORDERED_ACCESS 狀態。 在接下來的框架中,當引擎將資源系結為著色器資源檢視時,它會發現資源狀態不正確,而且會發出障礙,從 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_DESC 和 D3D12_RENDER_PASS_DEPTH_STENCIL_DESC 結構來執行此動作。
如需詳細資訊,請參閱 D3D12_RENDER_PASS_BEGINNING_ACCESS 和 D3D12_RENDER_PASS_ENDING_ACCESS 結構,以及 D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE 和 D3D12_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 列舉。