本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.c++
今年的WWDC
多是有史以來最重要的,至少目前咱們 - Metal
開發者 - 終於被關注了.我能夠誠心誠意地講這是我生命中最棒的一週!github
讓咱們看看Games and Graphics遊戲和圖形新聞.最意想不到
的是,將Metal
重命名爲Metal 2.自從它在2014年
第一次被髮布後,它終於有了最顯著的增長和加強,確實,但咱們必須認可:沒人預見到這一變化的來臨.最意想不到
的獎品是新的ARKit框架.在keynote演示以後僅僅幾周,就已經有了大量大膽有趣的加強現實項目放出來. ARKit融入Metal
很是容易.最後,最有影響力
的獎品是VR.這是由於虛擬現實如今可以實現更低延遲,更高幀率,還有更強勁的內置顯卡,如今也有了外置顯卡external GPUs.swift
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參數緩衝器/變元緩衝器 - 多是今年對框架最重要的添加.在傳統的加強模型中,咱們會爲每一個對象調用許多函數來設置緩衝器,紋理,線性採樣,最後爲該對象調用繪製命令. app
正如你想象的那樣,,當將該數量乘以物體總數量及繪製幀數時,調用數急劇增加.最終這會限制屏幕上出現的物體數量. 框架
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其它特性 - 包含但不限於:
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上.
下次見!