Metal框架詳細(xì)解析(四十六) —— Metal編程指南之Tessellation(十一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.11.11 星期日

前言

很多做視頻和圖像的,相信對(duì)這個(gè)框架都不是很陌生,它渲染高級(jí)3D圖形,并使用GPU執(zhí)行數(shù)據(jù)并行計(jì)算。接下來的幾篇我們就詳細(xì)的解析這個(gè)框架。感興趣的看下面幾篇文章。
1. Metal框架詳細(xì)解析(一)—— 基本概覽
2. Metal框架詳細(xì)解析(二) —— 器件和命令(一)
3. Metal框架詳細(xì)解析(三) —— 渲染簡(jiǎn)單的2D三角形(一)
4. Metal框架詳細(xì)解析(四) —— 關(guān)于GPU Family 4(一)
5. Metal框架詳細(xì)解析(五) —— 關(guān)于GPU Family 4之關(guān)于Imageblocks(二)
6. Metal框架詳細(xì)解析(六) —— 關(guān)于GPU Family 4之關(guān)于Tile Shading(三)
7. Metal框架詳細(xì)解析(七) —— 關(guān)于GPU Family 4之關(guān)于光柵順序組(四)
8. Metal框架詳細(xì)解析(八) —— 關(guān)于GPU Family 4之關(guān)于增強(qiáng)的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細(xì)解析(九) —— 關(guān)于GPU Family 4之關(guān)于線程組共享(六)
10. Metal框架詳細(xì)解析(十) —— 基本組件(一)
11. Metal框架詳細(xì)解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細(xì)解析(十二) —— 基本組件之器件選擇 - 計(jì)算處理的設(shè)備選擇(三)
13. Metal框架詳細(xì)解析(十三) —— 計(jì)算處理(一)
14. Metal框架詳細(xì)解析(十四) —— 計(jì)算處理之你好,計(jì)算(二)
15. Metal框架詳細(xì)解析(十五) —— 計(jì)算處理之關(guān)于線程和線程組(三)
16. Metal框架詳細(xì)解析(十六) —— 計(jì)算處理之計(jì)算線程組和網(wǎng)格大小(四)
17. Metal框架詳細(xì)解析(十七) —— 工具、分析和調(diào)試(一)
18. Metal框架詳細(xì)解析(十八) —— 工具、分析和調(diào)試之Metal GPU Capture(二)
19. Metal框架詳細(xì)解析(十九) —— 工具、分析和調(diào)試之GPU活動(dòng)監(jiān)視器(三)
20. Metal框架詳細(xì)解析(二十) —— 工具、分析和調(diào)試之關(guān)于Metal著色語言文件名擴(kuò)展名、使用Metal的命令行工具構(gòu)建庫和標(biāo)記Metal對(duì)象和命令(四)
21. Metal框架詳細(xì)解析(二十一) —— 基本課程之基本緩沖區(qū)(一)
22. Metal框架詳細(xì)解析(二十二) —— 基本課程之基本紋理(二)
23. Metal框架詳細(xì)解析(二十三) —— 基本課程之CPU和GPU同步(三)
24. Metal框架詳細(xì)解析(二十四) —— 基本課程之參數(shù)緩沖 - 基本參數(shù)緩沖(四)
25. Metal框架詳細(xì)解析(二十五) —— 基本課程之參數(shù)緩沖 - 帶有數(shù)組和資源堆的參數(shù)緩沖區(qū)(五)
26. Metal框架詳細(xì)解析(二十六) —— 基本課程之參數(shù)緩沖 - 具有GPU編碼的參數(shù)緩沖區(qū)(六)
27. Metal框架詳細(xì)解析(二十七) —— 高級(jí)技術(shù)之圖層選擇的反射(一)
28. Metal框架詳細(xì)解析(二十八) —— 高級(jí)技術(shù)之使用專用函數(shù)的LOD(一)
29. Metal框架詳細(xì)解析(二十九) —— 高級(jí)技術(shù)之具有參數(shù)緩沖區(qū)的動(dòng)態(tài)地形(一)
30. Metal框架詳細(xì)解析(三十) —— 延遲照明(一)
31. Metal框架詳細(xì)解析(三十一) —— 在視圖中混合Metal和OpenGL渲染(一)
32. Metal框架詳細(xì)解析(三十二) —— Metal渲染管道教程(一)
33. Metal框架詳細(xì)解析(三十三) —— Metal渲染管道教程(二)
34. Metal框架詳細(xì)解析(三十四) —— Hello Metal! 一個(gè)簡(jiǎn)單的三角形的實(shí)現(xiàn)(一)
35. Metal框架詳細(xì)解析(三十五) —— Hello Metal! 一個(gè)簡(jiǎn)單的三角形的實(shí)現(xiàn)(二)
36. Metal框架詳細(xì)解析(三十六) —— Metal編程指南之概覽(一)
37. Metal框架詳細(xì)解析(三十七) —— Metal編程指南之基本Metal概念(二)
38. Metal框架詳細(xì)解析(三十八) —— Metal編程指南之命令組織和執(zhí)行模型(三)
39. Metal框架詳細(xì)解析(三十九) —— Metal編程指南之資源對(duì)象:緩沖區(qū)和紋理(四)
40. Metal框架詳細(xì)解析(四十) —— Metal編程指南之函數(shù)和庫(五)
41. Metal框架詳細(xì)解析(四十一) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 1(六)
42. Metal框架詳細(xì)解析(四十二) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 2(七)
43. Metal框架詳細(xì)解析(四十三) —— Metal編程指南之?dāng)?shù)據(jù)并行計(jì)算處理:計(jì)算命令編碼器(八)
44. Metal框架詳細(xì)解析(四十四) —— Metal編程指南之緩沖和紋理操作:Blit命令編碼器(九)
45. Metal框架詳細(xì)解析(四十五) —— Metal編程指南之Metal工具(十)

Tessellation

可用于:iOS_GPUFamily3_v2OSX_GPUFamily1_v2

Tessellation用于從由控制點(diǎn)組成的四邊形或三角形補(bǔ)片構(gòu)造的初始表面計(jì)算更詳細(xì)的表面。 為了逼近高階曲面,GPU使用每個(gè)補(bǔ)丁tessellation因子將每個(gè)補(bǔ)丁細(xì)分為三角形


Metal Tessellation Pipeline - Metal Tessellation管道

圖12-1顯示了Metal tessellation管道,它使用計(jì)算機(jī)內(nèi)核,tessellatorpost-tessellation頂點(diǎn)函數(shù)。

Figure 12-1 The Metal tessellation pipeline

Tessellation對(duì)貼片進(jìn)行操作,每個(gè)貼片表示由一組控制點(diǎn)定義的任意幾何排列。 每個(gè)補(bǔ)丁tessellation因子,每補(bǔ)丁用戶數(shù)據(jù)和補(bǔ)丁控制點(diǎn)數(shù)據(jù)均存儲(chǔ)在單獨(dú)的MTLBuffer對(duì)象中。

1. Compute Kernel - 計(jì)算內(nèi)核

計(jì)算內(nèi)核是執(zhí)行以下操作的內(nèi)核函數(shù)(kernel function)

  • 計(jì)算per-patch tessellation因子。
  • (可選)計(jì)算每個(gè)修補(bǔ)程序的用戶數(shù)據(jù)。
  • (可選)計(jì)算或修改補(bǔ)丁控制點(diǎn)數(shù)據(jù)。

注意:計(jì)算內(nèi)核不需要每幀執(zhí)行,以計(jì)算每個(gè)補(bǔ)丁程序的tessellation,每個(gè)補(bǔ)丁程序用戶數(shù)據(jù)或補(bǔ)丁控制點(diǎn)數(shù)據(jù)。 只要您在需要時(shí)向tessellatorpost-tessellation頂點(diǎn)函數(shù)提供所需數(shù)據(jù),您就可以每n幀,離線或任何其他運(yùn)行時(shí)方式計(jì)算此數(shù)據(jù)。

2. Tessellator

tessellator是一個(gè)固定功能的流水線階段,它創(chuàng)建補(bǔ)丁表面的采樣模式并生成連接這些樣本的圖形基元。tessellator在規(guī)范化坐標(biāo)系中平鋪規(guī)范域,范圍從0.0到1.0。

tessellator配置為渲染管道的一部分,使用MTLRenderPipelineDescriptor對(duì)象構(gòu)建MTLRenderPipelineState對(duì)象。tessellator的輸入是每個(gè)補(bǔ)丁tessellator因子。

Tessellator Primitive Generation

tessellator每個(gè)補(bǔ)丁運(yùn)行一次,消耗輸入補(bǔ)丁并生成一組新的三角形。這些三角形是通過根據(jù)提供的每個(gè)補(bǔ)丁tessellator細(xì)分補(bǔ)丁而產(chǎn)生的。由tessellator生成的每個(gè)三角形頂點(diǎn)在標(biāo)準(zhǔn)化參數(shù)空間中具有關(guān)聯(lián)的(u,v)(u,v,w)位置,每個(gè)參數(shù)值的范圍從0.0到1.0。 (請(qǐng)注意,細(xì)分是以與實(shí)現(xiàn)相關(guān)的方式執(zhí)行的。)

3. Post-Tessellation Vertex Function - Post-Tessellation頂點(diǎn)函數(shù)

post-tessellation頂點(diǎn)函數(shù)是頂點(diǎn)函數(shù),其計(jì)算由tessellator生成的每個(gè)補(bǔ)片表面樣本的頂點(diǎn)數(shù)據(jù)。post-tessellation頂點(diǎn)函數(shù)的輸入是:

  • 補(bǔ)丁上的標(biāo)準(zhǔn)化頂點(diǎn)坐標(biāo)(由tessellator輸出)。
  • 每個(gè)補(bǔ)丁用戶數(shù)據(jù)(可選擇由計(jì)算內(nèi)核輸出)。
  • 補(bǔ)丁控制點(diǎn)數(shù)據(jù)(可選地由計(jì)算內(nèi)核輸出)。
  • 任何其他頂點(diǎn)函數(shù)輸入,例如紋理和緩沖區(qū)。

post-tessellation頂點(diǎn)函數(shù)生成tessellator三角形的最終頂點(diǎn)數(shù)據(jù)。在post-tessellation頂點(diǎn)函數(shù)完成執(zhí)行之后,對(duì)細(xì)分的圖元進(jìn)行柵格化,并且渲染管線的其余階段正常執(zhí)行。


Per-Patch Tessellation Factors

每個(gè)補(bǔ)丁tessellation因子指定tessellator對(duì)每個(gè)補(bǔ)丁進(jìn)行細(xì)分的程度。 每個(gè)貼片tessellation由四邊形貼片的MTLQuadTessellationFactorsHalf結(jié)構(gòu)或三角形貼片的MTLTriangleTessellationFactorsHalf結(jié)構(gòu)描述。

注意:雖然結(jié)構(gòu)成員的類型為uint16_t,但是提供給tessellator的每個(gè)補(bǔ)丁tessellation因子必須是half的一半。

1. Understanding Quad Patches - 了解四邊形補(bǔ)丁

對(duì)于四邊形貼片,貼片中的位置是(u,v)笛卡爾坐標(biāo),指示頂點(diǎn)相對(duì)于四邊形貼片邊界的水平和垂直位置,如圖12-2所示。 (u,v)值各自為0.0到1.0。

Figure 12-2 Quad patch coordinates in normalized parameter space

Interpreting the MTLQuadTessellationFactorsHalf structure - 解釋MTLQuadTessellationFactorsHalf結(jié)構(gòu)

MTLQuadTessellationFactorsHalf結(jié)構(gòu)定義如下:

typedef struct {
    uint16_t edgeTessellationFactor[4];
    uint16_t insideTessellationFactor[2];
} MTLQuadTessellationFactorsHalf;

結(jié)構(gòu)中的每個(gè)值都提供特定的tessellation因子:

  • edgeTessellationFactor [0]提供補(bǔ)丁邊緣的tessellation因子,其中u = 0(邊0)。
  • edgeTessellationFactor [1]提供補(bǔ)丁邊緣的tessellation因子,其中v = 0(邊緣1)。
  • edgeTessellationFactor [2]提供補(bǔ)丁邊緣的tessellation因子,其中u = 1(邊2)。
  • edgeTessellationFactor [3]提供補(bǔ)丁邊緣的tessellation因子,其中v = 1(邊3)。
  • insideTessellationFactor [0]為v的所有內(nèi)部值提供水平tessellation因子。
  • insideTessellationFactor [1]為u的所有內(nèi)部值提供垂直tessellation因子。

2. Understanding Triangle Patches - 了解三角形補(bǔ)丁

對(duì)于三角形貼片,貼片中的位置是(u,v,w)重心坐標(biāo),表示三角形的三個(gè)頂點(diǎn)對(duì)頂點(diǎn)位置的相對(duì)影響,如圖12-3所示。(u,v,w)值的范圍從0.0到1.0,其中u + v + w =?? 1.0

Figure 12-3 Triangle patch coordinates in normalized parameter space

Interpreting the MTLTriangleTessellationFactorsHalf structure - 解釋MTLTriangleTessellationFactorsHalf結(jié)構(gòu)

MTLTriangleTessellationFactorsHalf結(jié)構(gòu)定義如下:

typedef struct {
    uint16_t edgeTessellationFactor[3];
    uint16_t insideTessellationFactor;
} MTLTriangleTessellationFactorsHalf;

結(jié)構(gòu)中的每個(gè)值都提供特定的tessellation因子:

  • edgeTessellationFactor [0]提供補(bǔ)丁邊緣的曲面細(xì)分因子,其中u = 0(邊0)。
  • edgeTessellationFactor [1]提供補(bǔ)丁邊緣的曲面細(xì)分因子,其中v = 0(邊緣1)。
  • edgeTessellationFactor [2]提供補(bǔ)丁邊緣的曲面細(xì)分因子,其中w = 1(邊2)。
  • insideTessellationFactor提供內(nèi)部tessellation因子。

3. Rules for Discarding Patches - 丟棄補(bǔ)丁的規(guī)則

如果邊緣tessellation因子的值為負(fù),零或?qū)?yīng)于浮點(diǎn)NaN,則tessellator將丟棄該補(bǔ)丁。如果內(nèi)部tessellation因子的值為負(fù),則tessellation因子將被限制在tessellationPartitionMode屬性定義的范圍內(nèi),并且tessellator不會(huì)丟棄該修補(bǔ)程序。

如果未丟棄補(bǔ)丁且tessellationFactorScaleEnabled屬性設(shè)置為YES,則tessellator將邊緣和內(nèi)部tessellation因子乘以setTessellationFactorScale:方法中指定的比例因子。

當(dāng)丟棄補(bǔ)丁時(shí),不會(huì)生成新的基元,post-tessellation頂點(diǎn)函數(shù)不會(huì)執(zhí)行,并且不會(huì)為該補(bǔ)丁生成可見的輸出。

4. Specifying the Per-Patch Tessellation Factors Buffer - 指定Per-Patch Tessellation Factors緩沖區(qū)

每個(gè)補(bǔ)丁tessellation被寫入MTLBuffer對(duì)象,并通過調(diào)用setTessellationFactorBuffer:offset:instanceStride:方法作為輸入傳遞給tessellator。在對(duì)同一MTLRenderCommandEncoder對(duì)象發(fā)出補(bǔ)丁繪制調(diào)用之前,必須調(diào)用此方法。


Patch Functions - Patch 函數(shù)

本節(jié)總結(jié)了支持tessellation的Metal著色語言的主要更改。 有關(guān)詳細(xì)信息,請(qǐng)參閱Metal Shading Language GuideFunctions, Variables, and Qualifiers一章。

1. Creating a Compute Kernel - 創(chuàng)建計(jì)算內(nèi)核

計(jì)算內(nèi)核是使用現(xiàn)有kernel函數(shù)限定符標(biāo)識(shí)的內(nèi)核函數(shù)。 Listing 12-1是計(jì)算內(nèi)核函數(shù)簽名的示例。

Listing 12-1  Compute kernel function signature

kernel void my_compute_kernel(...) {...}

Metal著色語言的現(xiàn)有功能完全支持計(jì)算內(nèi)核。 計(jì)算內(nèi)核函數(shù)的輸入和輸出與常規(guī)內(nèi)核函數(shù)相同。

2. Creating a Post-Tessellation Vertex Function - 創(chuàng)建Post-Tessellation頂點(diǎn)函數(shù)

post-tessellation頂點(diǎn)函數(shù)是使用現(xiàn)有vertex函數(shù)限定符標(biāo)識(shí)的頂點(diǎn)函數(shù)。 另外,新的[[patch(patch-type),N]]屬性用于指定補(bǔ)丁類型(patch-type)patch (N)中的控制點(diǎn)數(shù)。 Listing 12-2是一個(gè)post-tessellation頂點(diǎn)函數(shù)簽名的示例。

Listing 12-2  Post-tessellation vertex function signature

[[patch(quad, 16)]]
vertex float4 my_post_tessellation_vertex_function(...) {...}

注意:在OS X中,必須始終指定修補(bǔ)程序中的控制點(diǎn)數(shù)。 在iOS和tvOS中,指定此值是可選的。 如果指定了此值,則它必須與補(bǔ)丁繪制調(diào)用的numberOfPatchControlPoints參數(shù)的值匹配。

Post-Tessellation Vertex Function Inputs - Post-Tessellation頂點(diǎn)函數(shù)輸入

post-tessellation頂點(diǎn)函數(shù)的所有輸入都作為以下一個(gè)或多個(gè)參數(shù)傳遞:

  • 緩沖區(qū)(在deviceconstant地址空間中聲明),紋理或采樣器等資源。
  • 每個(gè)補(bǔ)丁數(shù)據(jù)和補(bǔ)丁控制點(diǎn)數(shù)據(jù)。 這些可以直接從緩沖區(qū)中讀取,也可以作為使用[[stage_in]]限定符聲明的輸入傳遞給post-tessellation頂點(diǎn)函數(shù)。
  • 內(nèi)置變量,如表12-1所示。
Table 12-1 Attribute qualifiers for post-tessellation vertex function input arguments

Post-Tessellation Vertex Function Outputs - Post-Tessellation頂點(diǎn)函數(shù)輸出

post-tessellation頂點(diǎn)函數(shù)的輸出與常規(guī)頂點(diǎn)函數(shù)相同。 如果post-tessellation頂點(diǎn)函數(shù)寫入緩沖區(qū),則其返回類型必須為void


Tessellation Pipeline State - Tessellation管道狀態(tài)

本節(jié)總結(jié)了支持tessellation的Metal框架API的主要更改,這些更改與tessellation管道狀態(tài)有關(guān)。

1. Building a Compute Pipeline - 構(gòu)建計(jì)算管道

在構(gòu)建MTLComputePipelineState對(duì)象時(shí),計(jì)算內(nèi)核被指定為計(jì)算管道的一部分,如Listing 12-3所示。 為獲得最佳性能,應(yīng)在幀中盡早執(zhí)行計(jì)算內(nèi)核。 (為了支持計(jì)算內(nèi)核或tessellation,現(xiàn)有計(jì)算管道API沒有變化。)

Listing 12-3  Building a compute pipeline with a compute kernel

// Fetch the compute kernel from the library
id <MTLFunction> computeKernel = [_library newFunctionWithName:@"my_compute_kernel"];
 
// Build the compute pipeline
NSError *pipelineError = NULL;
_computePipelineState = [_device newComputePipelineStateWithFunction:computeKernel error:&pipelineError];
if (!_computePipelineState) {
    NSLog(@"Failed to create compute pipeline state, error: %@", pipelineError);
}

2. Building a Render Pipeline - 構(gòu)建渲染管道

tessellator配置為渲染管道的一部分,使用MTLRenderPipelineDescriptor對(duì)象構(gòu)建MTLRenderPipelineState對(duì)象。 使用vertexFunction屬性指定post-tessellation頂點(diǎn)函數(shù)。 Listing 12-4演示了如何使用tessellatorpost-tessellation頂點(diǎn)函數(shù)配置和構(gòu)建渲染管道。 有關(guān)詳細(xì)信息,請(qǐng)參閱MTLRenderPipelineDescriptor類引用的Specifying Tessellation StateMTLTessellationFactorStepFunction部分。

Listing 12-4  Building a render pipeline with a tessellator and a post-tessellation vertex function

// Fetch the post-tessellation vertex function from the library
id <MTLFunction> postTessellationVertexFunction = [_library newFunctionWithName:@"my_post_tessellation_vertex_function"];
 
// Fetch the fragment function from the library
id <MTLFunction> fragmentFunction = [_library newFunctionWithName:@"my_fragment_function"];
 
// Configure the render pipeline, using the default tessellation values
MTLRenderPipelineDescriptor *renderPipelineDescriptor = [MTLRenderPipelineDescriptor new];
renderPipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat;
renderPipelineDescriptor.fragmentFunction = fragmentFunction;
renderPipelineDescriptor.vertexFunction = postTessellationVertexFunction;
renderPipelineDescriptor.maxTessellationFactor = 16;
renderPipelineDescriptor.tessellationFactorScaleEnabled = NO;
renderPipelineDescriptor.tessellationFactorFormat = MTLTessellationFactorFormatHalf;
renderPipelineDescriptor.tessellationControlPointIndexType = MTLTessellationControlPointIndexTypeNone;
renderPipelineDescriptor.tessellationFactorStepFunction = MTLTessellationFactorStepFunctionConstant;
renderPipelineDescriptor.tessellationOutputWindingOrder = MTLWindingClockwise;
renderPipelineDescriptor.tessellationPartitionMode = MTLTessellationPartitionModePow2;
 
// Build the render pipeline
NSError *pipelineError = NULL;
_renderPipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&pipelineError];
if (!_renderPipelineState) {
    NSLog(@"Failed to create render pipeline state, error %@", pipelineError);
}

Patch Draw Calls - Patch繪制調(diào)用

本節(jié)總結(jié)了支持tessellation的Metal框架API的主要更改,與補(bǔ)丁繪制調(diào)用有關(guān)。

1. Drawing Tessellated Patches - 繪制Tessellated補(bǔ)丁

要呈現(xiàn)tessellated補(bǔ)丁的大量實(shí)例,請(qǐng)調(diào)用以下MTLRenderCommandEncoder方法之一:

注意:只有將vertexFunction屬性設(shè)置為post-tessellation頂點(diǎn)函數(shù)時(shí),才能調(diào)用這些補(bǔ)丁繪制調(diào)用。調(diào)用非補(bǔ)丁繪制調(diào)用會(huì)導(dǎo)致驗(yàn)證層報(bào)告錯(cuò)誤。
Patch繪制調(diào)用不支持基本重啟功能。

對(duì)于所有補(bǔ)丁繪制調(diào)用,每個(gè)補(bǔ)丁數(shù)據(jù)和一個(gè)補(bǔ)丁控制點(diǎn)數(shù)組被組織起來,用于在連續(xù)數(shù)組元素中進(jìn)行渲染,從baseInstance參數(shù)中指定的值開始。有關(guān)每個(gè)參數(shù)的詳細(xì)信息,請(qǐng)參閱MTLRenderCommandEncoder協(xié)議參考的Drawing Tessellated Patches部分。

為了呈現(xiàn)補(bǔ)丁數(shù)據(jù),補(bǔ)丁繪制調(diào)用每補(bǔ)丁數(shù)據(jù)和補(bǔ)丁控制點(diǎn)數(shù)據(jù)。補(bǔ)丁數(shù)據(jù)通常一起存儲(chǔ)在一個(gè)或多個(gè)緩沖區(qū)中的一個(gè)或多個(gè)網(wǎng)格的所有補(bǔ)丁中。執(zhí)行計(jì)算內(nèi)核以生成依賴于場(chǎng)景的per-patch tessellation;計(jì)算內(nèi)核可能決定僅為未被丟棄的補(bǔ)丁生成因子,在這種情況下補(bǔ)丁不是連續(xù)的。因此,補(bǔ)丁索引緩沖區(qū)用于標(biāo)識(shí)要繪制的補(bǔ)丁的補(bǔ)丁ID

來自[patchStart,patchStart + patchCount-1]的緩沖區(qū)索引(drawPatchIndex)用于引用數(shù)據(jù)。如果用于獲取每個(gè)補(bǔ)丁數(shù)據(jù)和補(bǔ)丁控制點(diǎn)數(shù)據(jù)的補(bǔ)丁索引不是連續(xù)的,則drawPatchIndex可以引用patchIndexBuffer,如圖12-4所示。

Figure 12-4 Using patchIndexBuffer to fetch per-patch data and patch control point data

patchIndexBuffer的每個(gè)元素都包含一個(gè)32位patchIndex值,該值引用每個(gè)補(bǔ)丁數(shù)據(jù)和補(bǔ)丁控制點(diǎn)數(shù)據(jù)。 從patchIndexBuffer獲取的patchIndex位于:(drawPatchIndex * 4)+ patchIndexBufferOffset

補(bǔ)丁的控制點(diǎn)索引通過以下方式計(jì)算:

patchIndex * numberOfPatchControlPoints *((patchIndex + 1)* numberOfPatchControlPoints) - 1

patchIndexBuffer還使用于讀取每個(gè)補(bǔ)丁數(shù)據(jù)和補(bǔ)丁控制點(diǎn)數(shù)據(jù)的patchIndex與用于讀取每補(bǔ)丁tessellation因子的索引不同。 對(duì)于tessellatordrawPatchIndex直接用作獲取每個(gè)補(bǔ)丁tessellation因子的索引。

如果patchIndexBufferNULL,則drawPatchIndexpatchIndex的值相同,如圖12-5所示。

Figure 12-5 Fetching per-patch data and patch control point data, if patchIndexBuffer is NULL

如果控制點(diǎn)在補(bǔ)丁之間共享或補(bǔ)丁控制點(diǎn)數(shù)據(jù)不連續(xù),請(qǐng)使用drawIndexedPatches方法。 patchIndex引用一個(gè)指定的controlPointIndexBuffer,它包含一個(gè)補(bǔ)丁的控制點(diǎn)索引,如圖12-6所示。 (tessellationControlPointIndexType描述controlPointIndexBuffer中控制點(diǎn)索引的大小,并且必須是MTLTessellationControlPointIndexTypeUInt16MTLTessellationControlPointIndexTypeUInt32。)

Figure 12-6 Using controlPointIndexBuffer to fetch patch control point data

controlPointIndexBuffer中第一個(gè)控制點(diǎn)索引的實(shí)際位置計(jì)算如下:

controlPointIndexBufferOffset +(patchIndex * numberOfPatchControlPoints * controlPointIndexType == UInt16?2:4)

幾個(gè)(numberOfPatchControlPoints)控制點(diǎn)索引必須連續(xù)存儲(chǔ)在controlPointIndexBuffer中,從第一個(gè)控制點(diǎn)索引的位置開始。


Sample Code - 示例代碼

有關(guān)如何設(shè)置基本tessellation的示例,請(qǐng)參閱MetalBasicTessellation示例。


Porting DirectX 11-Style Tessellation Shaders to Metal - 將DirectX 11樣式Tessellation著色器移植到Metal

本節(jié)介紹如何將DirectX 11樣式tessellation頂點(diǎn)和外殼著色器移植到Metal計(jì)算內(nèi)核。

注意:Metal tessellator執(zhí)行DirectX 11tessellator的等效計(jì)算。 Metal post-tessellation頂點(diǎn)函數(shù)執(zhí)行DirectX 11域著色器的等效計(jì)算。

DirectX 11中,為補(bǔ)丁的每個(gè)控制點(diǎn)執(zhí)行HLSL頂點(diǎn)著色器。 HLSL外殼著色器由兩個(gè)函數(shù)指定:一個(gè)為補(bǔ)丁的每個(gè)控制點(diǎn)執(zhí)行的函數(shù),另一個(gè)執(zhí)行每個(gè)補(bǔ)丁的函數(shù)。 頂點(diǎn)著色器的輸出是構(gòu)成外殼著色器的這兩個(gè)函數(shù)的輸入。

Listing 12-5顯示了一個(gè)簡(jiǎn)單的HLSL頂點(diǎn)和外殼著色器

Listing 12-5  Simple HLSL vertex and hull shader

struct VertexIn
{
    float3 PosL;
    float3 NormalL;
    float3 TangentL;
    float2 Tex;
};
 
struct VertexOut
{
    float3 PosW       : POSITION;
    float3 NormalW    : NORMAL;
    float3 TangentW   : TANGENT;
    float2 Tex        : TEXCOORD;
    float  TessFactor : TESS;
};
 
VertexOut VS(VertexIn vin)
{
    VertexOut vout;
 
    // Transform to world space space.
    vout.PosW     = mul(float4(vin.PosL, 1.0f), gWorld).xyz;
    vout.NormalW  = mul(vin.NormalL, (float3x3)gWorldInvTranspose);
    vout.TangentW = mul(vin.TangentL, (float3x3)gWorld);
 
    // Output vertex attributes for interpolation across triangle.
    vout.Tex = mul(float4(vin.Tex, 0.0f, 1.0f), gTexTransform).xy;
 
    float d = distance(vout.PosW, gEyePosW);
 
    // Normalized tessellation factor.
    // The tessellation is
    //   0 if d >= gMinTessDistance and
    //   1 if d <= gMaxTessDistance.
    float tess = saturate( (gMinTessDistance - d) /
                   (gMinTessDistance - gMaxTessDistance) );
 
    // Rescale [0,1] --> [gMinTessFactor, gMaxTessFactor].
    vout.TessFactor = gMinTessFactor + tess*(gMaxTessFactor-gMinTessFactor);
 
    return vout;
}
 
struct HullOut
{
    float3 PosW     : POSITION;
    float3 NormalW  : NORMAL;
    float3 TangentW : TANGENT;
    float2 Tex      : TEXCOORD;
};
 
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("PatchHS")]
HullOut HS(InputPatch<VertexOut,3> p,
           uint i : SV_OutputControlPointID,
           uint patchId : SV_PrimitiveID)
{
    HullOut hout;
 
    // Pass through shader.
    hout.PosW     = p[i].PosW;
    hout.NormalW  = p[i].NormalW;
    hout.TangentW = p[i].TangentW;
    hout.Tex      = p[i].Tex;
 
    return hout;
}
 
struct PatchTess
{
    float EdgeTess[3] : SV_TessFactor;
    float InsideTess  : SV_InsideTessFactor;
};
 
PatchTess PatchHS(InputPatch<VertexOut,3> patch,
                  uint patchID : SV_PrimitiveID)
{
    PatchTess pt;
 
    // Average tess factors along edges, and pick an edge tess factor for
    // the interior tessellation.  It is important to do the tess factor
    // calculation based on the edge properties so that edges shared by
    // more than one triangle will have the same tessellation factor.
    // Otherwise, gaps can appear.
    pt.EdgeTess[0] = 0.5f*(patch[1].TessFactor + patch[2].TessFactor);
    pt.EdgeTess[1] = 0.5f*(patch[2].TessFactor + patch[0].TessFactor);
    pt.EdgeTess[2] = 0.5f*(patch[0].TessFactor + patch[1].TessFactor);
    pt.InsideTess  = pt.EdgeTess[0];
 
    return pt;
}

這些簡(jiǎn)單的HLSL頂點(diǎn)和外殼著色器可以移植到Metal函數(shù),并且可以創(chuàng)建一個(gè)調(diào)用這些Metal函數(shù)的計(jì)算內(nèi)核,將這些函數(shù)作為單個(gè)內(nèi)核執(zhí)行。 移植的頂點(diǎn)和控制點(diǎn)外殼函數(shù)在計(jì)算內(nèi)核中被稱為每線程(per-thread),后跟一個(gè)線程組阻塞,然后每個(gè)補(bǔ)丁程序的外殼函數(shù)由線程組中的線程子集執(zhí)行。 能夠直接在內(nèi)核中調(diào)用已轉(zhuǎn)換的頂點(diǎn)和外殼函數(shù),使開發(fā)人員可以輕松地將其頂點(diǎn)和外殼著色器從DirectX 11移植到Metal

簡(jiǎn)單的HLSL頂點(diǎn)和外殼著色器可以移植到Listing 12-6中所示的Metal函數(shù)

Listing 12-6  Simple HLSL vertex and hull shader ported to Metal functions

struct VertexIn
{
    float3 PosL  [[ attribute(0) ]];
    float3 NormalL  [[ attribute(1) ]];
    float3 TangentL  [[ attribute(2) ]];
    float2 Tex  [[ attribute(3) ]];
};
 
struct VertexOut
{
    float3 PosW [[ position ]];
    float3 NormalW;
    float3 TangentW;
    float2 Tex;
    float  TessFactor;
};
 
struct ConstantData {
    …;
}
 
// The vertex control point function
VertexOut
VS(VertexIn vin,
   constant ConstantData &c)
{
    VertexOut vout;
 
    // Transform to world space space.
    vout.PosW     = mul(float4(vin.PosL, 1.0f), c.gWorld).xyz;
    vout.NormalW  = mul(vin.NormalL, (float3x3)c.gWorldInvTranspose);
    vout.TangentW = mul(vin.TangentL, (float3x3)c.gWorld);
 
    // Output vertex attributes for interpolation across triangle.
    vout.Tex = mul(float4(vin.Tex, 0.0f, 1.0f), c.gTexTransform).xy;
 
    float d = distance(vout.PosW, gEyePosW);
 
    // Normalized tessellation factor.
    // The tessellation is
    //   0 if d >= gMinTessDistance and
    //   1 if d <= gMaxTessDistance.
    float tess = saturate( (c.gMinTessDistance - d) /
                   (c.gMinTessDistance - c.gMaxTessDistance) );
 
    // Rescale [0,1] --> [gMinTessFactor, gMaxTessFactor].
    vout.TessFactor = c.gMinTessFactor +
                tess * (c.gMaxTessFactor - c.gMinTessFactor);
 
    return vout;
}
 
struct HullOut
{
    float3 PosW [[ position ]];
    float3 NormalW;
    float3 TangentW;
    float2 Tex;
}
 
// The patch control point function
HullOut
HS(VertexOut p)
{
    HullOut hout;
 
    // Pass through shader.
    hout.PosW     = p.PosW;
    hout.NormalW  = p.NormalW;
    hout.TangentW = p.TangentW;
    hout.Tex      = p.Tex;
 
    return hout;
}
 
struct PatchTess
{
    packed_half3 EdgeTess;
    half  InsideTess;
};
 
// The per-patch function
PatchTess
PatchHS(threadgroup VertexOut *patch)
{
    PatchTess pt;
 
    // Average tess factors along edges, and pick an edge tess factor for
    // the interior tessellation.  It is important to do the tess factor
    // calculation based on the edge properties so that edges shared by
    // more than one triangle will have the same tessellation factor.
    // Otherwise, gaps can appear.
    pt.EdgeTess[0] = 0.5f*(patch[1].TessFactor + patch[2].TessFactor);
    pt.EdgeTess[1] = 0.5f*(patch[2].TessFactor + patch[0].TessFactor);
    pt.EdgeTess[2] = 0.5f*(patch[0].TessFactor + patch[1].TessFactor);
    pt.InsideTess  = pt.EdgeTess[0];
 
    return pt;
}
A compute kernel that calls these vertex and hull functions can be:
struct KernelPatchInfo {
    uint numPatches; // total number of patches to process.
                     // we need this because this value may
                     // not be a multiple of threadgroup size.
    ushort numPatchesInThreadGroup; // number of patches processed by a
                                    // thread-group
    ushort numControlPointsPerPatch;
};  // passed as a constant buffer using setBytes by the runtime
 
kernel void
PatchKernel(VertexIn vIn [[ stage_in ]],
            constant ConstantData &c [[ buffer(1) ]],
            constant KernelPatchInfo &patchInfo [[ buffer(2) ]],
            PatchTess *tessellationFactorBuffer [[ buffer(3) ]],
            device HullOut *hullOutputBuffer [[ buffer(4) ]],
            threadgroup HullOut *hullOutputTGBuffer [[ threadgroup(0) ]],
            uint tID [[ thread_position_in_grid ]],
            ushort lID [[ thread_position_in_threadgroup ]],
            ushort lSize [[ threads_in_threadgroup ]],
            ushort groupID [[ threadgroup_position_in_grid ]])
{
    ushort n = patchInfo.numControlPointsPerPatch;
    uint patchGroupID = groupID * patchInfo.numPatchesInThreadGroup;
 
    // execute the vertex and control-point hull function per-thread
    if ( (lID <= (patchInfo.numPatchesInThreadGroup * n) &&
         (tID <= (patchInfo.numPatches * n)) )
    {
        uint controlPointID = patchGroupID * n + lID;
 
        VertexOut vOut = VS(vIn, c);
      HullOut hOut = HS(vOut);
 
        hullOutputTGBuffer[lID] = hOut;
      hullOutputBuffer[controlPointID] = hOut;
    }
 
    threadgroup_barrier(mem_flags::mem_threadgroup);
 
    // execute the per-patch hull function
    if (lID < patchInfo.numPatchesInThreadGroup)
    {
        uint patchID = patchGroupID + lID;
        tessellationFactorBuffer[patchID] = PatchHS(
                                                  hullOutputTGBuffer[lID*n]);
    }
}

注意:

  • 線程組大小應(yīng)設(shè)置為SIMD大小或SIMD大小的倍數(shù)。
  • 線程組中的補(bǔ)丁數(shù)由補(bǔ)丁中的threadgroup size / number of control points給出。
  • 每個(gè)補(bǔ)丁的控制點(diǎn)數(shù)量在HLSL外殼著色器中描述。
  • 在此移植示例中,輸入和輸出控制點(diǎn)的數(shù)量是相同的。 通過對(duì)計(jì)算內(nèi)核的一些修改,也可以支持輸入和輸出控制點(diǎn)的數(shù)量不相同的情況。

后記

本篇主要講述了Metal編程指南之Tessellation,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容