对 iOS 中 GPU 编程的高度优化的框架 Metal

序言

前段时间在研究SceneKit,感觉到iOS 系统有很多有趣又好玩的东西,我个人是比较喜欢折腾这些技术的,在研究SceneKit 的时候,发现它有两套渲染机制,OpenGL 和 Matal ,OpenGL 还算比较熟悉,但是Metal部分就不太清除,于是决定好好研究,一下这磨人的小妖精。

初步了解

1.什么是Matal?

Metal 是一个和 OpenGL ES 类似的面向底层的图形编程接口,通过使用相关的 api 可以直接操作 GPU ,最早在 2014 年的 WWDC 的时候发布,并于今年发布了 Metal 2。 Metal框架支持GPU硬件加速、高级3D图形渲染以及大数据并行运算。且提供了先进而精简的API来确保框架的细粒度(fine-grain),并且在组织架构、程序处理、图形呈现、运算指令以及指令相关数据资源的管理上都支持底层控制。其核心目的是尽可能的减少CPU开销,而将运行时产生的大部分负载交由GPU承担

2.主要的技能

1.3D图形渲染 2.并行运算

Metal 具有特点

  • Metal 是 iOS 平台独有的
  • 消除“隐藏”的性能瓶颈,如隐式状态验证。你可以在多线程异步控制GPU,有效用于平行创建和提交命令缓冲区
  • 描述了缓冲和纹理对象代表了GPU的内存分配。纹理对象有特定的像素格式,并可用于纹理图像或附件对象
  • 使用相同的数据结构和资源(如缓冲区、纹理和命令队列),用于图形和计算操作。此外,金属着色语言支持图形和计算功能。Metal使得资源能够和runtime接口、图形着色器、并计算函数之间共享
  • Metal 着色器可以和你的app代码一样在运行时加载,编译,这样的好处时能够更好的生成代码,以及编译调试
  • Metal 不能再后台执行命令代码,否则系统崩溃

有步骤讲解视频以及资料.因为简书文章没有地方放.大家可以加一下我的群获取一下。在群里讨论一下Metal这一块。iOS技术交流群:923910776.

基础流程

使用Metal和Objective-C来创建一个有基本脉络的应用:画一个简单的三角形。

注意:Metal应用不能跑在iOS模拟器上,它们需要一个设备,设备上装载着苹果A7芯片或者更新的芯片。所以需要一台这样的设备(iPhone 5S,iPad Air,iPad mini2)来完成代码的测试。

流程

1.配置 Device 和 Queue 2.获取 CommandBuffer 3.配置 CommandBufferEncoder 4.配置 PipelineState 5.创建资源 6.Encoder Buffer 【如有需要的话可以用 Threadgroups 来分组 Encoder 数据】 7.提交到 Queue 中

  • 如果有技术和我讨论或者想拿源码和思维导图资料的, 请联系我时,备注一下Matal
  • 加我技术交流QQ群:923910776

初始化

新建一个普通的工程 Single View Application,在 VC 中导入 Metal Framework。

MTLDevice

都说是操作 GPU 了,当然我们要拿到 GPU 对象,Metal 中提供了 MTLDevice 的接口,代表了 GPU。

//获取设备
id device = MTLCreateSystemDefaultDevice();
if (device == nil) {
    NSLog(@"don't support metal !");
    return;
}
复制代码

当设备不支持 Metal 的时候会返回空。

MTLCommandQueue

有了 GPU 之后,我们需要一个渲染队列 MTLCommandQueue,队列是单一队列,确保了指令能够按顺序执行,里面的是将要渲染的指令 MTLCommandBuffer,这是个线程安全的队列,可以支持多个 CommandBuffer 同时编码。 通过 MTLDevice 可以获取队列

id queue = self.device.newCommandQueue;
复制代码

MTKView

要用 Metal 来直接绘制的话,需要用特殊的界面 MTKView,同时给它设置对应的 device 为我们上面获取到 MTLDevice,并把它添加到当前的界面中。

_mtkView = [[MTKView alloc] initWithFrame:self.view.frame device:_device];
[self.view addSubview:_mtkView];
复制代码

渲染

我们配置好 MTLDevice,MTLCommandQueue 和 MTKView 之后,我们开始准备需要渲染到界面上的内容了,就是要塞进队列中的缓冲数据 MTLCommandBuffer 。 简单的流程就是先构造 MTLCommandBuffer ,再配置 CommandEncoder ,包括配置资源文件,渲染管线等,再通过 CommandEncoder 进行编码,最后才能提交到队列中去。

MTLCommandBuffer

有了队列之后,我们开始构建队列中的 MTLCommandBuffer,一开始获取的 Buffer 是空的,要通过 MTLCommandEncoder 编码器来 Encode ,一个 Buffer 可以被多个 Encoder 进行编码。

MTLCommandBuffer 是包含了多种类型的命令编码 - 根据不同的 编码器 决定 包含了哪些数据。 通常情况下,app 的一帧就是渲染为一个单独的 Command Buffer。MTLCommandBuffer 是不支持重用的轻量级的对象,每次需要的时候都是获取一个新的 Buffer。

Buffer 有方法可以 Label ,用来增加标签,方便调试时使用。

创建

  • MTLCommandQueue - commandBuffer 方法 ,只能加到创建它的队列中。
  • 获取 retain 的对象 commandBufferWithUnretainedReferences 能够重用 一般不推荐

这里我们通过如下方法创建

//command buffer
    id commandBuffer = [_queue commandBuffer];
复制代码

执行

  • enqueue 顺序执行
  • commit 插队尽快执行 (如果前面有 commit 就还是排队等着)

监听结果

commandBuffer.addCompletedHandler { (buffer) in
}
commandBuffer.waitUntilCompleted()

commandBuffer.addScheduledHandler { (buffer) in
}
commandBuffer.waitUntilScheduled()
复制代码

创建 Metal 资源

接下来我需要把我们需要绘制的内容 encode 到我们上面生成 MTLCommandBuffer 中。我 们这里是要画一个三角形,所以要有三个顶点,然后需要绘制三角形的图片。分别用 MTLBuffer 来读入三个顶点。

在 Metal 中是归一化的坐标系,以屏幕中心为原点(0, 0, 0),且是始终不变的。面对屏幕,你的右边是x正轴,上面是y正轴,屏幕指向你的为z正轴。长度单位这样来定:窗口范围按此单位恰好是(-1,-1)到(1,1),即屏幕左下角坐标为(-1,-1),右上角坐标为(1,1)。

所以我们要画在中间一个正三角形的话,三个顶点分别为

(0.577, -0.25, 0.0, 1.0) (-0.577, -0.25, 0.0, 1.0) (0.0, 0.5, 0.0, 1.0)

Shader (着色器) 和 Pipeline (渲染管线)

可以添加多个 Metal 文件,最后都会编译到二进制文件default.metallib 中。 通过 Xcode 的 File - New - File 菜单,新建一个 Metal 文件。

渲染管线

着色器这边的工作已经完成,下面我们需要把它和我们的 CommandBuffer 关联起来,就需要我们的 PipelineState 渲染管线了。

MTLCommandEncoder 编码器

有了资源文件,渲染管线之后,我们可以开始做最后的步骤了,构造 MTLCommandEncoder 编码器。 指令编码器包括 渲染 计算 位图复制三种编码器。

  • MTLRenderCommandEncoder 渲染 3D 编码器
  • MTLComputeCommandEncoder 计算编码器
  • MTLBlitCommandEncoder 位图复制编码器 拷贝 buffer texture 同时也能生成 mipmap

这里我们是为了渲染一个三角形,所以这里用的是 MTLRenderCommandEncoder 。

绘制

编码结束之后,就可以开始准备提交到 GPU 了。 配置需要绘制的 Layer,获取 MTKView 的 Layer 就可以。

现在所有的工作就都完成了,运行项目就可以看到三角形了

调试

如何进行调试和评估性能呢? 这里 iOS 提供了两个工具

  • Xcode 中的 Capute GPU Frame
  • Instruments 中的 Metal System Trace

总结

在 WWDC 2015,苹果发布了 Metal Performance Shaders (MPS) 框架,iOS 9 上的一组高性能的图像滤镜,其实就是边写好的 Shaders,提供了优秀的图像处理能力。同时还提供了高性能的矩阵运算的 Shaders ,能用来做机器学习的运算,在 GPU 上运行卷积神经网络。

提醒广大网友

Metal 是一门特别深的技术,可能需要你花费你半年左右的时间去学习它,希望有持之以恒的情深。

转载于:https://juejin.im/post/5cfe6106e51d45598611b943

你可能感兴趣的:(对 iOS 中 GPU 编程的高度优化的框架 Metal)