關於 ID3D12GraphicsCommandList::SetDescriptorHeaps()
前言
最近在研究 Direct3D12 ,手邊的資料有 Direct3D12 的範例與 [ 書籍 ] DirectX 12 3D 游戲開發實戰,出版商:人民郵電出版社,ISBN-13:9787115479211 ,本以為就入門來說應該很充足,但在實務上會碰到一些不知道在做什麼的 Function ,就像是 ID3D12GraphicsCommandList::SetDescriptorHeaps() ,這個 Function 不論是 [ MSDN ]ID3D12GraphicsCommandList::SetDescriptorHeaps method 或是手邊的資料都沒有說得很清楚,都使是在範例裡有寫這個步驟,但都不特別解釋,所以我就對這個 Funciton 做了研究,在此做個紀錄。內容
ID3D12GraphicsCommandList::SetDescriptorHeaps() 這個 Function 在介面上有些設計不良,介面如下
void SetDescriptorHeaps( UINT NumDescriptorHeaps, ID3D12DescriptorHeap * const *ppDescriptorHeaps );
介面看起來會讓人很直覺得可以輸入很多個 ID3D12DescriptorHeap ,但是事實上是輸入的 DescriptorHeap 是有限制的!每個 ID3D12DescriptorHeap 在 Create 的時候會需要 D3D12_DESCRIPTOR_HEAP_TYPE,定義如下
typedef enum D3D12_DESCRIPTOR_HEAP_TYPE { D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, // Constant buffer/Shader resource/Unordered access views D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, // Samplers D3D12_DESCRIPTOR_HEAP_TYPE_RTV, // Render target view D3D12_DESCRIPTOR_HEAP_TYPE_DSV, // Depth stencil view D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES // Simply the number of descriptor heap types } D3D12_DESCRIPTOR_HEAP_TYPE;
這個 Type 會決定 DescriptorHeap 的型態, Create 完後不能更改,而在 ID3D12GraphicsCommandList::SetDescriptorHeaps() 輸入的每個 DescriptorHeap 必須是不同的 D3D12_DESCRIPTOR_HEAP_TYPE ,加上這個 Function 其實只處理"輸入"的部分,所以D3D12_DESCRIPTOR_HEAP_TYPE_RTV 與 D3D12_DESCRIPTOR_HEAP_TYPE_DSV 就不會用到,所以實際上最多只能輸入兩個 DescriptorHeap !這些在 MSDN 上都沒有很詳述的說明,我是在輸入同一種 Type 後會出現 Debug 的錯誤訊息才知道這個 Fuction 不能輸入不同 Type 的 DescriptorHeap 。個人認為理想的介面應該如下
void SetDescriptorHeaps( ID3D12DescriptorHeap * pDescriptorHeapsType_CBV_SRV_UAV; ID3D12DescriptorHeap * pDescriptorHeapsType_SAMPLER; );
接著來說一下喚起的時機,如下圖
SetDescriptorHeaps() 的喚起時機 |
如中上方的部分說明只能接收兩種 DescriptorHeap ,接著下方的 Draw call 是指每次的繪圖會透過 SetGraphicsRootDescriptorTable() 時要注意 GPUDescriptorHandle 必須來自 SetDescriptorHeaps() 的 DescriptorHeap ,手邊的資料也都沒明說這樣的規則,這是經過多次的實驗得來的經驗。如果需要切換 DescriptorHeap 的話,要注意切換的時機要發生在 Draw call 與 Draw call 之間,不能發生在 Draw call 的裡面,舉個例來說,如果一個 Draw call 裡需要做兩次 SetGraphicsRootDescriptorTable() ,但期望來自兩個 DescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE 是一樣的),這個時候發生需要在同一個 Draw call 裡喚起兩次 SetDescriptorHeaps(),如以下範例
ComPtr<ID3D12DescriptorHeap> m_myHeap1; ComPtr<ID3D12DescriptorHeap> m_myHeap2; //Init heap1 and heap2 //... // ID3D12DescriptorHeap* ppHeaps[] = { m_myHeap1.Get()}; m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); m_commandList->SetGraphicsRootDescriptorTable(0,m_myHeap1->GetGPUDescriptorHandleForHeapStart() ); ppHeaps[0] = m_myHeap2.Get(); m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); m_commandList->SetGraphicsRootDescriptorTable(1,m_myHeap2->GetGPUDescriptorHandleForHeapStart() ); m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView); m_commandList->DrawInstanced(3, 1, 0, 0);
這個做法目前不確定是否為"正規"用法,因為這個用法在 NVIDIA GeForce GTX1050 的顯卡時可以正常工作,但在 Intel HD Graphics 630 時無法正常工作,所以不確定是否可以這樣用,
說明文件也沒說明是否可這樣用,這部分目前的解決方案是絕不在 Draw call 中切換 DescriptorHeap ,這樣可以取得較好的兼容性。
參考資料
[ MSDN ]ID3D12GraphicsCommandList::SetDescriptorHeaps method[ 書籍 ] DirectX 12 3D 游戲開發實戰,出版商:人民郵電出版社,ISBN-13:9787115479211
沒有留言:
張貼留言