本系列文章是对 http://metalkit.org 上面MetalKit内容的全面翻译和学习.
MetalKit 框架是在 WWDC 2015 上发布的,它给原Metal框架带来了大量改进和新特性.认识 MTKView, NSView/UIView
的一个子类. 它内置了一个Metal layer层,也同时管理着帧缓冲器framebuffer及其渲染目标附件render target attachments,还管理着绘制循环draw loop.
让我们创建一个Cocoa Application
(因为iOS
模拟器不支持Metal
).确保只有Swift
和Use Strotyboards
区域是选中状态.接下来,创建一个新的类,名为MetalView.swift继承于NSView
(暂时).然后,到storyboard中选中View Controller
下面的View
,设置Identity Inspector
中的类为MetalView
类型,如下图.
对View Controller
也是同样操作,删除Identity Inspector
中Class
下面的View Controller
,因为我们用不到它.删除ViewController.swift
因为我也不需要它了.现在回到MetalView.swift
文件中,输入import MetalKit.有两种方法可以让我们的类支持绘制:遵守MTKViewDelegate
协议并实现它的drawInView(:)
方法,或者继承MTKView
并重写它的drawRect(:)
方法.这里我们选择后者,所以将类的类型从NSView
改为MTKView,并创建一个新方法名为render()
,内容如下:
func render() {
let device = MTLCreateSystemDefaultDevice()!
self.device = device
let rpd = MTLRenderPassDescriptor()
let bleen = MTLClearColor(red: 0, green: 0.5, blue: 0.5, alpha: 1)
rpd.colorAttachments[0].texture = currentDrawable!.texture
rpd.colorAttachments[0].clearColor = bleen
rpd.colorAttachments[0].loadAction = .Clear
let commandQueue = device.newCommandQueue()
let commandBuffer = commandQueue.commandBuffer()
let encoder = commandBuffer.renderCommandEncoderWithDescriptor(rpd)
encoder.endEncoding()
commandBuffer.presentDrawable(currentDrawable!)
commandBuffer.commit()
}
让我们一行一行来细看这些代码.首先,我们创建一个device.我们将其设置为我们view的属性device
,否则该属性为nil
程序会崩溃.作为一个可选项,我们可以在绘制之前修改view的drawable
属性.接着,创建一个render pass descriptor(渲染通道描述符)以便我们配置渲染通道为current drawable’s texture
附着上初始颜色.为了有趣一点,我们创建一个很棒的颜色,由一半蓝色一半绿色组成,叫bleen.最后,我们使用命令缓冲器来创建render command encoder来执行绘制命令.对于每个绘制循环,当currentRenderPassDescriptor
查询时,创建一个新的MTLRenderPassDescriptor
对象.这个对象是基于currentDrawable
对象创建的.画面显示并不是MTKView
处理的,所以我们必须自己先检查currentRenderPassDescriptor
和currentDrawable
都为为nil
,然后再调用presentDrawable(:)
方法.
让我们参考一下Metal 文档中的细节.Metal
框架包含若干对象:
-
device设备
-对GPU
的抽象,处理命令队列中的渲染和计算命令 -
command queue命令队列
-一个命令缓冲器的串行队列,确保储存的命令按顺序执行 -
command buffer命令缓冲器
-储存从命令编码器中编译出的指令.当能问执行完所有命令后Metal会通知应用程序. -
command encoder命令编码器
-将API
命令编译成GPU
硬件命令-共有三种类型的编码器:render
(供图形渲染),compute
(供数据并行处理)及blit
(供资源复制操作).目前我们只需关注render command encoder渲染命令编码器
-
states状态
-例如混合和深度 -
shaders着色器
-源码 -
resources资源
-纹理和数据缓冲器
我们在本系列的下一节中将讨论最后3个对象.当前我们只关注device
,queue
,buffer
和encoder
.Render Command Encoder (RCE)渲染命令编码器
为每一个单独的渲染通道提供硬件命令,这意味着所有的渲染都被送入一个单一的framebuffer帧缓冲器
对象中(目标集合中).如果另一个帧缓冲器需要被渲染,会创建一个新的RCE.RCE会为从graphics popeline图形管线
中给出的vertex顶点
和fragment片段
确定状态,并且插入resources
,state changes
和draw calls
.利用RCE
的一个优点是无需绘制时编译;应用可以决定编译和状态检查何时发生,这样为程序员提供了很大的性能优势.
让我们再回到我们的代码.在drawRect(:)方法中调用render()
方法:
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
render()
}
如果你运行应用,你将会看到一个漂亮的,纯粹的bleen-ish
屏幕:
在下一节中,我们终于开始介绍shaders
,加载textures
及管理model data模型数据
.代码 source code 发布于Github
上.
下次见!