鍍金池/ 教程/ iOS/ Metal
與四軸無人機的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應用數(shù)據(jù)腳本化
一個完整的 Core Data 應用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡應用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應式 Android 應用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學習
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學習的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應式視圖
Square Register 中的擴張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設計的藝術(shù)
導航應用
線程安全類的設計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標簽
同步案例學習
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

Metal

Metal 框架支持 GPU 加速高級 3D 圖像渲染,以及數(shù)據(jù)并行計算工作。Metal 提供了先進合理的 API,它不僅為圖形的組織、處理和呈現(xiàn),也為計算命令以及為這些命令相關(guān)的數(shù)據(jù)和資源的管理,提供了細粒度和底層的控制。Metal 的主要目的是最小化 GPU 工作時 CPU 所要的消耗。

Metal Programming Guide

Metal 是針對 iPhone 和 iPad 中 GPU 編程的高度優(yōu)化的框架。其名字來源是因為 Metal 是 iOS 平臺中最底層的圖形框架 (意指 "最接近硬件")。

該框架被設計用來實現(xiàn)兩個目標: 3D 圖形渲染和并行計算。這兩者有很多共同點。它們都在數(shù)量龐大的數(shù)據(jù)上并行運行特殊的代碼,并可以在 GPU. 上執(zhí)行。

什么人應該使用 Metal?

在談論 API 和語言本身之前,我們應該討論一下什么樣的開發(fā)者能從 Metal 中受益。正如上面提過的,Metal 提供兩個功能: 圖形渲染和并行計算。

對于尋找游戲引擎的開發(fā)者來說,Metal 不是最佳選擇。蘋果官方的的 Scene Kit (3D) 和 Sprite Kit (2D) 是更好的選擇。這些 API 提供了包括物理模擬在內(nèi)的更高級別的游戲引擎。另外還有功能更全面的 3D 引擎,例如 Epic 的 Unreal EngineUnity,二者都是跨平臺的。使用這些引擎,你無需直接使用 Metal 的 API,就可以從 Metal 中獲益。

編寫基于底層圖形 API 的渲染引擎時,除了 Metal 以外的其他選擇還有 OpenGL 和 OpenGL ES。OpenGL 不僅支持包括 OSX,Windows,Linux 和 Android 在內(nèi)的幾乎所有平臺,還有大量的教程,書籍和最佳實踐指南等資料。目前,Metal 的資源非常有限,并且僅限于搭載了 64 位處理器的 iPhone 和 iPad。但另外一方面,因為 OpenGL 的限制,其性能與 Metal 相比并不占優(yōu)勢,畢竟后者是專門用來解決這些問題的。

如果想要一個 iOS 上高性能的并行計算庫,答案非常簡單。Metal 是唯一的選擇。OpenCL 在 iOS 上是私有框架,而 Core Image (使用了 OpenCL) 對這樣的任務來說既不夠強大又不夠靈活。

使用 Metal 的好處

Metal 的最大好處就是與 OpenGL ES 相比顯著降低了消耗。在 OpenGL 中無論創(chuàng)建緩沖區(qū)還是紋理,OpenGL 都會復制一份以防止 GPU 在使用它們的時候被意外訪問。出于安全的原因復制類似紋理和緩沖區(qū)這樣的大的資源是非常耗時的操作。而 Metal 并不復制資源。開發(fā)者需要負責在 CPU 和 GPU 之間同步訪問。幸運的是,蘋果提供了另一個很棒的 API 使資源同步訪問更加容易,那就是 Grand Central Dispatch。雖然使用 Metal 時仍然有些這方面的問題需要注意,但是一個在渲染時加載和卸載資源的先進的引擎,在避免額外的復制后能夠獲得更多的好處。

Metal 的另外一個好處是其預估 GPU 狀態(tài)來避免多余的驗證和編譯。通常在 OpenGL 中,你需要依次設置 GPU 的狀態(tài),在每個繪制指令 (draw call) 之前需要驗證新的狀態(tài)。最壞的情況是 OpenGL 需要再次重新編譯著色器 (shader) 以反映新的狀態(tài)。當然,這種評估是必要的,但 Metal 選擇了另一種方法。在渲染引擎初始化過程中,一組狀態(tài)被烘焙 (bake) 至預估渲染的 路徑 (pass) 中。多個不同資源可以共同使用該渲染路徑對象,但其它的狀態(tài)是恒定的。Metal 中一個渲染路徑無需更進一步的驗證,使 API 的消耗降到最低,從而大大增加每幀的繪制指令的數(shù)量。

Metal API

雖然這個平臺上許多 API 都暴露為具體的類,但 Metal 提供的大多是協(xié)議。因為 Metal 對象的具體類型取決于 Metal 運行在哪個設備上。這更鼓勵了面向接口而不是面向?qū)崿F(xiàn)編程。然而,這同時也意味著,如果不使用 Objective-C 運行時的廣泛而危險的操作,就不能子類化 Metal 的類或者為其增加擴展,

Metal 為了速度而在安全性上做了必要的妥協(xié)。對于錯誤,蘋果的其它框架顯得更加安全和健壯,而 Metal 則完全相反。在某些時候,你會收到指向內(nèi)部緩沖區(qū)的裸指針,你必須小心的同步訪問它。OpenGL 中發(fā)生錯誤時,結(jié)果通常是黑屏;然而在 Metal 中,結(jié)果可能是完全隨機的效果,例如閃屏和偶爾的崩潰。之所以有這些陷阱,是因為 Metal 框架是對 GPU 的非常輕量級抽象。

一個有趣的方面是蘋果并沒有為 Metal 實現(xiàn)可以在 iOS 模擬器上使用的軟件渲染。使用 Metal 框架的時候應用必須運行在真實設備上。

基礎(chǔ) Metal 程序

在這部分中,我們會介紹寫出第一個 Metal 程序所必要的部分。這個簡單的程序繪制了一個正方形的旋轉(zhuǎn)。你可以在 GitHub 中下載這篇文章的示例代碼。

雖然不能涵蓋每一個細節(jié),但我們盡量涉及至少所有的移動部分。你可以閱讀源代碼和參閱線上資源來深入理解。

使用 UIKit 創(chuàng)建設備和界面

在 Metal 中,設備是 GPU 的抽象。它被用來創(chuàng)建很多其它類型的對象,例如緩沖區(qū),紋理和函數(shù)庫。使用 MTLCreateSystemDefaultDevice 函數(shù)來獲取默認設備:

id<MTLDevice> device = MTLCreateSystemDefaultDevice();

注意 device 并不是一個詳細具體的類,正如前面提到的,它是遵循 MTLDevice 協(xié)議的類。

下面的代碼展示了如何創(chuàng)建一個 Metal layer 并將它作為 sublayer 添加到一個 UIView 的 layer:

CAMetalLayer *metalLayer = [CAMetalLayer layer];
metalLayer.device = device;
metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
metalLayer.frame = view.bounds;
[view.layer addSublayer:self.metalLayer];

CAMetalLayerCALayer 的子類,它可以展示 Metal 幀緩沖區(qū)的內(nèi)容。我們必須告訴 layer 該使用哪個 Metal 設備 (我們剛創(chuàng)建的那個),并通知它所預期的像素格式。我們選擇 8-bit-per-channel BGRA 格式,即每個像素由藍,綠,紅和透明組成,值從 0-255。

庫和函數(shù)

你的 Metal 程序的很多功能會被用頂點和片段函數(shù)的方式書寫,也就是我們所說的著色器。Metal 著色器用 Metal 著色器語言編寫,我們將在下面詳細討論。Metal 的優(yōu)點之一就是著色器函數(shù)在你的應用構(gòu)建到中間語言時進行編譯,這可以節(jié)省很多應用啟動時所需的時間。

一個 Metal 庫是一組函數(shù)的集合。你的所有寫在工程內(nèi)的著色器函數(shù)都將被編譯到默認庫中,這個庫可以通過設備獲得:

id<MTLLibrary> library = [device newDefaultLibrary]

接下來構(gòu)建渲染管道狀態(tài)的時候?qū)⑹褂眠@個庫。

命令隊列

命令通過與 Metal 設備相關(guān)聯(lián)的命令隊列提交給 Metal 設備。命令隊列以線程安全的方式接收命令并順序執(zhí)行。創(chuàng)建一個命令隊列:

id<MTLCommandQueue> commandQueue = [device newCommandQueue];

構(gòu)建管道

當我們在 Metal 編程中提到管道,指的是頂點數(shù)據(jù)在渲染時經(jīng)歷的變化。頂點著色器和片段著色器是管道中兩個可編程的節(jié)點,但還有其它一定會發(fā)生的事件 (剪切,柵格化和視圖變化) 不在我們的直接控制之下。管道特性中的后者的類組成了固定功能管道。

在 Metal 中創(chuàng)建一個管道,我們需要指定對于每個頂點和每個像素分別想要執(zhí)行哪個頂點和片段函數(shù) (譯者注: 片段著色器又被稱為像素著色器)。我們還需要將幀緩沖區(qū)的像素格式告訴管道。在本例中,該格式必須與 Metal layer 的格式匹配,因為我們想在屏幕上繪制。

從庫中通過名字來獲取函數(shù):

id<MTLFunction> vertexProgram = [library newFunctionWithName:@"vertex_function"];
id<MTLFunction> fragmentProgram = [library newFunctionWithName:@"fragment_function"];

接下來創(chuàng)建一個設置了函數(shù)和像素格式的管道描述器:

MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
[pipelineStateDescriptor setVertexFunction:vertexProgram];
[pipelineStateDescriptor setFragmentFunction:fragmentProgram];
pipelineStateDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;        

最后,我們從描述器中創(chuàng)建管道狀態(tài)。這會根據(jù)程序運行的硬件環(huán)境,從中間代碼中編譯著色器函數(shù)為優(yōu)化后的代碼。

id<MTLRenderPipelineState> pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:nil];

讀取數(shù)據(jù)到緩沖區(qū)

現(xiàn)在已經(jīng)有了一個構(gòu)建好的管道,我們需要用數(shù)據(jù)填充它。在示例工程中,我們繪制了一個簡單的幾何圖形: 一個旋轉(zhuǎn)的正方形。正方形由兩個共享一條邊的直角三角形組成:

static float quadVertexData[] =
{
     0.5, -0.5, 0.0, 1.0,     1.0, 0.0, 0.0, 1.0,
    -0.5, -0.5, 0.0, 1.0,     0.0, 1.0, 0.0, 1.0,
    -0.5,  0.5, 0.0, 1.0,     0.0, 0.0, 1.0, 1.0,

     0.5,  0.5, 0.0, 1.0,     1.0, 1.0, 0.0, 1.0,
     0.5, -0.5, 0.0, 1.0,     1.0, 0.0, 0.0, 1.0,
    -0.5,  0.5, 0.0, 1.0,     0.0, 0.0, 1.0, 1.0,
};  

每一行的前四個數(shù)字代表了每一個頂點的 x,y,z 和 w 元素。后四個數(shù)字代表每個頂點的紅色,綠色,藍色和透明值元素。

你可能會奇怪為什么需要四個數(shù)字來描述 3D 空間中的一個位置。第四個頂點位置元素,w,是一個數(shù)學上的便利,使我們能以一種統(tǒng)一的方式描述 3D 轉(zhuǎn)換 (旋轉(zhuǎn),平移,縮放)。這個細節(jié)在本文的示例代碼并沒有體現(xiàn)。

為了使用 Metal 繪制頂點數(shù)據(jù),我們需要將它放入緩沖區(qū)。緩沖區(qū)是被 CPU 和 GPU 共享的簡單的無結(jié)構(gòu)的內(nèi)存塊:

vertexBuffer = [device newBufferWithBytes:quadVertexData
                                       length:sizeof(quadVertexData)
                                      options:MTLResourceOptionCPUCacheModeDefault];

我們將使用另一個緩沖區(qū)來存儲用來旋轉(zhuǎn)正方形的旋轉(zhuǎn)矩陣。與預先提供數(shù)據(jù)不同,這里只是通過創(chuàng)建規(guī)定長度的緩沖區(qū)來創(chuàng)建一個空間。

uniformBuffer = [device newBufferWithLength:sizeof(Uniforms) 
                                        options:MTLResourceOptionCPUCacheModeDefault];

動畫

為了在屏幕上旋轉(zhuǎn)正方形,我們需要把轉(zhuǎn)換頂點作為頂點著色器的一部分。這需要更新每一幀的統(tǒng)一緩沖區(qū)。我們運用三角學知識,從當前旋轉(zhuǎn)角度生成一個旋轉(zhuǎn)矩陣,將它復制到統(tǒng)一緩沖區(qū)。

Uniforms 結(jié)構(gòu)體只有一個成員,該成員是一個保存了旋轉(zhuǎn)矩陣的 4x4 的矩陣。矩陣類型 matrix_float4x4 來自于蘋果的 SIMD 庫,該庫是一個類型的集合,它們可以從 數(shù)據(jù)并行操作 中獲益:

typedef struct
{
    matrix_float4x4 rotation_matrix;
} Uniforms;

為了將旋轉(zhuǎn)矩陣復制到統(tǒng)一緩沖區(qū)中,我們?nèi)〉盟膬?nèi)容的指針并將矩陣 memcpy 進去:

Uniforms uniforms;
uniforms.rotation_matrix = rotation_matrix_2d(rotationAngle);
void *bufferPointer = [uniformBuffer contents];
memcpy(bufferPointer, &uniforms, sizeof(Uniforms));

準備繪制

為了在 Metal layer 上繪制,首先我們需要從 layer 中獲得一個 'drawable' 對象。這個可繪制對象管理著一組適合渲染的紋理:

id<CAMetalDrawable> drawable = [metalLayer nextDrawable];

接下來我們創(chuàng)建一個渲染路徑描述器,它描述了在渲染之前和完成之后 Metal 應該執(zhí)行的不同動作。下面我們展示了一個渲染路徑,它將首先把幀緩沖區(qū)清除為純白色,然后執(zhí)行繪制指令,最后將結(jié)果存儲到幀緩沖區(qū)來展示:

MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = drawable.texture;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1, 1, 1, 1);
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;

發(fā)布繪制指令

要放入設備的命令隊列的命令必須被編碼到命令緩沖區(qū)里。命令緩沖區(qū)是一個或多個命令的集合,可以以一種 GPU 了解的緊湊的方式執(zhí)行和編碼。

id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];

為了真正編碼渲染命令,我們還需要另一個知道如何將我們的繪制指令轉(zhuǎn)換為 GPU 懂得的語言的對象。這個對象叫做命令編碼器。我們將上面創(chuàng)建的渲染路徑描述器作為參數(shù)傳入,就可以向命令緩沖區(qū)請求一個編碼器:

id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

在繪制指令之前,我們使用預編譯的管道狀態(tài)設置渲染命令編碼器并建立緩沖區(qū),該緩沖區(qū)將作為頂點著色器的參數(shù):

[renderEncoder setRenderPipelineState:pipelineState];
[renderEncoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[renderEncoder setVertexBuffer:uniformBuffer offset:0 atIndex:1];

為了真正的繪制幾何圖形,我們告訴 Metal 要繪制的形狀 (三角形) 和緩沖區(qū)中頂點的數(shù)量 (本例中 6 個):

[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];

最后,執(zhí)行 endEncoding 通知編碼器發(fā)布繪制指令完成。

[renderEncoder endEncoding];

展示幀緩沖區(qū)

現(xiàn)在我們的繪制指令已經(jīng)被編碼并準備就緒,我們需要通知命令緩沖區(qū)應該將結(jié)果在屏幕上顯示出來。調(diào)用 presentDrawable,使用當前從 Metal layer 中獲得的 drawable 對象作為參數(shù):

[commandBuffer presentDrawable:drawable];

執(zhí)行 commit 告訴緩沖區(qū)已經(jīng)準備好安排并執(zhí)行:

[commandBuffer commit];

就這么多!

Metal 著色語言

雖然 Metal 和 Swift 是在 WWDC keynote 上被一同發(fā)表的,但著色語言是基于 C++11 的,有一些有限制的特性和增加的關(guān)鍵字。

Metal 著色語言實踐

為了在著色器里使用頂點數(shù)據(jù),我們定義了一個對應 Objective-C 中頂點數(shù)據(jù)的結(jié)構(gòu)體:

typedef struct
{
    float4 position;
    float4 color;
} VertexIn;

我們還需要一個類似的結(jié)構(gòu)體來描述從頂點著色器傳入片段著色器的頂點類型。然而,在本例中,我們必須區(qū)分 (通過使用 [[position]] 屬性) 哪一個結(jié)構(gòu)體成員應該被看做是頂點位置:

typedef struct {
 float4 position [[position]];
 float4 color;
} VertexOut;

頂點函數(shù)在頂點數(shù)據(jù)中每個頂點被執(zhí)行一次。它接收頂點列表的一個指針,和一個包含旋轉(zhuǎn)矩陣的統(tǒng)一數(shù)據(jù)的引用。第三個參數(shù)是一個索引,用來告訴函數(shù)當前操作的是哪個頂點。

注意頂點函數(shù)的參數(shù)后面緊跟著標明它們用途的屬性。在緩沖區(qū)參數(shù)中,參數(shù)中的索引對應著我們在渲染命令編碼器中設置緩沖區(qū)時指定的索引。Metal 就是這樣來區(qū)分哪個參數(shù)對應哪個緩沖區(qū)。

在頂點函數(shù)中,我們用頂點的位置乘以旋轉(zhuǎn)矩陣。我們構(gòu)建矩陣的方式?jīng)Q定了效果是圍繞中心旋轉(zhuǎn)正方形。接著我們將這個轉(zhuǎn)換過的位置傳入輸出頂點。頂點顏色則從輸入?yún)?shù)中直接復制。

vertex VertexOut vertex_function(device VertexIn *vertices [[buffer(0)]],
                                     constant Uniforms &uniforms [[buffer(1)]],
                                     uint vid [[vertex_id]])
{
    VertexOut out;
    out.position = uniforms.rotation_matrix * vertices[vid].position;
    out.color = vertices[vid].color;
    return out;
}

片段函數(shù)每個像素就會被執(zhí)行一次。Metal 在 rasterization 過程中會通過在每個頂點中指定的位置和顏色參數(shù)中添加來生成參數(shù)。在這個簡單的片段函數(shù)中,我們只是簡單的返回了 Metal 添加的顏色。這會成為屏幕像素的顏色:

fragment float4 fragment_function(VertexOut in [[stage_in]])
{
    return in.color;
}

為什么不干脆擴展 OPENGL?

蘋果是 OpenGL 架構(gòu)審查委員會的成員,并且歷史上也在 iOS 上提供過它們自己的 GL 擴展。但從內(nèi)部改變 OpenGL 看起來是個困難的任務,因為它有著不同的設計目標。實際上,它必須有廣泛的硬件兼容性以運行在很多不同的設備上。雖然 OpenGL 還在持續(xù)發(fā)展,但速度緩慢。

而 Metal 則本來就是只為了蘋果的平臺而創(chuàng)建的。即使基于協(xié)議的 API 最初看起來不太常見,但和其它框架配合的很好。Metal 是用 Objective-C 編寫的,基于 Foundation,使用 GCD 在 CPU 和 GPU 之間保持同步。它是更先進的 GPU 管道的抽象,而 OpenGL 想達到這些的話只能完全重寫。

Mac 上的 Metal?

OS X 上支持 Metal 也是遲早的事。API 本身并不局限于 iPhone 和 iPad 使用的 ARM 架構(gòu)的處理器。Metal 的大多數(shù)優(yōu)點都可以移植到先進的 GPU 上。另外,iPhone 和 iPad 的 CPU 和 GPU 是共享內(nèi)存的,無需復制就可以交換數(shù)據(jù)。這一代的 Mac 電腦并未提供共享內(nèi)存,但這只是時間問題?;蛟S,API 會被調(diào)整為支持使用了專門內(nèi)存的架構(gòu),或者 Metal 只會運行在下一代的 Mac 電腦上。

總結(jié)

本文中我們嘗試給出公正并有所幫助的關(guān)于 Metal 框架的介紹。

當然,大多數(shù)的游戲開發(fā)者并不會直接使用 Metal。然而,頂層的游戲引擎已經(jīng)從中獲益,并且開發(fā)者無需直接使用 API 就可以從最新的技術(shù)中得到好處。另外,對于那些想要發(fā)揮硬件全部性能的開發(fā)者來說,Metal 或許可以讓他們在游戲中創(chuàng)建出與眾不同而華麗的效果,或者進行更快的并行計算,從而得到競爭優(yōu)勢。

資源

上一篇:Build 工具下一篇:初識 TextKit