BufferQueue的环形队列是什么设计的

1. 总览:固定槽位池 + 有界 FIFO

BufferQueue 的"环形"不是单块连续内存做取模指针,而是:

  • 一组固定大小的槽位数组(BufferSlot slots[NUM_BUFFER_SLOTS],AOSP 定义通常为 64 上限)存放 GraphicBuffer 指针与元数据;

  • 一条有界 FIFO 队列(mQueue,存 BufferItem,按帧序号入队出队)。

  • 槽位反复复用 + 队列首尾推进 ⇒ 形成逻辑上的环

常见参与方:

  • Producer(相机/MediaCodec/GL 等):dequeueBuffer() → 写图 → queueBuffer()

  • Consumer(SurfaceFlinger/TextureView 等):acquireBuffer() → 用图 → releaseBuffer()

2. 核心数据结构

  • BufferSlot[] :每个 slot 里保存:

    • sp(显存句柄)
    • 状态(见下一节)
    • fences、crop、transform、timestamp、damage 等元数据
  • mQueue (std::list / deque) :排队的可消费帧(指向某个 slot + 提交参数)

  • free 列表 / 索引集合:可 dequeue 的空闲 slot

  • 计数器:frameNumber(单调递增),maxBufferCount,maxAcquiredBufferCount

3. 槽位状态机(Slot State)

典型四态(不同版本略有扩展):

rust 复制代码
FREE -> DEQUEUED -> QUEUED -> ACQUIRED -> FREE
  • FREE:空闲,可被 Producer dequeueBuffer

  • DEQUEUED:Producer 已拿到,正在写

  • QUEUED:写完已入队,等待 Consumer 取

  • ACQUIRED:Consumer 正在用

  • releaseBuffer() 把 ACQUIRED 还为 FREE,进入下一轮复用

4. "环形"的本质

  • 槽位是固定池:数量有限,周而复始复用(不像无界分配/释放)。

  • mQueue 是有界 FIFO :队尾入、队首出;当容量用尽,Producer 会背压(dequeueBuffer 阻塞/失败),直到 Consumer 释放。

  • 因为有限容量 + 循环复用,所以从运行形态上呈现"环形队列"。

一个简单示意(3 缓冲典型):

less 复制代码
[FREE] [FREE] [FREE]        // 初始
  |dequeue→写
[DEQUEUED] [FREE] [FREE]
  |queue→入队
[QUEUED] [FREE] [FREE]
         |acquire→消费
[ACQUIRED] [FREE] [FREE]
         |release
[FREE] [FREE] [FREE]        // 再次回到开头(循环)

5. 关键流程时序(简化)

Producer 侧

  1. dequeueBuffer(slot, fence):找一个 FREE slot,返回写入前需要等待的 fence(保证上游 GPU/Consumer 已经不再使用该缓冲)

  2. 写图(GL/CPU/Codec)

  3. queueBuffer(slot, params):把该帧(BufferItem)压入 mQueue 尾部并标记 QUEUED

Consumer 侧

  1. acquireBuffer(&item, &fence):从队首取 BufferItem,变 ACQUIRED,拿到acquire fence 等待生产者完成写入

  2. 合成/渲染

  3. releaseBuffer(slot, releaseFence):归还该 slot,回到 FREE

6. Fence 同步与零拷贝

  • 通过 sync fence 保证读写时序:Producer 在 dequeue 时等 "写前 fence";Consumer 在 acquire 时等 "写完 fence";释放时给 Producer 返回 "release fence"。

  • 典型是零拷贝 共享(gralloc/allocator 分配的显存缓冲),跨进程只传递 引用 + fence

7. 缓冲数量与背压

  • maxBufferCount(通常 2/3 起步):决定环的"周长"。三缓冲常见,可减少 Producer 等待,平滑抖动。

  • maxAcquiredBufferCount(Consumer 设置,通常为 1):限制 Consumer 同时持有的帧数。

  • 当所有 slot 都处于 DEQUEUED/QUEUED/ACQUIRED(无 FREE)时,Producer 的 dequeueBuffer 会被阻塞或返回应对码,形成背压,防止无限堆积。

8. 与"真正环形数组"的区别

  • 不是单一 head/tail + 取模索引的数据结构实现;

  • 实际实现是槽位池 + FIFO 列表 ,但行为等价于固定容量的环形队列:队列深度有限、首尾推进、元素释放后回到池中再次被复用。

9. 常见调参与模式(概念)

  • Async/Non-async 模式:异步模式允许队列中保留更多已排队帧,减轻 Producer 阻塞,但可能增加延时。

  • Shared/Persistent Buffer(部分版本/场景):特定模式下共享单一缓冲或持久分配,减少分配次数。

  • 裁剪/变换/时间戳/损伤区:随 queueBuffer 一起入队,Consumer 合成时使用,降低带宽与重绘成本。

10. 一句话总结

BufferQueue 的"环形队列"= 固定数量槽位的循环复用 (Slot 池) + 有界 FIFO 帧队列 (mQueue),配合 fence 同步 + 背压 ,在 Producer/Consumer 之间实现零拷贝、低延时、可控容量的图像缓冲传递。

如果你要定位卡顿/延迟/撕裂等问题,优先检查:

  • 当前 maxBufferCount / maxAcquiredBufferCount 是否匹配场景;
  • dequeueBuffer 是否经常超时(背压过强);
  • fence 等待是否过长(GPU/合成拥堵);
  • Consumer 是否长期 ACQUIRED 不释放(忘记 releaseBuffer)。
相关推荐
!win !8 小时前
不定高元素动画实现方案(下)
前端·javascript·css
Song5592 天前
elpis框架抽离并发布 SDK-NPM 包
前端
Mintopia2 天前
低代码平台如何集成 AIGC 技术?核心技术衔接点解析
前端·javascript·aigc
Mintopia2 天前
Next.js + AI-SDK + DeepSeek:3 分钟建设属于你的 AI 问答 Demo
前端·javascript·next.js
anyup2 天前
历时 10 天+,速度提升 20 倍,新文档正式上线!uView Pro 开源组件库体验再升级!
前端·typescript·uni-app
沉默王二2 天前
金山还是小米,谁才是雷军的亲儿子?附小米线下一面面经(八股盛宴)
后端·面试
_AaronWong2 天前
仿 ElementPlus 的函数式弹窗调用
前端·element
用户21411832636022 天前
AI 当 “牛马”!免费云服务器 + 开源插件,7×24 小时写小说,一晚交出 70 章长篇
前端
橙序员小站2 天前
搞定系统设计题:如何设计一个订单系统?
java·后端·面试
IT_陈寒2 天前
React 18新特性全解析:这5个隐藏API让你的性能飙升200%!
前端·人工智能·后端