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 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.stride, index: 0) encoder.setRenderPipelineState(pipelineState) encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) encoder.endEncoding() commandBuffer.present(view.currentDrawable!) commandBuffer.commit() } }