版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.11.10 星期六 |
前言
很多做視頻和圖像的,相信對這個框架都不是很陌生,它渲染高級3D圖形,并使用GPU執行數據并行計算。接下來的幾篇我們就詳細的解析這個框架。感興趣的看下面幾篇文章。
1. Metal框架詳細解析(一)—— 基本概覽
2. Metal框架詳細解析(二) —— 器件和命令(一)
3. Metal框架詳細解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細解析(四) —— 關于GPU Family 4(一)
5. Metal框架詳細解析(五) —— 關于GPU Family 4之關于Imageblocks(二)
6. Metal框架詳細解析(六) —— 關于GPU Family 4之關于Tile Shading(三)
7. Metal框架詳細解析(七) —— 關于GPU Family 4之關于光柵順序組(四)
8. Metal框架詳細解析(八) —— 關于GPU Family 4之關于增強的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細解析(九) —— 關于GPU Family 4之關于線程組共享(六)
10. Metal框架詳細解析(十) —— 基本組件(一)
11. Metal框架詳細解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細解析(十二) —— 基本組件之器件選擇 - 計算處理的設備選擇(三)
13. Metal框架詳細解析(十三) —— 計算處理(一)
14. Metal框架詳細解析(十四) —— 計算處理之你好,計算(二)
15. Metal框架詳細解析(十五) —— 計算處理之關于線程和線程組(三)
16. Metal框架詳細解析(十六) —— 計算處理之計算線程組和網格大小(四)
17. Metal框架詳細解析(十七) —— 工具、分析和調試(一)
18. Metal框架詳細解析(十八) —— 工具、分析和調試之Metal GPU Capture(二)
19. Metal框架詳細解析(十九) —— 工具、分析和調試之GPU活動監視器(三)
20. Metal框架詳細解析(二十) —— 工具、分析和調試之關于Metal著色語言文件名擴展名、使用Metal的命令行工具構建庫和標記Metal對象和命令(四)
21. Metal框架詳細解析(二十一) —— 基本課程之基本緩沖區(一)
22. Metal框架詳細解析(二十二) —— 基本課程之基本紋理(二)
23. Metal框架詳細解析(二十三) —— 基本課程之CPU和GPU同步(三)
24. Metal框架詳細解析(二十四) —— 基本課程之參數緩沖 - 基本參數緩沖(四)
25. Metal框架詳細解析(二十五) —— 基本課程之參數緩沖 - 帶有數組和資源堆的參數緩沖區(五)
26. Metal框架詳細解析(二十六) —— 基本課程之參數緩沖 - 具有GPU編碼的參數緩沖區(六)
27. Metal框架詳細解析(二十七) —— 高級技術之圖層選擇的反射(一)
28. Metal框架詳細解析(二十八) —— 高級技術之使用專用函數的LOD(一)
29. Metal框架詳細解析(二十九) —— 高級技術之具有參數緩沖區的動態地形(一)
30. Metal框架詳細解析(三十) —— 延遲照明(一)
31. Metal框架詳細解析(三十一) —— 在視圖中混合Metal和OpenGL渲染(一)
32. Metal框架詳細解析(三十二) —— Metal渲染管道教程(一)
33. Metal框架詳細解析(三十三) —— Metal渲染管道教程(二)
34. Metal框架詳細解析(三十四) —— Hello Metal! 一個簡單的三角形的實現(一)
35. Metal框架詳細解析(三十五) —— Hello Metal! 一個簡單的三角形的實現(二)
36. Metal框架詳細解析(三十六) —— Metal編程指南之概覽(一)
37. Metal框架詳細解析(三十七) —— Metal編程指南之基本Metal概念(二)
38. Metal框架詳細解析(三十八) —— Metal編程指南之命令組織和執行模型(三)
39. Metal框架詳細解析(三十九) —— Metal編程指南之資源對象:緩沖區和紋理(四)
40. Metal框架詳細解析(四十) —— Metal編程指南之函數和庫(五)
41. Metal框架詳細解析(四十一) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 1(六)
Specifying Resources for a Render Command Encoder - 為渲染命令編碼器指定資源
本節中討論的MTLRenderCommandEncoder方法指定用作頂點和片段著色器函數的參數的資源,這些函數由MTLRenderPipelineState對象中的vertexFunction
和fragmentFunction
屬性指定。 這些方法將著色器資源(緩沖區,紋理和采樣器)分配給渲染命令編碼器中對應的參數表索引(atIndex)
,如圖5-3所示。
以下setVertex *
方法將一個或多個資源分配給頂點著色器函數的相應參數。
- setVertexBuffer:offset:atIndex:
- setVertexBuffers:offsets:withRange:
- setVertexTexture:atIndex:
- setVertexTextures:withRange:
- setVertexSamplerState:atIndex:
- setVertexSamplerState:lodMinClamp:lodMaxClamp:atIndex:
- setVertexSamplerStates:withRange:
- setVertexSamplerStates:lodMinClamps:lodMaxClamps:withRange:
這些setFragment *
方法類似地將一個或多個資源分配給片段著色器函數的相應參數。
- setFragmentBuffer:offset:atIndex:
- setFragmentBuffers:offsets:withRange:
- setFragmentTexture:atIndex:
- setFragmentTextures:withRange:
- setFragmentSamplerState:atIndex:
- setFragmentSamplerState:lodMinClamp:lodMaxClamp:atIndex:
- setFragmentSamplerStates:withRange:
- setFragmentSamplerStates:lodMinClamps:lodMaxClamps:withRange:
緩沖區參數表中最多有31個條目,紋理參數表中有31個條目,采樣器狀態參數表中有16個條目。
在Metal著色語言源代碼中指定資源位置的屬性限定符必須與Metal框架方法中的參數表索引匹配。 在Listing 5-7
中,為頂點著色器定義了兩個分別為索引0和1的緩沖區(posBuf
和texCoordBuf
)
Listing 5-7 Metal Framework: Specifying Resources for a Vertex Function
[renderEnc setVertexBuffer:posBuf offset:0 atIndex:0];
[renderEnc setVertexBuffer:texCoordBuf offset:0 atIndex:1];
在Listing 5-8
中,函數簽名具有與屬性限定符buffer(0)
和buffer(1)
相對應的參數。
Listing 5-8 Metal Shading Language: Vertex Function Arguments Match the Framework Argument Table Indices
vertex VertexOutput metal_vert(float4 *posData [[ buffer(0) ]],
float2 *texCoordData [[ buffer(1) ]])
類似地,在Listing 5-9
中,為片段著色器定義了一個緩沖區,一個紋理和一個采樣器(分別為fragmentColorBuf
,shadeTex
和sampler
),它們都具有索引0。
Listing 5-9 Metal Framework: Specifying Resources for a Fragment Function
[renderEnc setFragmentBuffer:fragmentColorBuf offset:0 atIndex:0];
[renderEnc setFragmentTexture:shadeTex atIndex:0];
[renderEnc setFragmentSamplerState:sampler atIndex:0];
在Listing 5-10
中,函數簽名分別具有屬性限定符buffer(0)
,texture(0)
和sampler(0)
的對應參數。
Listing 5-10 Metal Shading Language: Fragment Function Arguments Match the Framework Argument Table Indices
fragment float4 metal_frag(VertexOutput in [[stage_in]],
float4 *fragColorData [[ buffer(0) ]],
texture2d<float> shadeTexValues [[ texture(0) ]],
sampler samplerValues [[ sampler(0) ]])
1. Vertex Descriptor for Data Organization - 數據組織的頂點描述符
在Metal框架代碼中,每個管道狀態可以有一個MTLVertexDescriptor,用于描述輸入到頂點著色器函數的數據的組織,并在著色語言和框架代碼之間共享資源位置信息。
在Metal著色語言代碼中,每個頂點輸入(例如標量或整數或浮點值向量)可以組織在一個結構中,該結構可以在一個使用[[stage_in]]
屬性限定符聲明的參數中傳遞 ,如Listing 5-11
中示例頂點函數vertexMath
的VertexInput
結構中所示。 每頂點輸入結構的每個字段都有[[attribute(index)]]
限定符,它指定頂點屬性參數表中的索引。
Listing 5-11 Metal Shading Language: Vertex Function Inputs with Attribute Indices
struct VertexInput {
float2 position [[ attribute(0) ]];
float4 color [[ attribute(1) ]];
float2 uv1 [[ attribute(2) ]];
float2 uv2 [[ attribute(3) ]];
};
struct VertexOutput {
float4 pos [[ position ]];
float4 color;
};
vertex VertexOutput vertexMath(VertexInput in [[ stage_in ]])
{
VertexOutput out;
out.pos = float4(in.position.x, in.position.y, 0.0, 1.0);
float sum1 = in.uv1.x + in.uv2.x;
float sum2 = in.uv1.y + in.uv2.y;
out.color = in.color + float4(sum1, sum2, 0.0f, 0.0f);
return out;
}
要使用[[stage_in]]
限定符引用著色器函數輸入,請描述MTLVertexDescriptor對象,然后將其設置為MTLRenderPipelineState的vertexDescriptor屬性。 MTLVertexDescriptor
有兩個屬性:attributes和layouts。
MTLVertexDescriptor
的attributes屬性是一個MTLVertexAttributeDescriptorArray對象,它定義每個頂點屬性在映射到頂點函數參數的緩沖區中的組織方式。 attributes
屬性可以支持訪問在同一緩沖區中交錯的多個屬性(例如頂點坐標,曲面法線和紋理坐標)。著色語言代碼中成員的順序不必保留在框架代碼的緩沖區中。數組中的每個頂點屬性描述符都具有以下屬性,這些屬性提供頂點著色器函數信息以定位和加載參數數據:
-
bufferIndex,它是緩沖區參數表的索引,用于指定訪問哪個
MTLBuffer
。在 Specifying Resources for a Render Command Encoder中討論了緩沖區參數表。 -
format,指定如何在框架代碼中解釋數據。如果數據類型不是精確類型匹配,則可以轉換或擴展它。例如,如果著色語言類型為
half4
且框架格式為MTLVertexFormatFloat2,那么當數據用作頂點函數的參數時,它可以從float
轉換為一半并從兩個元素擴展為四個元素(使用0.0,最后兩個元素中的1.0)。 - offset,指定從頂點的開頭可以找到數據的位置。
圖5-4說明了Metal框架代碼中的MTLVertexAttributeDescriptorArray,它實現了一個交錯緩沖區,該緩沖區對應于Listing 5-11中著色語言代碼中頂點函數vertexMath
的輸入。
Listing 5-12
顯示了與圖5-4中所示的交錯緩沖區相對應的Metal框架代碼。
Listing 5-12 Metal Framework: Using a Vertex Descriptor to Access Interleaved Data
id <MTLFunction> vertexFunc = [library newFunctionWithName:@"vertexMath"];
MTLRenderPipelineDescriptor* pipelineDesc =
[[MTLRenderPipelineDescriptor alloc] init];
MTLVertexDescriptor* vertexDesc = [[MTLVertexDescriptor alloc] init];
vertexDesc.attributes[0].format = MTLVertexFormatFloat2;
vertexDesc.attributes[0].bufferIndex = 0;
vertexDesc.attributes[0].offset = 0;
vertexDesc.attributes[1].format = MTLVertexFormatFloat4;
vertexDesc.attributes[1].bufferIndex = 0;
vertexDesc.attributes[1].offset = 2 * sizeof(float); // 8 bytes
vertexDesc.attributes[2].format = MTLVertexFormatFloat2;
vertexDesc.attributes[2].bufferIndex = 0;
vertexDesc.attributes[2].offset = 8 * sizeof(float); // 32 bytes
vertexDesc.attributes[3].format = MTLVertexFormatFloat2;
vertexDesc.attributes[3].bufferIndex = 0;
vertexDesc.attributes[3].offset = 6 * sizeof(float); // 24 bytes
vertexDesc.layouts[0].stride = 10 * sizeof(float); // 40 bytes
vertexDesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
pipelineDesc.vertexDescriptor = vertexDesc;
pipelineDesc.vertexFunction = vertFunc;
MTLVertexDescriptor對象的attributes數組中的每個MTLVertexAttributeDescriptor對象對應于著色器函數中VertexInput
中的索引結構成員。 attributes [1] .bufferIndex = 0
指定在參數表中使用索引0處的緩沖區。 (在此示例中,每個MTLVertexAttributeDescriptor具有相同的bufferIndex,因此每個引用參數表中索引0處的相同頂點緩沖區。)。offset指定頂點內數據的位置,因此attributes[1].offset = 2 * sizeof(float)
定位從緩沖區起始處開始的相應數據的8個字節。選擇format值以匹配著色器函數中的數據類型,因此attributes[1].format = MTLVertexFormatFloat4
指定使用四個浮點值。
MTLVertexDescriptor
的layouts屬性是MTLVertexBufferLayoutDescriptorArray。對于layouts中的每個MTLVertexBufferLayoutDescriptor,屬性指定在Metal繪制基元時如何從參數表中的相應MTLBuffer獲取頂點和屬性數據。 (有關繪制圖元的更多信息,請參見Drawing Geometric Primitives。)。MTLVertexBufferLayoutDescriptor
的stepFunction屬性確定是為每個頂點,某些實例獲取屬性數據,還是僅獲取一次。如果將stepFunction
設置為獲取某些實例的屬性數據,則MTLVertexBufferLayoutDescriptor
的stepRate屬性將確定實例數。 stride屬性指定兩個頂點的數據之間的距離(以字節為單位)。
圖5-5描述了MTLVertexBufferLayoutDescriptor,它對應于 Listing 5-12中的代碼。 layouts [0]
指定如何從緩沖區參數表中的相應索引0獲取頂點數據。 layouts [0] .stride
指定兩個頂點的數據之間的距離為40個字節。 layouts [0] .stepFunction
,MTLVertexStepFunctionPerVertex的值指定在繪制時為每個頂點提取屬性數據。如果stepFunction
的值為MTLVertexStepFunctionPerInstance,則stepRate
屬性確定獲取屬性數據的頻率。例如,如果stepRate
為1,則為每個實例提取數據;如果stepRate
為2,則每兩個實例,依此類推。
Performing Fixed-Function Render Command Encoder Operations - 執行固定功能渲染命令編碼器操作
使用這些MTLRenderCommandEncoder方法設置固定功能圖形狀態值:
- setViewport:以屏幕坐標指定區域,該區域是虛擬3D世界投影的目標。視口是3D,因此它包含深度值;有關詳細信息,請參閱 Working with Viewport and Pixel Coordinate Systems。
-
setTriangleFillMode:確定是否使用直線(MTLTriangleFillModeLines)或填充三角形(MTLTriangleFillModeFill)柵格化三角形和三角形條帶基元。默認值為
MTLTriangleFillModeFill
。 -
setCullMode:和setFrontFacingWinding:一起用于確定是否以及如何應用剔除。您可以在某些幾何模型上使用剔除隱藏曲面去除,例如使用實心三角形渲染的可定向球體。 (如果其基元始終以順時針或逆時針順序繪制,則表面可定向。)
-
setFrontFacingWinding:的值指示正面基元是否以順時針(MTLWindingClockwise)或逆時針(MTLWindingCounterClockwise)順序繪制頂點。默認值為
MTLWindingClockwise
。 - setCullMode:的值確定是否執行剔除(MTLCullModeNone,如果禁用剔除)或者要剔除哪種類型的原語(MTLCullModeFront或MTLCullModeBack)。
-
setFrontFacingWinding:的值指示正面基元是否以順時針(MTLWindingClockwise)或逆時針(MTLWindingCounterClockwise)順序繪制頂點。默認值為
使用以下MTLRenderCommandEncoder方法對固定函數狀態更改命令進行編碼:
- setScissorRect:指定2D剪刀矩形。位于指定剪刀矩形之外的碎片將被丟棄。
- setDepthStencilState:設置深度和模板測試狀態,如 Depth and Stencil States中所述。
- setStencilReferenceValue:指定模板參考值。
- setDepthBias:slopeScale:clamp:指定用于將陰影貼圖與片段著色器輸出的深度值進行比較的調整。
- setVisibilityResultMode:offset:確定是否監視任何樣本是否通過深度和模板測試。如果設置為MTLVisibilityResultModeBoolean,則如果任何樣本通過深度和模板測試,則會將非零值寫入由MTLRenderPassDescriptor的visibilityResultBuffer屬性指定的緩沖區,如Creating a Render Pass Descriptor中所述。
您可以使用此模式執行遮擋測試。如果繪制邊界框并且沒有樣本通過,則可以得出結論,該邊界框內的任何對象都被遮擋,因此不需要渲染。
- setBlendColorRed:green:blue:alpha:指定常量混合顏色和alpha值,詳見Configuring Blending in a Render Pipeline Attachment Descriptor。
1. Working with Viewport and Pixel Coordinate Systems - 使用Viewport和像素坐標系
Metal將其標準化設備坐標(Normalized Device Coordinate - NDC)
系統定義為2x2x1
立方體,其中心位于(0,0,0.5)
。 NDC系統的x和y的左側和底部分別指定為-1。 NDC系統的x和y的右側和頂部分別指定為+1。
視口指定從NDC
到窗口坐標的轉換。 Metal視口是由MTLRenderCommandEncoder的setViewport:方法指定的3D轉換。 窗口坐標的原點位于左上角。
在Metal中,像素中心偏移(0.5,0.5)
。 例如,原點處的像素的中心位于(0.5,0.5)
; 右邊相鄰像素的中心是(1.5,0.5)
。 紋理也是如此。
2. Performing Depth and Stencil Operations - 執行深度和模板操作
深度和模板操作是您指定的片段操作,如下所示:
- 指定包含深度/模板狀態設置的自定義MTLDepthStencilDescriptor對象。創建自定義
MTLDepthStencilDescriptor
對象可能需要創建一個或兩個適用于前向基元和后向基元的TLStencilDescriptor對象。 - 通過使用深度/模板狀態描述符調用
MTDDevice
的newDepthStencilStateWithDescriptor:方法來創建MTLDepthStencilState對象。 - 要設置深度/模板狀態,請使用
MTLRenderCommandEncoder
的帶有MTLDepthStencilState值的setDepthStencilState:方法。 - 如果正在使用模板測試,請調用setStencilReferenceValue:以指定模板參考值。
如果啟用了深度測試,則渲染管道狀態必須包含深度附件以支持寫入深度值。要執行模板測試,渲染管道狀態必須包含模板附件。要配置附件,請參閱Creating and Configuring a Render Pipeline Descriptor。
如果要定期更改深度/模板狀態,則可能需要重用狀態描述符對象,根據需要修改其屬性值以創建更多狀態對象。
注意:要從著色器函數中的深度格式紋理進行采樣,請在著色器中實施采樣操作,而不使用
MTLSamplerState
。
使用MTLDepthStencilDescriptor對象的屬性,如下所示設置深度和模板狀態:
- 要將深度值寫入深度附件,請將depthWriteEnabled設置為
YES
。 -
depthCompareFunction指定深度測試的執行方式。如果片段的深度值未通過深度測試,則丟棄片段。例如,常用的
MTLCompareFunctionLess
函數導致比(先前寫入的)像素深度值更遠離觀察者的片段值無法進行深度測試;也就是說,片段被較早的深度值視為被遮擋。 -
frontFaceStencil和backFaceStencil屬性均為前向和后向基元指定單獨的MTLStencilDescriptor對象。要對前置和后置基元使用相同的模板狀態,可以為frontFaceStencil和backFaceStencil屬性分配相同的MTLStencilDescriptor。要顯式禁用一個或兩個面的模板測試,請將相應的屬性設置為
nil
(默認值)。
不必顯式禁用模板狀態。 Metal根據是否為有效的模板操作配置模板描述符來確定是否啟用模板測試。
Listing 5-13顯示了創建和使用MTLDepthStencilDescriptor對象以創建MTLDepthStencilState對象的示例,該對象隨后與渲染命令編碼器一起使用。 在此示例中,從深度/模板狀態描述符的frontFaceStencil屬性訪問前置基元的模板狀態。 對于背面基元,顯式禁用模板測試
Listing 5-13 Creating and Using a Depth/Stencil Descriptor
MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
if (dsDesc == nil)
exit(1); // if the descriptor could not be allocated
dsDesc.depthCompareFunction = MTLCompareFunctionLess;
dsDesc.depthWriteEnabled = YES;
dsDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
dsDesc.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
dsDesc.frontFaceStencil.depthFailureOperation = MTLStencilOperationIncrementClamp;
dsDesc.frontFaceStencil.depthStencilPassOperation =
MTLStencilOperationIncrementClamp;
dsDesc.frontFaceStencil.readMask = 0x1;
dsDesc.frontFaceStencil.writeMask = 0x1;
dsDesc.backFaceStencil = nil;
id <MTLDepthStencilState> dsState = [device
newDepthStencilStateWithDescriptor:dsDesc];
[renderEnc setDepthStencilState:dsState];
[renderEnc setStencilReferenceValue:0xFF];
以下屬性在MTLStencilDescriptor中定義模板測試:
- readMask是一個位掩碼;GPU使用模板參考值和存儲的模板值計算此掩碼的按位AND。模板測試是在得到的掩碼參考值和掩碼存儲值之間的比較。
- writeMask是一個位掩碼,用于限制模板操作將哪些模板值寫入模板附件。
-
stencilCompareFunction指定如何對片段執行模板測試。在
Listing 5-13
中,模板比較函數是MTLCompareFunctionEqual,因此如果掩碼的參考值等于已經存儲在片段位置的掩碼模板值,則模板測試通過。 -
stencilFailureOperation, depthFailureOperation
和depthStencilPassOperation指定如何對模板附件中存儲的模板值進行三種不同的測試結果:如果模板測試失敗,如果模板測試通過,如果深度測試失敗,或者如果模板和深度測試分別成功 。在前面的示例中,如果模板測試失敗,則模板值不變(MTLStencilOperationKeep),但如果模板測試通過則模板值會增加,除非模板值已經是最大可能值(MTLStencilOperationIncrementClamp)。
Drawing Geometric Primitives - 繪制幾何圖元
建立管道狀態和固定功能狀態后,可以調用以下MTLRenderCommandEncoder方法來繪制幾何圖元。這些繪制方法引用資源(例如包含頂點坐標,紋理坐標,曲面法線和其他數據的緩沖區)來執行具有著色器函數的管道以及先前使用 MTLRenderCommandEncoder
建立的其他狀態。
-
drawPrimitives:vertexStart:vertexCount:instanceCount:使用連續數組元素中的頂點數據呈現基元的多個實例
(instanceCount)
,從索引vertexStart
處數組元素的第一個頂點開始,到索引vertexStart + vertexCount - 1
處的數組元素結束。 -
drawPrimitives:vertexStart:vertexCount:與
instanceCount
為1的前一個方法相同。 -
drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:instanceCount:使用
MTLBuffer
對象indexBuffer
中指定的索引列表呈現基元的多個實例(instanceCount)
。indexCount
確定索引的數量。索引列表從索引開始,索引是indexBuffer
中數據中的indexBufferOffset
字節偏移量。indexBufferOffset
必須是索引大小的倍數,由indexType
決定。 -
drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:類似于上一個方法,
instanceCount
為1。
對于上面列出的每個基本渲染方法,第一個輸入值使用MTLPrimitiveType
值之一確定基元類型。其他輸入值確定用于組合基元的頂點。對于所有這些方法,instanceStart
輸入值確定要繪制的第一個實例,而instanceCount
輸入值確定要繪制的實例數。
如前所述,setTriangleFillMode:確定三角形是渲染為填充還是線框,setCullMode:和setFrontFacingWinding:設置確定GPU在渲染過程中是否剔除三角形。有關更多信息,請參閱Fixed-Function State Operations)。
渲染點基元時,頂點函數的著色器語言代碼必須提供[[point_size]]
屬性,或者點大小未定義。
渲染具有平面著色的三角形圖元時,第一個頂點(也稱為激發頂點)的屬性用于整個三角形。頂點函數的著色器語言代碼必須提供[[flat]]
插值限定符。
有關所有Metal著色語言屬性和限定符的詳細信息,請參見Metal Shading Language Guide
。
Ending a Rendering Pass - 結束渲染過程
要終止渲染過程,請在渲染命令編碼器上調用endEncoding。 在結束上一個命令編碼器之后,您可以創建任何類型的新命令編碼器,以將其他命令編碼到命令緩沖區中。
Code Example: Drawing a Triangle - 代碼示例:繪制三角形
Listing 5-14中所示的以下步驟描述了渲染三角形的基本過程。
- 1) 創建一個MTLCommandQueue并使用它來創建MTLCommandBuffer。
- 2) 創建一個MTLRenderPassDescriptor,它指定一組附件,這些附件用作命令緩沖區中編碼的渲染命令的目標。
在此示例中,僅設置和使用第一個顏色附件。 (假設變量currentTexture
包含用于顏色附件的MTLTexture。)然后MTLRenderPassDescriptor用于創建新的MTLRenderCommandEncoder。
- 3) 創建兩個MTLBuffer對象,
posBuf
和colBuf
,并調用newBufferWithBytes:length:options:分別將頂點坐標和頂點顏色數據posData
和colData
復制到緩沖區存儲中。 - 4) 調用MTLRenderCommandEncoder的setVertexBuffer:offset:atIndex:方法兩次以指定坐標和顏色。
setVertexBuffer:offset:atIndex:方法的atIndex
輸入值對應于頂點函數的源代碼中的屬性緩沖區(atIndex)
。
-
5) 創建MTLRenderPipelineDescriptor并在管道描述符中建立頂點和片段函數:
- 使用
progSrc
中的源代碼創建一個MTLLibrary,它被假定為包含Metal著色器源代碼的字符串。 - 然后調用MTLLibrary的newFunctionWithName:方法創建MTLFunction
vertFunc
,它表示名為hello_vertex
的函數,并創建表示名為hello_fragment
的函數的MTLFunctionfragFunc
。 - 最后,使用這些
MTLFunction
對象設置MTLRenderPipelineDescriptor的vertexFunction和fragmentFunction屬性。
- 使用
6) 通過調用newRenderPipelineStateWithDescriptor:error:或MTLDevice的類似方法,從MTLRenderPipelineDescriptor創建MTLRenderPipelineState。然后,MTLRenderCommandEncoder的setRenderPipelineState:方法使用創建的管道狀態進行渲染。
7) 調用MTLRenderCommandEncoder的drawPrimitives:vertexStart:vertexCount:方法來附加命令以執行填充三角形的渲染(類型MTLPrimitiveTypeTriangle)。
8) 調用endEncoding方法以結束此呈現過程的編碼。并調用MTLCommandBuffer的commit方法來執行設備上的命令。
Listing 5-14 Metal Code for Drawing a Triangle
id <MTLDevice> device = MTLCreateSystemDefaultDevice();
id <MTLCommandQueue> commandQueue = [device newCommandQueue];
id <MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
MTLRenderPassDescriptor *renderPassDesc
= [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDesc.colorAttachments[0].texture = currentTexture;
renderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(0.0,1.0,1.0,1.0);
id <MTLRenderCommandEncoder> renderEncoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDesc];
static const float posData[] = {
0.0f, 0.33f, 0.0f, 1.f,
-0.33f, -0.33f, 0.0f, 1.f,
0.33f, -0.33f, 0.0f, 1.f,
};
static const float colData[] = {
1.f, 0.f, 0.f, 1.f,
0.f, 1.f, 0.f, 1.f,
0.f, 0.f, 1.f, 1.f,
};
id <MTLBuffer> posBuf = [device newBufferWithBytes:posData
length:sizeof(posData) options:nil];
id <MTLBuffer> colBuf = [device newBufferWithBytes:colorData
length:sizeof(colData) options:nil];
[renderEncoder setVertexBuffer:posBuf offset:0 atIndex:0];
[renderEncoder setVertexBuffer:colBuf offset:0 atIndex:1];
NSError *errors;
id <MTLLibrary> library = [device newLibraryWithSource:progSrc options:nil
error:&errors];
id <MTLFunction> vertFunc = [library newFunctionWithName:@"hello_vertex"];
id <MTLFunction> fragFunc = [library newFunctionWithName:@"hello_fragment"];
MTLRenderPipelineDescriptor *renderPipelineDesc
= [[MTLRenderPipelineDescriptor alloc] init];
renderPipelineDesc.vertexFunction = vertFunc;
renderPipelineDesc.fragmentFunction = fragFunc;
renderPipelineDesc.colorAttachments[0].pixelFormat = currentTexture.pixelFormat;
id <MTLRenderPipelineState> pipeline = [device
newRenderPipelineStateWithDescriptor:renderPipelineDesc error:&errors];
[renderEncoder setRenderPipelineState:pipeline];
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
vertexStart:0 vertexCount:3];
[renderEncoder endEncoding];
[commandBuffer commit];
在Listing 5-14
中,MTLFunction對象表示名為hello_vertex
的著色器函數。MTLRenderCommandEncoder的方法setVertexBuffer:offset:atIndex:用于指定作為參數傳遞給 hello_vertex
的頂點資源(在本例中為兩個緩沖區對象)。setVertexBuffer:offset:atIndex:方法的atIndex
輸入值對應頂點函數源代碼中的屬性 buffer(atIndex)
,如Listing 5-15
所示。
Listing 5-15 Corresponding Shader Function Declaration
vertex VertexOutput hello_vertex(
const global float4 *pos_data [[ buffer(0) ]],
const global float4 *color_data [[ buffer(1) ]])
{
...
}
Encoding a Single Rendering Pass Using Multiple Threads - 使用多個線程編碼單個渲染通道
在某些情況下,單個渲染過程的編碼命令的單CPU工作負載可能會限制應用程序的性能。但是,嘗試通過將工作負載分成多個CPU線程上編碼的多個渲染通道來繞過此瓶頸也會對性能產生負面影響,因為每個渲染過程都需要其自己的中間附件存儲和加載操作來保留渲染目標內容。
而是使用MTLParallelRenderCommandEncoder對象,該對象管理共享相同命令緩沖區和渲染傳遞描述符的多個從屬MTLRenderCommandEncoder
對象。并行渲染命令編碼器確保附件加載和存儲操作僅在整個渲染過程的開始和結束時發生,而不是在每個從屬渲染命令編碼器的命令集的開始和結束時發生。使用此體系結構,您可以以安全且高性能的方式并行地將每個MTLRenderCommandEncoder
對象分配給其自己的線程。
要創建并行渲染命令編碼器,請使用MTLCommandBuffer
對象的parallelRenderCommandEncoderWithDescriptor:方法。要創建從屬渲染命令編碼器,請為要執行命令編碼的每個CPU線程調用一次MTLParallelRenderCommandEncoder
對象的renderCommandEncoder
方法。從同一并行渲染命令編碼器創建的所有從屬命令編碼器將命令編碼到同一命令緩沖區。命令按照創建渲染命令編碼器的順序編碼到命令緩沖區。要結束特定渲染命令編碼器的編碼,請調用MTLRenderCommandEncoder
的endEncoding方法。在并行渲染命令編碼器創建的所有渲染命令編碼器上結束編碼后,調用MTLParallelRenderCommandEncoder
的endEncoding方法以結束渲染過程。
Listing 5-16
顯示MTLParallelRenderCommandEncoder
創建三個MTLRenderCommandEncoder
對象:rCE1,rCE2和rCE3
。
Listing 5-16 A Parallel Rendering Encoder with Three Render Command Encoders
MTLRenderPassDescriptor *renderPassDesc
= [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDesc.colorAttachments[0].texture = currentTexture;
renderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,1.0);
id <MTLParallelRenderCommandEncoder> parallelRCE = [commandBuffer
parallelRenderCommandEncoderWithDescriptor:renderPassDesc];
id <MTLRenderCommandEncoder> rCE1 = [parallelRCE renderCommandEncoder];
id <MTLRenderCommandEncoder> rCE2 = [parallelRCE renderCommandEncoder];
id <MTLRenderCommandEncoder> rCE3 = [parallelRCE renderCommandEncoder];
// not shown: rCE1, rCE2, and rCE3 call methods to encode graphics commands
//
// rCE1 commands are processed first, because it was created first
// even though rCE2 and rCE3 end earlier than rCE1
[rCE2 endEncoding];
[rCE3 endEncoding];
[rCE1 endEncoding];
// all MTLRenderCommandEncoders must end before MTLParallelRenderCommandEncoder
[parallelRCE endEncoding];
命令編碼器調用endEncoding的順序與命令編碼和附加到MTLCommandBuffer的順序無關。 對于MTLParallelRenderCommandEncoder,MTLCommandBuffer始終按照創建從屬渲染命令編碼器的順序包含命令,如圖5-6所示。
后記
本篇主要講述了圖形渲染:渲染命令編碼器,感興趣的給個贊或者關注~~~