Skip to content

Instantly share code, notes, and snippets.

@dannyow
Created June 7, 2022 20:08
Show Gist options
  • Save dannyow/d93c8e4982625cbbff2bd668f013d09d to your computer and use it in GitHub Desktop.
Save dannyow/d93c8e4982625cbbff2bd668f013d09d to your computer and use it in GitHub Desktop.
Hello World in Metal (as a self-contained NSViewController)
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