Created
June 7, 2022 20:08
-
-
Save dannyow/d93c8e4982625cbbff2bd668f013d09d to your computer and use it in GitHub Desktop.
Hello World in Metal (as a self-contained NSViewController)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Cocoa | |
import MetalKit | |
// Self-contained MetalViewController based on https://metalbyexample.com/modern-metal-1/ | |
class MetalViewController: NSViewController { | |
var mtkView: MTKView! | |
var device: MTLDevice! | |
var commandQueue: MTLCommandQueue! | |
var pipelineState: MTLRenderPipelineState! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
mtkView = MTKView() | |
mtkView.translatesAutoresizingMaskIntoConstraints = false | |
view.addSubview(mtkView) | |
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|[mtkView]|", options: [], metrics: nil, views: ["mtkView": mtkView!])) | |
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[mtkView]|", options: [], metrics: nil, views: ["mtkView": mtkView!])) | |
device = MTLCreateSystemDefaultDevice()! | |
mtkView.device = device | |
mtkView.delegate = self | |
commandQueue = device.makeCommandQueue() | |
let shaders = """ | |
#include <metal_stdlib> | |
using namespace metal; | |
struct VertexIn { | |
packed_float3 position; | |
packed_float3 color; | |
}; | |
struct VertexOut { | |
float4 position [[position]]; | |
float4 color; | |
}; | |
vertex VertexOut vertex_main(device const VertexIn *vertices [[buffer(0)]], | |
uint vertexId [[vertex_id]]) { | |
VertexOut out; | |
out.position = float4(vertices[vertexId].position, 1); | |
out.color = float4(vertices[vertexId].color, 1); | |
return out; | |
} | |
fragment float4 fragment_main(VertexOut in [[stage_in]]) { | |
return in.color; | |
} | |
""" | |
do { | |
let library = try device.makeLibrary(source: shaders, options: nil) | |
let pipelineDescriptor = MTLRenderPipelineDescriptor() | |
pipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat | |
pipelineDescriptor.vertexFunction = library.makeFunction(name: "vertex_main") | |
pipelineDescriptor.fragmentFunction = library.makeFunction(name: "fragment_main") | |
pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor) | |
} catch { | |
fatalError() | |
} | |
} | |
} | |
extension MetalViewController: MTKViewDelegate { | |
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {} | |
func draw(in view: MTKView) { | |
guard let commandBuffer = commandQueue.makeCommandBuffer() else { return } | |
guard let passDescriptor = view.currentRenderPassDescriptor else { return } | |
guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return } | |
let vertexData: [Float] = [-0.5, -0.5, 0, 1, 0, 0, | |
0.5, -0.5, 0, 0, 1, 0, | |
0, 0.5, 0, 0, 0, 1] | |
encoder.setVertexBytes(vertexData, length: vertexData.count * MemoryLayout<Float>.stride, index: 0) | |
encoder.setRenderPipelineState(pipelineState) | |
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) | |
encoder.endEncoding() | |
commandBuffer.present(view.currentDrawable!) | |
commandBuffer.commit() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment