gpt4 book ai didi

swift - 究竟如何在后台线程上渲染 Metal?

转载 作者:行者123 更新时间:2023-12-05 04:58:58 27 4
gpt4 key购买 nike

This problem由用户界面交互引起,例如在全屏显示时显示标题栏。该问题的答案提供了解决方案,但未提供如何实现该解决方案。

解决方案是render on a background thread .问题是,苹果提供的代码覆盖了很多内容,所以大部分都是无关代码,所以即使我能看懂,也无法使用苹果的代码。而且我无法理解它,所以它只是简单的不是一个选项。我如何让一个简单的 Swift Metal 游戏使用尽可能简洁的后台线程?

以这个为例:

class ViewController: NSViewController {
var MetalView: MTKView {
return view as! MTKView
}

var Device: MTLDevice = MTLCreateSystemDefaultDevice()!

override func viewDidLoad() {
super.viewDidLoad()
MetalView.delegate = self
MetalView.device = Device
MetalView.colorPixelFormat = .bgra8Unorm_srgb
Device = MetalView.device
//setup code
}
}

extension ViewController: MTKViewDelegate {
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {

}

func draw(in view: MTKView) {
//drawing code
}
}

这是一个基本的 Metal 游戏的开始。如果它在后台线程上呈现,该代码会是什么样子?

要修复在 Metal 中显示标题栏时的错误,我需要在后台线程上渲染它。那么,如何在后台线程上呈现它?

我注意到了 this答案建议每秒手动重绘 60 次。大概使用后台线程上的循环?但这似乎......不是解决它的干净方法。有更清洁的方法吗?

最佳答案

让它工作的主要技巧似乎是设置 CVDisplayLink。这在 Swift 中很尴尬,但可行。经过一些工作后,我能够修改 Xcode 中的“游戏”模板,以使用由 CAMetalLayer 而不是 MTKView 支持的自定义 View ,以及在后台呈现的 CVDisplayLink,如您链接的示例代码中所建议的那样——见下文。


编辑 10 月 22 日:
this thread中提到的方法似乎工作得很好:仍然使用 MTKView,但从显示链接回调中手动绘制它。具体来说,我能够按照以下步骤操作:

  1. 在 Xcode 中创建一个新的 macOS 游戏项目。
  2. 修改 GameViewController 以添加 CVDisplayLink,类似于以下内容(有关从 Swift 使用 CVDisplayLink 的更多信息,请参阅 this question)。在 viewWillAppear 中启动显示链接,在 viewWillDisappear 中停止。
  3. 在 viewDidLoad 中设置 mtkView.isPaused = true 以禁用自动渲染,而是从显示链接回调中显式调用 mtkView.draw()

我修改后的 GameViewController.swift 的全部内容可用 here .

我没有审查 Renderer 类的线程安全性,因此我不能确定是否需要进行更多更改,但这应该能让您正常运行。


使用 CAMetalLayer 而不是 MTKView 的旧实现:

这只是一个概念证明,我不能保证这是做所有事情的最佳方式。您可能会发现这些文章也很有帮助:

class MyMetalView: NSView {
var displayLink: CVDisplayLink?
var metalLayer: CAMetalLayer!

override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setupMetalLayer()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupMetalLayer()
}
override func makeBackingLayer() -> CALayer {
return CAMetalLayer()
}
func setupMetalLayer() {
wantsLayer = true
metalLayer = layer as! CAMetalLayer?
metalLayer.device = MTLCreateSystemDefaultDevice()!
// ...other configuration of the metalLayer...
}

// handle display link callback at 60fps
static let _outputCallback: CVDisplayLinkOutputCallback = { (displayLink, inNow, inOutputTime, flagsIn, flagsOut, context) -> CVReturn in
// convert opaque context pointer back into a reference to our view
let view = Unmanaged<MyMetalView>.fromOpaque(context!).takeUnretainedValue()

/*** render something into view.metalLayer here! ***/

return kCVReturnSuccess
}

override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()

guard CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) == kCVReturnSuccess,
let displayLink = displayLink
else {
fatalError("unable to create display link")
}

// pass a reference to this view as an opaque pointer
guard CVDisplayLinkSetOutputCallback(displayLink, MyMetalView._outputCallback, Unmanaged<MyMetalView>.passUnretained(self).toOpaque()) == kCVReturnSuccess else {
fatalError("unable to configure output callback")
}

guard CVDisplayLinkStart(displayLink) == kCVReturnSuccess else {
fatalError("unable to start display link")
}
}

deinit {
if let displayLink = displayLink {
CVDisplayLinkStop(displayLink)
}
}
}

关于swift - 究竟如何在后台线程上渲染 Metal?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63709936/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com