iOS 开发者的 Metal 养成计划「2」 —— 块命令和渲染命令
发布于:2018-01-13 18:12,阅读数:774,点赞数:0
# 引言 看了上一篇文章以后相信读者对 Metal 有了一定的了解但是还完全不知道它是怎么用起来的。 那么看完本文以后就会了解绘制操作在 Metal 层是如何工作的。如果要真正使用起来,真正要画出东西,光靠 Metal 层的 API 是不够的,还会涉及到着色器编程的内容。只有整个流程从头到尾都走通以后,才能在屏幕上看到绘制的东西。 本文会介绍四种绘制命令中最常用的两种:块命令和渲染命令。 # Metal Command System Metal 中使用了一套和`Cocos2d-x`的渲染系统非常像的命令系统。 这个命令的思想在 OpenGL 中其实也是存在的,只是没有被单独拎出来做抽象。 Metal 的命令共有四种: - `Blit Command`:块命令,用于在`MTLBuffer`和`MTLTexture`之间拷贝数据。 - `Render Command`:渲染命令,用于绘图。 - `Parallel Render Command`:并行渲染命令。 - `Compute Command`:计算命令,可用于高并发数据处理。 本文会介绍这四种命令中的前两种,也是最常用的两种。 ### Blit Command > Describes how to copy data between textures and buffers. 使用`MTLBlitCommand`可以快速在`MTLBuffer`和`MTLTexture`之间复制内容。  `MTLBlitCommand`的使用应该是这最简单的一个,使用方法大致为: - 由`MTLCommandQueue`构造`MTLCommandBuffer`命令。 - 由`MTLCommandBuffer`构造`MTLBlitCommandEncoder`编码器。 - 编码器进行复制操作。 - 结束编码。 - 将命令提交并等待执行。 实现为: ```swift // 从队列中构造一个 MTLCommandBuffer guard let commandBuffer = commandQueue.makeCommandBuffer() else { // error } // 构造 MTLBlitCommandEncoder guard let commandEncoder = commandBuffer.makeBlitCommandEncoder() else { // error } // 编码 copy 操作 commandEncoder.copy... // 结束编码 commandEncoder.endEncoding() // 提交命令 commandBuffer.commit() // 等待命令执行结束 commandBuffer.waitUntilCompleted() ``` 这里的 copy 操作有四种,分别对应不同的四个方法: - `MTLBuffer`->`MTLBuffer` - `MTLBuffer`->`MTLTexture` - `MTLTexture`->`MTLBuffer` - `MTLTexture`->`MTLTexture` 更多内容请参考:[「Buffer and Texture Operations: Blit Command Encoder」](https://developer.apple.com/library/content/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Blit-Ctx/Blit-Ctx.html#//apple_ref/doc/uid/TP40014221-CH9-SW3)。 ### Render Command 绘制的核心部分就是`MTLRenderCommand`。 > Describes how to render 3D graphics, including how to distribute graphics operations across multiple threads. 使用`MTLRenderCommand`进行图形绘制。  使用方法大致为: - 构造`MTLRenderPipelineDescriptor`对象。这一步包含对管线的描述,包括颜色格式,深度格式,采样数(用于多重采样抗锯齿)。当然最重要的是设置顶点、片元着色器,它们都是`MTLFunction`格式的,由`MTLLibrary`构造。 - 由需要显示的`MTKView`获得`MTLRenderPassDescriptor`对象(`render pass`即`渲染通道`),使用`MTKView `的`currentRenderPassDescriptor`属性即可。这一步会获得需要显示的`MTKView `中的`Drawable`具体细节。Drawable 这个概念相信大家不陌生,它位于`QuartzCore`框架。这一步会取得这个`CAMetalDrawable`对象的具体细节,包括颜色,深度,背景颜色等。 - 由`MTLCommandQueue`构造`MTLCommandBuffer`命令。 - 由`MTLCommandBuffer`构造`MTLRenderCommandEncoder`编码器。 - 与传统 OpenGL 的渲染方式相同,绑定顶点、纹理、采样器等。但在 Metal 里是纯面向对象的。使用`MTLBuffer`、`MTLTexture`等对象即可。 - 与传统 OpenGL 的渲染方式相同,绘制图形。同样是面向对象的,使用编码器`drawIndexedPrimitives`或其他绘制函数。 - 结束编码。 - 调用`MTLCommandBuffer`的`present`方法。 - 将命令提交并等待执行。 实现为: 构造渲染管线: ```swift pipelineDescriptor = MTLRenderPipelineDescriptor.init() // 设置顶点着色器 pipelineDescriptor.vertexFunction = library.makeFunction(name: "...") // 设置片元着色器 pipelineDescriptor.fragmentFunction = library.makeFunction(name: "...") // 设置第一个颜色挂载的颜色格式 pipelineDescriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat // 设置深度挂载的格式 pipelineDescriptor.depthAttachmentPixelFormat = metalView.depthStencilPixelFormat // 设置采样数,可用于多重采样抗锯齿 pipelineDescriptor.sampleCount = metalView.sampleCount ``` 渲染: ```swift // 从 MTKView 中获得 Drawable guard let drawable = metalView.currentDrawable else { // error } // 从 MTKView 获得 Drawable 的细节描述 guard let renderPassDescriptor = metalView.currentRenderPassDescriptor else { // error } // 从队列中构造一个 MTLCommandBuffer guard let commandBuffer = commandQueue.createCommandBuffer() else { // error } // 构造 MTLRendererCommandEncoder guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { // error } renderEncoder.setRenderPipelineState(pipelineState) // 绑定顶点和纹理等 //encoder.setVertexBuffer(...) //encoder.setFragmentTexture(...) //encoder.setFragmentSamplerState(...) // ... // 绘制 renderEncoder.drawIndexedPrimitives(...) // 结束编码 renderEncoder.endEncoding() // 展现 commandBuffer.present(drawable) // 提交命令 commandBuffer.commit() // 等待命令执行结束 commandBuffer.waitUntilCompleted() ``` 其实这一系列的操作在 OpenGL 中都有对应的操作,流程是一致的。面向对象封装下的图形接口比 OpenGL 中的状态机在代码可读性上有质的飞跃。 更多内容请参考:[「Graphics Rendering: Render Command Encoder」](https://developer.apple.com/library/content/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Render-Ctx/Render-Ctx.html#//apple_ref/doc/uid/TP40014221-CH7-SW1)。 # 小结 在绘制命令中可以看到,真正的绘图和 OpenGL 是一模一样的,也是上传顶点数据,然后绘制三角形或三角形带。Metal 并没有对这里做过多的封装,对于没有 OpenGL 基础的读者,可能一下子无法理解如何根据数组画出一个个三维物体。这些内容本系列文章中不做过多介绍,感兴趣的读者可以自行查询数组的结构和三角形以及三角形带的绘制方法。 在下一篇文章中会介绍四种绘制命令中的另外两种:并行渲染命令和计算命令。
评论:0条