Surface中的BufferQueue
发布时间:2026-09-28 | 浏览:1
1. BufferQueue 是什么
本质 :一个 环形缓冲队列 (slots + fences),承接" 生产者 Producer 写帧 → 消费者 Consumer 取帧"的流。
本质 :一个 环形缓冲队列 (slots + fences),承接" 生产者 Producer 写帧 → 消费者 Consumer 取帧"的流。
关键目标 : 零拷贝/少拷贝 传递图像帧,降低延迟与功耗。
关键目标 : 零拷贝/少拷贝 传递图像帧,降低延迟与功耗。
典型 Producer :MediaCodec(硬解)、MediaPlayer/ExoPlayer、OpenGL(EGL)、Canvas CPU 绘制。
典型 Producer :MediaCodec(硬解)、MediaPlayer/ExoPlayer、OpenGL(EGL)、Canvas CPU 绘制。
典型 Consumer : SurfaceView: SurfaceFlinger (系统合成器,独立图层) TextureView: SurfaceTexture (把帧变为 GPU 纹理参与应用内合成)
SurfaceView: SurfaceFlinger (系统合成器,独立图层)
SurfaceView: SurfaceFlinger (系统合成器,独立图层)
TextureView: SurfaceTexture (把帧变为 GPU 纹理参与应用内合成)
TextureView: SurfaceTexture (把帧变为 GPU 纹理参与应用内合成)
2. 内部构成(你需要知道的名词)
Slots :固定数量的缓冲槽位(Android 源码里 NUM_BUFFER_SLOTS=64 的上限,实际使用远小于此,一般 2~4)。
GraphicBuffer :每个 slot 上的图像缓冲(显存/共享内存)。
Fence(栅栏) :同步原语,标记"GPU/硬件写入/读取完成"。Producer queue 时附 release fence ;Consumer acquire 时拿到 acquire fence 。
IGraphicBufferProducer / Consumer :BufferQueue 的 Binder 两端接口;Java Surface → native Surface.cpp → IGBP。
BufferQueueProducer/Consumer/Core :C++ 里三分体实现(队列核心 + 生产/消费端包装)。
3.1 Producer 侧(写帧)
dequeueBuffer() :向队列申请一个空闲 slot 若没有可用 slot,会 阻塞/失败 。
dequeueBuffer() :向队列申请一个空闲 slot
若没有可用 slot,会 阻塞/失败 。
请求/复用缓冲 :如果大小/格式/usage 不匹配,触发 重新分配 (避免每帧分配!)。
请求/复用缓冲 :如果大小/格式/usage 不匹配,触发 重新分配 (避免每帧分配!)。
渲染写入 : CPU:ANativeWindow_lock() → 绘制 → unlockAndPost() GL:向 egl window surface 画 → eglSwapBuffers() MediaCodec:解码器把帧直接写入绑定的 Surface
CPU:ANativeWindow_lock() → 绘制 → unlockAndPost()
GL:向 egl window surface 画 → eglSwapBuffers()
MediaCodec:解码器把帧直接写入绑定的 Surface
queueBuffer() :把填好的帧入队,并附上 release fence 、时间戳、裁剪、变换等元数据。 若队列满,可能阻塞或丢帧(async 模式)。
queueBuffer() :把填好的帧入队,并附上 release fence 、时间戳、裁剪、变换等元数据。
若队列满,可能阻塞或丢帧(async 模式)。
3.2 Consumer 侧(读帧)
acquireBuffer() :取队首可用帧;等待 acquire fence 完成再读(保证数据有效)。
acquireBuffer() :取队首可用帧;等待 acquire fence 完成再读(保证数据有效)。
消费 : SurfaceView:SurfaceFlinger 在下一次合成周期把这帧与其它图层进行 硬件合成/混合 。 TextureView:SurfaceTexture 把帧 更新到 GL 纹理 ,在应用进程的 ViewRoot 绘制时采样参与合成。
SurfaceView:SurfaceFlinger 在下一次合成周期把这帧与其它图层进行 硬件合成/混合 。
TextureView:SurfaceTexture 把帧 更新到 GL 纹理 ,在应用进程的 ViewRoot 绘制时采样参与合成。
releaseBuffer() :用完把 slot 归还队列(可再次被 Producer 复用)。
releaseBuffer() :用完把 slot 归还队列(可再次被 Producer 复用)。
关键点 : 双/三缓冲 常见。三缓冲可降低生产/消费不同步导致的阻塞,但会增加一帧延迟。
4. 容量/阻塞规则(为什么会"卡住/掉帧")
队列允许 Producer 同时"借出"的 buffer 数量受 maxDequeuedBufferCount 限制(默认根据 Consumer 能同时持有的数量决定)。
队列允许 Producer 同时"借出"的 buffer 数量受 maxDequeuedBufferCount 限制(默认根据 Consumer 能同时持有的数量决定)。
当 Producer 已借出过多 或 Consumer 持有未释放 ,再 dequeueBuffer() 可能 阻塞 。
当 Producer 已借出过多 或 Consumer 持有未释放 ,再 dequeueBuffer() 可能 阻塞 。
当 Producer queueBuffer() 太快 (消费者来不及合成/采样): 同步模式 :可能在 queueBuffer() 阻塞等待。 异步模式 (setAsyncMode(true)):不阻塞, 丢弃旧帧保留最新帧 (视频预览常用)。
当 Producer queueBuffer() 太快 (消费者来不及合成/采样):
同步模式 :可能在 queueBuffer() 阻塞等待。
异步模式 (setAsyncMode(true)):不阻塞, 丢弃旧帧保留最新帧 (视频预览常用)。
尺寸/格式改变 会导致老缓冲失配并重分配, 频繁 reallocate = 抖动/卡顿 。
尺寸/格式改变 会导致老缓冲失配并重分配, 频繁 reallocate = 抖动/卡顿 。
经验:视频/相机建议 3 缓冲 (Producer 可 deque 2 个,Consumer 同时 hold 1 个),并开启 异步模式 以避免阻塞主渲染。
5. SurfaceView vs TextureView:BufferQueue 的去向不同
相同点 :两者 Producer 都是对一个 Surface 写帧;BufferQueue 机制一致,只是"另一端是谁"不同。
6. 关键参数与调优点(Producer 侧常用)
尺寸 :ANativeWindow_setBuffersGeometry() / MediaCodec format.setInteger(KEY_WIDTH/HEIGHT, ...) → 只在必要时改 ,否则频繁重分配。
尺寸 :ANativeWindow_setBuffersGeometry() / MediaCodec format.setInteger(KEY_WIDTH/HEIGHT, ...)
→ 只在必要时改 ,否则频繁重分配。
格式 :PIXEL_FORMAT_RGBA_8888 等;视频解码一般 NV12/NV21/YUV420(由 Codec & HWC 决定)。
格式 :PIXEL_FORMAT_RGBA_8888 等;视频解码一般 NV12/NV21/YUV420(由 Codec & HWC 决定)。
Usage :GRALLOC_USAGE_SW_READ/WRITE, HW_TEXTURE, HW_COMPOSER, PROTECTED(DRM)等;决定缓冲放在哪、能否被哪类硬件高效访问。
Usage :GRALLOC_USAGE_SW_READ/WRITE, HW_TEXTURE, HW_COMPOSER, PROTECTED(DRM)等;决定缓冲放在哪、能否被哪类硬件高效访问。
Dataspace/ColorSpace :BT.709/BT.2020/HDR;和 HWC/GL 管线匹配,否则会偏色/错 gamma。
Dataspace/ColorSpace :BT.709/BT.2020/HDR;和 HWC/GL 管线匹配,否则会偏色/错 gamma。
Scaling/Transform/Crop :通过 queueBuffer() 的元数据携带。SurfaceView 通常用 HWC 处理,TextureView 用 setTransform()。
Scaling/Transform/Crop :通过 queueBuffer() 的元数据携带。SurfaceView 通常用 HWC 处理,TextureView 用 setTransform()。
卡住在 dequeue/queue 增加缓冲数(setBufferCount / setMaxDequeuedBufferCount); 开启 async 模式(允许丢旧帧保最新); 检查 Consumer 是否长期不 releaseBuffer()(例如合成阻塞、GL 卡帧)。
卡住在 dequeue/queue
增加缓冲数(setBufferCount / setMaxDequeuedBufferCount);
开启 async 模式(允许丢旧帧保最新);
检查 Consumer 是否长期不 releaseBuffer()(例如合成阻塞、GL 卡帧)。
频繁重分配 (alloc churn) 避免每帧改分辨率/格式; 先 detach 再统一 setBuffersGeometry ,或在场景切换时一次性变更。
频繁重分配 (alloc churn)
避免每帧改分辨率/格式; 先 detach 再统一 setBuffersGeometry ,或在场景切换时一次性变更。
黑屏/花屏/绿屏 TextureView 某些机型/解码器组合不稳 → 验证改用 SurfaceView; 确认 setSurface(null) 的时序,销毁前解绑定; 检查颜色空间/stride 对齐问题(尤其自研 GL)。
TextureView 某些机型/解码器组合不稳 → 验证改用 SurfaceView;
确认 setSurface(null) 的时序,销毁前解绑定;
检查颜色空间/stride 对齐问题(尤其自研 GL)。
高延迟 三缓冲 + async;降低渲染分辨率;减少在应用进程的二次合成(TextureView->考虑 SurfaceView)。
三缓冲 + async;降低渲染分辨率;减少在应用进程的二次合成(TextureView->考虑 SurfaceView)。
截图/录屏差异 SurfaceView 不参与应用内 View.draw() 截屏;系统录屏一般可见;DRM 可 setSecure(true) 禁止截屏。
SurfaceView 不参与应用内 View.draw() 截屏;系统录屏一般可见;DRM 可 setSecure(true) 禁止截屏。
adb shell dumpsys SurfaceFlinger --list / --displays / --latency:看图层、刷新、合成信息。
Systrace/Perfetto :Graphics、View、Sched、Freq 轨道看 queue/dequeue、合成耗时与丢帧。
dumpsys gfxinfo:帧时间分布、Janky 统计。
开启 debug.sf.* 系列系统属性(开发版 ROM)可见更详细日志(谨慎)。
9. 最小落地骨架(Player → Surface)
若用 EGL/GL 自绘:以 holder.surface 或 Surface(st) 创建 EGL window surface,再 eglSwapBuffers()。
BufferQueue 是 Surface 的"帧中转站":Producer 通过 dequeue→渲染→queue 写帧;Consumer acquire→显示→release 读帧。
在 SurfaceView ,帧被 SurfaceFlinger 直接合成;在 TextureView ,帧先变成纹理参与应用内合成。理解这一点,基本就能把"卡顿/黑屏/延迟"问题对症下药。