Metal框架详细解析(二十四) —— 基本课程之参数缓冲 - 基本参数缓冲(四)

版本记录

版本号 时间
V1.0 2018.10.09 星期二

前言

很多做视频和图像的,相信对这个框架都不是很陌生,它渲染高级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同步(三)

Argument Buffers - 参数缓冲

学习如何使用参数缓冲区。

  • Basic Argument Buffers

    • 演示如何使用参数缓冲区管理资源组。
  • Argument Buffers with Arrays and Resource Heaps

    • 演示如何使用数组定义参数缓冲区,并通过将参数缓冲区与资源堆组合来减少CPU开销。
  • Argument Buffers with GPU Encoding

    • 演示如何使用计算传递对参数缓冲区进行编码,然后在后续渲染过程中访问其参数。

Basic Argument Buffers - 基本参数缓冲区

演示如何使用参数缓冲区管理资源组。

Metal框架详细解析(二十四) —— 基本课程之参数缓冲 - 基本参数缓冲(四)_第1张图片

参数缓冲区表示一组资源,可以将这些资源共同指定为图形或计算功能的参数。 您可以使用参数缓冲区来降低CPU开销,简化资源管理并实现GPU驱动的管道。

在此示例中,您将学习如何在参数缓冲区中指定,编码,设置和访问资源。 特别是,您将了解在参数缓冲区中管理资源组而不是单个资源的优势。 该示例使用纹理,采样器,缓冲区和常量编码到参数缓冲区中呈现静态四边形。


CPU Overhead and Argument Buffers - CPU开销和参数缓冲区

Metal命令是高效的,当应用程序访问GPU时,会产生最小的CPU开销。 但是,每个命令都会产生一些开销;要进一步减少金额,请使用以下策略:

  • 使用更少的CPU命令执行更多GPU工作。
  • 避免重复昂贵的CPU命令。

Metal的参数缓冲区功能可以减少应用程序关键路径中CPU命令的数量和性能成本(例如,在应用程序的渲染循环中)。 参数缓冲区允许您在单个缓冲区内对多个资源进行分组和编码,而不是单独编码每个资源。 通过使用参数缓冲区,您可以将大量的CPU开销从应用程序的关键路径转移到其初始设置。


Individual Resources versus Argument Buffers - 个人资源与参数缓冲区

Metal应用程序,尤其是游戏,通常包含多个3D对象,每个对象都与一组资源相关联,如纹理,采样器,缓冲区和常量。 为了渲染每个对象,apps编码Metal命令,在发出绘制调用之前将这些资源设置为图形函数的参数。

您可以通过为每个资源调用MTLRenderCommandEncoder方法(如setVertexBuffer:offset:atIndex:setFragmentTexture:atIndex:)将各个资源设置为参数。

设置单个资源的命令可能变得繁多且昂贵,尤其是对于大型应用程序或游戏。 相反,您可以将相关资源分组到参数缓冲区中,然后将整个缓冲区设置为图形函数的单个参数。 这种方法大大降低了CPU开销,并且仍然提供对您资源的单独GPU访问。

参数缓冲区表示为MTLBuffer对象。 因此,您可以通过为每个参数缓冲区调用MTLRenderCommandEncoder方法(如setVertexBuffer:offset:atIndex:setFragmentBuffer:offset:atIndex:)将它们设置为参数。

Metal框架详细解析(二十四) —— 基本课程之参数缓冲 - 基本参数缓冲(四)_第2张图片

注意:要访问参数缓冲区中的各个资源,必须为要使用的每个资源调用useResource:usage:方法。 有关更多信息,请参阅Enable the GPU Memory of Resources in the Argument Buffer部分。


Define Argument Buffers - 定义参数缓冲区

参数缓冲区在Metal Shading Language中定义为自定义结构。 每个结构元素表示一个单独的资源,声明为纹理,采样器,缓冲区或常量数据类型。 结构元素还与使用[[id(n)]]属性限定符声明的整数相关联,该整数指定单个资源的索引。

此示例中的参数缓冲区被声明为FragmentShaderArguments结构,这是它的定义:

typedef struct FragmentShaderArguments {
    texture2d exampleTexture  [[ id(AAPLArgumentBufferIDExampleTexture)  ]];
    sampler         exampleSampler  [[ id(AAPLArgumentBufferIDExampleSampler)  ]];
    device float   *exampleBuffer   [[ id(AAPLArgumentBufferIDExampleBuffer)   ]];
    uint32_t        exampleConstant [[ id(AAPLArgumentBufferIDExampleConstant) ]];
} FragmentShaderArguments;

此参数缓冲区包含以下资源:

  • exampleTexture,索引为0的2D纹理。
  • exampleSampler,索引为1的采样器。
  • exampleBuffer,索引为2的浮点缓冲区。
  • exampleConstant,一个索引为3的uint32_t常量。

此示例的片段函数fragmentShader使用参数缓冲区作为单个参数。

fragment float4
fragmentShader(       RasterizerData            in                 [[ stage_in ]],
               device FragmentShaderArguments & fragmentShaderArgs [[ buffer(AAPLFragmentBufferIndexArguments) ]])

fragmentShaderArgs参数是FragmentShaderArguments类型的缓冲区。 当样本将MTLBuffer设置为片段函数的参数时,该函数将fragmentShaderArgs参数中的数据解释为具有纹理,采样器,缓冲区和常量的参数缓冲区(由FragmentShaderArguments结构定义)。


Encode Resources into an Argument Buffer - 将资源编码到参数缓冲区中

在函数访问缓冲区之前,必须将各个资源编码到参数缓冲区中。 这是通过从使用参数缓冲区的MTLFunction创建MTLArgumentBufferEncoder来实现的。

此示例从fragmentShader函数创建MTLArgumentBufferEncoder,该函数包含fragmentShaderArgs参数。

id  fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];

id  argumentEncoder
    = [fragmentFunction newArgumentEncoderWithBufferIndex:AAPLFragmentBufferIndexArguments];

argumentEncoderencodedLength属性确定包含参数缓冲区中所有资源所需的大小(以字节为单位)。 此示例使用该值创建一个新缓冲区_fragmentShaderArgumentBuffer,其长度参数与参数缓冲区所需的大小相匹配。

NSUInteger argumentBufferLength = argumentEncoder.encodedLength;

_fragmentShaderArgumentBuffer = [_device newBufferWithLength:argumentBufferLength options:0];

然后,此示例调用setArgumentBuffer:offset:方法以指定_fragmentShaderArgumentBuffer是可以编码资源的参数缓冲区。

[argumentEncoder setArgumentBuffer:_fragmentShaderArgumentBuffer offset:0];

此示例通过以下方式将各个资源编码到参数缓冲区中:

  • 为每种资源类型调用特定方法,例如setTexture:atIndex:,setSamplerState:atIndex:setBuffer:offset:atIndex
  • index参数的值与为FragmentShaderArguments结构的每个元素声明的[[id(n)]]属性限定符的值匹配。
[argumentEncoder setTexture:_texture atIndex:AAPLArgumentBufferIDExampleTexture];
[argumentEncoder setSamplerState:_sampler atIndex:AAPLArgumentBufferIDExampleSampler];
[argumentEncoder setBuffer:_indirectBuffer offset:0 atIndex:AAPLArgumentBufferIDExampleBuffer];

常量编码有点不同;常量数据直接嵌入到参数缓冲区中,而不是驻留在参数缓冲区指向的另一个对象中。 此示例调用constantDataAtIndex:方法以检索常量所在的参数缓冲区中的地址。 然后,该示例在检索到的地址处设置常量bufferElements的实际值。

uint32_t *numElementsAddress = [argumentEncoder constantDataAtIndex:AAPLArgumentBufferIDExampleConstant];

*numElementsAddress = bufferElements;

Enable the GPU Memory of Resources in the Argument Buffer - 在参数缓冲区中启用资源的GPU内存

Metal有效地管理GPU访问的内存;在GPU使用任何资源之前,Metal确保GPU可以访问资源的内存。 通过调用MTLRenderCommandEncoder方法(例如setVertexBuffer:offset:atIndex:setFragmentTexture:atIndex:)来单独设置资源可确保GPU可以访问资源的内存。

但是,当资源编码到参数缓冲区中时,设置参数缓冲区不会单独设置其每个资源。 Metal不检查参数缓冲区以确定在其中编码哪些资源(这种昂贵的操作会否定参数缓冲区的性能优势)。 因此,Metal无法确定GPU可以访问哪些资源的内存。 相反,您调用useResource:usage:方法来显式指示MTLRenderCommandEncoder使GPU可访问特定资源的内存。

注意:您只需要在MTLRenderCommandEncoder的生命周期内为每个资源调用一次useResource:usage:方法,即使您在多个绘制调用中使用该资源也是如此。useResource:usage:方法特定于参数缓冲区,但调用它比单独设置每个资源要便宜得多。


Set Argument Buffers - 设置参数缓冲区

此示例调用useResource:usage:方法,用于编入参数缓冲区的_texture_indirectBuffer资源。 这些调用指定MTLResourceUsage值,进一步指示对每个资源执行哪些GPU操作(对纹理进行采样并在GPU中读取缓冲区)。

[renderEncoder useResource:_texture usage:MTLResourceUsageSample];
[renderEncoder useResource:_indirectBuffer usage:MTLResourceUsageRead];

注意:useResource:usage:方法不适用于采样器或常量,因为它们不是MTLResource对象。

此示例仅将_fragmentShaderArgumentBuffer设置为片段函数的参数;它不单独设置_texture_indirectBuffer_samplerbufferElements资源。 此命令允许片段函数访问参数缓冲区及其编码资源。

[renderEncoder setFragmentBuffer:_fragmentShaderArgumentBuffer
                          offset:0
                         atIndex:AAPLFragmentBufferIndexArguments];

Access the Resources in an Argument Buffer - 访问参数缓冲区中的资源

在函数内,访问参数缓冲区中编码的资源类似于直接访问各个资源。 主要区别在于资源是作为参数缓冲区结构的元素访问的。

在此示例中,通过fragmentShader函数的fragmentShaderArgs参数访问参数缓冲区资源。

// Get the sampler encoded in the argument buffer
sampler exampleSampler = fragmentShaderArgs.exampleSampler;

// Sample the texture encoded in the argument buffer
half4 textureSample = fragmentShaderArgs.exampleTexture.sample(exampleSampler, in.texCoord);

// Use the fragment position and the constant encoded in the argument buffer to calculate an array index
uint32_t index = (uint32_t)in.position.x % fragmentShaderArgs.exampleConstant;

// Index into the buffer encoded in the argument buffer
float colorScale = fragmentShaderArgs.exampleBuffer[index];

该示例使用参数缓冲区中的所有四个资源来生成每个片段的最终颜色。

在此示例中,您学习了如何在参数缓冲区中指定,编码,设置和访问资源。

后记

本篇主要讲述了基本参数缓冲,感兴趣的给个赞或者关注~~~

Metal框架详细解析(二十四) —— 基本课程之参数缓冲 - 基本参数缓冲(四)_第3张图片

你可能感兴趣的:(Metal框架详细解析(二十四) —— 基本课程之参数缓冲 - 基本参数缓冲(四))