我需要对实时相机数据(仅来自 Y 平面)进行 CPU 端只读处理,然后在 GPU 上进行渲染。在处理完成之前不应渲染帧(因此我并不总是想渲染来自相机的最新帧,只是 CPU 端已完成处理的最新帧)。渲染与相机处理分离,目标是 60 FPS,即使相机帧到达的速率低于此。
有一个相关但更高级别的问题:Android 上的最低开销摄像头到 CPU 到 GPU 方法
更详细地描述当前的设置:我们有一个用于相机数据的应用程序端缓冲池,其中缓冲区是“空闲”、“显示中”或“待显示”。当来自相机的新帧到达时,我们获取一个空闲缓冲区,将帧(或者如果实际数据在某个系统提供的缓冲池中,则为它的引用)存储在那里,进行处理并将结果存储在缓冲区中,然后设置缓冲区“待显示”。在渲染器线程中,如果在渲染循环开始时有任何缓冲区“待显示”,我们将其锁定为“显示中”的缓冲区,渲染相机,并使用从相同计算的处理信息渲染其他内容相机框架。
感谢@fadden 对上面链接的问题的回复,我现在了解了 android camera2 API 的“并行输出”功能在各种输出队列之间共享缓冲区,因此至少在现代 android 上不应该涉及数据的任何副本。
在评论中,有人建议我可以同时锁定 SurfaceTexture 和 ImageReader 输出,然后“坐在缓冲区上”直到处理完成。不幸的是,我认为这不适用于我的情况,因为我们仍然希望以 60 FPS 的速度驱动解耦渲染,并且在处理新帧时仍然需要访问前一帧以确保事情不会得到不同步。
想到的一种解决方案是拥有多个 SurfaceTexture - 我们的每个应用程序端缓冲区中都有一个(我们目前使用 3 个)。使用该方案,当我们获得一个新的相机帧时,我们将从我们的应用端池中获得一个空闲缓冲区。然后我们会调用acquireLatestImage()
ImageReader 来获取数据进行处理,并调用updateTexImage()
空闲缓冲区中的 SurfaceTexture。在渲染时,我们只需要确保“显示中”缓冲区中的 SufaceTexture 是绑定到 GL 的那个,并且大部分时间一切都应该同步(正如@fadden 评论的那样,调用updateTexImage()
and之间存在竞争,acquireLatestImage()
但是时间窗口应该足够小以使其很少见,并且无论如何使用缓冲区中的时间戳可能是可检测和可修复的)。
我在文档中注意到updateTexImage()
只能在 SurfaceTexture 绑定到 GL 上下文时调用,这表明我在相机处理线程中也需要一个 GL 上下文,以便相机线程可以updateTexImage()
在“空闲”缓冲区中的 SurfaceTexture 上执行而渲染线程仍然能够从“显示中”缓冲区的 SurfaceTexture 进行渲染。
所以,对于问题:
- 这看起来是一种明智的做法吗?
- SurfaceTextures 基本上是共享缓冲池周围的轻包装,还是它们消耗一些有限的硬件资源并且应该谨慎使用?
- SurfaceTexture 调用是否足够便宜,以至于使用多个调用仍然比仅复制数据更胜一筹?
- 计划让两个线程具有不同的 GL 上下文并在每个线程中绑定不同的 SurfaceTexture 可能会起作用,还是我要求一个充满痛苦和错误驱动程序的世界?
听起来很有希望,我会试一试;但认为值得在这里问一下,以防有人(基本上是@fadden!)知道我忽略的任何内部细节,这会使这是一个坏主意。