[MetalKit]36-Introducing-Metal-2介紹Metal2

蘋果API搬運工發表於2017-12-14

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

MetalKit系統文章目錄


今年的WWDC可能是有史以來最重要的,至少目前我們 - Metal開發者 - 終於被關注了.我可以誠心誠意地講這是我生命中最棒的一週!

讓我們看看Games and Graphics遊戲和圖形新聞.最意想不到的是,將Metal重新命名為Metal 2.自從它在2014年第一次被髮布後,它終於有了最顯著的增加和增強,確實,但我們必須承認:沒人預見到這一變化的來臨.最意想不到的獎品是新的ARKit框架.在keynote演示之後僅僅幾周,就已經有了大量大膽有趣的擴增實境專案放出來. ARKit融入Metal非常容易.最後,最有影響力的獎品是VR.這是因為虛擬現實現在能夠實現更低延遲,更高幀率,還有更強勁的內建顯示卡,現在也有了外接顯示卡external GPUs.

vr.png

Model I/O, SpriteKitSceneKit框架也都新增了新特性.其它令人感興趣的增加是用於機器學習 machine learningCoreMLVision框架.本文只關注Metal中的新特色:

1). MPS - Metal Performance Shaders目前在macOS上也可以使用了,新增的新特性包括:

  • 四種新的圖片處理基本體(Image Keypoints影像關鍵點, Bilinear Rescale雙線性重縮放, Image Statistics影像統計, Element-wise Arithmetic Operations元素運算子).
  • 新的線性代數物件如MPSVector, MPSMatrixMPSTemporaryMatrix,還有*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引數緩衝器/變元緩衝器 - 可能是今年對框架最重要的新增.在傳統的增強模型中,我們會為每個物件呼叫許多函式來設定緩衝器,紋理,線性取樣,最後為該物件呼叫繪製命令.

ArgumentBuffers1.png

正如你想象的那樣,,當將該數量乘以物體總數量及繪製幀數時,呼叫數急劇增長.最終這會限制螢幕上出現的物體數量.

ArgumentBuffers2.png

Argument Buffers引數緩衝器引入了一個高效的新途徑來使用資源,通過採用始終存在的indirect behavior間接行為,來將其應用到紋理,取樣,狀態,指向其它緩衝器的指標,等等.引數緩衝器將只有每物體2個API呼叫:設定引數緩衝器,然後繪製.這樣可以繪製更多物體.

ArgumentBuffers3.png

引數緩衝器很容易使用,就像匹配著色器資料和主機資料一樣:

struct Material {
    float intensity;
    texture2d<float> aTexture;
    sampler aSampler;
}

kernel void compute(constant Material &material [[ buffer(0) ]]) {
    ...
}
複製程式碼

CPU上,引數緩衝器是被MTLArgumentEncoder物件建立和使用的,然後它可以輕易地在CPUGPU之間做位塊傳送:

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呼叫:一個設定到緩衝器,一個為大量例項繪製索引的基本體!

ArgumentBuffers4.png

另一個應用引數緩衝器的例子是,當執行粒子模擬時.對此,我們有resource setting on the GPU特性,它的意思是有一個引數緩衝器陣列,每個粒子(執行緒)一個緩衝器.所有的粒子特性(位置,材料,等)在GPU上的引數緩衝器中被建立和儲存的,這樣當一個粒子需要某個屬性時,比如材料,它將從引數緩衝器中複製出來,而不再需要從CPU獲取,這樣可以避免昂貴的CPUGPU之間的複製開銷.

ArgumentBuffers5.png

複製核心很簡單,可以讓你賦值一個常數,從源物件部分複製或者完全複製到目標物件:

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命令來保證.

RasterOrderGroups.png

在傳統混合模式下,要建立競爭條件:

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時典型的幀率是60FPS(16.6ms/frame):

promotion1.png

使用ProMotion後幀率提升到120FPS(8.3ms/frame),這對於使用者輸入非常有用,如手指觸控或pencil使用:

promotion2.png

ProMotion也提供了彈性機制來重新整理顯示圖片,所以我們不需要使用固定幀率.不使用ProMotion會有圖片重新整理不一致,這對使用者體驗很不好.開發者為了實現一致性,通常會將峰值幀率強制保持在30FPS,而不是理想的48FPS(20.83ms/frame):

promotion3.png

使用ProMotion我現在可以每4ms就有一個重新整理點,而不再是每16ms(豎直的白線):

promotion4.png

ProMotion同樣有助於掉幀.不使用ProMotion時,可能一幀畫面花了太長時間來顯示,就錯過了截止期限:

promotion5.png

ProMotion通過對幀擴充套件4ms而不是一個完整幀(16.6ms)來解決這個問題:

promotion6.png

UIKit動畫自動使用ProMotion,但是在Metal檢視中使用ProMotion,你需要在專案的Info.plist檔案中設定一下,禁用最小幀率.然後你就可以使用3個顯示APIs中的一個了.傳統的present(drawable:) 將會在GPU結束渲染一幀畫面(在固定幀率螢幕上為16.6ms,在ProMotion螢幕上為4ms)後直接呈現影像.第二個APIpresent(drawable, afterMinimumDuration:) 在固定幀率螢幕上提供了幀與幀之間的最大間隔.第三個APIpresent(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方案:

DirectToDisplay1.png

當建立一個不包含混合,縮放或其它檢視/圖層的全屏應用程式時,第二條途徑允許螢幕直接訪問我們渲染的記憶體,這樣節省了大量系統資源,避免了很多開銷:

DirectToDisplay2.png

然而,這需要滿足下列條件:

  • 圖層是不透明的
  • 沒有遮罩或圓角
  • 全屏,或帶有不透明的黑色狀態列和背景
  • 被渲染尺寸最大和螢幕尺寸一樣大
  • 顏色空間和畫素格式與螢幕相容

顏色空間的要求,使得判斷何時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組內被註冊者共享,避免載入/儲存操作:

    SIMDGroup.png

  • non-uniform threadgroup sizes非一致性執行緒組尺寸 - 幫助我們不浪費GPU迴圈,避免遇到邊緣/邊界情況:

    nonuniform.png

  • 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雙重來源混合 帶有兩源引數的附加混合模式

我已經為最重要的新特性製作了一張表,來說明在最新版本的作業系統上是否是新特性.

features.png

最後,還有幾行我寫的程式碼,來測試內建與外接GPU之間的不同點:

gpuCompare.png

所有圖片都來自WWDC報告. 原始碼source code已釋出在Github上.

下次見!

相關文章