【发布时间】:2016-09-27 09:18:12
【问题描述】:
我的应用程序需要先在 CPU 上对实时摄像机帧进行一些处理,然后再在 GPU 上渲染它们。 GPU 上还渲染了一些其他的东西,这取决于 CPU 处理的结果;因此,保持一切同步很重要,这样在 CPU 处理该帧的结果也可用之前,我们不会在 GPU 上渲染帧本身。
问题是在 android 上最低开销的方法是什么?
在我的例子中,CPU 处理只需要一张灰度图像,因此 Y 平面被打包的 YUV 格式是理想的(并且往往也与相机设备的本机格式很好地匹配)。 NV12、NV21 或全平面 YUV 都可以提供理想的低开销灰度访问,因此在 CPU 方面是首选。
在原始相机 API 中,setPreviewCallbackWithBuffer() 是将数据放到 CPU 上进行处理的唯一合理方法。这使 Y 平面分开,因此非常适合 CPU 处理。让 OpenGL 可以使用此帧以低开销的方式进行渲染是更具挑战性的方面。最后,我编写了一个 NEON 颜色转换例程来输出 RGB565,并使用 glTexSubImage2d 将其在 GPU 上可用。这首先是在 Nexus 1 时间范围内实现的,即使是 320x240 glTexSubImage2d 调用也需要 50 毫秒的 CPU 时间(我认为尝试进行纹理调配的驱动程序很差 - 这在后来的系统更新中得到了显着改善)。
过去,我研究了 eglImage 扩展之类的东西,但对于用户应用来说,它们似乎不可用或没有足够的文档记录。我稍微了解了内部 android GraphicsBuffer 类,但理想情况下希望留在受支持的公共 API 的世界中。
android.hardware.camera2 API 承诺能够将 ImageReader 和 SurfaceTexture 附加到捕获会话。不幸的是,我在这里看不到任何确保正确顺序管道的方法 - 在 CPU 处理完之前推迟调用 updateTexImage() 很容易,但是如果在该处理期间有另一帧到达,那么 updateTexImage() 将直接跳到最新的框架。似乎对于多个输出,每个队列中都会有独立的帧副本,理想情况下我希望避免。
理想情况下,这就是我想要的:
- 相机驱动程序用最新帧填充一些内存
- CPU 获得指向内存中数据的指针,无需复制即可读取 Y 数据
- 当帧准备好时,CPU 处理数据并在我的代码中设置一个标志
- 开始渲染帧时,检查新帧是否准备就绪
- 调用一些 API 来绑定与 GL 纹理相同的内存
- 当一个较新的帧准备就绪时,将保存前一帧的缓冲区释放回池中
我看不到在 android 上使用公共 API 完全实现零复制样式的方法,但最接近的方法是什么?
我尝试过的一件疯狂的事情似乎可行,但没有记录:ANativeWindow NDK API 可以接受数据 NV12 格式,即使适当的格式常量不是公共标头中的格式常量之一。这允许通过 memcpy() 用 NV12 数据填充 SurfaceTexture,以避免 CPU 端颜色转换和 glTexImage2d 中驱动程序端发生的任何混乱。这仍然是数据的额外副本,尽管感觉它应该是不必要的,并且再次因为它没有记录可能不适用于所有设备。支持的顺序零拷贝相机 -> ImageReader -> SurfaceTexture 或等效的将是完美的。
【问题讨论】:
标签: android opengl-es android-ndk android-camera