<如果窗口的視圖區(qū)大小和SwapChain的大小不一,那么DirectX將通過(guò)Stretch Blit來(lái)自動(dòng)處理圖像的伸縮變化。盡管這可能并不令人期待,因?yàn)檫@在視圖區(qū)變大的時(shí)候?qū)?dǎo)致圖像的模糊。> 你說(shuō)的這個(gè)問(wèn)題 要怎么才能解決?
有位博友這樣問(wèn)過(guò),因?yàn)樽罱儆猩喜┛?,于是沒(méi)有回答及時(shí),請(qǐng)見(jiàn)諒,我這里只能說(shuō)說(shuō)我們現(xiàn)在的解決辦法。
當(dāng)后臺(tái)緩沖區(qū)的分辨率和視圖區(qū)不統(tǒng)一的時(shí)候,會(huì)導(dǎo)致拉伸現(xiàn)象,使畫(huà)面變得模糊。
首先說(shuō)個(gè)題外話(huà):
正因?yàn)樽兊媚:?,因此有人故意將后臺(tái)緩沖區(qū)做得比視區(qū)稍大一點(diǎn),這樣來(lái)抗鋸齒,至于效果如何,沒(méi)有真正見(jiàn)過(guò),有興趣的可以試試。
下面說(shuō)說(shuō)解決辦法。
我們的解決辦法也很簡(jiǎn)單,就分三步
1,窗口改變的時(shí)候,告訴設(shè)備窗口大小改變。
2,按改變后的窗口重建緩沖區(qū)。
3。強(qiáng)制設(shè)備丟失,并重新加載需要的資源。
由于在做這個(gè)之前,設(shè)備丟失已經(jīng)做好了,于是就偷了個(gè)懶,窗口改變的時(shí)候就傳入窗口大小,并reset
這樣設(shè)備就強(qiáng)制處于丟失狀態(tài)。
不知道有沒(méi)有說(shuō)清楚,反正主要的就是要重建緩沖區(qū),并處理設(shè)備丟失問(wèn)題。。。
骨骼動(dòng)畫(huà)一直是我感興趣的內(nèi)容.雖然采用現(xiàn)成的CSkinMesh能夠使用XFile的骨骼動(dòng)畫(huà).但對(duì)自己來(lái)說(shuō)總感覺(jué)缺少了點(diǎn)什么.于是,還是深入理解理解為好!!!
ZDNet軟件頻道時(shí)間:
2008-03-24作者:Skyman | CSDN本文關(guān)鍵詞:骨骼動(dòng)畫(huà)) Animation Skeletal 游戲 Linux
骨骼動(dòng)畫(huà)(Skeletal Animation)又叫Bone Animation,它與關(guān)鍵幀動(dòng)畫(huà)(Key-frame Animation)相比,占用空間小,因?yàn)樗恍枰箨P(guān)鍵幀動(dòng)畫(huà)那樣要存儲(chǔ)每一幀的各個(gè)頂點(diǎn)的數(shù)據(jù),而是只需要存儲(chǔ)每一幀的骨骼,骨骼與頂點(diǎn)相比,當(dāng)然要少得多。所以骨骼動(dòng)畫(huà)有很多優(yōu)勢(shì),當(dāng)然其技術(shù)難度也很高。我個(gè)人覺(jué)得動(dòng)畫(huà)在計(jì)算機(jī)圖形學(xué)中是一個(gè)十分重要的內(nèi)容,不管是在游戲、電影動(dòng)畫(huà)還是虛擬現(xiàn)實(shí)中,生動(dòng)逼真的動(dòng)畫(huà)(人、動(dòng)物等)會(huì)使之增色不少。所以我決定今后的研究方向就是計(jì)算機(jī)動(dòng)畫(huà)。目前在研究Skeletal Animation,這是目前動(dòng)畫(huà)技術(shù)中的主流。歡迎同好與我交流,共同提高!
骨骼動(dòng)畫(huà)的實(shí)現(xiàn)思路是從我們?nèi)说纳眢w的運(yùn)動(dòng)方式而來(lái)的(所以VR就是對(duì)現(xiàn)實(shí)世界的虛擬嘛 :-))。動(dòng)畫(huà)人物的身體(肉、皮膚)是一個(gè)網(wǎng)格(Mesh)模型,網(wǎng)格的內(nèi)部是一個(gè)骨架結(jié)構(gòu)。當(dāng)人物的骨架運(yùn)動(dòng)時(shí),身體就會(huì)跟著骨架一起運(yùn)動(dòng)。骨架是由一定數(shù)目的骨骼組成的層次結(jié)構(gòu),每一個(gè)骨骼的排列和連接關(guān)系對(duì)整個(gè)骨架的運(yùn)動(dòng)有很重要的影響。每一個(gè)骨骼數(shù)據(jù)都包含其自身的動(dòng)畫(huà)數(shù)據(jù)。和每個(gè)骨架相關(guān)聯(lián)的是一個(gè)“蒙皮”(Skin)模型,它提供動(dòng)畫(huà)繪制所需要的幾何模型(Vertex,Normal,etc)和紋理材質(zhì)信息。每個(gè)頂點(diǎn)都有相應(yīng)的權(quán)值(Weight),這些權(quán)值定義了骨骼的運(yùn)動(dòng)對(duì)有關(guān)頂點(diǎn)的影響因子。當(dāng)把動(dòng)畫(huà)人物的姿勢(shì)和全局運(yùn)動(dòng)信息作用到骨架上時(shí),這個(gè)“蒙皮”模型就會(huì)跟隨骨架一起運(yùn)動(dòng)。如下圖所示:

所以關(guān)鍵是對(duì)骨架進(jìn)行動(dòng)畫(huà)生成,生成的方法也是用關(guān)鍵幀。關(guān)鍵幀動(dòng)畫(huà)是對(duì)人物的網(wǎng)格(Mesh)模型采用關(guān)鍵幀生成動(dòng)畫(huà);而骨骼動(dòng)畫(huà)則是對(duì)人物的骨架采用關(guān)鍵幀生成動(dòng)畫(huà),然后再讓網(wǎng)格(Mesh)模型跟隨骨架運(yùn)動(dòng)。關(guān)鍵幀動(dòng)畫(huà)實(shí)現(xiàn)的2個(gè)關(guān)鍵點(diǎn)是:關(guān)鍵幀的選取和中間幀的插補(bǔ)。
關(guān)鍵幀的指定有2種基本的方法:前向動(dòng)力學(xué)(FK)和逆向動(dòng)力學(xué)(IK)。前向動(dòng)力學(xué)用一組節(jié)點(diǎn)的角度來(lái)找到末端受動(dòng)器的位置;而逆向動(dòng)力學(xué)則是找到將末端受動(dòng)器置于所要位置所需的一組節(jié)點(diǎn)角度。前向動(dòng)力學(xué)的優(yōu)點(diǎn)是:計(jì)算簡(jiǎn)單,運(yùn)算速度快,缺點(diǎn)是:需指定每個(gè)關(guān)節(jié)的角度和位置,而由于骨架的各個(gè)節(jié)點(diǎn)之間有內(nèi)在的關(guān)聯(lián)性,直接指定各關(guān)節(jié)的值很容易產(chǎn)生不自然協(xié)調(diào)的動(dòng)作;逆向動(dòng)力學(xué)的優(yōu)點(diǎn)是:只需指定主要關(guān)節(jié)點(diǎn)的位置,負(fù)擔(dān)輕,缺點(diǎn)是:計(jì)算模型比較復(fù)雜,開(kāi)發(fā)者需要機(jī)械運(yùn)動(dòng)和動(dòng)力學(xué)、幾何學(xué)以及向量數(shù)學(xué)等方面的相關(guān)知識(shí)。
中間幀的插值分2步:(1) 根據(jù)當(dāng)前時(shí)間,通過(guò)插值計(jì)算出每個(gè)骨骼的旋轉(zhuǎn)、平移等值,形成中間幀的骨架。插值算法一般采用四元數(shù)(Quternion)的球面線(xiàn)性插值(Spherical linear interpolation)SLERP,SLERP特別適合在兩個(gè)方位之間進(jìn)行插值,不會(huì)出現(xiàn)像對(duì)歐拉角插值那樣出現(xiàn)萬(wàn)象鎖的現(xiàn)象,而且這種插值能產(chǎn)生更平滑和連續(xù)的旋轉(zhuǎn),表達(dá)方式也很簡(jiǎn)潔;(2) 根據(jù)骨架的變化情況,插值計(jì)算出骨架的“蒙皮”模型的各個(gè)頂點(diǎn)的位置變化。對(duì)于某個(gè)特定骨骼,“蒙皮”模型的頂點(diǎn)變換矩陣=初始姿勢(shì)的變換矩陣的逆×姿勢(shì)變換后的矩陣。另外還要考慮頂點(diǎn)可能受多個(gè)骨骼運(yùn)動(dòng)的影響。這時(shí)我們對(duì)每個(gè)與當(dāng)前頂點(diǎn)相關(guān)聯(lián)的骨骼,將其運(yùn)動(dòng)姿勢(shì)變換矩陣×當(dāng)前頂點(diǎn)相對(duì)于該骨骼的偏移向量×該骨骼對(duì)當(dāng)前頂點(diǎn)的影響因子(即權(quán)重Weight),對(duì)所有與當(dāng)前頂點(diǎn)相關(guān)聯(lián)的骨骼都這么處理,然后相加,就得到當(dāng)前頂點(diǎn)的新位置。
由此看出,如何設(shè)置各關(guān)鍵幀的骨架的各節(jié)點(diǎn)的位置和骨骼的轉(zhuǎn)向(也就是骨架的POSE)是其中的關(guān)鍵,有2種方法:一種是由動(dòng)畫(huà)師手工放置,這個(gè)對(duì)動(dòng)畫(huà)師的技術(shù)要求就比較高,要求動(dòng)畫(huà)師對(duì)現(xiàn)實(shí)生活中的人和動(dòng)物等的動(dòng)作有細(xì)心的觀察。否則設(shè)置的骨架動(dòng)作就會(huì)不自然、不協(xié)調(diào);另外一種是基于運(yùn)動(dòng)捕捉(Motion Capture)的方法,就是在人的各個(gè)關(guān)節(jié)處安置運(yùn)動(dòng)捕捉傳感器,當(dāng)人做各種動(dòng)作時(shí),捕捉儀器就將各節(jié)點(diǎn)的位置數(shù)據(jù)記錄下來(lái),這樣我們就可以根據(jù)這些節(jié)點(diǎn)數(shù)據(jù)進(jìn)行骨架建模。由于這是捕捉的真實(shí)的人的動(dòng)作,所以這種方式得到的動(dòng)畫(huà)就很自然、很真實(shí),但捕捉儀器造價(jià)昂貴,國(guó)內(nèi)估計(jì)只有很少幾家有財(cái)力的游戲公司才購(gòu)置了這些設(shè)備吧。
目前有好多3D模型格式支持Skeletal Animation,像Microsoft的.X格式、MilkShape的MS3D格式、Half Life的MDL格式、ID Software的MD5格式等。我準(zhǔn)備首先研究一下MS3D格式,因?yàn)樗泄_(kāi)的格式說(shuō)明文檔,閱讀起來(lái)比較容易,而且應(yīng)用很廣。當(dāng)然,首先要深入學(xué)習(xí)Skeletal Animation的底層技術(shù),打好堅(jiān)實(shí)的基礎(chǔ),呵呵!
將就看吧,有些單詞我實(shí)在不知道怎么翻譯,只可意會(huì)!
像素著色器需要依靠寄存器來(lái)取得頂點(diǎn)數(shù)據(jù),輸出像素?cái)?shù)據(jù),取得計(jì)算時(shí)的臨時(shí)結(jié)果和關(guān)聯(lián)紋理采樣通道(stage)。有幾種類(lèi)型的寄存器,每一種都有特殊的功能和用途。
像素著色器需要的用到的數(shù)據(jù)由寄存器保管,下面是寄器存的所有介紹
寄存器類(lèi)型:描述了四種可用的寄存器和他們各自的用途
讀取端口限制:?jiǎn)沃羔樖褂枚鄠€(gè)寄存器時(shí)的限制
R/RW: 描述了哪些寄存器可以用來(lái)讀,寫(xiě)或是讀寫(xiě)。
范圍:各個(gè)分量的范圍的詳細(xì)說(shuō)明
Register Types
Versions
Name Type 1_1 1_2 1_3 1_4
c# Constant register 8 8 8 8
r# Temporary register 2 2 2 6
t# Texture register 4 4 4 6
v# Color register 2 2 2 2 in phase 2
1,常量寄存器:常量寄存器容納了常量數(shù)據(jù)。數(shù)據(jù)可以用IDirect3DDevice9::SetPixelShaderConstantF函數(shù)將一個(gè)常量裝入常量寄存器中。也可以用def-ps來(lái)定義一個(gè)常量。 常量寄存器對(duì)紋理尋址指令來(lái)說(shuō)是不可用的,唯一例外的是texm3x3spec-ps指令,這個(gè)指令使用一個(gè)常量寄存器來(lái)提供一個(gè)視線(xiàn)向量(eye-ray vector)
2,臨時(shí)寄存器:臨時(shí)寄存器用來(lái)存立即結(jié)果。r0用來(lái)作為PS的最終輸出。shader的最后時(shí)刻r0中存放的是最后的像素顏色值
如果任何的著色器試圖從一個(gè)沒(méi)有被寫(xiě)入數(shù)據(jù)的臨時(shí)寄存器中讀取數(shù)據(jù)時(shí),著色器激IDirect3DDevice9::CreatePixelShader將會(huì)失?。╯hader validation will fail)。假設(shè)激活(validation)是可用狀態(tài)D3DXAssembleShader函數(shù)調(diào)用也會(huì)因?yàn)橄嗤脑蚨?。(不要使用D3DXSHADER_SKIPVALIDATION)
紋理寄存器:
在ps 1_1 到1_3中,紋理寄存器容納紋理數(shù)據(jù)或是紋理坐標(biāo)。當(dāng)一個(gè)紋理被采樣時(shí),紋理數(shù)據(jù)便被裝載到一個(gè)紋理寄存器中。
當(dāng)紋理通道狀態(tài)屬性被登記的時(shí)候紋理采樣使用紋理坐標(biāo)來(lái)查詢(xún)(look up)或采樣(sample)一個(gè)紋理坐標(biāo)(u,v,w,q)標(biāo)記的顏色值。紋理坐標(biāo)數(shù)據(jù)會(huì)根據(jù)頂點(diǎn)紋理坐標(biāo)數(shù)據(jù)進(jìn)行插值,并關(guān)聯(lián)到相關(guān)的紋理通道。紋理通道號(hào)與紋理坐標(biāo)聲明序列有一個(gè)一一對(duì)應(yīng)關(guān)系。默認(rèn)情況下,頂點(diǎn)格式中定義的第一個(gè)紋理坐標(biāo)與紋理通道0關(guān)聯(lián)。
在這些版本的像素著色器中,當(dāng)紋理寄存器用來(lái)做算術(shù)運(yùn)算的時(shí)候就和臨時(shí)寄存器的效果一樣了。
在ps_1_4中,紋理寄存器(t#)容納的是只讀紋理坐標(biāo)信息。這意味著紋理坐標(biāo)集和紋理通道編號(hào)是獨(dú)立的。紋理通道編號(hào)由目的寄存器(r0 to r5)決定。對(duì)于texld指令來(lái)說(shuō),紋理坐標(biāo)集由源寄存器t0 to t5決定。因此紋理坐標(biāo)集可以映射到任何的紋理通道上。另外,對(duì)于texld的源寄存器(指定紋理坐標(biāo)信息)也可以是臨時(shí)寄存器(r#)。在這樣的情況下,臨時(shí)寄存器記錄紋理坐標(biāo)。
顏色寄存器容納了每個(gè)像素的顏色值,這個(gè)值通過(guò)頂點(diǎn)數(shù)據(jù)中的漫反射和鏡面光顏色值迭代而來(lái)。對(duì)于ps_1_4。顏色寄存器只有在phase2中可用。如果著色模式設(shè)置為D3DSHADE_FLAT,那么頂點(diǎn)顏色中的顏色迭代將不可用。如果霧化開(kāi)啟的話(huà),那么渲染管線(xiàn)還是會(huì)忽略著色模式,對(duì)霧進(jìn)行顏色迭代。記住霧化比像素著色器后應(yīng)用。
通常我們會(huì)從v0加載頂點(diǎn)漫反射顏色數(shù)據(jù)。從v1加載頂點(diǎn)鏡面光顏色數(shù)據(jù)。
輸入顏色數(shù)據(jù)值將會(huì)被規(guī)范到0和1,因?yàn)檫@是像素著色器中的顏色寄存器的有效范圍
像素著色器對(duì)顏色寄存器進(jìn)行只讀操作。顏色寄存器中存放的是迭代值,但是迭代可能會(huì)造成比紋理坐標(biāo)低很多精度
用VS2005+DirectX9 SDK(手頭測(cè)試過(guò)的是2004年10月的DirectX SDK和2006年4月的DirectX SDK)編譯游戲會(huì)出現(xiàn)以下warning:
--------------------------------------------------------------------------------
d:\microsoft directx 9.0 sdk (october 2004)\include\d3d9types.h(1385) : warning C4819: The file contains a character that cannot be represented in the current code page (936). Save the file in Unicode format to prevent data loss
--------------------------------------------------------------------------------
要修正這個(gè)問(wèn)題,不必要存為UTF8的文件,而是搜索_D3DDEVINFO_VCACHE,然后會(huì)看到:
typedef struct _D3DDEVINFO_VCACHE ...{
DWORD Pattern; /**//* bit pattern, return value must be FOUR_CC(慍? 慉? 慍? 慔? */
DWORD OptMethod; /**//* optimization method 0 means longest strips, 1 means vertex cache based */
DWORD CacheSize; /**//* cache size to optimize for (only required if type is 1) */
DWORD MagicNumber; /**//* used to determine when to restart strips (only required if type is 1)*/
} D3DDEVINFO_VCACHE, *LPD3DDEVINFO_VCACHE;
那四個(gè)亂碼的去掉就可以了
參考了http://gamep.mmoh.jp/e39255.html
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/SONIC3D/archive/2007/11/01/1861794.aspx
文章來(lái)源:http://www.cnblogs.com/effulgent/archive/2009/02/10/1387438.html
深入理解D3D9對(duì)圖形程序員來(lái)說(shuō)意義重大,我把以前的一些學(xué)習(xí)筆記都匯總起來(lái),希望對(duì)朋友們有些所幫助,因?yàn)槭橇闵⒐P記,思路很雜,還請(qǐng)包涵。
其實(shí)只要你能完美理解D3DLOCK、D3DUSAGE、D3DPOOL、LOST DEVICE、QUERY、Present()、BeginScene()、EndScene()等概念,就算是理解D3D9了, 不知道大家有沒(méi)有同感。有如下幾個(gè)問(wèn)題,如果你能圓滿(mǎn)回答就算過(guò)關(guān):)。
1、 D3DPOOL_DEFAULT、D3DPOOL_MANAGED、D3DPOOL_SYSTEMMEM和D3DPOOL_SCRATCH到底有何本質(zhì)區(qū)別?
2、 D3DUSAGE的具體怎么使用?
3、 什么是Adapter?什么是D3D Device?HAL Device和Ref Device有何區(qū)別?Device的類(lèi)型又和Vertex Processing類(lèi)型有什么關(guān)系?
4、 APP(CPU)、RUNTIME、DRIVER、GPU是如何協(xié)同工作的?D3D API是同步函數(shù)還是異步函數(shù)?
5、 Lost Device到底發(fā)生了什么?為什么在設(shè)備丟失后D3DPOOL_DEFAULT類(lèi)型資源需要重新創(chuàng)建?
在D3D中有三大對(duì)象,他們是D3D OBJECT、D3D ADAPTER和D3D DEVICE。D3D OBJECT很簡(jiǎn)單,就是一個(gè)使用D3D功能的COM對(duì)象,其提供了創(chuàng)建DEVICE和枚舉ADAPTER的功能。ADAPTER是對(duì)計(jì)算機(jī)圖形硬件和軟件性能的一個(gè)抽象,其包含了DEVICE。DEVICE則是D3D的核心,它包裝了整個(gè)圖形流水管線(xiàn),包括變換、光照和光柵化(著色),根據(jù)D3D版本不同,流水線(xiàn)也有區(qū)別,比如最新的D3D10就包含了新的GS幾何處理。圖形管線(xiàn)的所有功能由DRIVER提供,而DIRVER分兩類(lèi),一種是GPU硬件DRIVER,另一種是軟件DRIVER,這就是為什么在D3D中主要有兩類(lèi)DEVICE, REF和HAL,使用REF DEVICE時(shí),圖形管線(xiàn)的光柵化功能由軟件DRIVER在CPU上模擬的,REF DEVICE從名字就可以看出這個(gè)給硬件廠商做功能參考用的,所以按常理它應(yīng)該是全軟件實(shí)現(xiàn),具備全部DX標(biāo)準(zhǔn)功能。而使用HAL DEVICE時(shí),RUNTIME則將使用HAL硬件層控制GPU來(lái)完成變換、光照和光柵化,而且只有HAL DEVICE中同時(shí)實(shí)現(xiàn)了硬件頂點(diǎn)處理和軟件頂點(diǎn)處理(REF DEVICE一般不能使用硬件頂點(diǎn)處理,除非自己在驅(qū)動(dòng)上做手腳,比如PERFHUD)。另外還有個(gè)一個(gè)不常用的SOFTWARE DEVICE,用戶(hù)可以使用DDI編寫(xiě)自己的軟件圖形驅(qū)動(dòng),然后注冊(cè)進(jìn)系統(tǒng),之后便可在程序中使用。
檢查系統(tǒng)軟件硬件性能。
在程序的開(kāi)始我們就要判斷目標(biāo)機(jī)的性能,其主要流程是:
確定要用的緩沖格式
GetAdapterCount()
GetAdapterDisplayMode
GetAdapterIdentifier //得到適配器描述
CheckDeviceType //判斷指定適配器上的設(shè)備是否支持硬件加速
GetDeviceCaps //指定設(shè)備的性能,主要判斷是否支持硬件頂點(diǎn)處理(T&L)
GetAdapterModeCount //得到適配器上指定緩沖格式所有可用的顯示模式
EnumAdapterModes //枚舉所有顯示模式
CheckDeviceFormat
CheckDeviceMultiSampleType
詳細(xì)使用請(qǐng)參考DX文檔。
WINDOWS圖形系統(tǒng)的主要分為四層:圖形應(yīng)用程序、D3D RUNTIME、SOFTWARE DRIVER和GPU。此四層是按功能來(lái)分的,實(shí)際上他們之間界限并不如此明確,比如RUNTIME中其實(shí)也包含有USER MODE的SOFTWARE DRIVER,詳細(xì)結(jié)構(gòu)這里不再多說(shuō)。而在RUNTIME里有一個(gè)很重要的結(jié)構(gòu),叫做command buffer,當(dāng)應(yīng)用程序調(diào)用一個(gè)D3D API時(shí),RUNTIME將調(diào)用轉(zhuǎn)換成設(shè)備無(wú)關(guān)的命令,然后將命令緩沖到這個(gè)COMMAND BUFFER中,這個(gè)BUFFER的大小是根據(jù)任務(wù)負(fù)載動(dòng)態(tài)改變的,當(dāng)這個(gè)BUFFER滿(mǎn)員之后,RUNTIME會(huì)讓所有命令FLUSH到KERNEL模式下的驅(qū)動(dòng)中,而驅(qū)動(dòng)中也是有一個(gè)BUFFER的,用來(lái)存儲(chǔ)已被轉(zhuǎn)換成的硬件相關(guān)的命令,D3D一般只允許其緩沖最多3個(gè)幀的圖形指令,而且RUNTIME和DRIVER都會(huì)被BUFFER中的命令做適當(dāng)優(yōu)化,比如我們?cè)诔绦蛑羞B續(xù)設(shè)置同一個(gè)RENDER STATE,我們就會(huì)在調(diào)試信息中看到如下信息“Ignoring redundant SetRenderState - X”,這便是RUNTIME自動(dòng)丟棄無(wú)用的狀態(tài)設(shè)置命令。在D3D9中可以使用QUERY機(jī)制來(lái)與GPU進(jìn)行異步工作,所謂QUERY就是查詢(xún)命令,用來(lái)查詢(xún)RUNTIME、DRIVER或者GPU的狀態(tài),D3D9中的QUERY對(duì)象有三種狀態(tài),SIGNALED、BUILDING和ISSUED,當(dāng)他們處于空閑狀態(tài)后會(huì)將查詢(xún)狀態(tài)置于SIGNALED STATE,查詢(xún)分開(kāi)始和結(jié)束,查詢(xún)開(kāi)始表示對(duì)象開(kāi)始記錄應(yīng)用程序所需數(shù)據(jù),當(dāng)應(yīng)用程序指定查詢(xún)結(jié)束后,如果被查詢(xún)的對(duì)象處于空閑狀態(tài),則被查詢(xún)對(duì)象會(huì)將查詢(xún)對(duì)象置于SIGNALED狀態(tài)。GetData則是用來(lái)取得查詢(xún)結(jié)果,如果返回的是D3D_OK則結(jié)果可用,如果使用D3DGETDATA_FLUSH標(biāo)志,表示將COMMAND BUFFER中的所有命令都發(fā)送到DRIVER?,F(xiàn)在我們知道D3D API絕大部分都是同步函數(shù),應(yīng)用程序調(diào)用后,RUNTIME只是簡(jiǎn)單的將其加入到COMMAND BUFFER,可能有人會(huì)疑惑我們?nèi)绾螠y(cè)定幀率?又如何分析GPU時(shí)間呢?對(duì)于第一個(gè)問(wèn)題我們要看當(dāng)一幀完畢,也就是PRESENT()函數(shù)調(diào)用是否被阻塞,答案是可能被阻塞也可能不被阻塞,要看RUNTIME允許緩沖中存在的指令數(shù)量,如果超過(guò)額度,則PRESENT函數(shù)會(huì)被阻塞下來(lái),如何PRESENT完全不被阻塞,當(dāng)GPU執(zhí)行繁重的繪制任務(wù)時(shí),CPU工作進(jìn)度會(huì)大大超過(guò)GPU,導(dǎo)致游戲邏輯快于圖形顯示,這顯然是不行的。測(cè)定GPU工作時(shí)間是件很麻煩的事,首先我們要解決同步問(wèn)題,要測(cè)量GPU時(shí)間,首先我們必須讓CPU與GPU異步工作,在D3D9中可以使用QUERY機(jī)制做到這點(diǎn),讓我們看看Accurately Profiling Driect3D API Calls中的例子:
IDirect3DQuery9* pQueryEvent;
//1.創(chuàng)建事件類(lèi)型的查詢(xún)事件
m_pD3DDevice->CreateQuery( D3DQUERYTYPE_EVENT, &pQueryEvent);
//2.在COMMAND BUFFER中加入一個(gè)查詢(xún)結(jié)束的標(biāo)記,此查詢(xún)默認(rèn)開(kāi)始于CreateDevice
pQueryEvent->Issue(D3DISSUE_END);
//3.將COMMAND BUFFER中的所有命令清空到DRIVER中去,并循環(huán)查詢(xún)事件對(duì)象轉(zhuǎn)換到SIGNALED狀態(tài),當(dāng)GPU完成CB中所有命令后會(huì)將查詢(xún)事件狀態(tài)進(jìn)行轉(zhuǎn)換。
while(S_FALSE == pQueryEvent->GetData( NULL, 0, D3DGETDATA_FLUSH) )
;
LARGE_INTEGER start, stop;
QueryPerformanceCounter(&start);
SetTexture();
DrawPrimitive();
pQueryEvent->Issue(D3DISSUE_END);
while(S_FALSE == pQueryEvent->GetData( NULL, 0, D3DGETDATA_FLUSH) )
;
QueryPerformanceCounter(&stop);
1.第一個(gè)GetData調(diào)用使用了D3DGETDATA_FLUSH標(biāo)志,表示要將COMMAND BUFFER中的繪制命令都清空到DRIVER中去,當(dāng)GPU處理完所有命令后會(huì)將這個(gè)查詢(xún)對(duì)象狀態(tài)置SIGNALED。
2.將設(shè)備無(wú)關(guān)的SETTEXTURE命令加入到RUNTIME的COMMAND BUFFER中。
3.將設(shè)備無(wú)關(guān)的DrawPrimitive命令加入到RUNTIME的COMMAND BUFFER中。
4.將設(shè)備無(wú)關(guān)的ISSUE命令加入到RUNTIME的COMMAND BUFFER中。
5.GetData會(huì)將BUFFER中的所有命令清空到DRIVER中去,注意這是GETDATA不會(huì)等待GPU完成所有命令的執(zhí)行才返回。這里會(huì)有一個(gè)從用戶(hù)模式到核心模式的切換。
6.等待DRIVER將所有命令都轉(zhuǎn)換為硬件相關(guān)指令,并填充到DRIVER BUFFER中后,調(diào)用從核心模式返回到用戶(hù)模式。
7.GetData循環(huán)查詢(xún) 查詢(xún)對(duì)象 狀態(tài)。當(dāng)GPU完成所有DRIVER BUFFER中的指令后會(huì)改變查詢(xún)對(duì)象的狀態(tài)。
如下情況可能清空RUNTIME COMMAND BUFFER,并引起一個(gè)模式切換:
1.Lock method(某些條件下和某些LOCK標(biāo)志)
2.創(chuàng)建設(shè)備、頂點(diǎn)緩沖、索引緩沖和紋理
3.完全釋放設(shè)備、頂點(diǎn)緩沖、索引緩沖和紋理資源
4.調(diào)用ValidateDevice
5.調(diào)用Present
6.COMMAND BUFFER已滿(mǎn)
7.用D3DGETDATA_FLUSH調(diào)用GetData函數(shù)
對(duì)于D3DQUERYTYPE_EVENT的解釋我不能完全理解(Query for any and all asynchronous events that have been issued from API calls)明白的朋友一定告訴我,只知道當(dāng)GPU處理完D3DQUERYTYPE_EVENT類(lèi)型查詢(xún)?cè)?span lang=EN-US>CB中加入的D3DISSUE_END標(biāo)記后,會(huì)將查詢(xún)對(duì)象狀態(tài)置SIGNALED狀態(tài),所以CPU等待查詢(xún)一定是異步的。為了效率所以盡量少在PRESENT之前使用BEGINSCENE ENDSCENE對(duì),為什么會(huì)影響效率?原因只能猜測(cè),可能EndScene會(huì)引發(fā)Command buffer flush這樣會(huì)有一個(gè)執(zhí)行的模式切換,也可能會(huì)引發(fā)D3D RUNTIME對(duì)MANAGED資源的一些操作。而且ENDSCENE不是一個(gè)同步方法,它不會(huì)等待DRIVER把所有命令執(zhí)行完才返回。
D3D RUTIME的內(nèi)存類(lèi)型,分為3種,VIDEO MEMORY(VM)、AGP MEMORY(AM)和SYSTEM MEMORY(SM),所有D3D資源都創(chuàng)建在這3種內(nèi)存之中,在創(chuàng)建資源時(shí),我們可以指定如下存儲(chǔ)標(biāo)志,D3DPOOL_DEFAULT、D3DPOOL_MANAGED、D3DPOOL_SYSTEMMEM和D3DPOOL_SCRATCH。VM就是位于顯卡上的顯存,CPU只能通過(guò)AGP或PCI-E總線(xiàn)訪(fǎng)問(wèn)到,讀寫(xiě)速度都是非常慢的,CPU連續(xù)寫(xiě)VM稍微快于讀,因?yàn)?span lang=EN-US>CPU寫(xiě)VM時(shí)會(huì)在CACHE中分配32或64個(gè)字節(jié)(取決于CACHE LINE長(zhǎng)度)的寫(xiě)緩沖,當(dāng)緩沖滿(mǎn)后會(huì)一次性寫(xiě)入VM;SM就是系統(tǒng)內(nèi)存,CPU讀寫(xiě)都非???,因?yàn)?span lang=EN-US>SM是被CACHE到2級(jí)緩沖的,但GPU卻不能直接訪(fǎng)問(wèn)到系統(tǒng)緩沖,所以創(chuàng)建在SM中的資源,GPU是不能直接使用的;AM是最麻煩的一個(gè)類(lèi)型,AM實(shí)際也存在于系統(tǒng)內(nèi)存中,但這部分MEM不會(huì)被CPU CACHE,意味著CPU讀寫(xiě)AM都會(huì)寫(xiě)來(lái)個(gè)CACHE MISSING然后才通過(guò)內(nèi)存總線(xiàn)訪(fǎng)問(wèn)AM,所以CPU讀寫(xiě)AM相比SM會(huì)比較慢,但連續(xù)的寫(xiě)會(huì)稍微快于讀,原因就是CPU寫(xiě)AM使用了“write combining”,而且GPU可以直接通過(guò)AGP或PCI-E總線(xiàn)訪(fǎng)問(wèn)AM。
如果我們使用D3DPOOL_DEFAULT來(lái)創(chuàng)建資源,則表示讓D3D RUNTIME根據(jù)我們指定的資源使用方法來(lái)自動(dòng)使用存儲(chǔ)類(lèi)型,一般是VM或AM,系統(tǒng)不會(huì)在其他地方進(jìn)行額外備份,當(dāng)設(shè)備丟失后,這些資源內(nèi)容也會(huì)被丟失掉。但系統(tǒng)并不會(huì)在創(chuàng)建的時(shí)候使用D3DPOOL_SYSTEMMEM或D3DPOOL_MANAGED來(lái)替換它,注意他們是完全不同的POOL類(lèi)型,創(chuàng)建到D3DPOOL_DEFAULT中的紋理是不能被CPU LOCK的,除非是動(dòng)態(tài)紋理。但創(chuàng)建在D3DPOOL_DEFAULT中的VB IB RENDERTARGET BACK BUFFERS可以被LOCK。當(dāng)你用D3DPOOL_DEFAULT創(chuàng)建資源時(shí),如果顯存已經(jīng)使用完畢,則托管資源會(huì)被換出顯存來(lái)釋放足夠的空間。 D3DPOOL_SYSTEMMEM和D3DPOOL_SCRATCH都是位于SM中的,其差別是使用D3DPOOL_SYSTEMMEM時(shí),資源格式受限于Device性能,因?yàn)橘Y源很可能會(huì)被更新到AM或VM中去供圖形系統(tǒng)使用,但SCRATCH只受RUNTIME限制,所以這種資源無(wú)法被圖形系統(tǒng)使用。 D3DRUNTIME會(huì)優(yōu)化D3DUSAGE_DYNAMIC 資源,一般將其放置于AM中,但不敢完全保證。另外為什么靜態(tài)紋理不能被LOCK,動(dòng)態(tài)紋理卻可以,都關(guān)系到D3D RUNTIME的設(shè)計(jì),在后面D3DLOCK說(shuō)明中會(huì)敘述。
D3DPOOL_MANAGED表示讓D3D RUNTIME來(lái)管理資源,被創(chuàng)建的資源會(huì)有2份拷貝,一份在SM中,一份在VM/AM中,創(chuàng)建的時(shí)候被放置L在SM,在GPU需要使用資源時(shí)D3D RUNTIME自動(dòng)將數(shù)據(jù)拷貝到VM中去,當(dāng)資源被GPU修改后,RUNTIME在必要時(shí)自動(dòng)將其更新到SM中來(lái),而在SM中修改后也會(huì)被UPDATE到VM去中。所以被CPU或者GPU頻發(fā)修改的數(shù)據(jù),一定不要使用托管類(lèi)型,這樣會(huì)產(chǎn)生非常昂貴的同步負(fù)擔(dān)。當(dāng)LOST DEVICE發(fā)生后,RESET時(shí)RUNTIME會(huì)自動(dòng)利用SM中的COPY來(lái)恢復(fù)VM中的數(shù)據(jù),因?yàn)閭浞菰?span lang=EN-US>SM中的數(shù)據(jù)并不是全部都會(huì)提交到VM中,所以實(shí)際備份數(shù)據(jù)可以遠(yuǎn)多于VM容量,隨著資源的不斷增多,備份數(shù)據(jù)很可能被交換到硬盤(pán)上,這是RESET的過(guò)程可能變得異常緩慢,RUNTIME給每個(gè)MANAGED資源都保留了一個(gè)時(shí)間戳,當(dāng)RUNTIME需要把備份數(shù)據(jù)拷貝到VM中時(shí),RUNTIME會(huì)在VM中分配顯存空間,如果分配失敗,表示VM已經(jīng)沒(méi)有可用空間,這樣RUNTIME會(huì)使用LRU算法根據(jù)時(shí)間戳釋放相關(guān)資源,SetPriority通過(guò)時(shí)間戳來(lái)設(shè)置資源的優(yōu)先級(jí),最近常用的資源將擁有高的優(yōu)先級(jí),這樣RUNTIME通過(guò)優(yōu)先級(jí)就能合理的釋放資源,發(fā)生釋放后馬上又要使用這種情況的幾率會(huì)比較小,應(yīng)用程序還可以調(diào)用EvictManagedResources強(qiáng)制清空VM中的所有MANAGED資源,這樣如果下一幀有用到MANAGED資源,RUNTIME需要重新載入,這樣對(duì)性能有很大影響,平時(shí)一般不要使用,但在關(guān)卡轉(zhuǎn)換的時(shí)候,這個(gè)函數(shù)是非常有用的,可以消除VM中的內(nèi)存碎片。LRU算法在某些情況下有性能缺陷,比如繪制一幀所需資源量無(wú)法被VM裝下的時(shí)候(MANAGED),使用LRU算法會(huì)帶來(lái)嚴(yán)重的性能波動(dòng),如下例子:
BeginScene();
Draw(Box0);
Draw(Box1);
Draw(Box2);
Draw(Box3);
Draw(Circle0);
Draw(Circle1);
EndScene();
Present();
假設(shè)VM只能裝下其中5個(gè)幾何體的數(shù)據(jù),那么根據(jù)LRU算法,在繪制Box3之前必須清空部分?jǐn)?shù)據(jù),那清空的必然是Circle0……,很顯然清空Box2是最合理的,所以這是RUNTIME使用MRU算法處理后續(xù)Draw Call能很好的解決性能波動(dòng)問(wèn)題,但資源是否被使用是按FRAME為單位來(lái)檢測(cè)的,并不是每個(gè)DRAW CALL都被記錄,每個(gè)FRAME的標(biāo)志就是BEGINSCENE/ENDSCENE對(duì),所以在這種情況下合理使用BEGINSCENE/ENDSCENE對(duì)可以很好的提高VM不夠情況下的性能。根據(jù)DX文檔的提示我們還可以使用QUERY機(jī)制來(lái)獲得更多關(guān)于RUNTIME MANAGED RESOURCE信息,但好像只在RUNTIME DEBUG模式下有用,理解RUNTIME如何MANAGE RESOURCE很重要,但編寫(xiě)程序的時(shí)候不要將這些細(xì)節(jié)暴露出來(lái),因?yàn)檫@些東西都是經(jīng)常會(huì)變的。最后還要提醒的是,不光RUNTEIME會(huì)MANAGE RESOURCE,DRIVER也很可能也實(shí)現(xiàn)了這些功能,我們可以通過(guò)D3DCAPS2_CANMANAGERESOURCE標(biāo)志取得DRIVER是否實(shí)現(xiàn)資源管理功能的信息,而且也可以在CreateDevice的時(shí)候指定D3DCREATE_DISABLE_DRIVER_MANAGEMENT來(lái)關(guān)閉DRIVER資源管理功能。
D3DLOCK探索D3D RUNTIME工作
如果LOCK DEFAULT資源會(huì)發(fā)生什么情況呢?DEFAULT資源可能在VM或AM中,如果在VM中,必須在系統(tǒng)內(nèi)容中開(kāi)辟一個(gè)臨時(shí)緩沖返回給數(shù)據(jù),當(dāng)應(yīng)用程序?qū)?shù)據(jù)填充到臨時(shí)緩沖后,UNLOCK的時(shí)候,RUNTIME會(huì)將臨時(shí)緩沖的數(shù)據(jù)傳回到VM中去,如果資源D3DUSAGE屬性不是WRITEONLY的,則系統(tǒng)還需要先從VM里拷貝一份原始數(shù)據(jù)到臨時(shí)緩沖區(qū),這就是為什么不指定WRITEONLY會(huì)降低程序性能的原因。CPU寫(xiě)AM也有需要注意的地方,因?yàn)?span lang=EN-US>CPU寫(xiě)AM一般是WRITE COMBINING,也就是說(shuō)將寫(xiě)緩沖到一個(gè)CACHE LINE上,當(dāng)CACHE LINE滿(mǎn)了之后才FLUSH到AM中去,第一個(gè)要注意的就是寫(xiě)數(shù)據(jù)必須是WEAK ORDER的(圖形數(shù)據(jù)一般都滿(mǎn)足這個(gè)要求),據(jù)說(shuō)D3DRUNTIME和NV DIRVER有點(diǎn)小BUG,就是在CPU沒(méi)有FLUSH到AM時(shí),GPU就開(kāi)始繪制相關(guān)資源產(chǎn)生的錯(cuò)誤,這時(shí)請(qǐng)使用SFENCE等指令FLUSH CACHE LINE。第二請(qǐng)盡量一次寫(xiě)滿(mǎn)一個(gè)CACHE LINE,否則會(huì)有額外延遲,因?yàn)?span lang=EN-US>CPU每次必須FLUSH整個(gè)CACHE LINE到目標(biāo),但如果我們只寫(xiě)了LINE中部分字節(jié),CPU必須先從AM中讀取整個(gè)LINE長(zhǎng)數(shù)據(jù)COMBINE后重新FLUSH。第三盡可能順序?qū)懀S機(jī)寫(xiě)會(huì)讓WRITE COMBINING反而變成累贅,如果是隨機(jī)寫(xiě)資源,不要使用D3DUSAGE_DYNAMIC創(chuàng)建,請(qǐng)使用D3DPOOL_MANAGED,這樣寫(xiě)會(huì)完全在SM中完成。
普通紋理(D3DPOOL_DEFAULT)是不能被鎖定的,因?yàn)槠湮挥?span lang=EN-US>VM中,只能通過(guò)UPDATESURFACE和UPDATETEXTURE來(lái)訪(fǎng)問(wèn),為什么D3D不讓我們鎖定靜態(tài)紋理,卻讓我們鎖定靜態(tài)VB IB呢?我猜測(cè)可能有2個(gè)方面的原因,第一就是紋理矩陣一般十分龐大,且紋理在GPU內(nèi)部已二維方式存儲(chǔ);第二是紋理在GPU內(nèi)部是以NATIVE FORMAT方式存儲(chǔ)的,并不是明文RGBA格式。動(dòng)態(tài)紋理因?yàn)楸砻鬟@個(gè)紋理需要經(jīng)常修改,所以D3D會(huì)特別存儲(chǔ)對(duì)待,高頻率修改的動(dòng)態(tài)紋理不適合用動(dòng)態(tài)屬性創(chuàng)建,在此分兩種情況說(shuō)明,一種是GPU寫(xiě)入的RENDERTARGET,一種是CPU寫(xiě)入的TEXTURE VIDEO,我們知道動(dòng)態(tài)資源一般是放置在AM中的,GPU訪(fǎng)問(wèn)AM需要經(jīng)過(guò)AGP/PCI-E總線(xiàn),速度較VM慢許多,而CPU訪(fǎng)問(wèn)AM又較SM慢很多,如果資源為動(dòng)態(tài)屬性,意味著GPU和CPU訪(fǎng)問(wèn)資源會(huì)持續(xù)的延遲,所以此類(lèi)資源最好以D3DPOOL_DEFAULT和D3DPOOL_SYSTEMMEM各創(chuàng)建一份,自己手動(dòng)進(jìn)行雙向更新更好。千萬(wàn)別 RENDERTARGET以D3DPOOL_MANAGED 屬性創(chuàng)建,這樣效率極低,原因自己分析。而對(duì)于改動(dòng)不太頻繁的資源則推薦使用DEFAULT創(chuàng)建,自己手動(dòng)更新,因?yàn)橐淮胃碌男蕮p失遠(yuǎn)比GPU持續(xù)訪(fǎng)問(wèn)AM帶來(lái)的損失要小。
不合理的LOCK會(huì)嚴(yán)重影響程序性能,因?yàn)橐话?span lang=EN-US>LOCK需要等待COMMAND BUFFER前面的繪制指令全部執(zhí)行完畢才能返回,否則很可能修改正在使用的資源,從LOCK返回到修改完畢UNLOCK這段時(shí)間GPU全部處于空閑狀態(tài),沒(méi)有合理使用GPU和CPU的并行性,DX8.0引進(jìn)了一個(gè)新的LOCK標(biāo)志D3DLOCK_DISCARD,表示不會(huì)讀取資源,只會(huì)全寫(xiě)資源,這樣驅(qū)動(dòng)和RUNTIME配合來(lái)了個(gè)瞞天過(guò)海,立即返回給應(yīng)用程序另外塊VM地址指針,而原指針在本次UNLOCK之后被丟棄不再使用,這樣CPU LOCK無(wú)需等待GPU使用資源完畢,能繼續(xù)操作圖形資源(頂點(diǎn)緩沖和索引緩沖),這技術(shù)叫VB IB換名(renaming)。
很多困惑來(lái)源于底層資料的不足,相信要是MS開(kāi)放D3D源碼,開(kāi)放驅(qū)動(dòng)接口規(guī)范,NV / ATI顯示開(kāi)放驅(qū)動(dòng)和硬件架構(gòu)信息,這些東西就很容易弄明白了。
順便做個(gè)書(shū)的廣告 《人工智能:一種現(xiàn)代方法》中文版 卓越網(wǎng)已經(jīng)有貨,AI巨作,不過(guò)閱讀需要相當(dāng)?shù)幕A(chǔ),對(duì)思維非常有啟迪,想買(mǎi)的朋友不要錯(cuò)過(guò)。后面我會(huì)將學(xué)習(xí)重點(diǎn)從圖形轉(zhuǎn)到AI上來(lái),對(duì)AI有興趣的朋友一起交流。