iOS 开发者的 Metal 养成计划「1」 —— 与 Metal 的邂逅
发布于:2018-01-12 17:17,阅读数:1496,点赞数:1
# 引言 Metal 已经问世很久了,相信大多数 iOS 开发者都知道它是什么。 随着公司的项目不再支持 iOS 7,Metal 时代已经全面到来了。是的,我们现在才放弃 iOS 7,因为仍然有超过 1% 的用户,对于一个日活过亿的产品,用户数量确实还不小。 言归正传,关于 Metal 引用前几天微博里的话: > Metal 对于我这种一直写 OpenGL 的人简直是一种救赎。 它解决了很多 OpenGL 的痛点,尤其是学习曲线陡峭这个问题。对于被面向对象开发惯坏了,完全无法理解 OpenGL 这类纯面向过程和状态的开发的同学,大大降低了入门门槛。 # Metal 我这么理解 Metal:它是一套面向 GPU 编程 API,设计上位于 OpenGL 同一层。附上[「官方文档」](https://developer.apple.com/library/content/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Blit-Ctx/Blit-Ctx.html#//apple_ref/doc/uid/TP40014221-CH9-SW3)。 熟悉 OpenGL 的同学,对于它的很多内容一眼就能看出来是干什么的,两者的思路几乎是完全一致的,学习成本非常低。那么对于不熟悉 OpenGL 的同学,它的学习成本要比 OpenGL 低得多得多。由于它们的思路是一致的,所以甚至可以选择先学习 Metal。  那么以上就是 Metal 的架构图。由`MTLDevice`出发,维护了一个`MTLCommandQueue`队列,队列中会有待处理的`MTLCommandBuffer`,而`MTLLibrary`负责加载写好的着色器程序并返回`MTLFunction`对象,`MTLHeap`封装有助于管理显存堆内存,`MTLFence`则是用来处理共享资源冲突的问题的。 ### MTLDevice 首先可以惊讶地发现,OpenGL 中贯穿生命的`上下文(Context)`这个鬼畜的东西不见了,取而代之的是`MTLDevice`。虽然这两者的概念有区别,但一般使用 OpenGL 时是以单个上下文使用的,在这个前提下,这两者没有区别。 > The MTLDevice protocol defines the interface to a single graphics processor unit (GPU). `MTLDevice `是 GPU 的入口。在 macOS 上,可以使用官方文档中提到`MTLCopyAllDevices()`这个方法,可以拿到 macOS 上所有的显卡。这个方法在 iOS 里是用不了的。  获得这个入口很容易: ```swift guard let device = MTLCreateSystemDefaultDevice() else { // error } ``` 那么之后所有关于 Metal 的操作,都会从这个`MTLDevice`出发。 ```swift // 获得 MTLCommandQueue guard let commandQueue = device.makeCommandQueue() else { // error } // 获得 MTLLibrary guard let library = device.makeDefaultLibrary() else { // error } // ... ``` 对于被面向对象编程惯坏了的读者们来说,这是一个救赎:`OpenGL 对 GPU 编程是完全面向过程的,而 Metal 是完全面向对象的。` ### MTLCommandQueue 这是一个命令队列。熟悉`cocos2d-x`的同学会发现这个东西格外眼熟。是的,跟 cocos2d-x 的渲染系统类似的,Metal 里的命令都会被封装成不同类型的命令后提交进入这个命令队列。 使用 Metal 渲染时,根据命令类型的不同(`MTLxxxCommandBuffer`),使用不同的命令编码器(`MTLxxxCommandEncoder`)统一编码成`MTLCommandBuffer`对象后提交进入这个队列。 命令系统是在写 Metal 时打交道最多的东西,所有的 Metal 操作都会被封装成命令,提交到这个队列中执行。在后续的文章中会详细讲命令队列的使用。如果读者看过`Cocos2d-x`的源码,会发现在`Cocos2d-x`的渲染系统里也有一个命令系统,它们是比较相似的,同样在后续的文章中会讲到。 ### MTLLibrary 在生成的 App 中可以看到`metallib`类型的文件的,Metal Library 负责加载着色器程序。在传统的 OpenGL 中,着色器程序以字符串形式保留,动态编译,不利于调试,且会有一定安全风险。不过在 OpenGL 3.x 中已经允许预先编译好着色器程序,在运行时直接加载二进制了。那么在 Metal 中,着色器程序会被预先编译后保存在`metallib`文件里面。  ### MTLHeap 在 OpenGL 里使用 Buffer 和 Texture 时,万恶的`槽位`或者叫`句柄`让无数初学者难以理解。那么正如上面说的,Metal 是完全面向对象的。所以想要在显存中保存的内容都被封装成了`MTLBuffer`,贴图都被封装成了`MTLTexture`,这些可以用很面向对象的 Style 在显存中创建了。 ```swift let heap = device.makeHeap(descriptor: MTLHeapDescriptor)! let texture = heap.makeTexture(descriptor: MTLTextureDescriptor) let buffer = heap.makeBuffer(length: Int, options: MTLResourceOptions) ``` ### MTLFence 从官方文档上理解,`MTLFence`似乎是用于命令间通信的。 > An object that can capture, track, and manage resource dependencies across command encoders. 在不同的命令编码器中有不同的`updateFence`和`waitForFence`实现。 > The event is updated at kernel submission to maintain global order and prevent deadlock. 正如`Fence`的字面意思,它是个栅栏,可以隔离操作,保证提交顺序来避免死锁等问题。 # 小结 以上提到的是从 MTLDevice 出发的第一层工具类,Metal 为我们抽象了这一系列的工具类来管理 GPU,管理渲染管线。跟 OpenGL 比起来要具象得多,入门门槛也低了很多。掌握 OpenGL 的同学学习 Metal 会非常轻松,同样学会了 Metal 后再去学习 OpenGL,也会比直接学轻松很多。 # 更新计划 在后续的文章中,会分别介绍: - Metal 命令系统的四种命令: - [Blit Command](https://blog.yuusann.com/corpus/article/18005) - [Render Command](https://blog.yuusann.com/corpus/article/18005) - [Parallel Render Command](https://blog.yuusann.com/corpus/article/18006) - [Compute Command](https://blog.yuusann.com/corpus/article/18006) - Metal 独有的着色器语言: - [Metal Shading Language](https://blog.yuusann.com/corpus/article/18008) - Metal 的三类着色器函数: - [Vertex Function](https://blog.yuusann.com/corpus/article/18008) - [Fragment Function](https://blog.yuusann.com/corpus/article/18008) - [Kernel Function](https://blog.yuusann.com/corpus/article/18009) - Metal 并发运算的性能 - [Performance of Compute Command](https://blog.yuusann.com/corpus/article/18009) - Metal 的官方工具类库: - [MetalKit](https://blog.yuusann.com/corpus/article/18010) - [MetalPerformanceShaders](https://blog.yuusann.com/corpus/article/18011)
评论:0条