什么是 Metal
Metal是一个和 OpenGL ES类似的面向底层的图形编程接口,通过使用相关的 api 可以直接操作 GPU,能最大的挖掘设备的 GPU 能力,进行复制的运算。
Metal 经过版本迭代,已经不再是 iOS 平台独有,现在同样支持在 macOS、watchOS 下。
Metal具有特点
- 和 CPU 并行处理数据(深度学习)
- 提供低功耗接口
- GPU 支持的 3D 渲染
- 和 CPU 共享资源内存
层级关系
UIKit -> Core Graphics -> Metal/OpenGL ES -> GPU Driver -> GPU

Metal渲染
Metal 渲染图形管线

Metal 渲染流程和 OpenGL ES 是基本一致的。
- CPU 将顶点数据传递到顶点着色器
- 顶点着色器处理顶点,将处理的结果传送到几何着色器
- 几何着色器进行图元装配
- 图元装配完成后,进入光栅化阶段,将图元光栅化为像素点
- 光栅化后,进入片段着色器,处理每一个像素点的颜色值
- 片段着色器会将数据存储到帧缓冲,并由视频控制器显示到屏幕上
Metal核心类
MTLDevice
既然是操作 GPU,当然需要去获取 GPU 对象。
Metal 中提供了 MTDevice 协议,代表了 GPU 的接口。
//获取设备
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
NSAssert(device, @"Don't support metal !");
MTLDevice提供了如下的能力:
- 查询设备状态
- 创建 buffer 和 texture
- 指令转换和队列化渲染进行指令的计算
MTKView
在 Metal 中直接绘制,需要用特殊的界面 MTKView,同时给它设置对应的 device,并把它添加到当前的界面上。
_mtkView = [[MTKView alloc] initWithFrame:self.view.frame device:_device];
[self.view addSubview:_mtkView];
在
GLKit中直接绘制,我们会使用GLKView
MTLCommandQueue
在获得 GPU 对象之后,我们需要一个渲染队列 MTLCommandQueue,通过 MTLDevice 获取队列
[_device newCommandQueue];
MTLCommandQueue具有以下特点:
- 队列是单一队列,确保了指令能够按顺序执行
- 里面的是将要渲染的指令
MTLCommandBuffer - 这是个线程安全的队列
- 支持多个 CommandBuffer 同时编码
渲染
在绘制之前,我们需要先配置好 MTLDevice、MTLCommandQueue 和 MTKView,然后就是要塞进队列中的缓冲数据 MTLCommandBuffer
简单的流程:
- 构造
MTLCommandBuffer - 配置
MTLRenderCommandEncoder,包括配置资源文件,设置渲染管线等 - 将
MTLRenderCommandEncoder进行编码 - 最后将
MTLCommandBuffer提交到队列中
MTLCommandBuffer
命令缓冲区 CommandBuffer 是包含了多种类型的命令编码。
MTLCommandBuffer是不支持重用的轻量级对象,需要每次都获取一个新的 Buffer。
通常情况下,app 的一帧就是渲染为一个单独的 Command Buffer
在创建了MTLCommandQueue之后,我们需要构建队列中的MTLCommandBuffer,一开始获取的 Buffer 对象是空的,要通过MTLCommandEncoder编码器来 Encode,一个 Buffer 可以被多个 Encoder进行编码
创建 Command Buffer
id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
执行 Command Buffer
Buffer在未提交之前,是不会开始执行的
提交方式:
enqueue顺序执行commit插队尽快执行(如果前面有 commit 还是需要排队等着)
MTLCommandEncoder
指令编码器是将GPU命令写入命令缓冲区的编码器。
包括四种编码器:
MTLRenderCommandEncoder图形渲染编码器MTLComputeCommandEncoder计算编码器MTLBlitCommandEncoder内存管理编码器(比如复制buffer texture)MTLParallelRenderCommandEncoder并行编码的多个图形渲染任务
在创建 CommandEncoder 之前,需要先创建渲染过程描述符 MTLRenderPassDescriptor
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
然后构造合适的指令编码器
id<MTLRenderCommandEncoder> renderCommandEncoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
最后结束编码
[renderCommandEncoder endEncoding];
总结
Metal的使用建议
苹果对metal的使用有以下几点建议:
Separate Your Rendering Loop分开渲染循环:不希望将渲染的处理放到VC中,希望将渲染循环封装在一个单独的类中Respond to View Events响应视图的事件,即MTKViewDelegate协议,也需要放在自定义的渲染循环中Metal Command Objects创建一个命令对象,即创建执行命令的GPU、与GPU交互的MTLCommandQueue对象以及MTCommandBuffer渲染缓存区湘
渲染流程
- 配置 Device 和 Queue
- 配置 PipelineState
- 构造 CommandBuffer
- 配置 CommandEncoder
- 将资源、渲染管线进行 Encode
- 提交 buffer 到 Queue

Metal能做什么?
- 图片处理、滤镜
- 视频处理
- 机器学习
- 大计算工作、分担 CPU 压力
参考资料: