本系列文章是對 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
最複雜的狀況是,在當masOS
處理緩衝器時,數據須要同時被CPU
和GPU
訪問.咱們選擇儲存模式時,是基於下面一個或多個條件爲真來決定的:學習
Shared
模式的源緩衝器,而後位塊傳送它的數據到一個Private
模式的目標緩衝器中.在本例中資源一致不是必須的,由於數據只被GPU
訪問.該操做是花費最低的(一個一次性花費).CPU
使用,另外一份儲存在GPU
內存中.資源一致性經過同步兩份副原本嚴格控制.CPU
和GPU
可見,可修改.資源一致性只在命令緩衝器界限內被保證.如何保證資源的一致性?首先,確保全部的來自CPU
的修改已經在命令緩衝器被提交(檢查命令緩衝器狀態屬性是不是MTLCommandBufferStatusCommitted)以前完成了.在GPU
結束執行命令緩衝器後,CPU
只應該在GPU
發信號給CPU
,告知CPU命令緩衝器結束執行(檢查命令緩衝器狀態屬性是不是MTLCommandBufferStatusCompleted)以後,再開始着手修改.ui
最後,讓咱們看看masOS
的資源是如何同步完成的.對於緩衝器:在CPU
寫入後使用didModifyRange() 將修改項通知GPU
,這樣Metal
能夠只更新這個數據區域; 在GPU
寫入後,在一個位塊傳送操做內,用synchronize(resource:) 來刷新緩存,這樣CPU
就能夠訪問更新後的數據. 對於紋理:在CPU
寫入後,使用兩個replace() 區域函數中的一個,將修改項通知GPU
,這樣Metal
能夠只更新這個數據區域;在GPU
寫入後,在一個位塊傳送操做內,使用兩個synchronize() 函數中的一個,來容許Metal
在GPU
結束脩改數據後再更新系統緩存副本.
源代碼source code已發佈在Github上.
下次見!