本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.
今年的WWDC
可能是有史以來最重要的,至少目前我們 - Metal
開發者 - 終於被關注了.我可以誠心誠意地講這是我生命中最棒的一週!
讓我們看看Games and Graphics遊戲和圖形新聞.最意想不到
的是,將Metal
重新命名為Metal 2.自從它在2014年
第一次被髮布後,它終於有了最顯著的增加和增強,確實,但我們必須承認:沒人預見到這一變化的來臨.最意想不到
的獎品是新的ARKit框架.在keynote演示之後僅僅幾周,就已經有了大量大膽有趣的擴增實境專案放出來. ARKit融入Metal
非常容易.最後,最有影響力
的獎品是VR.這是因為虛擬現實現在能夠實現更低延遲,更高幀率,還有更強勁的內建顯示卡,現在也有了外接顯示卡external GPUs.
Model I/O
, SpriteKit
和SceneKit
框架也都新增了新特性.其它令人感興趣的增加是用於機器學習 machine learning的CoreML
和Vision
框架.本文只關注Metal
中的新特色:
1). MPS - Metal Performance Shaders目前在macOS
上也可以使用了,新增的新特性包括:
- 四種新的圖片處理基本體(
Image Keypoints影像關鍵點
,Bilinear Rescale雙線性重縮放
,Image Statistics影像統計
,Element-wise Arithmetic Operations元素運算子
). - 新的線性代數物件如
MPSVector
,MPSMatrix
和MPSTemporaryMatrix
,還有*BLAS-style matrix-matrix and matrix-vector multiplication - BLAS式的矩陣-矩陣及矩陣-向量乘法,
LAPACK-style triangular matrix factorization and linear solvers - LAPACK式三角矩陣分解與線性求解器`. - 一系列新的
CNN
基本體. Binary二進位制卷積
,XNOR同或卷積
,Dilated空洞卷積
,Sub-pixel子畫素卷積
和Transpose轉置卷積
的卷積層被新增到現有的Standard標準
卷積基本體內.- 新增了一個新的
Neural Network Graph神經網路圖形
API,在用過濾器和圖形節點來描述神經網路時非常有用. - 現在也有了
Recurrent Neural Networks迴圈神經網路
,它可以改善CNNs
一對一的侷限性,實現一對多和多對多的關係.
2). Argument Buffers引數緩衝器/變元緩衝器 - 可能是今年對框架最重要的新增.在傳統的增強模型中,我們會為每個物件呼叫許多函式來設定緩衝器,紋理,線性取樣,最後為該物件呼叫繪製命令.
正如你想象的那樣,,當將該數量乘以物體總數量及繪製幀數時,呼叫數急劇增長.最終這會限制螢幕上出現的物體數量.
Argument Buffers引數緩衝器
引入了一個高效的新途徑來使用資源,通過採用始終存在的indirect behavior間接行為,來將其應用到紋理,取樣,狀態,指向其它緩衝器的指標,等等.引數緩衝器將只有每物體2個API呼叫
:設定引數緩衝器,然後繪製.這樣可以繪製更多物體.
引數緩衝器很容易使用,就像匹配著色器資料和主機資料一樣:
struct Material {
float intensity;
texture2d<float> aTexture;
sampler aSampler;
}
kernel void compute(constant Material &material [[ buffer(0) ]]) {
...
}
複製程式碼
在CPU
上,引數緩衝器是被MTLArgumentEncoder物件建立和使用的,然後它可以輕易地在CPU
和GPU
之間做位塊傳送:
let function = library.makeFunction(name: "compute")
let encoder = function.makeIndirectArgumentEncoder(bufferIndex: 0)
encoder.setTexture(myTexture, index: 0)
encoder.constantData(at: 1).storeBytes(of: myPosition, as: float4)
複製程式碼
但是使用dynamic indexing動態索引
特性的話還可以更好.舉例,當渲染擁擠時,引數緩衝器陣列可以將所有例項(物體)的資料打包到一起.然後,不再需要每個物體呼叫兩次,改為每幀2個API呼叫
:一個設定到緩衝器,一個為大量例項繪製索引的基本體!
另一個應用引數緩衝器的例子是,當執行粒子模擬時.對此,我們有resource setting on the GPU
特性,它的意思是有一個引數緩衝器陣列,每個粒子(執行緒)一個緩衝器.所有的粒子特性(位置,材料,等)在GPU
上的引數緩衝器中被建立和儲存的,這樣當一個粒子需要某個屬性時,比如材料,它將從引數緩衝器中複製出來,而不再需要從CPU
獲取,這樣可以避免昂貴的CPU
與GPU
之間的複製開銷.
複製核心很簡單,可以讓你賦值一個常數,從源物件部分複製或者完全複製到目標物件:
kernel void reuse(constant Material &source [[ buffer(0) ]],
device Material &destination [[ buffer(1) ]]) {
destination.intensity = 0.5f;
destination.aTexture = source.aTexture;
destination = source;
}
複製程式碼
最後,還有一個應用例子是引用其它引數緩衝器(multiple indirections
).想象有一個instance結構體表示一個指向Material材料
結構體的例項(特徵),這樣的話就會有許多例項指向同一個材料.同樣的,想象另一個表示一個樹狀節點的結構體,其中每個節點
將會有一個指標指向Instance例項
結構體,起到節點中的一個例項陣列的作用:
struct Instance {
float4 position;
device Material *material;
}
struct Node {
device Instance *instances;
}
複製程式碼
注意:目前為止,只有
Tier 2
設定支援所有的引數緩衝器特性.從Metal 2
開始,GPU
裝置分成了Tier 1
(整體式)和Tier 2
(分離式).
3). Raster Order Groups光柵掃描順序組 - 一個新的片段著色器同步基本體,它在片段著色器訪問記憶體時允許對順序進行更精細的控制.例如,當使用自定義混合時,大部分圖形APIs
保證混合是按繪製呼叫順序發生的.然而,GPU
的並行執行緒需要一個方法來防止競爭條件.Raster Order Groups光柵掃描順序組
通過提供給我們一個隱含的Wait
命令來保證.
在傳統混合模式下,要建立競爭條件:
fragment void blend(texture2d<float, access::read_write> out[[ texture(0) ]]) {
float4 newColor = 0.5f;
// non-atomic memory access without any synchronization
float4 oldColor = out.read(position);
float4 blended = someCustomBlendingFunction(newColor, oldColor);
out.write(blended, position);
}
複製程式碼
需要做的就是新增Raster Order Groups
屬性到紋理(或資源)中來解決訪問衝突:
fragment void blend(texture2d<float, access::read_write>
out[[texture(0), raster_order_group(0)]]) {
float4 newColor = 0.5f;
// the GPU now waits on first access to raster ordered memory
float4 oldColor = out.read(position);
float4 blended = someCustomBlendingFunction(newColor, oldColor);
out.write(blended, position);
}
複製程式碼
4). ProMotion自適應重新整理率 - 目前只在iPad Pro螢幕上可用.沒有ProMotion
時典型的幀率是60
FPS(16.6
ms/frame):
使用ProMotion
後幀率提升到120
FPS(8.3
ms/frame),這對於使用者輸入非常有用,如手指觸控或pencil使用:
ProMotion
也提供了彈性機制來重新整理顯示圖片,所以我們不需要使用固定幀率.不使用ProMotion
會有圖片重新整理不一致,這對使用者體驗很不好.開發者為了實現一致性,通常會將峰值幀率強制保持在30
FPS,而不是理想的48
FPS(20.83
ms/frame):
使用ProMotion
我現在可以每4
ms就有一個重新整理點,而不再是每16
ms(豎直的白線):
ProMotion
同樣有助於掉幀.不使用ProMotion
時,可能一幀畫面花了太長時間來顯示,就錯過了截止期限:
ProMotion
通過對幀擴充套件4
ms而不是一個完整幀(16.6
ms)來解決這個問題:
UIKit
動畫自動使用ProMotion
,但是在Metal
檢視中使用ProMotion
,你需要在專案的Info.plist
檔案中設定一下,禁用最小幀率.然後你就可以使用3個顯示APIs
中的一個了.傳統的present(drawable:) 將會在GPU
結束渲染一幀畫面(在固定幀率螢幕上為16.6
ms,在ProMotion
螢幕上為4
ms)後直接呈現影像.第二個API
是present(drawable, afterMinimumDuration:) 在固定幀率螢幕上提供了幀與幀之間的最大間隔.第三個API
是present(drawable, atTime:),在建立自定義動畫迴圈時非常有用,或者試圖與其它輸出,比如音訊,同步時非常有用.這裡是如何實現它的例子:
let targetTime = 0.1
let drawable = metalLayer.nextDrawable()
commandBuffer.present(drawable, atTime: targetTime)
// after 1-2 frames
let presentationDelay = drawable.presentedTime - targetTime
複製程式碼
首先,當你想要展示畫面時,設定一個時間,然後渲染場景到一個命令緩衝器中.然後等待下一幀(下幾幀),最後檢驗延遲,這樣你就可以調整下一幀的時間.
5). Direct to Display直連螢幕 - 新的方式,用於將渲染器的內容以最小延遲直接傳送到外接顯示器(如,VR
中使用的頭戴顯示裝置).一個畫面在GPU
結束渲染後到最終出現在螢幕上之前會有兩條路徑可選.第一條是當系統將其它檢視和層混合起來形成最終影像時,典型的UI
方案:
當建立一個不包含混合,縮放或其它檢視/圖層的全屏應用程式時,第二條途徑允許螢幕直接訪問我們渲染的記憶體,這樣節省了大量系統資源,避免了很多開銷:
然而,這需要滿足下列條件:
- 圖層是不透明的
- 沒有遮罩或圓角
- 全屏,或帶有不透明的黑色狀態列和背景
- 被渲染尺寸最大和螢幕尺寸一樣大
- 顏色空間和畫素格式與螢幕相容
顏色空間的要求,使得判斷何時Direct to Display
模式能使用變得簡單.例如,如果你在使用P3
螢幕卻禁用了P3
模式,當試圖使用Direct to Display
模式時會很容易探測出來.
6). Other Features其它特性 - 包含但不限於:
- memory usage queries記憶體使用查詢 - 現在有了新的
APIs
可以在每次分配時查詢記憶體使用,還有裝置的總GPU
記憶體分配:
MTLResource.allocatedSize
MTLHeap.currentAllocatedSize
MTLDevice.currentAllocatedSize
複製程式碼
-
SIMDGroup scoped functions單指令多資料流(SIMD)群組作用域函式 - 允許資料在
SIMD
組內被註冊者共享,避免載入/儲存操作: -
non-uniform threadgroup sizes非一致性執行緒組尺寸 - 幫助我們不浪費
GPU
迴圈,避免遇到邊緣/邊界情況: -
Viewport Arrays視口陣列 - 在
macOS
上現在支援多達16
視口,以供頂點函式在渲染時選擇頂點,在VR
中結合例項非常有用. -
Multisample Pattern Control多重取樣型別控制 - 允許選擇在單個畫素中
MSAA
處於什麼取樣模式,對於自定義反走樣非常有用. -
Resource Heaps資源堆 - 現在在
macOS
上也可以使用了.它允許以下時間:控制記憶體分配,快速重新分配,資源混疊,快速繫結的組相關資源. -
其它特性,包括:
Feature特性 | Description描述 |
---|---|
Linear Textures線性紋理 | 從MTLBuffer 建立紋理,無需複製 |
Function Constant for Argument Indexes為引數索引的函式常量 | 特殊的二進位制編碼,供著色器引數改變繫結索引 |
Additional Vertex Array Formats附加頂點陣列格式 | 新增1個/2個元件的頂點格式,及一個BGRA8 頂點格式. |
IOSurface Textures IO表面紋理 | 在iOS 上從IOSurfaces 建立MTLTextures |
Dual Source Blending雙重來源混合 | 帶有兩源引數的附加混合模式 |
我已經為最重要的新特性製作了一張表,來說明在最新版本的作業系統上是否是新特性.
最後,還有幾行我寫的程式碼,來測試內建與外接GPU
之間的不同點:
所有圖片都來自WWDC
報告.
原始碼source code已釋出在Github上.
下次見!