關於Metal® Programming Guide Tutorial and Reference via Swift-中文.pdf的學習日誌
第一部分:Metal Basics
import UIKit
import Metal //導入Metal
import QuartzCore //創建Core Animation層
class ViewController: UIViewController {
//1: 創建MTL設備
var device:MTLDevice! = nil
//2:創建CAMetalLayer
var metalLayer:CAMetalLayer?
//3:創建一個頂點緩衝區
var vertexBuffer: MTLBuffer! = nil
//4:需要設置一個對象來保存渲染管道需要引用的所有狀態
var pipelineState: MTLRenderPipelineState! = nil
//5:命令隊列是負責通過GPU完成工作來調度時間的對象
var commandQueue: MTLCommandQueue! = nil
//6:需要視圖控制器類才能知道它需要刷新屏幕。這是使用CADisplayLink完成的
var timer: CADisplayLink! = nil
override func viewDidLoad() {
super.viewDidLoad()
//1: 創建MTL設備
device = MTLCreateSystemDefaultDevice()
//2:創建CAMetalLayer
metalLayer = CAMetalLayer()
metalLayer?.device = device
metalLayer?.pixelFormat = .bgra8Unorm
metalLayer?.framebufferOnly = true
metalLayer?.frame = view.layer.frame
view.layer.addSublayer(metalLayer!)
//三角形座標數據
let vertexData:[Float] = [ 0.0, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0]
//3:創建一個頂點緩衝區
let dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0])
vertexBuffer = device.makeBuffer(bytes: vertexData,length:dataSize,options: [])
// Set the Rendering Pipeline
//需要創建一個庫來保存着色器程序Shaders.metal
let defaultLibrary = device.makeDefaultLibrary()
let fragmentProgram = defaultLibrary?.makeFunction(name: "basic_fragment")
let vertexProgram = defaultLibrary?.makeFunction(name: "basic_vertex")
//4:需要設置一個對象來保存渲染管道需要引用的所有狀態
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
do {
try pipelineState = device.makeRenderPipelineState(descriptor:pipelineStateDescriptor)
}catch let error {
print("Failed to create pipeline state, error \(error)") }
//5:命令隊列是負責通過GPU完成工作來調度時間的對象,命令隊列是一個昂貴的對象,因此您只想創建一個並重用它
commandQueue = device.makeCommandQueue()
timer = CADisplayLink(target:self,selector:#selector(ViewController.gameloop))
timer.add(to:RunLoop.main,forMode:RunLoop.Mode.default)
}
//設置渲染過程
func render() {
let renderPassDescriptor = MTLRenderPassDescriptor()
guard let drawable = metalLayer?.nextDrawable() else {return}
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red:
221.0/255.0, green: 160.0/255.0, blue: 221.0/255.0, alpha: 1.0)
//命令隊列不直接採用渲染過程。相反,它需要命令緩衝區對象
let commandBuffer = commandQueue.makeCommandBuffer()
//命令緩衝區需要編碼渲染命令
let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor:renderPassDescriptor)
renderEncoder?.setRenderPipelineState(pipelineState)
renderEncoder?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
renderEncoder?.drawPrimitives(type: .triangle,vertexStart: 0,vertexCount: 3)
renderEncoder?.endEncoding()
commandBuffer?.present(drawable)
commandBuffer?.commit()
}
@objc func gameloop() { autoreleasepool { self.render()} }
}
創建一個新文件。從模板選項中選擇Metal,並將新文件命名爲Shaders.metal。確保將文件保存到項目目錄中。
//兩種着色器類型都是用金屬着色語言(MSL)編寫的,它基於C ++ 14
#include <metal_stdlib>
using namespace metal;
//創建頂點着色器,所有頂點着色器必須以關鍵字vertex開頭。它們還必須至少以float4對象的形式返回頂點的位置數據。
vertex float4 basic_vertex(const device packed_float3* vertex_array [[ buffer(0) ]], unsigned int vid [[ vertex_id ]]) {
return float4(vertex_array[vid], 1.0);
}
//創建片段着色器,片段着色器負責計算屏幕上每個像素的顏色
fragment half4 basic_fragment(){
return half4(1.0);
}