WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

Enum發表於2019-03-04

Session 604 : Metal for OpenGL Developers

關於作者:可以在這裡找到一些關於我的資訊。

引言

Metal 是 Apple 開發的一款圖形引擎。本文將對比 OpenGL,詳細介紹 Metal 的物件模型以及開發思想,旨在幫助 OpenGL 開發者更容易地轉向 Metal 開發。

由於 Metal 與 OpenGL 同為底層圖形引擎,因此閱讀本文需要一定的圖形基礎。本文假定讀者已經具備一定圖形學知識並對 OpenGL 熟悉。

為何選擇 Metal

對於廣大圖形開發者來說,有著非常多的工具可供選擇。

上層框架

對於普通的 2D、3D 圖形開發者來說,有 Apple 原生的SpriteKitSceneKit等框架,而對於遊戲開發者來說,有Unity虛幻等強大的第三方遊戲引擎。

在條件允許的情況下,開發者們應該儘可能使用上層框架進行開發,以專注於業務,遮蔽圖形學細節。以上上層框架應是開發者們的首選。

OpenGL

但在某些特定場景如要求跨平臺、包大小限制等場景下,開發者們可能不得不使用 OpenGL 來開發。因為 OpenGL 跨平臺,效能佳,且不佔用過大的包大小等優點,使 OpenGL 至今仍然廣泛被使用。

但有著超過25年曆史的 OpenGL 技術本身,隨著現代圖形技術的發展,遇到了一些問題:

  • 現代 GPU 的渲染管線已經發生變化。
  • 不支援多執行緒操作。
  • 不支援非同步處理。

隨著圖形學的發展,OpenGL 本身設計上存在的問題已經影響了 GPU 真正效能的發揮,因此 Apple 設計了 Metal。

Metal

為了解決這些問題,Metal 誕生了。

它為現代 GPU 設計,並面向 OpenGL 開發者。它擁有:

  • 更高效的 GPU 互動,更低的 CPU 負荷。
  • 支援多執行緒操作,以及執行緒間資源共享能力。
  • 支援資源和同步的控制。

Metal 簡化了 CPU 參與渲染的步驟,儘可能地讓 GPU 去控制資源。與此同時,擁有更現代的設計,使操作處於可控,結果可預測的狀態。在優化設計的同時,它仍然是一個直接訪問硬體的框架。與 OpenGL 相比,它更加接近於 GPU,以獲得更好的效能。

小結

古老的 OpenGL 已經無法適應現代圖形技術的發展,而 Metal 為現代圖形技術而設計,是 OpenGL 的優良替代品。

Apple 早在 2014 年就推出了 Metal,經過四年的鋪墊,於今年 WWDC 祭出了大殺器:

  • OpenGL 和 OpenCL 將於 macOS 10.14 棄用。
  • OpenGL ES 將於 iOS 12 棄用。

雖然目前 API 還能夠使用,但是被標記棄用的 API 很可能會在未來的某一刻被永遠抹去。

因此,是時候開始使用 Metal 了。

Metal 的物件模型

在 OpenGL 中,所有資源如 Buffer,Texture 等都依附於一個上下文(Context)。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

而在 Metal 中,情況則完全不同。Metal 使用一系列更小,職責更加清晰的物件去分別管理各類資源,開發者們從物件名中一眼就能認出他的職責。

Metal Device

Metal Device 可以看做是 GPU 的入口,從此為入口可以去生成,操作更加具體的資源和物件。

由 Metal Device 可以繼續構造出 Texture、Buffer 和 Pipeline(Pipeline 中包含了著色器程式)等資源物件。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

熟悉 OpenGL 的讀者會發現在 OpenGL 中也存在這些概念。它們的用途是大同小異的,但在構造和管理等的工程設計上有很大的區別。

Metal 資源物件

紋理(Texture)、緩衝區(Buffer)等資源物件將直接從 Metal Device 物件建立,建立以後,物件是不可變的,但內部的影象資料是可變的。

渲染管線(Render Pipeline)、深度模板(Depth Stencil)等物件通過狀態描述(Descriptor)建立,物件以及內部資料都是不可變的。

由於不可變物件的存在,使得 Metal 在只需要在建立物件時檢查一次物件即可。而 OpenGL 在每次繪製以前都需要檢查物件是否發生變化,在這一點上 Metal 將會獲得更好的效能。

除此之外,由於不可變物件的存在,在多執行緒中,Metal 不需要使用執行緒鎖,因此也會得到更好的效能。

Metal 命令系統

Metal 將渲染進一步抽象成了命令,以命令和命令佇列的形式進行管理。

命令佇列

上文中提到的 Metal Device,可以生成一個命令佇列(Command Queue)。這個命令佇列由 Metal 自行維護,而開發者只需要往這個佇列裡面丟命令就可以了。

命令

Metal 中的命令(Command Buffer)是 GPU 任務的抽象封裝,近似於OpenGL 中的一次繪製呼叫(draw call)。如果讀者閱讀過 cocos2d-x 的原始碼的話就會發現,cocos2d-x 的渲染系統也進行了類似的封裝,以便於進行合併、批量回執等優化操作。

Metal 中的命令分為四種型別:

  • 渲染命令(Render Command):渲染影象的命令。
  • 塊傳輸命令(Blit Command):紋理和緩衝區進行復制的命令。
  • 計算命令(Compute Command):GPU 平行計算的命令。
  • 並行渲染命令(Parallel Render Command):併發的渲染命令。

這些命令被新增到命令佇列以後,Metal 會自行按順序執行命令。

命令編碼器

命令如何被建立呢,可以通過命令編碼器(Command Buffer Encoder)建立。上文提到命令有四種型別,所以命令編碼器也有四種型別,分別編碼四個型別的命令。命令編碼階段完全由 CPU 負責。

在命令編碼器中,開發者可以具體設定命令的各項引數,並最終生成命令物件交於命令佇列。

小結

Metal 的物件模型在設計上有許多優於 OpenGL 的地方。例如不可變物件的存在可以簡化多執行緒操作以及節約物件檢查的時間,命令系統的存在使渲染系統能更好地進行優化。

除了這些,Metal 還擁有優秀的物件導向封裝。相比於 API 晦澀,到處使用數字控制程式碼的 OpenGL,在開發和維護的效率上都有質的飛躍。

在瞭解了 Metal 的物件模型以後,就可以開始實戰了。

以 OpenGL 程式移植為例,來具體體驗一下 Metal 的實戰。

Metal 實戰

本節將會分構建時、初始化時和渲染時這三個階段來講。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

構建時

在程式編譯構建之時,Metal 的著色器程式將會被提前編譯。

Metal 著色器語言

Metal 所使用的著色器語言 Metal SL 是一套基於 C++ 擴充套件的語言。class、namespace、enum 等 C++ 中的特性都可以應用在 Metal 的著色器中,甚至還可以使用 template。相比基於C語言擴充套件的 OpenGL SL,可謂是質的改變。

當然,毫無疑問的,向量矩陣運算,圖形相關的類必然也是內建好的,下面來具體看看 Metal 的著色器程式該怎麼寫。

渲染時所需的頂點著色器:


vertex VertexOutput myVertexShader(uint vid [[ vertex_id ]],
                                   device Vertex * vertices [[ buffer(0) ]],
                                   constant Uniforms & uniforms [[ buffer(1) ]])
{
    VertexOutput out;
    out.clipPos = vertices[vid].modelPos * uniforms.mvp;
    out.texCoord = vertices[vid].texCoord;
    return out;
}
複製程式碼

vertex字首代表這是一個頂點著色器,VertexOutput是函式的返回值,這是一個自定義的結構體,具體結構暫且先不管。myVertexShader為函式名,後面跟的是引數。

這個函式有兩個引數,一個是uint型別的引數名為vid,而後面跟的[[ vertex_id ]]是引數的控制程式碼。

這是一個新的概念,Metal 給每個引數擴充套件了一個控制程式碼,這和 OpenGL 類似。每個引數會有個控制程式碼,在 CPU 往 GPU 傳遞引數時需要這個對應的控制程式碼才可以傳過來。那麼這裡vid引數的控制程式碼為vertex_id,這是一個內建的控制程式碼,表示繪製時頂點的索引數。

第二個引數為型別為Vertex指標,Vertex也是個自定義結構體,具體內容暫且不管。它是一個結構體指標,控制程式碼為[[ buffer(0) ]]。在 Metal 中,不同型別的引數的控制程式碼是分開計算的。vertices引數的控制程式碼為buffer0

同理,下面是個片元著色器函式:


fragment float4 myFragmentShader(VertexOutput in [[ stage_in ]],
                                 constant Uniforms & uniforms [[ buffer(3) ]],
                                 texture2d<float> colorTex [[ texture(0) ]],
                                 sampler texSampler [[ sampler(1) ]])
{
    return colorTex.sample(texSampler, in.texCoord * uniforms.coordScale);
}

複製程式碼

它的輸入時頂點著色器的輸出,且擁有紋理、取樣器等引數。返回值是四維浮點陣列,即該片元的顏色值。

下面來看看這兩個自定義的結構:

struct Vertex
{
    float4 modelPos; 
    float2 texCoord;
};

struct VertexOutput
{
    float4 clipPos [[position]];
    float2 texCoord;
};
複製程式碼

Vertex由 CPU 輸入,獲得模型的三維座標以及貼圖的對映座標,經過頂點著色器處理之後輸出VertexOutput,這個結構作為片元著色器的輸入,進入片元著色器計算片元顏色。這個流程與傳統的 OpenGL 相同。

結構中包含內簡控制程式碼position的引數,片元著色器的輸入結構中必須包含有此控制程式碼的引數,否則會無法通過編譯。

使用著色器程式

有了上文中的著色器程式,使用時如何傳遞引數給著色器程式呢?

在 OpenGL 中,開發者需要首先要根據引數名獲得引數控制程式碼,然後利用控制程式碼進行引數傳遞(高版本的 OpenGL 也支援通過 layout 寫死控制程式碼),而使用 Metal 時,是在編碼器編碼階段通過編碼器和控制程式碼直接進行引數傳遞。

[renderEncoder setFragmentBuffer:myUniformBuffer offset:0 atIndex:0];
[renderEncoder setFragmentTexture:myColorTexture atIndex:0];
[renderEncoder setFragmentSampler:mySampler atIndex:1];
複製程式碼

函式中使用的 index 即是該引數的控制程式碼。通過編碼器的這一系列操作,已經能夠將引數正確傳遞到著色器程式中了。

SIMD

SIMD 是 Apple 提供的一款方便原生程式與著色器程式共享資料結構的庫。

開發者可以在標頭檔案中定義一系列結構,在原生程式碼和著色器程式中通過#include包含這個標頭檔案,兩者就都有了這個結構的定義。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

使用 SIMD 能最大程度減少由於結構 layout 上的不同引發的問題。

其他

Metal 的著色器程式在編譯時會被編譯成型別為metallib的檔案,在這個檔案中,著色器程式並沒有真正被編譯成二進位制,而是隻經過了編譯器前端的中間態。在執行時會被真正編譯成二進位制,這一步僅需要完全編譯的時間的一半。

當然,Metal 也支援在執行時編譯著色器原始碼,但 Apple 並不支援這麼做。這樣許多問題無法在編譯時定位,不方便開發與維護。

小結

Metal 使用了比 OpenGL 更為高階的著色器語言,並在編譯時編譯著色器程式碼以快速暴露錯誤,以及 SIMD 等工具庫可以幫助開發者們更快速地開發與維護圖形程式碼。

初始化時

在 Metal 初始化時,需要根據上文提到的物件模型,構造一系列物件。

Device

Device 象徵著一個 GPU,所有紋理、緩衝區等都基於這個物件產生。

id<MTLDevice> device = MTLCreateSystemDefaultDevice();
複製程式碼

在 iOS 中,只有一個 GPU,因此只會有一個MTLDevice物件,在 macOS 中,多塊顯示卡就會帶來多 GPU,即多個MTLDevice物件。

Command Queue

id<MTLCommandQueue> commandQueue = [device newComandQueue];
複製程式碼

Texture

建立 Texture 時,將使用一個 TextureDescriptor 物件。它包含了一系列 Texture 所需要的屬性,並使用這個 Descriptor,從 Device 建立一個 Texture Object 物件。Texture Object 會管理一塊記憶體,真正存放紋理的資料。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

對於真正存放紋理資料的記憶體,開發者選擇儲存模式以控制 CPU 和 GPU 如何管理這片記憶體:

  • Shared Storage:CPU 和 GPU 均可讀寫這塊記憶體。
  • Private Storage: 僅 GPU 可讀寫這塊記憶體,可以通過 Blit 命令等進行拷貝。
  • Managed Storage: 僅在 macOS 中允許。僅 GPU 可讀寫這塊記憶體,但 Metal 會建立一塊映象記憶體供 CPU 使用。

Apple 推薦在 iOS 中使用 shared mode,而在 macOS 中使用 managed mode。

在瞭解了記憶體管理方法後,建立一個 Texture 實現如下:

MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new];
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
textureDescriptor.width = 512;
textureDescriptor.height = 512;
textureDescriptor.storageMode = MTLStorageModeShared;

id<MTLTexture> texture = [device newTextureWithDescriptor:textureDescriptor];
複製程式碼

填充影象資料:

NSUInteger bytesPerRow = 4 * image.width;

MTLRegion region =
{
    {0,0,0}, //Origin
    { 512, 512, 1 } // Size
};

[texture replaceRegion:region
           mipmapLevel:0
             withBytes:imageData
           bytesPerRow:bytesPerRow];
複製程式碼

與 OpenGL 不同的是,Metal 中的 Texture:

  • Metal 中取樣器不屬於紋理的一部分。
  • Metal 不會翻轉紋理,原點在左上角,OpenGL 的原點是左下角。
  • Metal 不轉換資料格式。

Metal 還提供了 MetalKit 來快速建立 Texture。

Buffer

Metal 中,所有無結構的資料都使用 Buffer 來管理。與 OpenGL 類似的,頂點、索引等資料都通過 Buffer 管理。

由於資料是無結構的,因此如何管理由開發者自己制定。如以下方法可以利用編譯器來計算資料偏移來管理資料:

id<MTLBuffer> buffer = [device newBufferWithLength:bufferDataByteSize
                                       options:MTLResourceStorageModeShared];

struct MyUniforms *uniforms = (struct MyUniforms*) buffer.contents;
uniforms->modelViewProjection = modelViewProjection;
uniforms->sunPosition = sunPosition;
複製程式碼

這種方式下,開發者需要考慮記憶體對其因素。float3int3uint3等結構佔用的記憶體空間並非12位元組,而是16位元組。如果確實需要這樣打包資料,需要使用packed_float3等這類資料結構。

Pipeline

Pipeline 同樣需要通過一個 Descriptor 來建立。以下是建立一個渲染管線需要的引數:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

在制定了著色器函式,各類渲染狀態以後,就可以使用這個 Descriptor,通過 Device 建立一個 Pipeline 物件。以下是建立渲染管線的實現:

id<MTLLibrary> defaultLibrary = [device newDefaultLibrary];

id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];

MTLRenderPipelineDescriptor *pipelineStateDescriptor = [MTLRenderPipelineDescriptor new];
pipelineStateDescriptor.vertexFunction = vertexFunction;
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
id<MTLRenderPipelineState> pipelineState;
pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
                                                       error:nil];
複製程式碼

在 OpenGL 中,一個 Shader Program 物件只包含頂點著色器和片元著色器,而在 Metal 中,包含了以上描述提到的所有屬性。因此在構造一個渲染管線以前,必須確定全部這些引數以後才能夠建立。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

小結

以上資源物件都需要付出昂貴的開銷。

Pipeline 建立需要後臺編譯,Texture 和 Buffer 需要分配記憶體。因此這些操作應儘可能在初始化時一次性操作。

渲染時

初始化結束以後,程式將會進入主題 —— 渲染迴圈。

在 Metal 中,命令系統對渲染迴圈進行了封裝。開發者們只要在一個渲染迴圈內將要做的事編碼成命令後丟入命令佇列即可。

命令

上文中已經介紹了 Metal 中的四種命令,它們都派生自同一個父類,它們的使用方法是一樣的。一個完整的命令執行閉環是這樣的:

  • 使用 CPU 進行命令編碼,放入編碼佇列等待執行。
  • 等待過程中,開發者可以選擇阻塞 CPU,或讓 CPU 去做別的事情。
  • GPU 按次序執行命令,執行完畢釋放阻塞或通過閉包回撥 CPU。

阻塞式的完整的閉環實現如下:

id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];

// 編碼命令...

[commandBuffer commit];

[commandBuffer waitUntilCompleted];
複製程式碼

非阻塞式的閉環實現如下:

id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];

// 編碼命令...

commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer) {
	// 回撥 CPU...
}

[commandBuffer commit];
複製程式碼

當然,非阻塞的使用方法更值得推薦。以下是一種推薦使用的資源三重緩衝的模式,利用回撥來使 CPU 和 GPU 更高效地配合。

資源更新

建立三幀的資源緩衝區來形成一個緩衝池。CPU 將每一幀的資料按順序寫入緩衝區供 GPU 使用。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

當 GPU 觸發回撥時,CPU 將釋放該幀的緩衝區,並於下一幀使用。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

以此來減少 GPU 和 CPU 互相等待的環節,提高效能。三重緩衝的實現如下:

首先構造緩衝區以及訊號量:

id <MTLBuffer> myUniformBuffers[3];

dispatch_semaphore_t frameBoundarySemaphore = dispatch_semaphore_create(3);

NSUInteger currentUniformIndex = 0;

複製程式碼

在渲染迴圈中通過訊號量來實現三重緩衝的迴圈。

dispatch_semaphore_wait(frameBoundarySemaphore, DISPATCH_TIME_FOREVER);
    
currentUniformIndex = (currentUniformIndex + 1) % 3;

[self updateUniformResource: myUniformBuffers[currentUniformIndex]];

[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer) {
    dispatch_semaphore_signal(frameBoundarySemaphore);
}];

[commandBuffer commit];

複製程式碼

以上就是 Apple 推薦的資源更新方式。

渲染

在有了資源的更新方式以後,就要進行渲染了。

渲染命令和渲染管線的狀態息息相關,因此在建立渲染命令以前需要知道渲染管線的狀態。開發者們可以通過一個狀態描述物件來描述渲染管線的狀態。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

由渲染管線狀態獲得渲染命令編碼器的實現如下:

MTLRenderPassDescriptor * desc = [MTLRenderPassDescriptor new];
desc.colorAttachment[0].texture = myColorTexture;
desc.depthAttachment.texture = myDepthTexture;

id <MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor: desc];
複製程式碼

GPU 渲染影象的步驟大致可以分為:載入、渲染、儲存。開發者可以指定這三個步驟具體做什麼事。

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

經過這個步驟會得到最終的影象。注意途中的深度緩衝區在儲存步驟時候被標記為了 Don't care,結果會被拋棄(discard),不會被儲存。

是否需要拋棄隨影象渲染的用途而定,如果是用於顯示的影象,那麼深度資訊已經沒有用了,沒有必要被儲存。而如果是用於其他表面貼圖或是用於後處理,深度資訊可能仍然有用,需要儲存下來。

儲存的步驟是相對昂貴的,因為視訊記憶體頻寬是非常寶貴的資源,因此應該儘可能拋棄不必要的資料。

如何指定這三個步驟的行為呢?

MTLRenderPassDescriptor * desc = [MTLRenderPassDescriptor new];
desc.colorAttachment[0].texture = myColorTexture;

// 指定三個步驟的行為
desc.colorAttachment[0].loadAction = MTLLoadActionClear;
desc.colorAttachment[0].clearColor = MTLClearColorMake(0.39f, 0.34f, 0.53f, 1.0f);
desc.colorAttachment[0].storeAction = MTLStoreActionStore;

id <MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor: desc];
複製程式碼

顯示

在經過一系列繪製命令以後,影象已經被離屏繪製到了一個 Texture 上。那麼如何把影象最終顯示在螢幕上呢?

關於顯示的容器,Apple 為開發者提供了MTKView,這是一個來自MetalKit的檢視。這個檢視包含了一個 drawable 物件。對於CoreAnimation的開發者來說 drawable 這個概念應該不會陌生。

有了這個檢視,就可以用於顯示 Texture 上的影象了。

MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;

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

// 編碼渲染命令...

[renderCommandEncoder endEncoding];

[commandBuffer presentDrawable:view.currentDrawable];
[commandBuffer commit];
複製程式碼

最終使用 Command Buffer 的presentDrawable方法即可。

當這個命令被 GPU 執行完成以後,就能看到影象被顯示在螢幕上了。

小結

本節從 Metal 渲染影象的構建時、初始化時以及渲染時三個步驟詳細描述了 Metal 渲染影象的流程。經過本節,相信大多數讀者已經清楚 Metal 渲染影象的流程了。

為了更清晰地與 OpenGL 的渲染流程作對比,以下是 OpenGL 和 Metal 渲染影象的流程對比:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

Tips

Metal & OpenGL 混編

雖然 Metal 和 OpenGL 的職能是一樣的,但並不意味著程式中只能有 Metal 或 OpenGL 一方。

Metal 和 OpenGL 在程式中可以被混合使用。IOSurfaceCVPixelBuffer等資料結構可以提供資料交換支撐。

這意味著開發者們如果想要移植 OpenGL 程式到 Metal,並不需要一口氣全部移植過去,因為它們支援混編。

關於混編,開發者們可以參考這裡的程式碼。

多執行緒編碼

由於 Metal 針對 CPU 多執行緒進行了設計,因此可以儘可能發揮多執行緒的作用,利用多執行緒進行命令編碼。

Apple 已經為開發者做好了多執行緒編碼的準備工作,開發者可以使用MTLParallelRenderCommandEncoder編碼器來進行多執行緒並行編碼。

GPU 計算

Metal 原生支援 GPU 計算。

這意味著許多可以高併發的任務可以通過 Metal 的計算命令交給 GPU 執行了。

在粒子系統,物理模擬等規模比較大的計算任務上,GPU 可以自己計算,自己渲染,在一定程度上解放了 CPU。

除錯工具

關於除錯工具,Apple 也已經為開發者們準備好了。

GPU 偵錯程式,可用於單步除錯:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

著色器偵錯程式,可像除錯普通函式一樣除錯著色器函式:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

著色器效能偵錯程式:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

渲染管線偵錯程式:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

Metal 追蹤偵錯程式,可用於除錯 Metal 完整行為:

WWDC 2018:寫給 OpenGL 開發者們的 Metal 開發指南

結語

Metal 解決了很多 OpenGL 設計本身存在的問題。它是一款真正為現代裝置而設計的圖形引擎。它的物件模型,多執行緒支援經過了精心設計以滿足現代開發的需要。經過四年的發展和沉澱,Metal 本身以及配套工具已經日趨成熟。

隨著今年 WWDC 蘋果宣佈 OpenGL 和 OpenCL 被棄用,宣佈著 Metal 的時代即將到來。那麼,是時候開始使用 Metal 了。

檢視更多 WWDC 18 相關文章請前往 老司機x知識小集xSwiftGG WWDC 18 專題目錄

相關文章