Apple Metal文档(2):使用Metal绘制视图内容

创建MetalKit视图和渲染通道来绘制视图内容

本文翻译自苹果Metal的官方文档:Using Metal to Draw a View’s Contents,目的主要是为了加深自己的理解,如果哪里翻译有误,请大家指出,我会及时更正。欢迎转载,但禁止用于商业用途。谢谢!

概述

在本示例中,将学习使用 Metal 渲染图形内容的基础知识。 将使用 MetalKit 框架创建一个用于Metal绘制图形的视图。 然后,将编写代码完成擦除视图内容为一个固定的背景颜色的操作。

注:MetalKit能够自动执行窗口系统任务、加载纹理并处理3D模型数据。MetalKit

准备绘制一个MetalKit视图

MetalKit提供了一个叫做MTKView的类,它是NSView(macos)和UIView(ios和tvOs)。在使用Matel渲染图形时,MetalKit会处理其实现细节。

为创建内部资源,MetalKit需要获取一个Metal设备对象的引用,因此第一步是为视图的device属性设置一个存在的MTLDevice对象。

_view.device = MTLCreateSystemDefaultDevice();

MetalKit上的其他一些属性同样也需要进行初始化。为了能够实现对渲染内容进行清除、显示背景颜色的操作,需要对clearColor属性进行配置,可以使用MTLClearColorMake函数完成对其的初始化。MTLClearColorMake共有四个参数,分别对应红、绿、蓝、透明度通道。

_view.clearColor = MTLClearColorMake(0.0, 0.5, 1.0, 1.0);

因为这个示例不需要绘制动画,设置视图仅在需要更新内容的时候刷新,例如当视图的形状发生改变时。

_view.enableSetNeedsDisplay = YES;

委托绘制工作

MetalKit需要依赖Metal向应用程序发送绘制指令,MetalKit需要通过delegate属性设置应用程序何时进行渲染。若要通过delagate实现回调,需要将视图的delegate属性设置为符合 MTKViewDelegate 协议的对象。

_view.delegate = _renderer;

这个delegate内实现两个函数:

  1. 无论何时,当视图的尺寸发生变化时,会自动调用mtkView(_:drawableSizeWillChange:) 。除了尺寸属性变化,这一过程还包括渲染的目标系统发生改变时。
  2. 当视图的内容需要更新时,会调用draw(in:)函数。其主要流程是,创建一个命令缓冲区(command buffer),包含通知 GPU 绘制什么并何时在屏幕上显示的命令,并将该命令缓冲区排入队列以由 GPU 执行。 这有时被称为绘制框架。 每绘制一帧是生成单个图像并显示在屏幕上。 在像游戏这样的交互式应用程序中,您每秒可能会绘制许多帧。

在示例中,使用一个叫做AAPLRender的类实现委托,并承担绘制的工作。在MetalView中创建了一个这个类的接口,通过view的delegate进行配置。

创建渲染通道描述符

当您绘制时,GPU 将结果存储到Texture中,Texture是包含图像数据并且可供 GPU 访问的内存块。 在这个示例中,MTKView 创建了您需要绘制到视图中的所有Texture。 它创建多个纹理,以便在渲染到另一个纹理时显示这个纹理的内容。

为了绘制,需要创建一个渲染通道(render pass),它是一系列用于绘制纹理的渲染指令。在渲染通道被使用时,纹理也被称为渲染目标。为创建一个渲染通道,首先需要一个MTLRenderPassDescriptor的指针。在本示例中,是通过MetalKit获取,而不是配置自己的渲染通道描述符。

MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor == nil)
{
    return;
}

渲染通道描述符描述了一组渲染目标,以及在渲染通道的开始和结束时应该如何处理它们。 渲染过程还定义了不属于此示例的渲染的其他一些方面。 视图返回一个带有单个color attchment的渲染通道描述符,该attchment指向视图的纹理之一,否则根据视图的属性配置渲染通道。 默认情况下,这意味着在渲染过程开始时,渲染目标被擦除为与视图的 clearColor 属性匹配的纯色,并且在渲染过程结束时,所有更改都将存储回纹理。

由于一个视图的渲染通道可能为空(nil),因此需要在创建渲染通道之前测试渲染通道描述符(Render Pass Descriptor)是否为空。

创建一个渲染通道

创建一个渲染通道(render pass)并将其放入指令缓存(command buffer)中需要使用一个MTLRenderCommandEncoder对象,可通过调用command buffer的 makeRenderCommandEncoder(descriptor:),并把render pass descriptor参数传入,以完成MTLRenderCommandEncoder对象的创建。

id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

在本示例中,不需要编写任何绘制指令,tender pass唯一需要做的就是清除texture,通过调用编码器的endEncodeing函数,以注明渲染已经完成。

在屏幕上显示一个Drawable

绘制到纹理不会自动在屏幕上显示新内容。 实际上,屏幕上只能呈现一些纹理。 在 Metal 中,可以在屏幕上显示的纹理由可绘制对象(Drawable)管理,并且要显示内容,您需要呈现可绘制对象。

MetalKit会自动的Drawable去管理它的纹理,读取 currentDrawable 属性以获取拥有作为render pass目标纹理的可绘制对象。 视图返回一个 CAMetalDrawable 对象,它是一个连接到Core Animation的对象。

id<MTLDrawable> drawable = view.currentDrawable;

在command buffer上调用present(_:),并传递drawable

[commandBuffer presentDrawable:drawable];

这个方法告诉 Metal,当command buffer被执行时,Metal 应该与 Core Animation 协调以在渲染完成后显示纹理。 当 Core Animation 呈现纹理时,它成为视图的新内容。 在此示例中,这意味着已擦除的纹理将成为视图的新背景。 该更改与 Core Animation 为屏幕用户界面元素所做的任何其他视觉更新一起发生。

提交Command Buffer

现在已完成绘制那一帧的全部指令,最终提交Command Buffer

[commandBuffer commit];

See also

comments powered by Disqus