Metal简述
Metal是苹果公2014年推出的一套取代OpenGLES的渲染应用程序编程接口,支持到iOS8以上。Metal不单延续了OpenGLES中的渲染高级3D图形
,还可以使用GPU
高效完成数据并行计算。
Core Image
, SpriteKit
, 和 SceneKit
已经在使用了。
Metal优化点
- 最大程度的降低了CPU的相关开销
- 最大程度的使用GPU的性能
- 最大限度的提高了CPU、GPU的并行能力
- 最大限度的进行系统资源的有效管理
graphics pipeline(图形管道)
- 和OpenGL中的图形管道相比相似度非常高;
- 顶点处理:物体矩阵、世界矩阵、观察者矩阵(相当于MVP),裁剪
- 图元装配方式:点、线、线环、三角形、三角形带
- 片段处理:纹理、模板、透明度、混合
Cpu:处理顶点数据->GPU: 顶点处理(顶点着色器)->图元装配(5种)->光栅化->片段处理(片元着色器)->帧缓存区
OpenGl
可以对照这幅图回忆一下自己的知识结构和OpenGLES中的知识点。
在使用Metal前,Apple有一些建议
-
Separate Your Rendering Loop
分开渲染循环:不希望将渲染的处理逻辑放到ViewController | View中。建议单独创建一个类来完成各种Metal的渲染绘制工作。 -
Respond to View Events
响应代理<MTKViewDelegate>的事件 -
Metal Command Objects
创建一个命令对象,即创建执行命令的GPU
Metal中对象之间的关系
图一
图二
两张图可以对比来看
838133-8b76a2d6479f743b.jpg
- 命令缓存区(command buffer) 是从命令队列列(command queue) 创建的
- 命令编码器器(command encoders) 将命令编码到命令缓存区中
- 提交命令缓存区并将其发送到GPU
- GPU执⾏行行命令并将结果呈现为可绘制
Metal中常见Api
MTKView
MTKView理解上可以对标GLKView来理解。 相同点: 提供用于绘制layer的专属视图。
不同点:
- 没有MTKViewController。
- GLKView初始化时需要提供GLKContent,而MTKView需要确定
MTLDevice
MTLDevice
Metal是直接操作GPU的,所以需要获取GPU的使用权限。在iOS中一般是通过默认的方式MTLCreateSystemDefaultDevice()
获取GPU的使用权限。也可以使用MTLCopyAllDevices()
来获取系统中所有Metal设备对象的引用数组。
MTLDevice协议表示可以执行命令的GPU,提供了如下功能
- 创建新的命令队列
- 从内存分配缓冲区
- 创建纹理
- 查询设备功能
MTLCommandQueue
在获取了GPU后,还需要一个渲染队列,即命令队列Command Queue类型是MTLCommandQueue
,该队列是与GPU交互的第一个对象,队列中存储的是将要渲染的命令MTLCommandBuffer
。正好对应上方图。
创建方式:
代码语言:javascript复制_commandQueue = [_device newCommandQueue];
- 该对象的创建需要消耗大量资源,加之这个命令队列生命周期很长,所以建议该对象作为全局,而不是反复创建和消耗。
MTLCommandBuffer
命令缓存区Command Buffer主要是用于存储编码的命令,其生命周期是指导缓存区被提交到GPU执行为止,单个的命令缓存区可以包含不同的编码命令,主要取决于用于构建它的编码器的类型和数量。
创建方式:
代码语言:javascript复制commandBuffer = [_commandQueue commandBuffer]
- buffer的创建需要通过
Command Queue
来创建的 - buffer中生成的
CommandEncoder
都需要通过当前buffer来进行提交、渲染、绘制.
commandBuffer在未提交命令缓存区之前,是不会开始执行的。等待提交后命令缓存区将按其按照加入队的顺序进行执行。当然这是整个编码步骤中的最后一步。 commandBuffer的执行顺序有以下两种:
- enqueue:顺序执行,enqueue方法在命令队列中为命令缓存区保留一个位置,此时并未提交命令缓存区,当最终提交命令缓存区后,按照命令队列的顺序依次执行
- commit:插队尽快执行,如果前面有commit还是需要排队等着
MTLRenderCommandEncoder
MTLRenderCommandEncoder表示单个渲染过程中相关联的渲染状态和渲染命令(可以对标OpenGL中的上下文状态机来理解),有以下功能:
- 指定
图形资源
,例如缓存区和纹理对象,其中包含顶点、片元、纹理图片数据 - 指定
MTLRenderPipelineState对象
,其中包含编译的渲染状态、顶点着色器、片段着色器 - 指定
固定功能状态
,包括视口,三角形填充模式,剪刀矩形,深度和模板测试以及其他值 - 绘制3D图元
编码器执行流程
- 通过调用
MTLCommandBuffer
对象的makeRenderCommandEncoder(descriptor :)
方法来创建MTLRenderCommandEncoder
对象。 - 调用
setRenderPipelineState(_ :)
方法以指定MTLRenderPipelineState
,该状态定义图形渲染管道的状态,包括顶点和片段函数。 - 指定用于顶点和片元函数输入和输出的资源,并在对应的参数中设置每个资源的位置(即索引),即将顶点数据等通过
commandEncoder
调用setVertexBytes:length:atIndex:
函数传递到metal shader Language
文件的顶点着色器和片元着色器函数 - 指定其他的固定功能状态,例如通过
commandEncoder
调用setViewport:
函数设置视口大小等 - 绘制图形
- 调用
endEncoding()
方法以终止渲染命令编码器。
MTLRenderPipelineDescriptor
MTLRenderPipelineDescriptor管道状态描述符:在渲染过程中使用的渲染配置状态,包括光栅化(例如多重采样),可见性,混合,镶嵌和图形功能状态,主要是渲染管道描述符中指定顶点或片段函数。
举例:
代码语言:javascript复制 MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
//管道名称
pipelineStateDescriptor.label = @"Simple Pipeline";
//可编程函数,用于处理渲染过程中的各个顶点
pipelineStateDescriptor.vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
//可编程函数,用于处理渲染过程中各个片段/片元
pipelineStateDescriptor.fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
//一组存储颜色数据的组件
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
//同步创建并返回渲染管线状态对象
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
MTLRenderPassDescriptor
官方文档定义为渲染命令编码器描述符:用于保存渲染过程中的一组结果
下图中红圈位置代表MTLRenderPassDescriptor
在Metal整个渲染流程中的位置,也可以对标OpenGLES中的frameBuffer
来理解
Metal渲染流程