受控紋理,也稱為自動紋理管理,自第6版以來,已在 DirectX 中使用,後續版本進行了數個修訂和增強功能。 自 Direct3D 9 API 發行起,自動資源管理包含紋理、頂點緩衝區和索引緩衝區的支援,全都具有一致的共用介面。 藉由使用 Direct3D 資源管理員,應用程式可以大幅簡化遺失裝置狀況的處理,並可依賴系統處理視訊記憶體資源的合理過度承諾量。
開發人員有時很難使用受控資源,部分原因是系統的抽象本質。 雖然許多常見的資源情境很適合管理資源,但某些情境在使用非管理資源時表現更佳。 本文討論一般處理資源的最佳做法、Managed 和 Unmanaged 資源的行為方式,並提供運行時間和驅動程式通常如何處理資源的一些詳細數據。
本文涵蓋下列概念:
視訊記憶體
若要讓視訊系統使用資源,它必須位於 GPU 可存取的記憶體中。 本機視訊記憶體為 GPU 提供最佳效能,而且某些資源(例如轉譯目標和深度/樣本緩衝區)必須位於本機視訊記憶體中。 隨著 AGP 的出現,GPU 也可以直接存取系統記憶體的一部分。 這個記憶體區域稱為 AGP 光圈,稱為非本機視訊記憶體,不適用於其他用途。 非本機視訊記憶體可由 CPU 讀取和寫入,其通常沒有本機視訊記憶體的高效能存取權,因此非常適合作為共用記憶體資源使用。 AGP 記憶體的重要注意事項是,像本機視訊記憶體一樣,在裝置遺失的情況下會變得無效,因此必須重新還原位於其中的持續資產。
圖 1. GPU、CPU、視訊 RAM 和系統 RAM 的關聯性
GPU、CPU、視訊記憶體和系統記憶體的關聯性
某些整合式影片解決方案會使用統一記憶體架構 (UMA),其中主要記憶體可由系統的所有元件尋址。 Direct3D 支援 UMA,而不需要對應用程式進行任何變更,使用與本機視訊記憶體組態相同的提示。 針對這類系統,資源都存放在系統記憶體中,而驅動程式負責確保資源像在傳統架構中一樣運作,同時利用UMA的特性及硬體實作的特定行為。
圖 2. GPU 和 CPU 在統一記憶體架構中具有系統 RAM 的相等存取權
受控資源
在 POOL_MANAGED中,您的大部分資源應該被建立為管理資源。 系統會在系統記憶體中建立所有資源,然後根據需要複製到視訊記憶體中。 系統記憶體複本會自動處理遺失裝置的情況。 由於並非所有受控資源都必須同時放入視訊記憶體中,因此您可以超額分配記憶體,其中較小的視訊記憶體資源工作集即可滿足在任何給定畫面中進行轉譯所需的所有資源。 請注意,大部分的備份存放區系統記憶體可能會隨著時間被分頁到磁碟,這就是為什麼重設操作可能會比較慢,因為需要將這些數據重新分頁以還原遺失的視訊記憶體。
運行時間會在上次使用資源時保留時間戳,而且當視訊記憶體配置因載入所需的受控資源而失敗時,它會以 LRU 方式根據此時間戳釋放資源。 SetPriority 的使用優先順序高於時間戳,因此較常用的資源應該設定為較高的優先順序值。 Direct3D 9.0 對於驅動程式所管理之視訊記憶體的資訊有限,因此運行時間可能需要收回數個資源,才能建立足夠大的區域,讓配置成功。 適當的優先順序有助於消除某些專案被收回的情況,然後不久之後又需要一次。 應用程式也可以呼叫 EvictManagedResources,強制移除所有受控資源。 同樣地,這可以是一項耗時的作業,以重載下一個畫面格所需的所有資源,但對於工作集大幅變更及移除視訊記憶體片段的層級轉換非常有用。
幀計數也會保留,以讓執行時環境偵測剛選擇移除的資源是否在當前幀早期被使用,這意味著在單一幀中使用的資源超過視訊記憶體可容納的情況,導致資源過度交換。 這會觸發取代原則,切換至 MRU 方式,而非 LRU,在後續過程中這種方法通常在這類情況下表現得稍微更好。 這種打亂行為會大幅影響轉譯效能。 請注意,「當前畫面」的概念與 EndScene紧密相关,因此任何使用管理資源的應用程式都必須定期調用此方法。
開發人員想要尋找其應用程式中受控資源運作方式的詳細資訊,可以透過 IDirect3DQuery9 介面來使用 RESOURCEMANAGER 事件查詢。 這只有在使用偵錯運行時才有效,因此應用程式不能依靠此資訊,但它提供有關運行時管理的資源的深入細節。
雖然了解資源管理員在微調和偵錯應用程式時如何運作,但請務必不要將應用程式過於緊密地系結至目前運行時間或驅動程序的實作詳細數據。 驅動程式的修訂或硬體的變更可能會顯著改變其行為,未來版本的 Direct3D 在資源管理上將更為精細和先進。
Driver-Managed 資源
Direct3D 驅動程式可以自由實作驅動程式管理紋理功能,如D3DCAPS2_CANMANAGERESOURCE所指示,這可讓驅動程式處理資源管理,而不是運行時間。 針對實作這項功能的 (罕見) 驅動程式,驅動程序資源管理員的確切行為可能會有很大的差異,您應該連絡驅動程序的廠商,以取得其實作運作方式的詳細數據。 或者,您可以藉由在建立裝置時指定D3DCREATE_DISABLE_DRIVER_MANAGEMENT,確保始終使用執行階段管理員。
默認資源
雖然受控資源簡單、有效率且容易使用,但有時候會偏好直接使用視訊記憶體,甚至是必要的。 這類資源會在 POOL_DEFAULT 類別中建立。 使用這類資源會造成應用程式的額外複雜問題。 程式代碼必須處理所有 POOL_DEFAULT 資源的裝置遺失情況,並且在將資料複製到這些資源時,必須考慮效能。 未指定USAGE_WRITEONLY或使渲染目標可鎖定,也可能會對性能造成嚴重影響。
在POOL_DEFAULT資源上呼叫 鎖定,比使用POOL_MANAGED資源更可能導致 GPU 停止運作,除非使用特定提示旗標。 視資源的位置而定,傳回的指標可以是暫存系統記憶體緩衝區,也可以是直接進入 AGP 記憶體的指標。 如果它是暫時的系統記憶體緩衝區,在 解除鎖定 呼叫之後,必須將數據傳輸至視訊記憶體。 如果視訊資源不是唯寫的,在 鎖定期間,數據必須傳輸到暫存緩衝區。 如果是 AGP 記憶體區域,則會避免暫存複本,但所需的快取行為可能會導致效能變慢。
務必小心將完整的快取行數據寫入指向 AGP 光圈記憶體的任何指標,以避免寫入合併的效應,這會導致讀寫周期,且優先執行順序存取記憶體區域。 如果您的應用程式需要在建立期間對數據進行隨機存取,而且您不想使用緩衝區的受控資源,您應該改用系統記憶體複本。 建立數據之後,您就可以將結果串流至鎖定的資源記憶體,以避免對快取寫入合併作業付出高昂的代價。
LOCK_NOOVERWRITE標誌可用來為某些資源以有效率的方式追加資料,但理想情況下,可以避免對相同資源進行多次 鎖定 和 解除鎖定 呼叫。 適當使用各種鎖定旗標對於最佳效能而言很重要,用快取友好的資料存取模式填滿鎖定的記憶體也是如此。
同時使用管理和預設資源
混合配置管理的和 POOL_DEFAULT 資源可能會導致視訊記憶體的碎片化,並在運行時混淆對管理資源可用視訊記憶體的檢視。 在理想情況下,您應該先建立所有 POOL_DEFAULT 資源再使用 POOL_MANAGED 資源,或者在配置未管理資源之前,使用 EvictManagedResources 呼叫。 請記住,來自 POOL_DEFAULT 的所有配置會在影像記憶體中佔用存留期內的記憶體,使其無法被資源管理員或其他用途使用。
請注意,與舊版 Direct3D 不同的是,第 9 版執行階段在因視訊記憶體不足而失敗的非受管資源配置被放棄之前,會自動收回某些受管資源,但這可能會導致額外的資源碎片化問題,甚至強制資源進入次佳的位置(例如,將靜態材質放在非本機視訊記憶體中)。 同樣地,最好事先配置所有必要的非控管資源,再使用任何受控資源。
動態預設資源
以高頻率產生和更新的數據不需要備份存放區,因為還原裝置時會重新建立所有資訊。 這類數據通常最好在POOL_DEFAULT中建立,並使用USAGE_DYNAMIC作為提示,這樣驅動程式在放置資源時可以根據通常會頻繁更新的特性做出優化決策。 這通常表示將資源放入非本機視訊記憶體中,因此,GPU 的存取速度通常比本機視訊記憶體慢得多。 針對 UMA 架構,驅動程式可能會選擇動態資源的特定位置,以針對 CPU 寫入存取進行優化。
此使用方式通常應用於軟體蒙皮解決方案和基於 CPU 的粒子系統填充頂點及索引緩衝區,而 LOCK_DISCARD 標誌可確保在資源仍在上一幀中使用的情況下,不會產生停滯。 在此情況下,使用受管理資源會更新系統記憶體緩衝區,然後將其複製到視訊記憶體,只用於一個或兩個畫面,再進行替換。 對於具有非本機視訊記憶體的系統,會藉由適當地使用此動態模式來消除額外的複本。
標準紋理無法鎖定,且只能透過 UpdateSurface 或 UpdateTexture更新。 某些系統支持動態紋理,可以被鎖定並使用LOCK_DISCARD模式,但在使用這類資源之前,必須先檢查功能位元(D3DCAPS2_DYNAMICTEXTURES)。 針對高度動態紋理(視訊或程式性),您的應用程式可以使用 UpdateTexture來建立相符的POOL_DEFAULT和POOL_SYSTEMMEM資源,並處理視訊記憶體更新。 對於高度頻繁和部分更新,UpdateTexture 範例可能是較佳的選擇。
如同動態資源一樣有用,在設計高度依賴動態提交的系統時,請務必小心。 靜態資源應放入POOL_MANAGED,以確保本機視訊記憶體的良好使用率,並更有效率地使用有限的總線和主要記憶體頻寬。 對於半靜態的資源,您可能會發現偶爾上傳至本機視訊記憶體的成本遠小於讓它們成為動態所產生的常數總線流量。
系統記憶體資源
您也可以在 POOL_SYSTEMMEM 中建立資源。 雖然圖形管線無法使用它們,但可以使用 UpdateSurface 或 UpdateTexture來作為更新 POOL_DEFAULT 資源的來源。 其鎖定行為很簡單,但如果使用其中一個先前提及的方法,可能會造成停滯。
雖然它們位於系統記憶體中,但POOL_SYSTEMMEM資源受限於設備驅動器支援的相同格式和功能(例如大小上限)。 POOL_SCRATCH資源類型是另一種系統記憶體資源形式,可利用運行時間支援的所有格式和功能,但無法由裝置存取。 Scratch資源主要供內容工具使用。
圖 3. 視訊 RAM、AGP 光圈和系統 RAM 中的記憶體資源
一般建議
取得資源管理正確的技術實作詳細數據,對於為您的應用程式達成效能目標有很長的路要走。 規劃如何向 Direct3D 呈現資源,以及以及時方式載入數據的架構設計,是一項更複雜的工作。 針對您的應用程式做出這些決策時,建議您使用一些最佳做法:
- 預先處理所有資源。 在開發期間,依賴資源的昂貴載入時轉換和優化固然方便,但這樣做會對使用者的電腦造成極大的性能負擔。 預先處理的資源更快速地載入、更快速地使用,並提供您執行複雜的離線工作選項。
- 避免為每個畫面建立許多資源。 所需的驅動程式互動可以串行化CPU和 GPU,而牽涉到的作業很重,因為它們通常需要核心轉換。 將創建過程分配到多個畫面,或在不建立/釋放資源的情況下重複使用資源。 在理想情況下,您應該先等候數個幀,再鎖定或釋放最近用來渲染的資源。
- 在框架結尾,請務必解除系結所有資源通道(也就是數據流來源、紋理階段和目前索引)。 這樣做可確保在導致資源管理員保留實際不再使用資源之前,移除懸空的資源參考。
- 針對紋理,請使用壓縮格式(例如 DXTn)並使用 Mipmaps,並考慮使用紋理圖集。 這些會大幅降低頻寬需求,而且可以降低資源的整體大小,進而使其更有效率。
- 針對幾何,請利用索引幾何,因為這有助於壓縮頂點緩衝區資源,而新式視訊硬體在重複使用頂點時會大幅優化。 藉由使用可程式化的頂點著色器,您可以壓縮頂點資訊,並在頂點處理期間展開之。 同樣地,這有助於降低頻寬需求,並讓頂點緩衝區資源更有效率。
- 避免過度優化您的資源管理。 未來的驅動程式、硬體和作業系統的更新若過於針對特定組合進行調整,可能會造成相容性問題。 由於大部分應用程式都是 CPU 系結,因此昂貴的 CPU 型管理通常會造成比解決更多的效能問題。
相關主題