CAMetalLayer nextDrawable 返回 nil 因为分配失败
CAMetalLayer nextDrawable returning nil because allocation failed
我是 Swift 和 Mac 开发的新手,我正在尝试制作一个可以打开 Metal window 的框架(仅限 macOS)。我在 macOS 版本 10.15.2 上使用 Xcode 版本 11.3.1。
我从网上找到的示例开始(macOS/Command Line Tool 项目开始),但我看不出如何更正此错误:
[CAMetalLayer nextDrawable] returning nil because allocation failed.
当使用故事板(macOS/App 项目)创建项目时,window 打开时显示预期的内容(填充为红色)但是当我尝试用代码替换故事板时(macOS/Command Line Tool project) 上面提到的错误执行循环.
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) {
}
}
在我的研究中,我发现只有一个 post 可以帮助我使用 NSViewControllerRepresentable
,但没有解决我的问题。
这里最直接的问题是您的 CAMetalLayer
,支持您的 MTKView
的层具有大小 (0, 0)。因此图层无法为您分配可绘制对象。
您的图层大小为零的原因是您的 MTKView
大小为零,而您的视图大小为零的原因是您创建它时未指定框架。根据文档,"Setting contentViewController
causes the window to resize based on the current size of the contentViewController
;" 因此您的 window 大小也是零。
在创建您的 MTKView
时提供尺寸,然后事情会 "work":
mtkView = MTKView(frame: NSMakeRect(0, 0, 100, 100))
与上面的评论相呼应,我建议不要使用这种方法,因为 NSApplication
的 run()
方法并没有做所有必要的事情来使应用程序成为一个好的 macOS 公民。该应用程序不会有 Dock 磁贴,它不会有主菜单,并且您将无法使用 Cmd+Q 等常见的键盘快捷键,而不需要额外的努力。如果您设计的框架是为了让人们更轻松地以最少的努力创建 windows,那么这就太简单了。 NSApplicationMain
代表您用 public API 执行的所有操作也不可能也不支持。鼓励框架的用户遵循平台设计模式,而不是试图以简单的名义从他们手中夺走控制权。如果他们已经 运行 使用适当的 运行 循环构建成熟的应用程序,您可以代表他们创建 windows,而无需触及 NSApplication
层。
接受的答案绝对指向这里的真正问题,所以请向他致敬。但就我而言,还有另一个障碍,即自动布局调用周期。
程序化自动布局
因此,如果您正在处理一个项目,该项目采用编程方法来管理自动布局,那么您必须在此之后创建您的视图。就我而言,我还添加了 Metal 视图作为另一个子视图控制器。所以在我的案例中,解决方案基本上是在正确的生命周期方法中调用。
我刚刚在 viewDidLayoutSubviews() 方法中移动了我的方法调用(添加子视图控制器)。
override func viewDidLayoutSubviews() {
add(self.cameraVC, frame: container.frame)
}
希望这对像我这样的案例有所帮助。
我是 Swift 和 Mac 开发的新手,我正在尝试制作一个可以打开 Metal window 的框架(仅限 macOS)。我在 macOS 版本 10.15.2 上使用 Xcode 版本 11.3.1。
我从网上找到的示例开始(macOS/Command Line Tool 项目开始),但我看不出如何更正此错误:
[CAMetalLayer nextDrawable] returning nil because allocation failed.
当使用故事板(macOS/App 项目)创建项目时,window 打开时显示预期的内容(填充为红色)但是当我尝试用代码替换故事板时(macOS/Command Line Tool project) 上面提到的错误执行循环.
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) {
}
}
在我的研究中,我发现只有一个 post 可以帮助我使用 NSViewControllerRepresentable
,但没有解决我的问题。
这里最直接的问题是您的 CAMetalLayer
,支持您的 MTKView
的层具有大小 (0, 0)。因此图层无法为您分配可绘制对象。
您的图层大小为零的原因是您的 MTKView
大小为零,而您的视图大小为零的原因是您创建它时未指定框架。根据文档,"Setting contentViewController
causes the window to resize based on the current size of the contentViewController
;" 因此您的 window 大小也是零。
在创建您的 MTKView
时提供尺寸,然后事情会 "work":
mtkView = MTKView(frame: NSMakeRect(0, 0, 100, 100))
与上面的评论相呼应,我建议不要使用这种方法,因为 NSApplication
的 run()
方法并没有做所有必要的事情来使应用程序成为一个好的 macOS 公民。该应用程序不会有 Dock 磁贴,它不会有主菜单,并且您将无法使用 Cmd+Q 等常见的键盘快捷键,而不需要额外的努力。如果您设计的框架是为了让人们更轻松地以最少的努力创建 windows,那么这就太简单了。 NSApplicationMain
代表您用 public API 执行的所有操作也不可能也不支持。鼓励框架的用户遵循平台设计模式,而不是试图以简单的名义从他们手中夺走控制权。如果他们已经 运行 使用适当的 运行 循环构建成熟的应用程序,您可以代表他们创建 windows,而无需触及 NSApplication
层。
接受的答案绝对指向这里的真正问题,所以请向他致敬。但就我而言,还有另一个障碍,即自动布局调用周期。
程序化自动布局
因此,如果您正在处理一个项目,该项目采用编程方法来管理自动布局,那么您必须在此之后创建您的视图。就我而言,我还添加了 Metal 视图作为另一个子视图控制器。所以在我的案例中,解决方案基本上是在正确的生命周期方法中调用。
我刚刚在 viewDidLayoutSubviews() 方法中移动了我的方法调用(添加子视图控制器)。
override func viewDidLayoutSubviews() {
add(self.cameraVC, frame: container.frame)
}
希望这对像我这样的案例有所帮助。