Search code examples
xcodegpumetalmetalkit

Why doesn't Metal render my simple clear window code?


I have been following this tutorial. I downloaded the source and tried "translating" it to Swift. This is my "translated" code:

import Cocoa
import AppKit
import MetalKit

import simd


class MetalViewController: NSViewController {

    @IBOutlet var inview: MTKView!
    override func viewDidLoad() {
        super.viewDidLoad()
        let _view: MTKView = self.inview
        _view.device = MTLCreateSystemDefaultDevice()
        let _renderer: Renderer=initView(view: _view)
        _view.delegate=_renderer as? MTKViewDelegate
        _view.preferredFramesPerSecond=60
    }
}
class Renderer: NSObject {
    init(device: MTLDevice){
        self._device=device
        self._commandQueue=_device.makeCommandQueue()!
        super.init()
    }
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {

    }
    func draw(in view: MTKView) {
        let color = Color(red: 1.0,green: 0.0,blue: 0.0,alpha: 0.0)
        view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha)
        let commandbuffer = _commandQueue.makeCommandBuffer()
        let renderpassdescriptor: MTLRenderPassDescriptor = view.currentRenderPassDescriptor!
        let renderencoder: MTLRenderCommandEncoder = (commandbuffer?.makeRenderCommandEncoder(descriptor: renderpassdescriptor))!
        renderencoder.endEncoding()
        commandbuffer!.present(view.currentDrawable!)
        commandbuffer!.commit()
    }

    var _device: MTLDevice
    var _commandQueue: MTLCommandQueue

}

struct Color{
    var red, green, blue, alpha: Double
}

func initView(view: MTKView) -> Renderer{
    var renderer: Renderer
    renderer=Renderer(device: view.device!)
    return renderer
}

So I put the AAPLRenderer and AAPLViewControllers into one file, and made it so that there are no header files. I linked the view with @IBOutlet to the view controller because the view was a NSView and I cannot cast it to MTKView without getting a compile time error. The AppDelegate is the original one and I do not have a main file.

I end up with a window that does not show red, but rather shows nothing. I do not understand why this is happening. Please help me, thank you.


Solution

  • I see two issues.

    1) MTKView's delegate property is a weak var, which means that if you don't hold onto an instance of your renderer, it'll be immediately deinited and never receive any delegate callbacks. Keep a reference to your renderer as a property on your view controller.

    class MetalViewController: NSViewController {
        @IBOutlet var inview: MTKView!
        var renderer: Renderer!
    
        override func viewDidLoad() {
            // ...
            let view: MTKView = self.inview
            // ...
            renderer = initView(view: view)
            view.delegate = renderer
            // ...
        }
    }
    

    2) Because the Renderer class doesn't explicitly declare conformance to the MTKViewDelegate protocol, the conditional cast when assigning it as the view's delegate fails. Make Renderer explicitly conform to the protocol, and remove the conditional cast as shown above.

    class Renderer: NSObject, MTKViewDelegate