從 Direct3D 11 移植到 Direct3D 12
本節提供從自定義 Direct3D 11 圖形引擎移植到 Direct3D 12 的一些指引。
裝置建立
Direct3D 11 和 Direct3D 12 都共用類似的裝置建立模式。 現有的 Direct3D 12 驅動程式全 都D3D_FEATURE_LEVEL_11_0 或更好,因此您可以忽略較舊的功能層級和相關聯的限制。
同時請記住,使用 Direct3D 12 時,您應該使用 DXGI 介面明確列舉裝置資訊。 在 Direct3D 11 中,您可以從 Direct3D 裝置鏈結回 DXGI 裝置,而 Direct3D 12 不支援此功能。
藉由提供從 IDXGIFactory4::EnumWarpAdapter取得的明確配接器,即可在 Direct3D 12 上建立 WARP 軟體裝置。 Direct3D 12 的 WARP 裝置僅適用於已啟用圖形工具選用功能的系統上。
注意
沒有相當於 D3D11CreateDeviceAndSwapChain。 即使使用 Direct3D 11,我們仍不建議使用此函式,因為最好以不同的步驟建立裝置和交換鏈。
已認可的資源
在 Direct3D 11 中使用下列介面建立的物件,會轉譯為 Direct3D 12 中所謂的「認可資源」。 已認可的資源是同時具有虛擬位址空間和與其相關聯的實體頁面的資源。 這是 Direct3D 12 所依據之 Windows 設備驅動器 2 (WDD2) 記憶體模型Microsoft的概念。
Direct3D 11 資源:
- ID3D11Resource
- ID3D11Buffer 和 ID3D11Device::CreateBuffer
- ID3D11Texture1D 和 ID3D11Device:CreateTexture1D
- ID3D11Texture2D 和 ID3D11Device::CreateTexture2D
- ID3D11Texture3D 和 ID3D11Device::CreateTexture3D
在 Direct3D 12 中,這些全都以 ID3D12Resource 和 ID3D12Device::CreateCommittedResource 表示。
保留的資源
保留的資源是只配置虛擬位址空間的資源,在呼叫 ID3D12Device::CreateHeap之前,不會配置物理記憶體。 這基本上與 Direct3D 11 中的磚資源概念相同。
Direct3D 11 中用來設定磚資源的旗標 (D3D11_RESOURCE_MISC_FLAG),然後將它們對應至物理記憶體。
- D3D11_RESOURCE_MISC_TILED
- D3D11_RESOURCE_MISC_TILE_POOL
上傳數據
在 Direct3D 11 中,會有單一時間軸的外觀(依序呼叫,例如使用 D3D11_SUBRESOURCE_DATA 初始化的數據,然後呼叫 ID3D11DeviceContext::UpdateSubresource,然後呼叫 ID3D11DeviceContext::Map)。 Direct3D 11 開發人員並不明顯建立數據的複本數目。
在 Direct3D 12 中,有兩個時間軸:GPU 時間軸(透過呼叫 CopyTextureRegion 和 CopyBufferRegion 從可對應記憶體設定)和 CPU 時間軸(由呼叫 Map 決定)。 會提供協助程式函式(在 d3dx12.h 檔案中),稱為使用共享時程表的 Updatesubresources。 此協助程式函式有數個變化,其中一個使用 ID3D12Device::GetCopyableFootprints、另一個使用堆積配置機制,另一個使用堆疊配置機制。 這些協助程式函式會透過記憶體的中繼暫存區域,將資源複製到 GPU 和 CPU。
GPU 和 CPU 通常會有自己的資源複本,並系結至自己的時間軸。 共享時程表方法同樣會維護兩個複本。
著色器和著色器物件
在 Direct3D 11 中,有許多著色器和狀態物件的建立,並使用 ID3D11Device 建立方法和 ID3D11DeviceContext set 方法設定這些物件的狀態。 一般而言,這些方法會進行大量呼叫,然後由驅動程式在繪製時間合併,以設定正確的管線狀態。
在 Direct3D 12 中,管線狀態的設定已合併成單一物件(計算引擎的 CreateComputePipelineState,以及圖形引擎的 CreateGraphicsPipelineState),然後附加至命令列表,然後再附加至繪製呼叫 SetPipelineState。
這些呼叫會取代 Direct3D 11 中設定著色器、輸入配置、混合狀態、轉譯器狀態、深度樣板狀態等所有個別呼叫
- 裝置 11 方法:
CreateInputLayout
、CreateXShader
、CreateDepthStencilState
和CreateRasterizerState
。 - 裝置內容 11 方法:
IASetInputLayout
、xxSetShader
、OMSetBlendState
、OMSetDepthStencilState
、 和RSSetState
。
雖然 Direct3D 12 可以支援舊版編譯的著色器 Blob,但著色器應該使用著色器模型 5.1 搭配 FXC/D3DCompile API 來建置,或使用著色器模型 6 使用 DXIL DXC 編譯程式。 您應該使用 CheckFeatureSupport 和 D3D12_FEATURE_SHADER_MODEL 來驗證著色器模型 6 支援。
將工作提交至 GPU
在 Direct3D 11 中,幾乎無法控制實際提交工作的方式,但主要由驅動程式處理,不過有些控件是透過 ID3D11DeviceContext::Flush 和 IDXGISwapChain1::P resent1 呼叫來啟用。
在 Direct3D 12 工作提交中,應用程式非常明確且受控制。 提交工作的主要建構是 ID3D12GraphicsCommandList,用來記錄所有應用程式命令(而且在概念上與ID3D11延遲內容相當類似)。 命令清單的備份存放區是由 ID3D12CommandAllocator提供,可讓應用程式藉由實際公開 Direct3D 12 驅動程式將用來儲存命令清單的記憶體,來管理命令清單的記憶體使用量。
最後, ID3D12CommandQueue 是先出先出佇列,其會儲存命令清單的正確順序以提交至 GPU。 只有在 GPU 上完成執行一個命令清單時,驅動程式才會提交來自佇列的下一個命令清單。
在 Direct3D 11 中,沒有明確的命令佇列概念。 在 Direct3D 12 的常見設定中,目前開啟 的D3D12_COMMAND_LIST_TYPE_DIRECT 命令清單可以視為類似 Direct3D 11 實時內容。 這提供許多相同的函式。
D3D11DeviceContext | ID3D12GraphicsCommand 清單 |
---|---|
ClearDepthStencilView | ClearDepthStencilView |
ClearRenderTargetView | ClearRenderTargetView |
ClearUnorderedAccess* | ClearUnorderedAccess* |
Draw、DrawInstanced | DrawInstanced |
DrawIndexed、DrawIndexedInstanced | DrawIndexedInstanced |
分派 | 分派 |
IASetInputLayout、xxSetShader 等。 | SetPipelineState |
OMSetBlendState | OMSetBlendFactor |
OMSetDepthStencilState | OMSetStencilRef |
OMSetRenderTargets | OMSetRenderTargets |
RSSetViewports | RSSetViewports |
RSSetScissorRects | RSSetScissorRects |
IASetPrimitiveTopology | IASetPrimitiveTopology |
IASetVertexBuffers | IASetVertexBuffers |
IASetIndexBuffer | IASetIndexBuffer |
ResolveSubresource | ResolveSubresource |
CopySubresourceRegion | CopyBufferRegion |
UpdateSubresource | CopyTextureRegion |
CopyResource | CopyResource |
注意
使用 D3D12_COMMAND_LIST_TYPE_BUNDLE 建立的命令清單會與延後的內容相等。 Direct3D 12 也支援存取即時內容的某些功能,同時透過D3D12_COMMAND_LIST_TYPE_COPY和D3D12_COMMAND_LIST_TYPE_COMPUTE命令清單類型進行轉譯。
CPU/GPU 同步處理
在 Direct3D 11 CPU/GPU 同步處理中,基本上是自動的,應用程式不需要維護物理記憶體的狀態。
在 Direct3D 12 中,應用程式必須明確管理兩個時間軸(CPU 和 GPU)。 這需要應用程式維護資訊,以取得 GPU 所需的資源,以及多久的時間。 這也表示應用程式負責確保資源的內容(例如認可資源、堆積、命令配置器)在 GPU 完成使用之前不會變更。
同步時間軸的主要物件是 ID3D12Fence 物件。 柵欄的作業相當簡單,可讓 GPU 在完成工作時發出訊號。 GPU 和 CPU 可以同時發出訊號,而且兩者都可以等待柵欄。
一般而言,在提交命令清單以供執行時,GPU 會在完成時傳輸柵欄訊號(完成讀取數據時),讓 CPU 能夠重複使用或終結資源。
在 Direct3D 11 中, ID3D11DeviceContext::Map 旗標D3D11_MAP_WRITE_DISCARD基本上會將每個資源視為應用程式所能寫入的無盡記憶體供應(稱為「重新命名」的程式)。 在 Direct3D 12 中,程式是明確的:需要配置額外的記憶體,而且應該使用柵欄來同步處理作業。 環緩衝區(由大型緩衝區組成)可能是很好的技巧,請參閱柵欄式資源管理中的環形緩衝區案例。
資源系結
Direct3D 11 中的檢視(著色器資源檢視、轉譯目標檢視等等),在 Direct3D 12 中基本上已取代為描述元的概念。 Direct3D 12 中仍然存在建立方法(例如 CreateShaderResourceView 和 CreateRenderTargetView),這些方法會在建立描述元堆積之後呼叫,以將數據寫入堆積。 Direct3D 12 中的系結現在由根簽章中所述的描述項句柄處理,並使用 SetGraphicsRootDescriptorTable 或 SetComputeRootDescriptorTable 方法提交。
根簽章詳細數據根簽章位置編號和描述元數據表之間的對應,其中描述元數據表可以包含頂點著色器、圖元著色器和其他著色器可用資源的參考,例如常數緩衝區、著色器資源檢視和取樣器。 此彈性會中斷 HLSL 快取器空間與 Direct3D 12 中 API 系結空間的連線,不同於 Direct3D 11,其中在這些對應之間有一對一對應。
此系統的其中一個含意是應用程式負責重新命名描述元數據表,這可讓開發人員了解變更每一繪製呼叫之單一描述元的效能成本。
Direct3D 12 的新功能是應用程式可以控制哪些描述元在哪些著色器階段之間共用。 在 Direct3D 11 資源中,例如 UAV 會在所有著色器階段之間共用。 藉由讓特定著色器階段停用描述項,已停用之描述項所使用的緩存器可供特定著色器階段啟用的描述項使用。
下表顯示範例根簽章。
根參數位置 | 描述項數據表專案 |
---|---|
0 | VS 描述元範圍 b0-b13 |
1 | VS 描述元範圍 t0-t127 |
2 | VS 描述元範圍 s0-s16 |
3 | PS 描述元範圍 b0-b13 |
... | |
14 | DS 描述元範圍 s0-16 |
15 | 共用描述元範圍 u0-u63 |
資源狀態
應用程式不會維護 Direct3D 11 資源狀態,而是由驅動程序維護。
在 Direct3D 12 中,維護資源狀態會成為應用程式的責任,若要在命令清單的錄製中啟用完整平行處理原則:應用程式必須處理命令清單的錄製時程表(可以平行完成),以及必須循序的執行時程表。
ResourceBarrier 方法會處理資源狀態轉換。 主要是應用程式必須在資源使用量變更時通知驅動程式。 例如,如果資源正當做轉譯目標使用,然後在下一個繪製呼叫時做為頂點著色器的輸入,則這可能需要在 GPU 作業中短暫停滯,才能處理頂點著色器之前完成轉譯目標作業。
此系統可啟用圖形管線的精細粒度同步處理(GPU 停滯),以及快取排清,以及可能有些記憶體配置變更(例如,將目標檢視轉譯為深度樣板檢視解壓縮)。
這稱為轉換屏障。 Direct3D 11 中有其他類型的障礙,ID3D11DeviceContext2::TiledResourceBarrier 可讓兩個不同的磚資源使用相同的物理記憶體。 在 Direct3D 12 中,這稱為「別名屏障」。 別名屏障可用於 Direct3D 12 中並排和放置的資源。 此外,還有 UAV 屏障。 在 Direct3D 11 中,所有 UAV 分派和繪製作業都必須串行化,即使這些作業可以管線處理或平行運作也一樣。 對於 Direct3D 12,新增 UAV 屏障會移除此限制。 UAV屏障可確保 UAV 作業是循序的,因此,如果第二個作業要求第一個完成,第二個作業會強制等待增加屏障。 UAV 的預設作業只是作業會儘快繼續進行。
如果工作負載可以平行處理,則明顯會有效能提升。
交換鏈結
DXGI 交換鏈結是 Direct3D 11 和 12 中交換鏈結的基礎。 在 Direct3D 11 中,有一些微差異,交換鏈結的三種類型為 SEQUENTIAL、DISCARD 和 FLIP_SEQUENTIAL。 針對 Direct3D 12,只有兩種類型:FLIP_SEQUENTIAL和FLIP_DISCARD。 如上所述,您應該透過 IDXGIFactory4或更新版本明確建立交換鏈,並針對任何配接器列舉使用相同的介面。
在 Direct3D 11 中,有自動反緩衝器旋轉:後台緩衝區 0 只需要一個轉譯目標檢視。 在 Direct3D 12 緩衝區輪替中,每個後端緩衝區都必須有轉譯目標檢視。 使用IDXGISwapChain3::GetCurrentBackBufferIndex 方法來選取要轉譯的IDXGISwapChain3::GetCurrentBackBufferIndex 方法。 同樣地,這個額外的彈性可啟用更大的平行處理。
注意
雖然有許多方式可以設定您的應用程式,但一般而言,應用程式每個交換鏈結緩衝區都有一個 ID3D12CommandAllocator 。 這可讓應用程式繼續為下一個畫面建置一組命令,而 GPU 轉譯上一個畫面。
固定函式轉譯
在 Direct3D 11 中,有一些方法可簡化各種較高層級的作業,例如 GenerateMips(建立完整 Mip 鏈結)和 DrawAuto(使用數據流輸出作為著色器輸入,而不需進一步輸入應用程式)。 Direct3D 12 中無法使用這些方法,應用程式必須藉由建立著色器來執行這些作業來處理這些作業。
賠率和結束數
下表顯示 Direct3D 11 與 12 之間類似但並非完全相同的一些功能。
Direct3D 11 | Direct3D 12 |
---|---|
ID3D11Query | ID3D12QueryHeap 可讓查詢群組在一起,降低成本。 |
ID3D11Predicate | 現在,在完全透明緩衝區中擁有數據,來啟用述詞。 Direct3D 11 ID3D11Predicate 物件會取代為 ID3D12Resource::Map,該對象必須遵循 ResolveQueryData 的呼叫,並使用柵欄來等候數據準備就緒。 請參閱 述詞。 |
UAV/SO 隱藏計數器 | 應用程式負責 SO/UAV 計數器的配置和管理。 請參閱數據流輸出計數器和UAV計數器。 |
資源動態 MinLOD (詳細資料的迷你層級) | 這已移至 SRV 描述元靜態 MinLOD。 |
Draw*Indirect/DispatchIndirect | 繪製間接方法全都會合併到一個 ExecuteIndirect 方法中。 |
DepthStencil 格式交錯 | DepthStencil 格式是平面格式。 例如,24 位深度的格式,8 位樣板會以 24/8/24/8 格式儲存...Direct3D 11 中的等,但 24/24/24...後面接著 8/8/8...在 Direct3D 12 中。 請注意,每個平面都是 D3D12 中自己的子資源(請參閱 子資源)。 |
ResizeTilePool | 保留的資源可以對應至多個堆積。 當磚集區在 D3D11 中成長時,可以改為在 D3D12 中配置額外的堆積。 |