在Metal架構中,MTLDevice協議定義了代表單個GPU的接口。該MTLDevice協議支持用于詢問設備屬性的方法,用于創建其他設備特定對象(如緩沖區和紋理),以及編碼和排隊渲染和計算命令以提交給GPU執行。
一個命令隊列包含一個隊列的命令緩沖區,并且一命令隊列整理那些命令緩沖區的執行順序。命令緩沖區包含用于在特定設備上執行的編碼命令。一個命令編碼器附加渲染,計算,和位圖傳送命令到命令緩沖器,并且這些命令緩沖器最終被致力于用于在設備上執行。
該MTLCommandQueue協議定義了一個接口,用于命令隊列,主要支撐用于創建命令緩沖區對象的方法。該MTLCommandBuffer協議定義了一個接口,用于命令緩沖區并提供用于創建命令編碼器,入隊命令緩沖區以供執行,檢查狀態和其他操作的方法。該MTLCommandBuffer協議支持以下命令編碼器類型,這些接口用于將不同類型的GPU工作負載編碼到命令緩沖區中:
- 該MTLRenderCommandEncoder協議對單個渲染通過的圖形(3D)渲染命令進行編碼
- 該MTLComputeCommandEncoder協議編碼的數據并行計算的工作量。
- 該MTLBlitCommandEncoder協議編碼緩沖區和紋理之間的簡單復制操作,以及諸如mipmap生成的實用程序操作。
在任何時間點,只有一個命令編碼器可以被激活并將命令追加到命令緩沖區中。每個命令編碼器必須結束,才能創建另一個命令編碼器,以便與同一個命令緩沖區一起使用。“每個命令緩沖區的一個活動命令編碼器”規則的一個例外是在
使用多個線程編碼單個渲染通過中MTLParallelRenderCommandEncoder討論的協議。
一旦完成所有編碼,您將提交MTLCommandBuffer對象本身,它將命令緩沖區標記為可以由GPU執行。該MTLCommandQueue協議控制MTLCommandBuffer執行提交對象中的命令,相對于MTLCommandBuffer已在命令隊列中的其他對象。
圖2-1顯示了命令隊列,命令緩沖區和命令編碼器對象如何緊密相關。圖表頂部的每一列組件(緩沖區,紋理,采樣器,深度和模板狀態,流水線狀態)表示特定于特定命令編碼器的資源和狀態。
設備對象表示GPU
一個MTLDevice對象表示一個GPU能夠執行的命令。該MTLDevice協議具有創建新的命令隊列,從內存分配緩沖區,創建紋理以及查詢設備功能的方法。要獲取系統上的首選系統設備,請調用該MTLCreateSystemDefaultDevice功能。
Metal的瞬態和非瞬態物體
Metal中的某些物體被設計為瞬態且極其輕便,而其他物體則開銷更大,并且可以持續很長時間,也許在該應用的曾哥生命周期中。
命令緩沖區和命令編碼器對象是瞬態的,并且設計用于單次使用。它們非常便宜地分配和釋放,所以他們的創建方法返回自動釋放的對象。
以下對象不是暫時的。在性能敏感代碼中重用這些對象,并避免重復創建它們。
- 命令隊列
- 數據緩沖區
- 紋理
- 采樣器狀態
- Libraries
- 計算狀態
- 渲染管道狀態
- 深度/模板狀態
命令隊列
命令隊列接受GPU執行的命令緩沖區的有序列表。發送到單個隊列的所有命令緩沖區都保證以命令緩沖區入隊的順序執行。通常,命令隊列是線程安全的,并且允許多個活動命令緩沖器被同時編碼。
要創建一個命令隊列,請調用對象的newCommandQueue方法或newCommandQueueWithMaxCommandBufferCount:方法MTLDevice。
一般來說,命令隊列預計會長期存在,因此不應該重復創建和銷毀。
命令緩沖區
命令緩沖區存儲編碼的命令,直到緩沖區被提交以供GPU執行。單個命令緩沖區可以包含許多不同類型的編碼命令,這取決于用于構建它的編碼器的數量和類型。在典型的應用程序中,即使呈現該幀涉及多個渲染遍,計算處理函數或blit操作,整個渲染幀也被編碼為單個命令緩沖區。
命令緩沖區是暫時的一次性對象,不支持重用。一旦命令緩沖區被提交執行,唯一有效的操作是等待命令緩沖區被調度或完成 - 通過在命令緩沖區執行的注冊處理程序塊中討論的同步調用或處理程序塊,并檢查命令緩沖區執行。
命令緩沖區還表示應用程序唯一可獨立追蹤的工作單元,并且它們定義了Metal存儲器模型建立的一致性邊界,如資源對象:緩沖區和紋理中所詳述。
創建命令緩沖區
要創建MTLCommandBuffer對象,請調用commandBuffer方法MTLCommandQueue。一個MTLCommandBuffer對象只能提交到MTLCommandQueue創建它的對象。
由該commandBuffer方法創建的命令緩沖區將保留執行所需的數據。對于某些情況,在執行MTLCommandBuffer對象期間,在其他地方持有這些對象的保留,您可以通過調用commandBufferWithUnretainedReferences方法來創建命令緩沖區MTLCommandQueue。使用該commandBufferWithUnretainedReferences方法僅用于極其重要的性能關鍵型應用程序,這些應用程序可以保證關鍵對象在命令緩沖區執行完成之前在應用程序的其他位置都有引用 否則,不再具有其他引用的對象可能會被過早釋放,并且命令緩沖區執行的結果未定義。
執行命令
該MTLCommandBuffer協議使用以下方法在命令隊列中建立命令緩沖區的執行順序。命令緩沖區在提交之前不會開始執行。一旦提交,命令緩沖區按照它們排隊的順序執行。
該enqueue方法在命令隊列中為命令緩沖區保留一個位置,但不提交執行命令緩沖區。當此命令緩沖區最終提交時,它將在關聯的命令隊列中的任何先前入隊的命令緩沖區之后執行。
-
該commit方法使命令緩沖區盡快執行,但在同一命令隊列中的任何先前入隊的命令緩沖區都被提交之后。如果命令緩沖區以前沒有排隊,commit則進行隱式enqueue調用。
有關使用enqueue多個線程的示例,請參閱多線程,命令緩沖區和命令編碼器。
注冊處理程序塊用于命令緩沖區執行
下面MTLCommandBuffer列出的方法監視命令執行。在未定義的線程上按執行順序調用計劃和完成的處理程序。在這些處理程序中執行的任何代碼都應該快速完成; 如果需要花費昂貴或者阻塞的工作,將工作推遲到另一個線程。
該addScheduledHandler:方法在調度命令緩沖區時注冊要調用的代碼塊。當系統中其他對象或其他API 提交的工作之間的任何依賴關系得到滿足時,命令緩沖區將被視為安排的
MTLCommandBuffer。您可以為命令緩沖區注冊多個計劃的處理程序。該waitUntilScheduled方法在調度命令緩沖區之后同步等待并返回,并且該addScheduledHandler:方法注冊的所有處理程序都完成。
該addCompletedHandler:方法在設備完成命令緩沖區的執行后立即注冊要立即調用的代碼塊。您可以為命令緩沖區注冊多個完成的處理程序。
-
該waitUntilCompleted方法在設備完成命令緩沖區的執行后同步等待并返回,并且返回了由該addCompletedHandler:方法注冊的所有處理程序。
該presentDrawable:方法是完成處理程序的特殊情況。CAMetalDrawable當調度命令緩沖區時,這種方便方法呈現可顯示資源(對象)的內容。有關該presentDrawable:方法的詳細信息,請參閱與Core Animation集成:CAMetalLayer。
監視命令緩沖區執行狀態
只讀status屬性包含MTLCommandBufferStatus列出的枚舉值Command Buffer Status Codes,反映了此命令緩沖區的生命周期中的當前調度階段。如果執行成功完成,則只讀error屬性的值為nil。如果執行失敗,則status設置為MTLCommandBufferStatusError,并且error屬性可能包含列出的值Command Buffer Error Codes,表示失敗的原因。
命令編碼器
命令編碼器是一個瞬態對象,您可以使用一次將命令和狀態寫入GPU可以執行的格式的單個命令緩沖區。許多命令編碼器對象方法將命令附加到命令緩沖區。當命令編碼器處于活動狀態時,它具有為其命令緩沖區附加命令的專有權限。完成編碼命令后,調用該endEncoding方法。要寫入其他命令,請創建一個新的命令編碼器。
創建命令編碼器對象
因為命令編碼器將命令附加到特定的命令緩沖區中,所以您可以通過從MTLCommandBuffer要使用它的對象請求一個命令來創建命令編碼器。使用以下MTLCommandBuffer方法創建每種類型的命令編碼器:
該renderCommandEncoderWithDescriptor:方法創建一個MTLRenderCommandEncoder用于圖形呈現到一個附件的對象MTLRenderPassDescriptor。
該computeCommandEncoder方法創建一個MTLComputeCommandEncoder用于數據并行計算的對象。
該blitCommandEncoder方法創建一個MTLBlitCommandEncoder用于內存操作的對象。
該parallelRenderCommandEncoderWithDescriptor:方法創建一個MTLParallelRenderCommandEncoder對象,使MTLRenderCommandEncoder對象能夠在不同的線程上運行,同時仍然呈現給在共享中指定的附件MTLRenderPassDescriptor。
渲染命令編碼器
圖形渲染可以在來描述渲染通道。一個MTLRenderCommandEncoder對象表示與單個渲染過程相關聯的渲染狀態和繪制命令。AMTLRenderCommandEncoder需要包含作為渲染命令的目的地的顏色,深度和模板附件的關聯MTLRenderPassDescriptor(描述在創建渲染通過描述符中)。將MTLRenderCommandEncoder有以下方法:
指定包含頂點,片段或紋理圖像數據的圖形資源,如緩沖區和紋理對象
指定MTLRenderPipelineState包含編譯渲染狀態的對象,包括頂點和片段著色器
指定固定功能狀態,包括視口,三角形填充模式,剪刀矩形,深度和模板測試以及其他值
繪制3D圖元
有關MTLRenderCommandEncoder協議的詳細信息,請參閱圖形渲染:渲染命令編碼器。
計算命令編碼器
對于數據并行計算,該MTLComputeCommandEncoder協議提供了在命令緩沖區中對可以指定計算函數及其參數(例如,紋理,緩沖區和采樣器狀態)進行編碼的命令的方法,并分派計算函數執行。要創建一個計算命令編碼器對象,請使用以下computeCommandEncoder方法MTLCommandBuffer。有關MTLComputeCommandEncoder方法和屬性的詳細信息請參見數據并行計算處理:計算命令編碼器。
Blit命令編碼器
該MTLBlitCommandEncoder協議具有追加緩沖區(MTLBuffer)和紋理(MTLTexture)之間的內存復制操作的命令的方法。該MTLBlitCommandEncoder協議還提供了一些方法,以填補與純色紋理和生成的mipmap。要創建blit命令編碼器對象,請使用blitCommandEncoder方法MTLCommandBuffer。有關MTLBlitCommandEncoder方法和屬性的詳細信息,請參閱緩沖區和紋理操作:Blit命令編碼器。
多線程,命令緩沖區和命令編碼器
大多數應用程序使用單個線程來編碼單個命令緩沖區中單個幀的渲染命令。在每一幀結束時,您提交命令緩沖區,這兩個調度計劃都將執行。
如果要并行化命令緩沖區編碼,則可以同時創建多個命令緩沖區,并使用單獨的線程對每個緩沖區進行編碼。如果您提前知道命令緩沖區應該執行什么順序,那么該enqueue方法MTLCommandBuffer可以在命令隊列中聲明執行順序,而無需等待命令被編碼和提交。否則,當提交命令緩沖區時,在任何先前入隊的命令緩沖區之后,將在命令隊列中分配一個位置。
時間只有一個CPU線程可以訪問命令緩沖區。多線程應用程序可以使用每個命令緩沖區中的一個線程并行創建多個命令緩沖區。
圖2-2顯示了三個線程的示例。每個線程都有自己的命令緩沖區。對于每個線程,一次一個命令編碼器可以訪問其關聯的命令緩沖區。圖2-2還顯示了每個命令緩沖區從不同的命令編碼器接收命令。完成編碼后,調用endEncoding命令編碼器的方法,然后新的命令編碼器對象可以開始將命令編碼到命令緩沖區。
一個MTLParallelRenderCommandEncoder對象允許單個渲染通過在多個命令編碼器之間分解并分配給單獨的線程。有關詳細信息MTLParallelRenderCommandEncoder,請參閱使用多個線程編碼單個渲染傳遞。