DirectX 12 學習之路(三)

一、圖元拓撲 通過指定圖元拓撲(primitive topology,或稱基元拓撲)來告知Direct3D如何用頂點數來表示幾何圖元。
1、點列表 (D3D_PRIMITIVE_TOPOLOGY_POINTLIST)(point list)
所有的頂點都將在繪製調用的過程中被繪製爲一個單獨的點。
2、線條帶(D3D_PRIMITIVE_TOPOLOGY_LINESTRIP)(line strip)
頂點將在繪製調用的過程中被連接爲一系列的連續線段,若有n+1個頂點就會生成n條線段。
3、線列表(D3D_PRIMITIVE_TOPOLOGY_LINELIST)(line list)
每對頂點繪製調用的過程中都會組成單獨的線段,每2n個頂點就會生成n條線段。
線列表和線條帶的區別在於:線列表中的線段可以彼此分開,而線條帶中的線段則相連的。
4、三角形帶(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)(triangle strip)
所繪製的三角形會被連接成帶狀,處於中間位置的頂點將被相鄰的三角形所共同使用。利用n個頂點即可生成n-2個三角形。
在三角形帶中,次序爲偶數的三角形和爲奇數的三角形的繞序(windling order,也譯爲環繞順序等,即裝配圖元的頂點順序爲逆時針或順時針方向)是不同的,這就是剔除(culling,又稱消隱)問題的由來。爲了解決這個問題,GPU內部會對偶數三角形中的後(DirectX 中應該是後,OpenGL是前)兩個頂點的順序進行調換,以此使它們與奇數三角形的繞序保持一致。
5、三角形列表(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST)(triangle list)
在繪製調用的過程中會將每3個頂點裝配成獨立的三角形,所以每3n個頂點會生成n個三角形。
三角形列表與三角形帶的區別是:三角形列表中的三角形可以彼此分離,而三角形帶中的三角形則是相連的。
6、具有鄰接數據的圖元拓撲(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ)
對於存有鄰接數據的三角形列表而言,每個三角形都有3個與之相鄰的鄰接三角形(adjacent triangle)。
爲了使幾何着色器可以順利地獲得這些鄰接三角形的信息,我們就需要藉助頂點緩衝區與索引緩衝區將它們隨主三角形一併提交至渲染流水線。
注意,鄰接圖元的頂點只能用作幾何着色器的輸入數據,卻並不會被繪製出來 。即使程序沒有用到幾何着色器,但依舊不會繪製鄰接圖元。
6n個頂點可以生成n個三角形及其鄰接數據,線列表、線條帶和三角形帶也存在有鄰接數據的圖元拓撲。
7、控制點面片列表(D3D_PRIMITIVE_TOPOLOGY_N_CONTROL_POINT_PATCHLIST)
將頂點數據解釋爲具有N個控制點(control point)的面片列表( patch list)。此圖元常用於渲染流水線的曲面細分階段(tessellation stage)。

二、在計算三角形法線的方法中,根據觀察者的視角去看,頂點繞序爲順時針方向的三角形爲正面朝向,而頂點繞序爲逆時針方向的三角形爲背面朝向。但是,通過對Direct3D渲染狀態的設置,我們也可以將這個約定“顛倒”過來。

三、輸入佈局描述(input layout description):向Direct3D提供頂點結構體的描述,是它瞭解應怎樣來處理結構體中的每個成員。通過語義(semantic)將頂點結構體中的元素與頂點着色器輸入簽名(簽名 signature 可以理解爲着色器中輸入或輸出參數列表)中的元素一一映射起來。

四、對於靜態幾何體(static geometry,即每一幀都不會發生改變的幾何體)而言,我們會將其頂點緩衝區置於默認堆(D3D12_HEAP_TYPE_DEFAULT)中來優化性能。一般來說,遊戲中的大多數幾何體(如樹木、建築物、地形和動畫角色)都是如此處理。
除了創建頂點緩衝區資源本身外,我們還需要用D3D12_HEAP_TYPE_UPLOAD這種堆類型來創建一個處於中介位置的上傳緩衝區(uplaod buffer)。通過把資源提交至上傳堆,才得以將數據從CPU複製到GPU顯存中。在創建了上傳緩衝區之後,我們就可以將頂點數據從系統內存複製到上傳緩衝區,而後再把頂點數據從上傳緩衝區複製到真正的頂點緩衝區中。

五、爲了將頂點緩衝區綁定到渲染流水線上,我們需要給這種資源創建一個頂點緩衝區視圖(vertex buffer view)。和RTV(render texture view)不同的是,我們無須爲頂點緩衝區視圖創建描述符堆。而且,頂點緩衝區視圖是由D3D12_VERTEX_BUFFER_VIEW結構體來表示。
在頂點緩衝區及其對應的視圖創建完成後,便可以將它與渲染流水線上的一個輸入槽(input slot)相綁定,這樣一來,我們就可以向流水線中的輸入裝配器階段傳遞數據了。通過ID3D12GraphicsCommandList::IASetVertexBuffers()來完成。(IA input Assembler 輸入裝配器)
將頂點緩衝區設置到輸入槽上並不會對其執行實際的繪製操作,而是僅爲頂點數據送至渲染流水線做好準備而已。這最後一步是通過ID3DGraphicsCommandList::DrawInstanced()或ID3DGraphicsCommandList::DrawIndexedInstanced()方法真正地繪製頂點。

六、被繪製爲點、線列表還是三角形列表,圖元拓撲狀態實由ID3DGraphicsCommandList::IASetPrimitiveToplogy()方法來設置。

七、需要給索引緩衝區資源創建一個索引緩衝區視圖(index buffer view)。和頂點緩衝區視圖一樣,我們也無需給索引緩衝區視圖創建描述符堆。但是索引緩衝區視圖要由結構體D3D12_INDEX_BUFFER_VIEW來表示。
通過ID3D12GraphicsCommandList::IASetIndexBuffer()(IA input Assembler 輸入裝配器)方法即可將索引緩衝區綁定到輸入裝配器階段。

八、HLSL(High Level Shading Language)高級着色語言。在HLSL中,所有的函數都是內聯(inline)函數。
SV_POSITION語義比較特殊(SV表示系統值,system value),它所修飾的頂點着色器輸出元素存有齊次裁剪空間中的頂點位置信息。因爲,我們必須爲輸出位置信息的參數附上SV_POSITION語義,使GPU可以在進行例如裁剪、深度測試和光柵化等處理之時,藉此實現其他屬性所無法介入的有關運算。值得注意的是,對於任何不具有系統輸出參數而言,我們都可以根據需求以合法的語義名修飾它。
如果沒有使用幾何着色器,那麼頂點着色器必須使用SV_POSITION語義來輸出頂點在齊次裁剪空間中的位置,因爲(在沒有使用幾何着色器的情況下)執行完頂點着色器之後,硬件期望獲取頂點位於齊次裁剪空間之中的座標。如果使用了幾何着色器,則可以把輸出頂點在齊次裁剪空間中的位置的工作交給它來處理。
在頂點着色器(或幾何着色器)中是無法進行透視除法的,此階段只能實現投影矩陣這一環節的運算。而透視除法將在後面交由硬件執行。

九、輸入佈局中的元素格式確定的是,在數據未進入着色器寄存器之前,應運用何種數據轉換算法來確定各元素的具體格式。而着色器輸入簽名則定義的是,在不修改輸入寄存器中所存數據的情況下,頂點着色器將如何來解釋這些數據的類型。

十、由於硬件的原因,某些像素片段在移送至像素着色器之前,可能已經被渲染流水線所剔除(例如提前深度剔除, early-z rejection,也有譯爲早期深度剔除、早期z剔除等)。然而也有一些對情況能夠禁止提前深度剔除優化。比如說,倘若在像素着色器中有對像素深度值進行修改的操作,那麼像素着色器就必須針對每個像素各執行一次,因爲在像素着色器修改像素深度值以前,我們並不知道每個像素的最終深度值。

十一、像素着色器返回一個4D顏色值,而位於此函數參數列表後的SV_TARGET語義則表示該返回值的類型應當與渲染目標格式(render target format)相匹配(該輸出值會被存於渲染目標之中)。

十二、常量緩衝區(constant buffer)是一種GPU資源(ID3D12Resource),其數據內容可供着色器程序所引用。和頂點緩衝區和索引緩衝區不同的是,常量緩衝區通常由CPU每幀更新一次。所以,我們會把常量緩衝區創建到一個上傳堆而非默認堆中,這樣能夠使我們從CPU端更新常量。
常量緩衝區對硬件也有特別的需求:常量緩衝區的大小必須爲硬件最小分配空間(256B)的整數倍。

十三、Direct3D 12 不僅保證了Map與Unmap函數在多線程中調用的安全性,還令Map函數可以嵌套使用。第一次調用Map函數時,Direct3D會在CPU端分配一塊虛擬內存地址範圍,用來映射CPU中的資源。而最後一次調用Unmap函數時,則會釋放這塊CPU虛擬地址範圍。Map函數會在必要的時對CPU緩存執行invalidate操作(標記相關緩存無效,令CPU讀取主存中的數據),以使CPU端可以讀取GPU端對這段地址內容所做的修改;相反地,Unmap函數則會在必要時對CPU緩存進行flush操作(令緩存中的數據寫會主存),以令GPU端可以讀取CPU端對這層地址內容所做的修改。

十四、一般來講,物體的世界矩陣將隨其移動/旋轉/縮放而改變,觀察矩陣隨虛擬攝像機的移動/旋轉而改變,投影矩陣隨窗口大小的調整而改變。

十五、常量緩衝區描述符都存放在以D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV類型所創建的描述符堆裏,這種堆內可以混合存儲常量緩衝區描述符、着色器資源描述符和無序訪問描述符。創建這種描述符堆和創建渲染目標和深度/模板緩衝區這兩種資源描述符堆的過程很相似,但是,有一個重要的區別,那就是在創建供着色器程序訪問資源的描述符時,我們需要把標誌Flags指定爲DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE。

十六、通常來講,在繪製調用開始執行之前,我們應將不同的着色器程序所需的各種類型的資源綁定到渲染流水線上。 實際上,不同類型的資源會被綁定到特定的寄存器槽(register slot)上,以供着色器程序訪問。
寄存器槽就是向着色器傳遞資源的手段,register(#)中表示寄存器傳遞的資源類型,可以是t(表示着色器資源視圖)、s(採樣器)、u(無序訪問視圖)以及b(常量緩衝區視圖),#則爲所用的寄存器編號。

十七、根簽名(root signature)定義的是:在執行繪製命令之前,那些應用程序將綁定到渲染流水線上的資源,它們會被映射到着色器的對應輸入寄存器。根簽名一定要與使用它的着色器相兼容(即在繪製開始之前,根簽名一定要爲着色器提供其執行期間需要綁定到渲染流水線的所有資源),在創建流水線狀態對象(pipeline state object)時會對此進行驗證。不同的繪製調用可能會用到一組不同的着色器程序,這也就意味着要用到不同的根簽名。
如果我們把着色器程序當作一個函數,而將輸入資源看作着色器的函數參數,那麼根簽名則定義了函數簽名(這也就是根簽名一次的由來)。通過綁定不同的資源作爲參數,着色器的輸出也將有所差別。例如,頂點着色器的輸出取決於實際向它輸入的頂點數據以及爲它綁定的具體資源。
在Direct3D中,根簽名由ID3D12RootSignature接口來表示,並以一組描述繪製調用過程中着色器所需資源的根參數(root parameter)定義而成。根參數可以是根常量(root constant)、根描述符(root descriptor)或者描述符表(descriptor table)。
Direct3D 12規定,必須先將根簽名的描述佈局進行序列化處理(serialize),待其轉換爲以ID3DBlob接口表示的序列化數據格式後,纔可以將它傳入CreateRootSignature方法,正式創建根簽名。
根簽名只定義了應用程序要綁定到渲染流水線的資源,卻沒有真正地執行任何資源綁定的操作。只要率先通過命令列表(Command list)設置好根簽名,我們就能用ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable()方法令描述符表與渲染流水線相綁定。
處於性能考慮,我們應當使根簽名的規模儘可能的小,除此之外,還要試着儘量減少每幀渲染過程中根簽名的修改次數。

十八、光柵化狀態不是可編程的,只能接受配置。由結構體D3D12_RASTERIZER_DESC來表示。

十九、大多數控制圖形流水線狀態的對象被統稱爲流水線狀態對象(pipeline state object,PSO),用接口ID3D12PipelineState來表示,要創建PSO,先填寫一份描述其細節的D3D12_GRAPHICS_PIPELINE_STATE_DESC結構體實例。
ID3D12PipelineState對象集合了大量的流水線狀態信息,爲了保證性能,我們將所有這些對象都集合在一起,一併送至渲染流水線上。通過這樣一個集合,Direct3D便可以確定所有的狀態是否彼此兼容,而驅動程序則能夠據此而提前生成硬件本地指令及其狀態。在Direct3D 11的狀態模型中,這些渲染狀態片段都是要分開配置的,然而這些狀態實際都有一定的聯繫,以致如果其中一個狀態發生改變,那麼驅動程序可能要爲了另一個相關的獨立狀態而對硬件重新進行編程。由於一些狀態在配置流水線時需要改變,因而硬件狀態也就可能被頻繁地改寫。爲了避免這些冗餘的操作,驅動程序往往會推遲針對硬件狀態的編程動作,直到明確整條流水線的狀態發起繪製調用後,才正式生成對應的本地指令與狀態。但是,這種延遲操作需要驅動在運行時進行額外的記錄工作,即追蹤狀態的變化,而後才能在運行時生成改寫硬件狀態的本地代碼。在Direct3D 12的新模型中,驅動程序可以在初始化期間生成對流水線狀態編程的全部代碼,這便是我們將大多數的流水線狀態指定爲一個集合所帶來的好處。
Direct3D實質上是一種狀態機(state machine),裏面的事物會保持他們各自的狀態,直到我們將其改變。
考慮到性能問題,我們應當儘量減少改變PSO狀態的次數,爲此,若能以一個PSO繪製出所有的物體,絕不用第二個PSO。
切記,不要在每次繪製調用時都修改PSO!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章