DirectX 11 程式設計指南
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
微軟在2009年8月的DirectXSDK中釋出了DirectX的正式版本。基於對DirectX的一貫興趣,我把DirectX Graphics的文件邊看邊譯為中文。也算是一份學習筆記吧。
眾所周知,Direct3D是在Windows平臺上開發實時3D應用的軟體開發介面。隨著Windows作業系統的升級,Direct3D的版本也隨著升級。WindowsXP對應Direct3D9的各個版本,Vista對應D3D10,10.1,Windows7則對應D3D11。所以在XP平臺上是沒法做D3D10及以上的開發了。不過看起來因為Win7實際上是Vista的一個升級,所以微軟也準備在Vista上支援D3D11。
D3D11是D3D10的一個超集,即它包含了D3D10的所有功能,並在其渲染管線上添加了一些新的特性。根據官方的文件,D3D11的新特性包括:
ComputerShader(計算著色器)
計算著色器能把GPU當成一種通用功能的並行處理器來使用。其實在D3D9時代就已經可以應用GPU來進行GP(General-Purpose)計算了,只不過那時候需要使用一些特殊的手段來處理(比如將資料包裝成“紋理”)。在D3D11中專門實現了這樣一個著色器階段來支援GP計算,應該使得開發這類應用更為方便一些了。(參考NVIDIA的CUDA 技術以及OpenCL標準)
雖然計算著色器也是通過D3DDevice進行訪問的,並能和其他的圖形著色器共享記憶體資源,但它並不直接連線到其他的著色器階段上面。這大概是因為ComputerShader並不直接與圖形渲染功能相關吧。
Dynamic Shader Linkage(動態著色器連結)
D3D11包含的SM5(Shader Model規範)支援面嚮物件語言的構建方式,並支援執行時的著色器連結。
多執行緒(Multithreading)
隨著CPU的多核化趨勢漸漸普及,D3DAPI對多執行緒的支援勢在必行。D3D11對多執行緒提供了這樣的功能支援:
- 併發的物件建立 - 可以在多個執行緒中併發地進行物件(資源、著色器等)的建立。
- 在多執行緒中建立命令列表 - 命令列表是一個記錄了圖形處理命令的序列。在D3D11中,可以在多個執行緒中建立命令列表,然後又主渲染執行緒來發送這個命令列表到渲染硬體。
Tessellation(細分)
細分技術使得能夠用不同的精度等級(LOD,Level of detail)來渲染一個模型。GPU可以根據所需的LOD來處理輸入的模型,以得到相應負責度的實際模型。這項技術可以大大減少記憶體頻寬對三角形數量的限制。這項技術通過兩個新的著色器階段來實現:Hull Shader(殼著色器)和Domain Shader(域著色器)。
除了上面的4項主要的特性外,D3D11還支援:
- 通過在建立Device的時候指定特性等級(feature level),D3D11可以在低於等級的硬體(即不完全支援D3D11規格的硬體)上執行。
- ShaderModel 5
- 大記憶體支援 支援超過4GB的資源。
- …(有待深入瞭解)
DirectX11 不但包含了新的Direct3D 11。 還引入了兩個新的圖形元件: Direct2D和DirectWrite。
Direct2D是一套硬體加速的、立即模式的2D圖形繪製介面。(是用來取代GDI,GDI+,DirectDraw嗎?) 它能高效能、高質量地繪製2D圖形、點陣圖以及文字。D2D能與D3D及GDI良好地互操作。
DirectWrite支援高質量的文字渲染,與解析度無關的字型輪廓線繪製,全unicode文字支援及佈局處理等等。
在D3D API 中, Device(裝置)是一個核心的概念,Device是對一個圖形硬體的抽象,它負責建立和銷燬其他的物件,渲染圖元等功能。D3D11中,Device物件主要用來管理資源,而DeviceContext物件執行渲染操作。一個物件只能被建立它的Device使用。多個Device可以通過共享資源來共享資料,但這個共享的資源也只能在建立它的Device內使用。(意思就是說多個Device都可以訪問共享物件裡面的資料,但處了建立Device之外,其他Device不能直接把這個資源連線到渲染管線上使用)
每個Device都由一個ID3D11Device介面來代表,可以通過D3D11CreateDevice或者D3D11CreateDeviceAndSwapChain函式來建立這個介面。每個Device都可以使用一個或多個DeviceContext,這主要是用來支援多執行緒操作。
D3D11CreateDevice函式的原型如下:
HRESULT D3D11CreateDevice(IDXGIAdapter*pAdapter,D3D_DRIVER_TYPEDriverType,HMODULESoftware,UINTFlags,CONST D3D_FEATURE_LEVEL*pFeatureLevels,UINTFeatureLevels,UINTSDKVersion,ID3D11Device **ppDevice,D3D_FEATURE_LEVEL*pFeatureLevel,ID3D11DeviceContext **ppImmediateContext);
引數解析:
IDXGIAdapter *pAdapter: 顯示介面卡介面。表示要建立的Device與這個介面卡硬體對應。一臺電腦上可能安裝多個顯示介面卡(即顯示卡),可以使用IDXGIFactory::EnumAdapters函式來遍歷所有的介面卡。傳入NULL的話就表示使用預設的介面卡(你的電腦上至少應該有一個顯示卡吧)。
D3D_DRIVER_TYPEDriverType:建立的Device的型別。可能的值有:
- D3D_DRIVER_TYPE_UNKNOWN: 未知型別,建立裝置時不能使用
- D3D_DRIVER_TYPE_HARDWARE: 硬體裝置,指使用硬體來實現D3D的功能。它使用硬體來加速D3D功能,並用軟體實現硬體不支援的部分,因此能提供最好的效能。這一種裝置型別通常都用一個HAL(Hardware Abstraction Layer,硬體抽象層)來代表。
- D3D_DRIVER_TYPE_REFERENCE: 參考裝置,即使用軟體(CPU上執行)來實現D3D的所有功能。參考設備註重功能的準確性而不保證速度。因此主要用來做功能的測試、演示與除錯等。參考裝置由DirectXSDK提供(D3D11Ref.dll ?)。這個裝置通常叫做REF Device或Reference rasterizer(光柵器)
- D3D_DRIVER_TYPE_NULL: 空裝置,即不提供D3D渲染功能的裝置,它主要用來測試一些非測試的API的功能。它也是由DirectXSDK提供。
- D3D_DRIVER_TYPE_SOFTWARE: 軟體渲染器,也是由軟體實現D3D的所有功能。這個渲染器必須由使用者自己實現,並通過一個DLL提供。
- D3D_DRIVER_TYPE_WARP: Windows Advanced Resterization Platform。 一個高效能的軟體光柵器。提供特性集9.1到10.1的支援。
HMODULESoftware:軟體渲染器DLL,如果不用軟體渲染器的話,傳入NULL
UINTFlags:建立標記,這寫標記主要用來控制建立的Device中的層次,這些標記可以按位與操作。如果不特別指定的話,傳入0。 D3D11_CREATE_DEVICE_FLAG 的取值有:
- D3D11_CREATE_DEVICE_SINGLETHREADED = 0x1, 設定Device為單執行緒使用。D3D11預設是支援多執行緒使用的,即在每個API的呼叫中都會進行鎖保護。如果確定不會在多執行緒環境中使用,則設定該標記,則不會進行鎖保護,這樣可以提高效能。
- D3D11_CREATE_DEVICE_DEBUG = 0x2, 讓Device支援除錯層
- D3D11_CREATE_DEVICE_SWITCH_TO_REF = 0x4, 同時建立REF和HAL裝置,這樣就可以在執行時進行切換,以支援除錯功能。
- D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS = 0x8, 阻止建立多執行緒(這個需要深入瞭解)
- D3D11_CREATE_DEVICE_BGRA_SUPPORT = 0x20, 在允許D2D與D3D互操作時設定
CONST D3D_FEATURE_LEVEL *pFeatureLevels, UINTFeatureLevels:指向一組特徵集。建立函式會按順序嘗試這些特徵集,並選擇第一個能支援的特徵集。傳入NULL的話,則會依次嘗試從高到低(SM5->SM2)的所有特徵集。 特徵集的可選項有:
- D3D_FEATURE_LEVEL_9_1 = 0x9100, SM2
- D3D_FEATURE_LEVEL_9_2 = 0x9200, SM2
- D3D_FEATURE_LEVEL_9_3 = 0x9300, SM3
- D3D_FEATURE_LEVEL_10_0 = 0xa000, SM4
- D3D_FEATURE_LEVEL_10_1 = 0xa100, SM4
- D3D_FEATURE_LEVEL_11_0 = 0xb000, SM5
UINTSDKVersion: SDK版本,傳入D3D11_SDK_VERSION,這是由SDK設定的一個常數
ID3D11Device **ppDevice,D3D_FEATURE_LEVEL *pFeatureLevel,ID3D11DeviceContext**ppImmediateContext 三個返回值,返回建立的Device,立即DeviceContext及當前的特徵集。
Device Context 包含了Device的環境或設定。更精確地說,DeviceContext用來設定渲染狀態,產生渲染命令,並使用資源。D3D11實現了兩種Context,一種是立即渲染(immediate rendering),一種是延遲渲染(deferred rendering)。它們都由ID3D11DeviceContext介面代表。每個執行緒只能有一個Context,一般主執行緒擁有立即Context,用於渲染,其他工作執行緒擁有延遲Context。
一個Device有且只有一個立即Context,它用來直接繪製到硬體,它能夠從GPU獲取資料,也可以用來立即執行(或回放)一個命令列表。立即Context可以在建立Device時獲取,也可以通過ID3D11Device::GetImmediateContext()介面獲取。
延遲Context將GPU命令記錄到命令列表之中。它主要用來在主執行緒之外記錄渲染命令。建立延遲Context時將不會繼承任何的立即Context的狀態。延遲Context通過ID3D11Device::CreateDeferredContext()介面建立。
ID3D11Device介面函式都是執行緒安全的,但ID3D11DeviceContext介面不是執行緒安全的,即必須只能在一個執行緒內進行呼叫。這區別於ID3D10Device介面。
D3D11 執行時是按照層次構建的,核心層提供了最基本的功能,外圍層則提供了一些可選功能及輔助程式設計師進行開發的功能。通常來講,外圍層新增新的功能,但不改變裡層的已有行為。在建立裝置時核心層是必須的,外圍層可以使用D3D11_CREATE_DEVICE_FLAG標記進行配置。
Core Layer: 核心層預設就被建立。它提供了圖形裝置驅動和API之間的簡單對映。它只提供最關鍵性的校驗,以儘量減少開銷,提供最佳的效能。
Debug Layer: 除錯層提供了更多的一致性檢查,並提供更多新返回資訊。這會導致一定的效能損失,並且需要額外的DLL來支援(D3D11SDKLayers.dll)
所以,在釋出版本中只保留Core Layer,而在開發過程中可以使用Debug Layer。
資源為渲染管線提供資料。它可以是從外部載入的,也可以在執行時動態生成。典型的資源包括紋理資料、頂點資料(幾何資料)和著色器資料。資源可以是強型別的或無型別的。也可以指定資源的讀寫屬性。資源可以被CPU或/和GPU訪問。每個渲染管線狀態至多可以有128個活動的資源。
有兩種方式指定資源的記憶體佈局(即格式),一種是在建立資源時,另一種是在將資源繫結到管線上時。前一種稱為有型別的資源,後一種稱為無型別資源。無型別資源可以通過資源檢視來指導型別並繫結到渲染管線上,一個資源可以同時擁有多個檢視,並同時繫結到多個渲染管線階段上。
資源主要分為紋理和快取兩大類。
D3D11資源系統的類圖如下:
渲染管線狀態根據一個檢視來解析資源資料。一個檢視就像一種轉換,將資料解析成適合某種用途的格式。
四種檢視:
- ID3D11DepthStencilView 訪問一個作為深度-模板快取的紋理資源
- ID3D11RenderTargetView 訪問一個作為渲染目標的紋理資源
- ID3D11ShaderResourceView 訪問一個著色器資源,比如常數快取,紋理快取,紋理或者取樣器
- ID3D11UnorderedAccessView 質畫素著色器或計算著色器中訪問無序的資源。
子資源(Subresource) 是一個資源的一部分。一個Buffer資源只有一個子資源,而紋理資源根據維、Mipmap及array的不同,會有多種情況。紋理的每一個Mipmap等級,array中的每個元素都單獨構成一個子資源。
Buffer是用來存放描述幾何體的資料,幾何體索引資料以及著色器資料。buffer資源必須是有型別的,一個buffer是由一個個元素組成的,每個元素由一到4個元件組成。
Buffer可以用來存放頂點資料,法線資料,紋理座標,索引,以及裝置狀態等。 Buffer分為3種:
VertexBuffer(頂點快取) 存放每個頂點的資料
IndexBuffer(索引快取) 存放頂點索引資料
ConstantBuffer(常數快取) 存放著色器常量資料,也可以存放Stream-output的輸出。從概念上講,它就像一個單元件元素的陣列。常數快取只能使用D3D11_BIND_CONSTANT_BUFFER繫結。常數快取的大小必須是16bytes的整數倍,而且必須小於或等於D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT
Buffer使用ID3D11Device::CreateBuffer介面進行建立
HRESULT CreateBuffer(const D3D11_BUFFER_DESC *pDesc,const D3D11_SUBRESOURCE_DATA*pInitialData,ID3D11Buffer **ppBuffer);
引數解析如下:
const D3D11_BUFFER_DESC *pDesc: buffer描述結構
- UINT ByteWidth; Buffer的總大小,以byte記。
- D3D11_USAGE Usage; Buffer的用途,即讀寫屬性。這主要根據對buffer的訪問/更新頻率來確定。
- UINT BindFlags; 指定Buffer如何繫結到渲染管線上。這個值是可以是D3D11_BIND_FLAG 的值的“或”,但通常只選擇一種繫結方式。
- UINT CPUAccessFlags; Buffer的CPU訪問許可權,0表示CPU無法讀寫
- UINT MiscFlags; 其他輔助功能選項。
- UINT StructureByteStride; 當作為有結構的Buffer時,結構的大小
DXGI(DirectX Graphics Infrastructure,DirectX 圖形基礎架構) 提供了對圖形硬體進行底層管理的功能。這些功能不涉及顯示卡的高階特性(比如不需要區別D3D9級別顯示卡和D3D11級別顯示卡),因此是與D3D的圖形功能是分開的。 DXGI是提供了一個底層的通用框架來支援未來的硬體。DXGI最初是在D3D10中引入的。現在它支援D3D11。即DXGI和D3D可以分開進行進化,而且DXGI的進化應該比D3D慢。
DXGI的主要功能有:列舉顯示硬體裝置,將渲染好的幀呈現到輸出裝置,調整Gamma,以及全屏模式的切換等。DXGI的目的是溝通核心模式驅動和系統硬體。一個應用程式可以直接呼叫DXGI介面,也可以通過D3D介面間接使用DXGI的功能。
DXGI類結構圖如下:
列舉顯示介面卡(Adapter)
介面卡是對顯示硬體或軟體的抽象,它可以是一個顯示卡,也可以是一個軟體渲染器。一臺計算機可以有多個介面卡。一個介面卡可以有一個或多個輸出終端(Monitor)。每個輸出終端用一個IDXGIOutput介面來代表,介面卡用IDXGIAdapter來代表。IDXGIFactory用來列舉Adapter
呈現(Presentation)
D3D的功能是渲染到幀快取,然後DXGI將幀呈現到輸出終端上。為了得到最好的效能和效果,至少需要兩個快取,一個用於D3D的繪製,一個用於DXGI的呈現。也可能有不止兩個的幀。這樣一組幀快取就組成一個交換鏈(Swap Chain)SwapChain用IDXGISwapChain介面來表示。SwapChain有一個前快取,一到多個後快取。為了得到最好的效率SwapChain總是建立在顯示子系統(這個顯示子系統就是一個介面卡)中,這個系統包括GPU,DAC(Digital to Analog Converter,數模轉換器)以及記憶體。這個子系統通常是一個顯示卡,但也可能在主機板上實現。SwapChain分配在顯示子系統的記憶體中以提高呈現的速度。顯示子系統負責把SwapChain 前快取的資料呈現到輸出終端上。SwapChain可以被設定使用全屏或視窗模式繪製,這樣就不需要知道顯示終端是全螢幕還是一個視窗。全屏模式的SwapChain能切換顯示器的解析度以優化效能。
建立SwapChain
SwapChain包含一組Buffer,它們都有相同的尺寸和格式。SwapChain由IDXGIFactory::CreateSwapChain 函式建立。
HRESULT CreateSwapChain(IUnknown *pDevice,DXGI_SWAP_CHAIN_DESC*pDesc,IDXGISwapChain **ppSwapChain);
引數解析如下:
IUnknown *pDevice: D3D Device,SwapChain在建立時就必須繫結到Device。這個Device將繪製到SwapChain的Buffer中。
DXGI_SWAP_CHAIN_DESC *pDesc: SwapChain描述結構。欄位有:
- DXGI_MODE_DESC BufferDesc; 設定顯示模式,比如解析度,重新整理率,快取格式,掃描線模式,縮放模式。預設選項都是0,所以只需要改變你想改變的值。
- DXGI_SAMPLE_DESC SampleDesc; 設定多重取樣(multi-sampling)引數包括每畫素的取樣數量及圖形質量等級。預設不進行多重取樣,則兩個數值分別為1和0
- DXGI_USAGE BufferUsage; 設定表面的用法及CPU的訪問選項。後快取可以作為著色器的輸入或者渲染目標。
- UINT BufferCount; 快取的數量,包括前快取,
- HWND OutputWindow; 顯示視窗,不能為空
- BOOL Windowed; 是否視窗模式
- DXGI_SWAP_EFFECT SwapEffect; 設定當表面呈現之後對快取的處理方式。可選方式為:DXGI_SWAP_EFFECT_DISCARD = 0, 丟棄快取,即快取內容將不再被使用。應用程式只能操作索引為0的快取。 DXGI_SWAP_EFFECT_SEQUENTIAL = 1, 快取將不會被丟棄,按順序使用快取鏈中的快取,不能在多重取樣時使用。
- UINT Flags; 其他可選項。DXGI_SWAP_CHAIN_FLAG_NONPREROTATED = 1, 關閉影象自動旋轉DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH = 2, 允許切換顯示模式。DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE = 4, 允許使用GDI來訪問快取。
D3D11 的圖形渲染管線是在D3D10 的基礎上進行擴充套件而得到的。D3D10的功能都得到了保留,並添加了一些新的特性。
兩個主要的新特性就是ComputerShader和Tessellation技術。注意ComputerShader並不是對圖形渲染的功能的擴充套件,所以沒有在渲染管線上體現出來。
DirectX Effect(特效?)是一組管線狀態的集合。如何組織管線狀態,也就決定了會得到什麼樣的渲染結果,所以Effect這個名字是名副其實的。Effect中的表示式是用HLSL語言寫成的,再加上一些Effect特定的語法。經過編譯以後,EffectAPI就可以載入Effect並控制渲染硬體進行渲染。Effect不止可以控制可程式設計階段的著色器,也可以控制不可程式設計階段的設定。
Effect可以管理的狀態包括:著色器狀態(shader state),在著色器中使用的紋理及取樣狀態(Texture and Sampler state),非可程式設計管線階段狀態。effect程式碼是純文字形式的,通常以fx為副檔名。
一個Effect檔案中可以包含多個渲染效果,每個渲染效果是一組完整的狀態集合,這組集合稱為一個Technique。通常的做法是把適合不同硬體平臺的效果放到不同的Technique中。一組Technique可以組織到一個group中(使用fxgroup)。通常可以這樣組織:一個Effect檔案包含多個Group,每個Group表示一種材質(即渲染效果),每個Group包含多個Technique,用來對應不同的硬體等級(配置),每個Technique都可以由一到多個pass組成。
Effect系統API是作為D3DX庫的一部分提供的,即它不是D3D核心API。它只是作為一個輔助工具提供,在實際開發中完全可以不用Effect來組織你的Material。
Effect系統API的意義都很直接,與Effect檔案的內容也幾乎是一一對應的,只是為應用程式提供了一個介面使用Effect檔案以及與Effect交換資料。
奇怪的是,D3D11的經典Sample Basic HLSL 也沒有使用ID3DX11Effect介面。Effect看起來很強大,但實際上還是不夠靈活啊。
D3D11支援在多執行緒中進行物件的建立和渲染。多執行緒的主要目的是調高效能。
D3D11的多執行緒支援分為兩個部分:
1 多執行緒式的物件建立
2 立即渲染與延遲渲染