据我所知,GPUImage执行DAG遍历和使用 信号量保护OpenGL使用,同时将其作为单一使用资源与帧缓冲纹理缓存一起处理。
有理由在这里使用信号量吗?他们不是不必要地使情况复杂化吗?它们提供了什么好处,以及通过为每个过滤器DAG使用单独的线程实现而不是在runloop中的单独线程上运行会遇到什么样的问题。 是否有特定的设计考虑因素为当前GPUImage架构的决策提供了信息?
答案 0 :(得分:3)
使用OpenGL(ES)上下文时,如果一次从多个线程访问它,则会发生错误。您可以简单地在主线程上执行所有渲染和交互代码,但这会干扰您的UI并在UI事件期间停止任何图像或视频处理(如下拉菜单)。还有significant performance advantages在后台线程上进行OpenGL(ES)渲染。
因此,您需要一种在后台线程上执行OpenGL(ES)渲染的方法,同时仍然可以防止同时访问。手动创建的线程和锁是实现此目的的一种方法,但是锁具有显着的性能开销,并且正确管理手动创建的线程可能会添加大量代码(并且可能浪费资源)。
一次一个块的Grand Central Dispatch队列是一种有效且相对简单的方法,可以提供对此类共享资源的安全,无锁访问。您希望在上下文中执行OpenGL(ES)渲染的任何位置,只需将其包装在要在上下文的串行调度队列中调度的块中。这样可以很容易地看到代码中这些访问的位置,并使您免于维护手动线程,runloops和锁的性能和代码开销。
我讨论了在my answer here中使用调度信号量的原因,但这是一种选择性地丢弃传入帧以响应负载的方法。
使用像这样的串行调度队列,我想确保在任何给定时间我只有一个图像或视频帧在队列中运行。使用单个GPU,一次渲染多个图像没有任何优势。
但是,如果您有一台摄像机提供每秒30-60帧的帧处理,并且您的处理管道偶尔需要超过1/30或1/60秒才能对这些图像进行处理,那么必须做出决定。您是丢弃传入的帧,还是将它们排入队列?如果是后者,你将继续在队列中构建越来越多的帧,直到耗尽可用的处理和内存资源,并且你还会看到处理中存在越来越大的延迟。
如果已经在串行调度队列中处理了帧,则调度信号量允许我立即删除帧,并以高效和安全的方式执行此操作。它也只添加了几行代码,几乎所有代码都可以在my answer here中找到(这在Swift 3中更短,更易读)。
我上面描述的架构已经过全面的分析,并且是我找到的满足这些需求的最佳解决方案。我已经使用它多年来在旧的iOS硬件上提供60 FPS OpenGL ES渲染分子模型,在Mac上提供实时机器视觉处理,在iOS上提供实时视频过滤。考虑到多线程代码可能出错的所有问题,它已被证明非常可靠且易于维护。 GCD队列和信号量的开销从未接近我的视频渲染中的性能瓶颈。