如何强制Unity更新/重绘整个场景?Android相机纹理延迟排查
Unity Android原生相机纹理渲染延迟问题解答
实测延迟数据
- 直接SurfaceTexture渲染:~80ms
- OpenGL OES转FBO后渲染:~90ms
- Unity 60fps渲染:100-150ms(额外近60ms延迟不符合预期)
理论渲染流程
- CameraHAL → OES纹理 → TextureView
- CameraHAL → OES纹理 → 绘制到FBO(耗时<10ms)→ GLView
- CameraHAL → OES纹理 → 绘制到FBO(耗时<10ms)→ Unity渲染最终纹理
当前实现逻辑
Android原生插件渲染线程中,onFrameAvailable()触发时创建FBO(或双缓冲FBO),将OES纹理绘制到FBO;Unity端在Update()中创建ExternalTexture(nativeFboTextureId)并绑定到着色器。预期延迟仅来自相机线程到绘制线程切换、OES转FBO、Unity 16.67ms绘制周期。
1. Unity是否存在内部延迟?
是,Unity渲染管线存在多个可能导致额外延迟的环节:
- 垂直同步(VSync)对齐:Unity默认会对齐设备VSync周期(60fps下为16.67ms),若原生帧到达时机错过VSync窗口,会多等待一个周期甚至更久。
- 外部纹理缓存机制:Unity对
ExternalTexture有内部缓存,不会每帧主动读取最新原生纹理数据,可能延迟1-2帧更新。 - 线程调度开销:Unity主线程、渲染线程与原生插件渲染线程的调度存在同步成本,当帧到达时机与Unity渲染周期不匹配时,会产生等待延迟。
2. Unity是否会使用最新的外部纹理重绘?
默认情况下不一定:
- 在
Update()中创建ExternalTexture是在Unity主线程执行,而渲染线程实际采样纹理是在后续渲染阶段,可能错过刚生成的FBO帧数据。 - Unity不会主动监听原生纹理的更新事件,若无主动通知,无法及时获取最新帧。
3. 如何在原生帧完成时强制Unity重绘?
可以通过以下方案解决:
- 触发Unity主动渲染:原生FBO绘制完成后,通过JNI调用
UnitySendMessage,通知Unity侧调用目标相机的RequestRender()方法,强制触发一次渲染,无需等待下一个Update周期。 - 复用ExternalTexture:初始化时创建一次
ExternalTexture,后续原生帧更新后,通过JNI调用ExternalTexture.UpdateExternalTexture()直接更新纹理数据,避免重复创建的开销。 - 双缓冲FBO正确切换:使用双缓冲FBO,帧绘制完成后立即切换缓冲,将已完成的FBO纹理ID传递给Unity,确保Unity读取的是已完成的最新帧,同时避免读取正在绘制的纹理。
- 调整VSync设置:在Unity
QualitySettings中将VSync Count设为Don't Sync,关闭垂直同步(可能导致画面撕裂,需权衡);或在原生侧控制相机帧输出频率与Unity渲染周期对齐。 - 渲染线程级更新:在Unity侧使用
OnRenderObject()回调(渲染线程执行)更新外部纹理,减少主线程到渲染线程的同步延迟;或用CommandBuffer提前设置纹理采样逻辑,确保渲染时使用最新FBO数据。
内容的提问来源于stack exchange,提问作者fluke
相关产品推荐
相关产品推荐

