GUI系统之SurfaceFlinger(5)BufferQueue内部原理

本文深入探讨Android GUI系统中的BufferQueue,分析其内部原理,包括BufferQueue如何作为生产者和消费者之间的中介,管理缓冲区的状态迁移,并通过ConsumerListener通知消费者处理就绪的buffer。

文章都是通过阅读源码分析出来的,还在不断完善与改进中,其中难免有些地方理解得不对,欢迎大家批评指正。
转载请注明:From LXS. http://blog.youkuaiyun.com/uiop78uiop78/

GUI系统之SurfaceFlinger章节目录:
blog.youkuaiyun.com/uiop78uiop78/article/details/8954508



1.1 BufferQueue详解

上一小节我们已经看到了BufferQueue,它是SurfaceTextureClient实现本地窗口的关键。从逻辑上来推断,BufferQueue应该是驻留在SurfaceFlinger这边的进程中。我们需要进一步解决的疑惑是:

Ø  每个应用程序可以对应几个BufferQueue,它们是一对一、多对一或者是一对多?

Ø  应用程序所需要的绘图空间是由谁分配的?

在音频系统的学习中,我们知道AudioTrack和AudioFlinger是通过共享内存的形式来进行数据传递的,那么显示系统中是否也是类似情况?

Ø  应用程序与SurfaceFlinger如何互斥共享数据区

和在Audio系统中遇到的问题一样,我们面临的是经典的“生产者-消费者”模型。显示系统又是如何协调好这两者间的互斥访问的呢?

1.1.1 BufferQueue的内部原理

先来解析下BufferQueue的内部构造,如下图所示:


图 11‑12 BufferQueue内部变量

 

因为BufferQueue是ISurfaceTexture的本地实现,所以它必须重载接口中的各虚函数,比如queueBuf

<think>好的,我现在需要帮助用户解决他们遇到的GraphicBufferAllocator和SurfaceFlinger错误。根据用户提供的错误日志:“E GraphicBufferAllocator: Failed to allocate (1920x1080) layerCount 1 format 2130706433 usage 10004903 error 5”以及他们提到的SurfaceFlinger bufferqueue blocked needFeedback 1,我需要结合之前的回答和用户提供的引用资料来制定解决方案。 首先,回顾之前的回答,错误可能由内存不足、参数不兼容或硬件限制引起。用户现在提到了SurfaceFlinger的问题,这可能与形缓冲区的分配和队列管理有关。此外,用户提供的引用[3]提到了SurfaceFlinger/gralloc的内存错误,通过调整内核中的帧缓冲大小来解决,这可能是一个相关的点。 接下来,我需要考虑用户提供的其他引用是否有帮助。引用[1]讨论了CUDA内存不足的问题,解决方法包括释放GPU内存、调整batch size等,虽然主要是针对CUDA,但思路可能适用于形内存管理,比如释放未使用的资源或调整缓冲区大小。引用[2]提到调整Kafka的内存设置,这可能类比到调整Android系统的内存分配参数。引用[4]处理的是文件句柄不足的问题,通过修改系统参数解决,这可能提示我需要检查系统级的资源限制,比如inotify参数,但需要确认是否与当前错误相关。 用户当前的问题涉及GraphicBufferAllocator无法分配指定参数的缓冲区,并且SurfaceFlingerbufferqueue被阻塞。需要从以下几个方面考虑: 1. **内存不足**:错误代码5对应GRALLOC1_ERROR_NO_RESOURCES,可能是系统或进程内存不足。需要检查系统内存、GPU显存以及进程的内存使用情况。解决方案可能包括释放内存、优化资源使用或调整内存分配参数。 2. **参数兼容性**:检查缓冲区格式(2130706433,即HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)和usage标志(10004903)是否被硬件支持。可能需要强制使用标准格式如RGBA_8888,或调整usage标志以避免冲突。 3. **SurfaceFlingerBufferQueue阻塞**:bufferqueue被阻塞可能是因为生产者或消费者处理不及时,导致缓冲区无法释放。需要检查代码中是否有正确的缓冲区释放逻辑,或是否在UI线程中进行了耗时操作,导致帧未能及时提交。 4. **系统级配置**:根据引用[3],可能需要调整内核的帧缓冲大小或Gralloc模块的配置,以增加可用内存。此外,类似引用[4]中的系统参数调整,可能需要修改与形内存相关的内核参数。 5. **硬件限制**:旧设备可能存在最大分辨率或缓冲区数量的限制,需要动态检测并降级处理。 在整合这些信息时,需要确保解决方案步骤清晰,结合代码示例和命令行操作,方便用户实施。同时,用户可能需要高级调试,如查看Gralloc日志,这需要root权限。此外,引用[1]中的方法如调整batch size可能对应到形处理中的减少缓冲区大小或数量,而释放资源的方法可以类比杀掉占用内存的进程或优化应用的内存管理。 最后,确保回答结构符合用户的要求:原因分析、排查步骤、修复方案,以及相关问题。需要引用之前提供的引用资料中的相关点,并正确标注引用标识。</think>### GraphicBufferAllocator 与 SurfaceFlinger 错误解决方案 --- #### 一、错误根源分析 错误日志 `E GraphicBufferAllocator: Failed to allocate (1920x1080) layerCount 1 format 2130706433 usage 10004903 error 5` 表明形缓冲区分配失败,结合 `SurfaceFlinger bufferqueue blocked needFeedback 1`,可能原因如下: 1. **显存/内存不足** - **系统级内存耗尽**:SurfaceFlinger 无法从 Gralloc 分配足够显存(`error 5` 对应 `GRALLOC1_ERROR_NO_RESOURCES`),可能因多应用共享 GPU 资源导致[^1][^3]。 - **进程内存泄漏**:应用未正确释放 `GraphicBuffer` 或 `SurfaceTexture`,导致 `BufferQueue` 阻塞(如生产者持续提交但消费者未消费)[^3]。 2. **参数兼容性问题** - **格式冲突**:`format 2130706433`(`HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED`)可能与当前硬件驱动不兼容,需验证设备支持的像素格式(如强制使用 `RGBA_8888`)[^1]。 - **Usage 标志冲突**:`usage 10004903` 可能包含 `SW_READ`(`0x1000`)和 `HW_COMPOSER`(`0x800`)等冲突标志,需检查 Gralloc 模块的 `isSupported` 接口[^4]。 3. **SurfaceFlinger 队列阻塞** - **生产者-消费者失衡**:`bufferqueue blocked` 表明生产者(如 Camera 或 OpenGL)提交帧速率超过消费者(SurfaceFlinger)处理能力,导致缓冲区堆积[^3]。 --- #### 二、具体解决方案 ##### 1. **显存资源释放(优先尝试)** - **强制清理 GPU 缓存**: ```bash adb shell dumpsys SurfaceFlinger --list | grep "Allocated buffers" adb shell dumpsys SurfaceFlinger --kill # 重启 SurfaceFlinger 服务(需 root) ``` 若输出显示 `Total buffers: 2000+`,表明显存碎片化严重,需重启服务释放资源[^3]。 - **主动释放应用层资源**: ```java // 在 Activity.onDestroy() 或 SurfaceHolder.Callback.surfaceDestroyed() 中 if (surfaceTexture != null) { surfaceTexture.release(); // 解除 GraphicBuffer 绑定 surfaceTexture = null; } ``` ##### 2. **参数适配与降级策略** - **格式降级**:强制使用通用像素格式: ```java // 在 SurfaceView 或 TextureView 初始化时 surfaceHolder.setFormat(PixelFormat.RGBA_8888); ``` - **Usage 标志优化**:移除非必要标志(如 `SW_READ` 在纯渲染场景中可省略): ```java int usageFlags = GraphicBuffer.USAGE_HW_RENDER | GraphicBuffer.USAGE_HW_COMPOSER; ``` ##### 3. **BufferQueue 阻塞修复** - **限制帧率**:避免生产者过快地提交帧(如 Camera 预览限制为 30fps): ```java camera.setPreviewCallbackWithBuffer(new PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { // 处理帧数据后,添加同步锁控制提交频率 synchronized (bufferLock) { surfaceTexture.updateTexImage(); // 触发消费者消费 } camera.addCallbackBuffer(data); } }); ``` - **异步渲染**:将 OpenGL 渲染移至独立线程,避免阻塞 UI 线程: ```kotlin GLSurfaceView.RENDERMODE_CONTINUOUSLY -> GLSurfaceView.RENDERMODE_WHEN_DIRTY ``` ##### 4. **系统级调整(需 root)** - **Gralloc 内存池扩容**:修改 Gralloc 模块配置(参考引用[3]中调整 `VIDEOMEMSIZE` 的思路): ```bash adb pull /vendor/lib64/hw/gralloc.default.so # 使用二进制编辑器搜索 MAX_BUFFER_SIZE 并修改为 64MB adb push gralloc.default.so /vendor/lib64/hw/ ``` - **调整 SurfaceFlinger 参数**: ```bash adb shell setprop debug.sf.latch_unsignaled 1 # 允许缓冲区重用 adb shell setprop debug.sf.disable_backpressure 1 # 禁用背压检测(慎用) ``` ##### 5. **兜底策略** - **动态降分辨率**:根据设备能力调整缓冲区尺寸: ```java WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(metrics); int maxSize = metrics.widthPixels * metrics.heightPixels; if (requestedWidth * requestedHeight > maxSize) { requestedWidth = metrics.widthPixels / 2; requestedHeight = metrics.heightPixels / 2; } ``` --- #### 三、高级调试方法 - **Gralloc 详细日志**: ```bash adb shell setprop debug.gralloc.log_level 7 adb logcat -s gralloc ``` 观察日志中 `gralloc_alloc: try_alloc` 的失败原因(如 `width too large` 或 `format not supported`)。 - **SurfaceFlinger 状态分析**: ```bash adb shell dumpsys SurfaceFlinger | grep -A 20 "BufferQueue" ``` 检查 `status: blocked` 的队列及关联进程(如 `com.example.app/Producer`)。 --- ### 相关问题 1. 如何检测 Android 设备的 Gralloc 模块支持的最大缓冲区数量? 2. `SurfaceFlinger bufferqueue blocked` 与 ANR 有何关联? 3. 在 Android NDK 中如何安全释放 `ANativeWindowBuffer`? --- [^1]: `HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED` 需与 Gralloc 实现匹配 [^2]: 内存超限时触发 `GRALLOC1_ERROR_NO_RESOURCES` 的典型场景 [^3]: SurfaceFlinger 队列阻塞与生产者-消费者模型的关系 [^4]: Gralloc `isSupported` 接口对 Usage 标志的校验逻辑
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值