我正在寻找有关如何正确实现金属渲染循环的方向。我的渲染循环从AVPlayer
.
这是我当前的实现:
- A以 60hz 的频率
CVDisplayLink
查询播放器的。AVPlayerItemVideoOutput
如果有一个新帧,它会CVPixelBufferRef
被捕获/保存*作为MTKView
它将被渲染到的属性。(此时,之前捕获的视频帧被释放)。 - My
MTKView
设置为isPaused
和enableSetNeedsDisplay
toNO
。换句话说,内部计时器的MTKView
任务是定期调用其drawRect
方法。 - 在
drawRect
我首先将 new-arrived* 转换CVPixelBuffer
为 aMTLTexture
,然后出现一堆渲染阶段。 - 最后,我
presentDrawable
在方法结束时调用drawRect
。
*注意:对 的互斥访问CVPixelBufferRef
由一对 dispatch_semaphore_wait
和控制dispatch_semaphore_signal
。
这是一种正确的做事方式吗? 尽管偶尔会丢失一些帧,但它似乎相当高效。在时间方面,Metal 的 Xcode 分析告诉我,我MTLCommandBuffer
的运行时间通常不到 3 毫秒。
话虽如此,我看到了一些替代的可能性:
- 放弃
CVDisplayLink
实现并抓住其中的框架drawRect
- 反转渲染过程;显示之前捕获的帧并
MTLTexture
首先在drawRect
方法中渲染,然后commit
是 Metal 命令缓冲区并presentDrawable
立即调用。在那之后,捕获下一个视频帧并在下一次调用之前运行它的渲染阶段(在退出drawRect
之前这样做吗?)。drawRect
另一个问题:我的印象是CVDisplayLink
和drawRect
方法都没有在这个配置的主线程上运行。我感到困扰的是,每当我松开应用程序的一个菜单时,视频帧的传递就会出现明显的卡顿——这是主线程进行 UI 更新并阻塞的症状。NSCollectionView
当重新加载屏幕并在屏幕上显示其内容时,会观察到相同的行为。这让我相信我的假设是不正确的。如何MTKView
制作这些渲染循环来避免这些问题? 想知道整个打开/配置是否也AVPlayer
需要脱离主线程。
更新#1
我修复了鼠标悬停在任何项目上时会出现的“另一个问题”口吃问题。NSMenu
这是我的解决方案:
MTKView
用isPaused=YES
和配置每个enableSetNeedsDisplay=NO
。这意味着draw
视图需要显式调用才能呈现其内容。- 在
CVDisplayLink
回调中,发出对on a的draw
调用,因此:MTKView
dispatch_global_queue
__block MTKView *aView = ....;
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{ [aView draw]; });