金属渲染循环优化

zzy*_*yzy 5 drawrect avplayer metal cvpixelbuffer metalkit

我正在寻找有关如何正确实现 Metal 渲染循环的一些指导。我的渲染循环正在从AVPlayer.

这是我当前的实现:

  1. A以60hzCVDisplayLink查询播放器的频率。AVPlayerItemVideoOutput如果存在新帧,则会将其捕获/保存*作为它将渲染到的CVPixelBufferRef属性。MTKView(此时,先前捕获的视频帧被释放)。
  2. 我的MTKView设置是用isPausedenableSetNeedsDisplayto NO。换句话说,内部定时器的任务MTKView是定期调用其drawRect方法。
  3. drawRect首先将新到达的 * 转换CVPixelBuffer为 a MTLTexture,然后发生一堆渲染阶段。
  4. 最后,我在方法presentDrawable结束时调用drawRect

*注意:对 的互斥访问由和CVPixelBufferRef配对控制 。dispatch_semaphore_waitdispatch_semaphore_signal

这是正确的做事方式吗? 尽管偶尔会丢失一些帧,但它看起来性能相当不错。在时间方面,Metal 的 Xcode 分析告诉我,我的MTLCommandBuffer运行时间通常不到 3 毫秒。

话虽如此,我看到了一些替代的可能性:

  • 放弃CVDisplayLink实现并获取其中的框架drawRect
  • 反转渲染过程;首先在方法中显示先前捕获的帧并渲染,然后显示 Metal 命令缓冲区并立即调用。之后,捕获下一个视频帧并在下一次调用之前运行它的渲染阶段(在退出之前这样做吗?)。MTLTexturedrawRectcommitpresentDrawabledrawRectdrawRect

另一个问题:我的印象是,使用此配置CVDisplayLink, 和drawRect方法都没有在主线程上运行。让我感到困扰的是,每当我放松应用程序的一个菜单时,视频帧的传输就会出现明显的卡顿——这是主线程执行 UI 更新并且被阻塞的症状。当重新加载屏幕上的NSCollectionView内容并且其内容在屏幕上呈现动画时,会观察到相同的行为。这让我相信我的假设是错误的。如何MTKView制作这些渲染循环来避免这些问题? 想知道整个打开/配置是否AVPlayer也需要脱离主线程。

更新#1

我修复了将鼠标悬停在任何项目上时会发生的“另一个问题NSMenu”口吃问题。这是我的解决方案:

  1. MTKView使用isPaused=YES和配置每个enableSetNeedsDisplay=NO。这意味着视图draw需要显式调用来呈现其内容。
  2. CVDisplayLink回调中,发出drawMTKViewa 的调用dispatch_global_queue,因此:

__block MTKView *aView = ....;
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{ [aView draw]; });
Run Code Online (Sandbox Code Playgroud)