概述
在 《 Metal 框架之 MTKView 》 文章中介绍了如何使用 MTKView 来创建、配置和展示 Metal 对象,本文可作为上篇文章的补充篇,将介绍有关 Metal 渲染图形内容的基础知识。
本示例中,会创建一个 MetalKit 视图和一个渲染通道,然后指定擦除背景色,通过提交渲染通道命令来更新视图。
准备 MetalKit 视图
MetalKit 提供了一个叫做 MTKView 的类,它是 NSView(在 macOS 中)或 UIView(在 iOS 和 tvOS 中)的子类。 MTKView 封装了一些使用 Metal 渲染内容到屏幕的许多细节。
MTKView 是依托于 Metal 设备对象来创建资源的,因此需要将视图的设备属性与现有的 MTLDevice 相关联 。
```
_view.device = MTLCreateSystemDefaultDevice();
```
设置 MTKView 上的 clearColor 属性来更改视图内容的背景色,可以通过使用 MTLClearColorMake(_ : : : _:) 来设置颜色。
```
_view.clearColor = MTLClearColorMake(0.0, 0.5, 1.0, 1.0);
```
示例中没有动画的展示,所以当视图改变形状时才需要重新绘制:
```
_view.enableSetNeedsDisplay = YES;
```
委托绘图职责
MTKView 依赖于 App 向 Metal 提交命令来生成视图内容。 MTKView 使用委托模式来通知何时应该绘制。要接收委托回调,需要将视图的 delegate 指定为遵循 MTKViewDelegate 协议的对象。
```
_view.delegate = _renderer;
```
委托需要实现如下两个方法:
-
每当内容的大小发生变化时,视图都会调用 mtkView(_ :drawableSizeWillChange:) 方法。当视图的窗口大小变化,或者当设备方向改变时(在 iOS 上),就会发生这种情况。这允许 App 根据视图的大小来调整分辨率。
-
每当需要更新视图的内容时,视图都会调用 draw(in:) 方法。在此方法中,需要创建一个命令缓冲区,对命令进行编码来告诉 GPU 绘制的内容以及显示的时机,并把该命令缓冲区入队列以供 GPU 执行。这有时称为一帧,它所做的事情就是在屏幕上显示单个图像。
创建渲染通道描述符
绘图时,GPU 将结果存储到纹理中,纹理是包含图像数据并可被 GPU 访问的内存块。在本示例中,使用 MTKView 创建了许多纹理,视图显示的内容将被渲染到这些纹理中。一次性创建多个纹理,以便渲染到一个纹理时可以显示另一个纹理的内容。
绘制需要创建一个渲染通道,它是一系列渲染命令,通过它可以绘制一组纹理。在渲染通道中,纹理也称为渲染目标。要创建渲染通道,需要一个渲染通道描述符,Metal 中使用一个 MTLRenderPassDescriptor 的实例来表示。
```
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor == nil)
{
return;
}
```
渲染通道描述符描述了一组渲染目标,以及在渲染通道的开始和结束时应如何处理它们。视图返回一个渲染通道描述符,该描述符带有一个单一的颜色附件,附件指向一个纹理。默认情况下,在渲染通道开始时,渲染目标被擦除为与视图的 clearColor 属性匹配的纯色,并且在渲染通道结束时,所有更改都存储回纹理。
由于视图的渲染通道描述符可能为空,所以在创建渲染通道之前需要确保渲染通道描述符非空。
创建渲染通道
通过使用 MTLRenderCommandEncoder 对象将渲染通道描述符编码到命令缓冲区来创建渲染通道。调用命令缓冲区的 makeRenderCommandEncoder(descriptor:) 方法并传入渲染通道描述符生成 MTLRenderCommandEncoder 对象。
```
id commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
```
在本例中,没有对任何绘图命令进行编码,渲染通道所做的唯一事情就是擦除纹理,因此可以直接调用编码器的 endEncoding 方法来指示传递完成。
```
[commandEncoder endEncoding];
```
渲染到屏幕
绘制到纹理不会自动在屏幕展示出来。实际上,仅有一部分纹理才能在屏幕显示。在 Metal 中,可以在屏幕上显示的纹理由可绘制对象管理,通过可绘制对象纹理才会显示出来。
MTKView 自动创建可绘制对象来管理其纹理。读取 currentDrawable 属性以获取可绘制对象,渲染通道将渲染结果绘制到可绘制对象关联的纹理中。
```
id drawable = view.currentDrawable;
```
在命令行缓冲区中调用 present(_ :) 方法,传入可绘制对象作为参数。
```
[commandBuffer presentDrawable:drawable];
```
该方法告诉 Metal,当命令缓冲区被调度执行时,Metal 应该与 Core Animation 协调配合以在渲染完成后显示纹理。当 Core Animation 呈现纹理时,该纹理成为视图的新内容。在本例中,这意味着擦除的纹理成为视图的新背景。视图背景的变化与 Core Animation 刷新用户界面元素同时发生。
提交命令缓冲区
最后提交命令缓冲区来操作 GPU 工作,进而渲染更新视图的帧。
总结
本文介绍有关 Metal 渲染图形内容的基础知识,使用 Metal 绘制视图内容时,需要创建一个 MetalKit 视图和一个渲染通道,配置必要的视图属性,将可绘制对象与命令缓冲区关联起来,最后提交命令缓冲区来完成绘制。