CAMetalLayer nextDrawable 返回 nil 因为分配失败

Gra*_*aou 3 macos runtime-error storyboard swift metal

我是 Swift 和 Mac 开发的新手,我正在尝试制作一个可以打开 Metal 窗口的框架(仅限 macOS)。我在 macOS 版本 10.15.2 上使用 Xcode 版本 11.3.1。

我从我在网络上找到的示例开始(要启动的 macOS/命令行工具项目),但我不知道如何更正此错误:

[CAMetalLayer nextDrawable] 由于分配失败而返回 nil。

当使用故事板(macOS/App 项目)创建项目时,窗口会以预期的内容(填充为红色)打开,但是当我尝试用代码(macOS/命令行工具项目)替换故事板时,执行循环出现错误上文提到的 。

主文件

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()
Run Code Online (Sandbox Code Playgroud)

视图控制器.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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

渲染器.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) {

    }
}
Run Code Online (Sandbox Code Playgroud)

在我的研究中,我发现只有一篇可以帮助我的帖子正在使用,NSViewControllerRepresentable但并没有解决我的问题。

war*_*enm 7

这里最直接的问题是您的CAMetalLayer,即支持您的层MTKView,其大小为 (0, 0)。因此该层无法分配可绘制对象供您绘制。

你的层大小为零的原因是你的大小MTKView为零,而你的视图大小为零的原因是因为你在没有指定框架的情况下创建它。根据文档,“设置contentViewController会导致窗口根据 ; 的当前大小调整大小contentViewController” 因此您的窗口大小也为零。

在创建时提供一个大小MTKView,事情就会“工作”:

mtkView = MTKView(frame: NSMakeRect(0, 0, 100, 100))
Run Code Online (Sandbox Code Playgroud)

回应上面的评论,我建议不要使用这种方法,因为NSApplicationrun()方法并没有做所有必要的事情来使应用程序成为一个好的 macOS 公民。该应用程序将没有 Dock 磁贴,也没有主菜单,而且您将无法使用 Cmd+Q 等常用键盘快捷键而无需付出额外的努力。如果您正在设计一个框架,使人们更容易以最少的努力创建窗口,那么这太少了。也不可能或不支持NSApplicationMain使用公共 API来实现代表您所做的一切。鼓励您的框架的用户遵循平台设计模式,而不是试图以简单的名义从他们手中夺取控制权。如果他们已经在运行具有适当运行循环的成熟应用程序,NSApplication 地层。