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 侧
-
dequeueBuffer(slot, fence):找一个 FREE slot,返回写入前需要等待的 fence(保证上游 GPU/Consumer 已经不再使用该缓冲)
-
写图(GL/CPU/Codec)
-
queueBuffer(slot, params):把该帧(BufferItem)压入 mQueue 尾部并标记 QUEUED
Consumer 侧
-
acquireBuffer(&item, &fence):从队首取 BufferItem,变 ACQUIRED,拿到acquire fence 等待生产者完成写入
-
合成/渲染
-
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)。