[MetalKit]35-Working-with-memory-in-Metal-part-2內存管理2

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.git

MetalKit系統文章目錄github


在深刻內存管理時有不少話題須要探討.上次咱們已經瞭解了建立MTLBuffer對象有三種選項設置:用新數據分配一塊新內存,用已存在區域複製數據到一塊新內存,重用一塊已經存在的分配區不復制數據.由於咱們之前並不關注內存,就讓咱們驗證一下證實這確實是真的.首先咱們複製數據到另外一分配區:swift

let count = 2000
let length = count * MemoryLayout< Float >.stride
var myVector = [Float](repeating: 0, count: count)
let myBuffer = device.makeBuffer(bytes: myVector, length: length, options: [])
withUnsafePointer(to: &myVector) { print($0) }
print(myBuffer.contents())
複製代碼

注意: withUnsafePointer() 函數提供給咱們的實際數據的內存地址在堆上,而不是指向數據的指針(在棧上)的地址.緩存

你的輸出看起來會像這樣:less

0x000000010043e0e0
0x0000000102afd000
複製代碼

注意到上面地址的最後三位數字了嗎?這是來自於頁對齊數據,由於地址是以0 mod pageSize肯定的,由於最後三位是0,由於咱們的頁尺寸是0x1000.ide

如今咱們接着看Storage Modes,咱們上次曾簡短提到過.至少須要記住四條主要規則,每種儲存模式一條:函數

Mode Description
Shared macOS緩衝器,iOS/tvOS資源上爲默認;masOS紋理上不可用.
Private 主要用於數據只被GPU訪問的狀況下
Memoryless 僅用於iOS/tvOS芯片的臨時渲染目標(紋理).
Managed macOS紋理的默認模式;在iOS/tvOS資源上不可用.

對於一個更好的大圖片,下面是完整的做弊表,讓你無需記憶上面的規則,更容易使用:post

storage-modes.png

最複雜的狀況是,在當masOS處理緩衝器時,數據須要同時被CPUGPU訪問.咱們選擇儲存模式時,是基於下面一個或多個條件爲真來決定的:學習

  • Private - 對於最多隻改變一次的大尺寸數據,那它就不是"髒"的.建立一個Shared模式的源緩衝器,而後位塊傳送它的數據到一個Private模式的目標緩衝器中.在本例中資源一致不是必須的,由於數據只被GPU訪問.該操做是花費最低的(一個一次性花費).
  • Managed - 對於不多改變(每幾幀)的中等尺寸數據,它就是部分"髒"的.一個數據副本儲存在系統內存中給CPU使用,另外一份儲存在GPU內存中.資源一致性經過同步兩份副原本嚴格控制.
  • Shared - 對於每幀都更新的小尺寸數據,那它就是徹底"髒"的.數據存放在系統內存中,並同時對CPUGPU可見,可修改.資源一致性只在命令緩衝器界限內被保證.

如何保證資源的一致性?首先,確保全部的來自CPU的修改已經在命令緩衝器被提交(檢查命令緩衝器狀態屬性是不是MTLCommandBufferStatusCommitted)以前完成了.在GPU結束執行命令緩衝器後,CPU只應該在GPU發信號給CPU,告知CPU命令緩衝器結束執行(檢查命令緩衝器狀態屬性是不是MTLCommandBufferStatusCompleted)以後,再開始着手修改.ui

最後,讓咱們看看masOS的資源是如何同步完成的.對於緩衝器:在CPU寫入後使用didModifyRange() 將修改項通知GPU,這樣Metal能夠只更新這個數據區域; 在GPU寫入後,在一個位塊傳送操做內,用synchronize(resource:) 來刷新緩存,這樣CPU就能夠訪問更新後的數據. 對於紋理:在CPU寫入後,使用兩個replace() 區域函數中的一個,將修改項通知GPU,這樣Metal能夠只更新這個數據區域;在GPU寫入後,在一個位塊傳送操做內,使用兩個synchronize() 函數中的一個,來容許MetalGPU結束脩改數據後再更新系統緩存副本.

源代碼source code已發佈在Github上.

下次見!

相關文章
相關標籤/搜索