是否需要在主线程上绘制到 MTKView 或 CAMetalLayer?
Posted
技术标签:
【中文标题】是否需要在主线程上绘制到 MTKView 或 CAMetalLayer?【英文标题】:Is drawing to an MTKView or CAMetalLayer required to take place on the main thread? 【发布时间】:2019-01-19 15:08:54 【问题描述】:众所周知,更新AppKit
或UIKit
中的用户界面需要在主线程上进行。在提交drawable
时,Metal 是否有相同的要求?
在我一直在使用的层托管NSView
中,我注意到我可以从dispatch_queue
调用[CAMetalLayer nextDrawable]
,而不是main_queue
。然后我可以像往常一样更新该drawable的纹理并呈现它。
这似乎工作正常,但我觉得这很可疑。除非我忽略了文档中的某些内容,否则我找不到任何提及 Metal 的主线程要求(支持或反对)。
(我正在 macOS 10.13 上进行测试,但我认为 ios 的主线程要求也相同......?)
【问题讨论】:
【参考方案1】:在后台线程上绘制是安全的。 docs for -nextDrawable
说:
调用此方法会阻塞当前 CPU 线程,直到有新的可绘制对象可用。
(强调。)如果它只能在主线程上调用,那可能不会那么概括。此外,Apple 的一般建议是避免阻塞主线程,所以你会认为他们会在这里以某种方式指出这个事实,例如建议你不要调用它,除非你很确定它不会阻塞。
关于如何使用(而不是获取)drawable,请注意一个典型的用例是调用命令缓冲区的-presentDrawable:
方法。该方法便于添加调度的处理程序块(如通过-addScheduledHandler:
),然后将在可绘制对象上调用-present
。未指定将在哪个线程或队列上调用处理程序块,这表明对可绘制对象的 -present
调用不会发生在主线程上。
即使在那之后,drawable 在屏幕上的实际呈现在对-present
的调用中也不是同步的。可绘制对象等待直到渲染或写入其纹理的任何命令完成,然后才呈现到屏幕上。没有具体说明如何实现这种异步性,但它进一步表明调用哪个线程 -present
并不重要。
Metal Programming Guide 中有一些关于多线程的讨论,尽管它并不像人们希望的那样直接。尤其见the section on Multiple Threads, Command Buffers, and Command Encoders。请注意,这里讨论了由后台线程填充的命令缓冲区,并且没有关于使用可绘制对象的具体警告。同样,这是一种缺乏证据的论点,但我认为这很清楚。他们确实指出,一次只能有一个线程作用于给定的命令缓冲区,因此他们正在考虑线程安全问题。
【讨论】:
我倾向于同意,即使这与我多年来的 AppKit 规则背道而驰。 MTKView 的自动显示循环只调用drawRect
,这确实发生在主线程上。如果我滚动自己的“渲染循环”并输出到可能托管在NSView
中的CAMetalLayer
,听起来我可以渲染到该金属层的nextDrawable
,而无需同步回主线程。相反,您将如何强制 Metal 确保当前呈现的纹理 与 UI 同步? (例如当图层的内容需要准确反映鼠标拖动的位置时。)
对于该问题,请参阅-[CAMetalLayer presentsWithTransaction]
的文档。将其设置为 true,不要调用 -[MTLCommandBuffer presentDrawable:]
,而是先调用 -waitUntilScheduled
,然后再调用 -[MTLDrawable present]
。在这种情况下,您将必须在主线程上执行此操作以与其CATransaction
同步。
啊,我在做 waitUntilScheduled
和 present
部分,但不是 presentsWithTransaction
部分。我会添加它并玩更多。以上是关于是否需要在主线程上绘制到 MTKView 或 CAMetalLayer?的主要内容,如果未能解决你的问题,请参考以下文章