Metal 框架之使用 Metal 来绘制视图内容

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

概述

《 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<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

复制代码

在本例中,没有对任何绘图命令进行编码,渲染通道所做的唯一事情就是擦除纹理,因此可以直接调用编码器的 endEncoding 方法来指示传递完成。


[commandEncoder endEncoding];

复制代码

渲染到屏幕

绘制到纹理不会自动在屏幕展示出来。实际上,仅有一部分纹理才能在屏幕显示。在 Metal 中,可以在屏幕上显示的纹理由可绘制对象管理,通过可绘制对象纹理才会显示出来。

MTKView 自动创建可绘制对象来管理其纹理。读取 currentDrawable 属性以获取可绘制对象,渲染通道将渲染结果绘制到可绘制对象关联的纹理中。    


id<MTLDrawable> drawable = view.currentDrawable;

复制代码

在命令行缓冲区中调用 present(_ :)  方法,传入可绘制对象作为参数。


[commandBuffer presentDrawable:drawable];


复制代码

该方法告诉 Metal,当命令缓冲区被调度执行时,Metal 应该与 Core Animation 协调配合以在渲染完成后显示纹理。当 Core Animation 呈现纹理时,该纹理成为视图的新内容。在本例中,这意味着擦除的纹理成为视图的新背景。视图背景的变化与 Core Animation 刷新用户界面元素同时发生。

提交命令缓冲区

最后提交命令缓冲区来操作 GPU 工作,进而渲染更新视图的帧。

总结

本文介绍有关 Metal 渲染图形内容的基础知识,使用 Metal 绘制视图内容时,需要创建一个 MetalKit 视图和一个渲染通道,配置必要的视图属性,将可绘制对象与命令缓冲区关联起来,最后提交命令缓冲区来完成绘制。

本文示例代码下载

Guess you like

Origin juejin.im/post/7031453635284303885