原文https://developer.apple.com/documentation/metal/devices_and_commands
设备和命令
展示CPU如何访问GPU以及交互
示例代码
概览
Metal 提供底层和低功耗的方式去访问用户设备的图像处理单元(简称GPU).
GPU 高效的APP .
开发一个GPU Metal APP的关键就是 需要理解软件和硬件.
在这个例子中,你将要学习如何写 通过Metal 以及 基础的渲染命令 创建APP.
尤其是 你将学校如何包含一个Metal设备 ,配置MetalKit View 创建和执行GPU命令,显示渲染内容.
Metal 和MetalKit 框架
示例 使用两个框架 显示 渲染内容. Metal 和 MetalKit .
Metal提供访问GPU ,MetalKit 提供一个通用的工具 让开发者更简单的开发Metal APP.
集成在OS 和 其他框架 ,所以你只需要关注GPU 编程.
MetalKit 框架 使用最多的特性之一 就是 MTKView
类, 这个类 是 封装了 UIView
或 NSView
对象, 已经配置 Metal 特定的 核心动画 功能.
尤其是 一个MetalKit 视图 自动设置和关键 持续渲染循环 为你提供 2D 和显示的资源 ,通用的可绘画对象, 为每一帧.
注意:
你可以直接使用 核心动画 来开发一个Metal app ,但是使用MetlKit 框架 更简单 更快速,更方便.
分割你的渲染循环
当开发一个Metal APP 时候 ,经常 去 分割 你的渲染循环 到自定义类中. 使用分离的类 ,你可以更好的管理你的初始化Metal 设置代码 以及 每一帧 Metal 命令.
这是一个通用架构 展示 AAPLRenderer 类 , 初始化 MetalKit view 和 设置view 的代理.
_renderer = [[AAPLRenderer alloc] initWithMetalKitView:_view];
if(!_renderer)
{
NSLog(@"Renderer failed initialization");
return;
}
_view.delegate = _renderer;
响应视图事件
MTKViewDelegate
对象 实现 mtkView:drawableSizeWillChange:
and drawInMTKView:
方法,这两个方法 通知你的MetalKit 视图大小更改 和 绘画事件.
- 这个视图调用
mtkView:drawableSizeWillChange:
方法 当Window 尺寸大小 改变时(MacOS), 或者 重新布局操作, 比如 设备方向改变 (主要发生在 iOS 和 TvOS ), 你能响应 视图的新尺寸 和 改变你的渲染分辨率 ,如果你需要的话.
- 视图调用
drawInMTKView:
方法 当 渲染新的一帧的时候, 可以指定 帧率 60 FPS ,通过preferredFramesPerSecond
属性.
回调 通常 是 主要 事件 在 你的渲染循环开始执行.
Metal 命令对象
MTLDevice 对象代表 GPU. 可以 通过调用MTLCreateSystemDefaultDevice ()
,包含 一个单一的MTLDevice 对象 代表 默认的GPU 设备.
MacOS 设备 有多个GPU ,调用MTLCopyAllDevices ()
返回 设备中所有的GPU 设备数据, 一个MTLDevice 对象 提供关于一个GPU的信息.
但是 主要的目的是 创建其他对象 能够和GPU 互相的.
第一个对象是 所有的app 需要 和 MTLCommandQueue
交互.
_commandQueue = [_device newCommandQueue];
你使用 MTLCommandQueue
对象 去创建和管理MTLCommandBuffer
对象,确保 他们按照正确的顺序 发送指令到GPU
每一帧 , MTLCommandBuffer
对象 是 创建 和 填充 命令 被GPU 执行.
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
这有不同类型的GPU , 每一个接受 和中断命令在自己独一无二的方法.
MTLCommandBuffer
对象 合并 这些命令 到单一的提交. 但是他们必须第一个编码设备无关的方法 使用 MTLCommandEncoder
对象. 这是少数不同的类型 MTLCommandEncoder
类 , 每一个使用不同类型在GPU.
这示例 使用 MTLRenderCommandEncoder
子类 指定编码渲染命令到 commandBuffer.
本示例 使用MTLRenderCommandEncoder
对象 编码GPU命令, 渲染像素到MetalKit视图的可绘画对象.
渲染编码器 必须指定 与可绘画对象 相关联.
为了创建 MTLRenderCommandEncoder
你必须创建一个MTLRenderPassDescriptor
MTLRenderPassDescriptor
是轻量级,临时对象, 一些配置属性 使用 现存的 MTLCommandBuffer
对象 创建 新的 MTLRenderCommandEncoder
.
之后,MTLRenderPassDescriptor
不再需要.
下面的示意插图 表达 Metal 命令对象之间的关系,总结为:
Command Queue
创建Command buffers
Command encoders 将命令编码后 传入到 Command Buffers
Command buffers 被提交到GPU
GPU 执行编码命令 和将渲染后的结果显示到可绘画对象.
Prepare a Frame
MetalKit视图 创建 新的 MTLRenderPassDescriptor
在每一帧. 提供currentRenderPassDescriptor
属性.
该渲染管道描述 是 预编译的 视图指定属性, 一些起源来自 视图的可绘画对象, 能够使用容易和方便的 去创建 新的 MTLRenderCommandEncoder
对象.
// Obtain a renderPassDescriptor generated from the view's drawable textures
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
// If we've gotten a renderPassDescriptor we can render to the drawable, otherwise we'll
// skip any rendering this frame because we have no drawable to draw to
if(renderPassDescriptor != nil)
{
id <MTLRenderCommandEncoder> renderEncoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
命令编码 到 MTLRenderCommandEncoder
对象渲染 可绘画对象 , 默认情况下 创建MTLRenderCommandEncoder
对象 默认 编码 清除命令(clear command) 到 GPU执行 在 其他渲染命令之前. 清除命令(clear command
) 设置 可绘画对象的像素 去 清除颜色 更新 在渲染循环的开始.
view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha);
Finalize a Frame
Metal app 调用MTLRenderCommandEncoder
方法 编码明确的渲染命令 到 command buffer. 简单来说 这个案例 不实际编码 渲染命令. 仅仅含蓄的清除命令被编码. 在创建 MTLRenderCommandEncoder
之后 , 本案例简单的调用 endEncoding
方法 指示 编码器已经完成编码操作.
因为GPU没有直接绘制在屏幕, 防止2 绘画像素 在完成绘制之前 执行命令. 为了避免糟糕的用户体验 结果 不完全可绘画, 你可以调用 presentDrawable :
方法. 该方法告诉Metal 等待 直到 GPU完成渲染可绘画对象 在展示屏幕之前.
[commandBuffer presentDrawable:view.currentDrawable];
GPU 不能立即执行命令, 调用MTLRenderCommandEncoder
或者MTLCommandBuffer
对象 执行 在提交方法以后调用. Metal 调度 command buffer 的执行
当GPU 开始执行的时候,可绘画对象 被清理为新的颜色. 当GPU完成执行以后,渲染可绘画对象呗展示到屏幕上.
[commandBuffer commit];
接下来
在本示例中, 你学习了如何利用Metal框架编写一个app以及了解基本的渲染命令到GPU.
在 你好,三角形示例中, 你将要通过Metal框架学习基本的几何体.