在主线程上绘制MTKView或CAMetalLayer是否是必须的?

8
众所周知,在更新AppKitUIKit用户界面时,必须在主线程上进行。当涉及到呈现drawable时,Metal是否有相同的要求?
在我玩耍的一个层主持的NSView中,我注意到我可以从不是main_queuedispatch_queue中调用[CAMetalLayer nextDrawable]。然后我可以像往常一样更新该drawable的纹理并呈现它。
这似乎可以正常工作,但我觉得这相当可疑。除非我在文档中忽略了什么,否则我找不到关于Metal主线程要求的任何提及(无论是支持还是反对)。
(我正在macOS 10.13上测试,但我认为iOS的主线程要求也是相同的...?)
1个回答

6
在后台线程上绘制是安全的。 -nextDrawable 文档说:

调用此方法会阻塞当前CPU线程,直到新的可绘制对象可用。

(已加重强调)。如果它只能在主线程上调用,那可能就不会那么通用了。另外,苹果的通用建议是避免阻塞主线程,因此你会认为他们会以某种方式指出这个事实,比如告诉你不要调用它,除非你相当确定它不会阻塞。
对于可绘制对象的使用(而不是获取),请注意典型用法是调用命令缓冲区的 -presentDrawable:方法。该方法是通过添加计划处理程序块(如通过 -addScheduledHandler:)来提供便利的,然后将调用-present方法。未指定处理程序块将在哪个线程或队列上调用,这表明不能保证在主线程上调用可绘制对象的-present方法。

即使如此,在调用 -present 后,将可绘制对象实际呈现到屏幕上也不是同步的。可绘制对象会等待任何渲染或写入其纹理的命令完成后,才会呈现到屏幕上。虽然没有指定异步性是如何实现的,但进一步表明在哪个线程上调用 -present 并不重要。

Metal编程指南中有关于多线程的讨论,尽管它不是那么直接。特别是要看关于多个线程,命令缓存和命令编码器的部分。请注意,讨论了后台线程填充命令缓存,没有关于使用可绘制对象的具体警告。再次强调,这是一种通过证据缺失的争论方式,但我认为它很清楚。他们指出只有一个线程可以同时对一个给定的命令缓存进行操作,因此他们正在考虑线程安全问题。


我倾向于同意,即使这与我的多年的AppKit规则相悖。MTKView的自动显示循环只调用drawRect,这确实发生在主线程上。如果我自己编写“渲染循环”并输出到可能托管在NSView中的CAMetalLayer,那么似乎我可以渲染到该金属层的nextDrawable而无需返回主线程进行同步。相反,您如何强制Metal确保当前呈现的纹理*与UI同步?(例如,当图层内容需要准确反映鼠标拖动的位置时。) - kennyc
1
对于这个问题,请参阅 -[CAMetalLayer presentsWithTransaction] 的文档。将其设置为 true,不要调用 -[MTLCommandBuffer presentDrawable:],而是先调用 -waitUntilScheduled,然后再调用 -[MTLDrawable present]。在这种情况下,您需要在主线程上执行此操作以与其 CATransaction 同步。 - Ken Thomases
啊,我已经做了 waitUntilScheduledpresent 这两个部分了,但是没有做 presentsWithTransaction 部分。我会加上去并再试一下。 - kennyc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接