I'm new to Swift and Mac development and I'm trying to make a framework (only macOS) that can open a Metal window. I use Xcode Version 11.3.1 on macOS version 10.15.2.
I started from the examples that I found on the web (macOS/Command Line Tool project to start) and I do not see how to correct this error:
[CAMetalLayer nextDrawable] returning nil because allocation failed.
When the project is created with a storyboard (macOS/App project) the window opens with the expected content (filling in red) but when I try to replace the storyboard with code (macOS/Command Line Tool project) the execution loops on the error mentioned above .
Main.swift
import AppKit
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
var viewController: ViewController!
func applicationDidFinishLaunching(_ notification: Notification) {
NSLog("Start app")
viewController = ViewController()
window = NSWindow(contentRect: NSMakeRect(0, 0, 1024, 768),
styleMask: .borderless,
backing: .buffered,
defer: false)
window.contentViewController = viewController
window.title = "Hey, new Window!"
window.backgroundColor = NSColor.blue
window.orderFrontRegardless()
}
func applicationWillTerminate(_ notification: Notification) {
NSLog("Terminate app")
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
let app = NSApplication.shared
let appDelegate = AppDelegate()
app.delegate = appDelegate
app.run()
ViewController.swift
import Cocoa
import Metal
import MetalKit
class ViewController: NSViewController {
var mtkView: MTKView!
var renderer: Renderer!
override func loadView() {
mtkView = MTKView()
self.view = mtkView
}
override func viewDidLoad() {
super.viewDidLoad()
guard let defaultDevice = MTLCreateSystemDefaultDevice() else {
print("Metal is not supported on this device")
return
}
print("My GPU is: \(defaultDevice)")
mtkView.device = defaultDevice
guard let tempRenderer = Renderer(mtkView: mtkView) else {
print("Renderer failed to initialize")
return
}
renderer = tempRenderer
mtkView.delegate = renderer
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Renderer.swift
import Foundation
import Metal
import MetalKit
class Renderer : NSObject, MTKViewDelegate {
let device: MTLDevice
let commandQueue: MTLCommandQueue
init?(mtkView: MTKView) {
device = mtkView.device!
commandQueue = device.makeCommandQueue()!
}
func draw(in view: MTKView) {
guard let commandBuffer = commandQueue.makeCommandBuffer() else { return }
guard let renderPassDescriptor = view.currentRenderPassDescriptor else { return }
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1, 0, 0, 1)
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }
renderEncoder.endEncoding()
commandBuffer.present(view.currentDrawable!)
commandBuffer.commit()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
}
In my research I found only one post that could help me that was using NSViewControllerRepresentable but that did not solve my problem.