摘要
受管貼圖(Managed textures,也就是咱們一般所謂的「自動管理貼圖」),在DX6中首次被引入,通過一系列的改進和加強,在DX9中自動管理的資源類型增長到貼圖,頂點緩衝,頂點索引緩衝,全部這些資源使用統一的公共接口。經過使用D3D資源管理器,應用程序能夠輕鬆的處理設備丟失、處理稍微過量的顯存使用。
有時開發者在使用受管資源會遇到一些困難,這部分歸咎與系統的抽象特性。在大多數狀況下使用受管對象是不錯的選擇,但有時出於性能考慮也會使用非託管資源。這篇文章將討論通常狀況下如何處理資源,受管與非受管資源的行爲差異。
內容
l 顯示內存
l 受管資源
l 驅動管制資源
l 默認資源
l 系統內存資源
l 通常性的建議
顯示內存
爲 了使得資源能夠利用顯存,GPU須要經過內存訪問定位他。GPU訪問(Local video memory)顯存是很是高效的,而且某些資源(例如RenderTarget,深度、模板緩衝)必須在本地顯存(Local video memory)定位。因爲AGP的出現,GPU能夠直接訪問部分系統內存,而這部分系統內存區域就是所謂的非本地顯存(non-local video memory),固然這部份內存(顯存)也是不能挪作它用的。非本地顯存僅能被GPU訪問,與訪問本地顯存相比,其效率低一些。須要明確的是,全部AGP 內存在設備丟失時都會失效,都須要在恢復他們。
一些集成顯卡使用統一內存結構(Unified Memory Architecture),這樣主內存能夠被系統任何一個設備尋址。D3D支持UMA而不須要修改任何代碼,這樣咱們把系統內存配置爲本地顯存,硬件確保資源的定位就像傳統的結構同樣進行工做。
受管資源
大部分資源應該使用POOL_MANAGED方式建立,即受管資源。全部受管資源將被建立在系統內存,在須要的時候複製到顯存。當發生設備丟失時會自動 copy系統內存到顯存。既然不是全部受管資源都須要一次送入顯存,這樣你能夠提交超過渲染每幀所必須使用的最小內存容量,可是這樣會使得大量顯存內容由於分頁操做而寫到磁盤上,這是很是耗時的。這也是爲何恢復設備如此耗時,由於須要將大量磁盤數據複製到顯存。
DX會爲每份資源在最後一次使用時加上時間戳,這樣當顯存分配失敗時,它會釋放那些最近最少使用的資源(LRU算法)。使用SetPriority函數能夠標記資源的重要程度,重要的資源優於時間戳的判斷,因此那些比較經常使用的資源應該設置高優先級,而不用擔憂由於時間戳過時而致使資源被釋放。在DX9中,驅動程序提供的顯存管理信息是很是有限的,運行時可能不得不清除大量資源用於分配足夠的內存。設置適合的優先級是很是有用的,這樣D3D不會清除那些立刻又須要使用的資源。應用程序能夠強制調用EvictManagedResources清除全部受管資源,可是若是下一幀又須要從新加載這些資源,這將是很是耗時的,不過這個函數在那些場景明顯須要改變(好比進入下一個關卡)的狀況下,仍是很是有用的。
若是「當前幀」內須要很是多資源用於渲染,這將是件麻煩的事情,用前面的LRU方式調度資源效率就不太理想了,這個時候使用MRU資源調度方式取代,即優先清理那些比較活躍的資源。注意,這裏「當前幀」的概念是指 BeginScene和EndScene之間的須要渲染的幀。
開發人員若是想獲得關於受管資源的更多信息,能夠經過IDirect3DQuery9接口查詢,可是這個接口僅能用於調試模式(debug runtimes),在發佈版本中,應用程序不能依靠改接口的信息作任何假定。
瞭解資源管理如何工做能夠幫助咱們調試、調整程序,重要的是應用程序不要太過依賴當前的運行庫(或者驅動程序)的資源管理方式,驅動更新有可能致使其行爲發生變化,未來的D3D將會有套久經考驗的資源管理方式。
驅動程序管理的資源
D3D 驅動能夠自由的實現「由驅動管理貼圖」的特性,經過D3DCAPS2_CANMANAGERESOURCE段能夠查詢硬件驅動是否支持這個特性,這樣驅動將代替D3D運行庫管理資源。對於級少數的硬件是支持這個特性的,對於大多數硬件則不盡相同,你能夠諮詢你的產品提供商得到這方面信息。通常狀況下,你但是使用D3DCREATE_DISABLE_DRIVER_MANAGEMENT方式建立設備,這樣將由D3D運行庫來管理資源。
缺省資源管理(非受管資源)
雖然受管資源很是簡單,容易使用,高效,可是有時咱們但願直接往顯存裏寫東西,這種狀況下咱們須要使用POOL_DEFAULT方式建立資源。使用這種方式會增長程序的複雜性,代碼須要應付全部設備丟失的狀況,並須要謹慎考慮什麼時候複製數據到顯存。錯誤的指定USAGE_WRITEONLY標記或者鎖定渲染目標(Render Target)將嚴重影響性能。
鎖定POOL_DEFAULT類型的資源極可能致使GPU中止運轉,這與 POOL_MANAGED類型的資源是不一樣的,除非使用一些特性的指示標記。根據資源當前的位置不一樣,鎖定後獲得的指針也不相同,多是一塊臨時的系統內存,也可能直接指向AGP內存。若是是臨時的系統內存,Unlock後將把這段數據送入顯存,這是由於若是顯卡資源不是隻寫的(write- only),Lock的時候數據將不得不被送入一段臨時的內存;若是指向的AGP內存區域,臨時的拷貝是能夠避免的,可是cache的行爲將會下降性能。
爲了不在寫入一整行數據(a full cache line of data)進入AGP內存區致使write-combing性能降低(通常是因爲發生了一次讀寫週期),順序的訪問AGP內存是推薦的作法,若是你的程序須要隨機的訪問AGP內存,而你又不但願使用受管資源,那麼你可使用系統內存做爲替代方案,這樣當你生成了數據以後,能夠lock後拷貝,這樣不會帶來太大的性能損失,這裏的性能損失通常是由緩衝的「寫搜索」操做引發。(注,這裏關於詞彙cache write-combing譯者也不知道對應的中文含義,只能按照字面意思翻譯,見諒)
對於某些類型的資源,使用 LOCK_NOOVERWRITE標記會使添加數據比較有效率,可是屢次的Lock,Unlock同一資源仍是須要儘可能避免的,適當的利用多種不一樣的鎖定標記對於效率優化使很是重要的,就像填充鎖定內存區域最好使用cache友好的(cache-friendly)數據訪問方式同樣。
受管資源和缺省資源混合使用
受管資源與非受管資源的混合分配使用可能致使顯存碎塊,而且擾亂受管資源使用的內存區域。最好在使用受管資源前使用非受管資源,或者使用受管資源後使用 EvictManagedResources函數清除那些受管資源再使用非受管資源。記住,全部非受管資源都會常駐顯存,這樣其餘內存需求就不能使用了。
注意,與以往的DX版本不一樣,在顯存缺少時,若是分配非受管資源失敗,DX9會自動清除受管資源,這有可能致使潛在的顯存碎塊,甚至把資源放入不適當的地方(好比非本地內存的靜態貼圖區)。因此,最好在使用受管資源以前分配所有的非受管資源。
動態缺省資源
若是數據須要很高頻率更新,那最好使用非受管資源,並使用USAGE_DYNAMIC標記,這樣驅動會決定最適合的地方放置這些須要常常更新的數據。這一般意味着放置在非本地顯存中,這樣對於GPU來講,訪問速度可能相對要慢一些。而對於UMA架構,驅動將會選擇CPU訪問效率較高的特殊地方放置這些數據。
這種用法(動態缺省資源類型)通常用於軟蒙皮和基於CPU計算的粒子系統的頂點/頂點索引的Buffer填充,LOCK_DISCARD標記能夠保證資源仍被使用的時候,鎖定操做不會致使系統中止暫停工做。在這種狀況下,使用受管資源會更新系統內存,而後拷貝到顯存。對於系統的非本地內存,多餘拷貝是不須要的。
標準的貼圖是不容許鎖定的,僅僅能夠經過UpdateSurface和 UpdateTexture函數更新。一些系統支持動態貼圖,它能夠經過配合使用LOCK_DISCARD標記進行鎖定,但這須要檢查 D3DCAPS2_DYNAMICTEXTURES硬件能力。對於高動態貼圖(如視頻、程序生成貼圖),最好使用非受管資源和系統內存資源,而且經過 UpdateTexture函數更新貼圖。對於高頻度的粒子更新,UpdateTexture函數多是最好的選擇。
在有限的總線-內存帶寬下,靜態貼圖資源應該使用POOL_MANAGED方式,這樣能夠確保它最好的利用本地顯存,並有較好的效率。對於「半靜態」資源,使用動態類型資源有時會得到更好的效率。
系統內存資源
資源可使用POOL_SYSTEMMEM方式建立。但他們不能用於圖形管線,他們僅能作爲源數據用於更新POOL_DEFAULT類型的資源,這是經過 UpdateSurface和UpdateTexture函數完成的。他們的鎖定操做也很是簡單,儘管他們一樣可能由於前面提到的緣由致使系統中止運轉。
雖然是在系統內存中建立資源,但POOL_SYSTEMMEM所支持的資源格式和能力(好比最大尺寸)是受硬件、驅動限制的。一樣是在系統內存中建立資源的POOL_SCRATCH則沒有這方面限制,它支持全部格式和能力,但設備卻不能直接訪問它。SCRATCH類型資源通常用於內容建立工具。
通常性的建議
瞭解資源管理的技術實現細節對達成你的程序的性能目標是有很是大的幫助的,規劃你的資源如何交給D3D並設計好的結構以便能及時加載必要的數據是一件很是複雜的工做,爲此咱們給出一些好的實踐經驗作爲通常性的原則:
l 預處理你的資源。不要將耗時的加載資源、資源轉換、資源優化丟給用戶去作,雖然這樣便於開發,但確讓用戶沒法忍受。預處理這些資源能夠加快加載,更快使用,你的用戶也會發現你的程序跑的更快了。
l 避免在每幀建立過多的資源。對於過多的資源加載,能夠把他們分到多幀裏完成或者不要急於釋放那些暫時不用的資源。
l 確保在一幀結束時已經斷開了全部資源通道。(好比,頂點流,texture stages,頂點索引)。
l 對於貼圖,建議使用壓縮貼圖(DXTn)格式,建議使用mip-map或者將小貼圖拼接爲大貼圖使用。
l 建議使用頂點索引,這將減小數據傳輸量。
l 對於過渡的優化資源管理是須要謹慎的。若是你的程序過度依賴驅動、硬件和操做系統的某些特徵,那麼這些程序、硬件的修改將會致使潛在的性能問題。算法