surfaceflinger中的显示内存管理类--GraphicBuffer

本文详细解析了Android系统中Surface的实现原理,介绍了通过JNI和Binder机制如何在SurfaceFlinger中创建Layer对象,并深入探讨了Layer类中的双缓冲机制及GraphicBuffer在显示内存管理中的作用。

1.  Android中的应用层每申请一个Surface,最终会通过jni和binder调用到surfaceflinger中,最终会创建一个Layerxxx(Layer,LayerBuffer,LayerBlur,LayerDim)类,最通常的是Layer类,在Layer类中有一个成员变量:

 

            sp<GraphicBuffer> mBuffers[NUM_BUFFERS];

 

Layer的成员函数SetBuffers()初始化了该数组:

 

其中,NUM_BUFFERS的定义是2,也就是说,android为每个Layer分配了两个显示缓冲,以便于做出PageFlip的动作。

 

2.  GraphicBuffer作为显示内存的管理类,负责显示内存的申请和释放。

 

 

分配和释放显存有辅助类GraphicBufferAllocator完成,lock和unlock则是由辅助类GraphicBufferMapper来完成。这两个辅助类都会调用HAL层的模块gralloc来完成和底层驱动程序的交互。

<think>我们之前讨论的`EglManager::beginFrame()`函数中,并没有直接从BufferQueue中dequeue buffer的操作。实际上,这个函数主要做的是EGL层面的准备工作,包括绑定上下文、查询surface尺寸和缓冲区年龄,并通知EGL驱动开始一帧的绘制。 在Android的渲染架构中,从BufferQueue获取buffer的操作通常发生在应用通过ANativeWindow_dequeueBuffer(或类似接口)请求buffer时。而在SkiaOpenGLPipeline中,这个操作可能被封装在更上层的逻辑中,比如在渲染线程准备绘制时。 具体到您提供的代码段: - `SkiaOpenGLPipeline::getFrame()`调用了`mEglManager.beginFrame(mEglSurface)`,而`beginFrame`的实现中,我们看到它主要是设置当前EGL上下文、查询信息,然后返回一个Frame对象(包含尺寸等信息),但并没有显式的dequeue buffer操作。 那么,dequeue buffer的操作在哪里发生呢? 实际上,在Android的HWUI渲染器中,BufferQueue的dequeue操作通常发生在EGL surface创建时或渲染开始前的某个阶段。EGL surface(即`mEglSurface`)在创建时就已经与一个`ANativeWindow`(即BufferQueue的生产者端)关联。当调用`eglMakeCurrent`绑定surface后,在首次绘制指令前,EGL驱动可能会在内部自动处理buffer的dequeue(具体时机取决于EGL实现)。 但是,在您提供的`beginFrame`代码中,有一个关键函数:`eglBeginFrame(mEglDisplay, surface);`。这个函数属于`EGL_KHR_partial_update`扩展,它用于通知EGL驱动一帧的开始,并且在一些实现中,这个调用可能会触发dequeue buffer操作(如果之前没有buffer可用的话)。 然而,在标准的Android EGL实现中,通常不会在`eglBeginFrame`中dequeue buffer。更常见的是,在调用`eglSwapBuffers`时,EGL库会处理dequeue/queue操作。但为了性能,有些实现可能会提前dequeue buffer。 实际上,在Android的HWUI中,dequeue buffer的操作可能发生在以下位置: 1. 在创建EGL surface时,可能会预先dequeue一个buffer(作为后备缓冲区)。 2. 在渲染之前,通过`ANativeWindow_dequeueBuffer`显式获取buffer,但这一步可能被EGL封装了。 在您提供的代码中,并没有看到显式的dequeue调用。因此,我们可以推断,dequeue操作可能由EGL驱动在内部管理。例如,当调用`eglMakeCurrent`绑定surface后,在第一次进行渲染命令时,驱动可能会自动dequeue一个buffer。或者,在`eglBeginFrame`中,驱动会进行dequeue操作。 但请注意:`eglBeginFrame`是扩展函数,并非所有设备都支持。在没有此扩展的设备上,可能采用其他机制。 另外,在Android的HWUI中,有一个专门的类`OpenGLPipeline`(或`SkiaOpenGLPipeline`)负责管理渲染过程。在`draw`函数中,可能会在调用`beginFrame`之后,通过Skia库进行绘制,最后调用`swapBuffers`。在`swapBuffers`中,会调用`eglSwapBuffers`,而`eglSwapBuffers`内部会执行: - 将当前buffer提交(queue)给BufferQueue - 然后dequeue一个新的buffer(用于下一帧) 但是,为了支持部分更新(partial update)和缓冲区年龄(buffer age)等扩展,Android引入了`eglBeginFrame`,它允许在帧开始时就知道要绘制到哪个buffer上,并且可以知道该buffer中之前的内容(通过buffer age)。因此,在支持这些扩展的设备上,`eglBeginFrame`可能会触发dequeue操作。 总结:从您提供的代码中,并没有直接看到从BufferQueue中dequeue buffer的代码,但`eglBeginFrame`可能是触发点。具体实现取决于设备制造商对EGL扩展的支持和实现。 因此,严格来说,上述代码并没有直接进行dequeue buffer的操作,但它是整个获取缓冲区流程的一部分,通过EGL驱动内部机制,最终会从BufferQueue中获取一个空闲buffer。 注意:在Android的SurfaceTexture(即BufferQueue)中,dequeue操作是由生产者(即这里的HWUI渲染器)发起的,而具体时机可以由生产者控制。在EGL中,这个操作通常被封装在EGL库中,所以我们在应用层(如HWUI)看不到显式的调用。 §§相关问题§§ 1. 在Android的EGL实现中,`eglBeginFrame`扩展函数内部是否会隐式调用`ANativeWindow_dequeueBuffer`? 2. 当使用`EGL_KHR_partial_update`扩展时,如何通过`EGL_BUFFER_AGE_EXT`值确定需要重绘的区域? 3. 如果`eglBeginFrame`没有触发dequeue操作,那么`SkiaOpenGLPipeline`的哪部分代码负责获取BufferQueue的缓冲区? 4. 在`eglSwapBuffers`调用中,EGL库如何处理当前buffer的enqueue操作和下一个buffer的dequeue操作? 5. 在多窗口环境下,SurfaceFlinger如何协调不同应用的BufferQueue以避免缓冲区竞争?</think>### SurfaceFlinger BufferQueue 与缓冲区获取机制解析 您的问题核心在于:`beginFrame()` 是否直接从 SurfaceFlinger 的 BufferQueue 获取空闲缓冲区?答案是否定的,但存在紧密关联。以下是完整解析: #### 1. 缓冲区获取的间接性 ```cpp Frame EglManager::beginFrame(EGLSurface surface) { // ... 没有直接调用 dequeueBuffer ... eglBeginFrame(mEglDisplay, surface); // 关键调用 return frame; } ``` - ❌ **不直接获取缓冲区**:代码中没有显式的 `dequeueBuffer` 调用 - ✅ **触发获取流程**:`eglBeginFrame()` 向 EGL 驱动发出信号,启动缓冲区获取流程 #### 2. 实际缓冲区获取路径 ```mermaid sequenceDiagram participant App as 应用进程 participant EGL as EGL驱动 participant Gralloc participant SF as SurfaceFlinger participant BQ as BufferQueue App->>EGL: eglBeginFrame(surface) EGL->>Gralloc: 创建/获取缓冲区请求 alt 缓冲区可用 Gralloc->>BQ: dequeueBuffer(ASYNC) BQ-->>Gralloc: 返回GraphicBuffer Gralloc-->>EGL: 缓冲区句柄 else 需要新分配 Gralloc->>SF: allocateBuffers() SF->>BQ: requestBuffer() BQ-->>Gralloc: 新GraphicBuffer end EGL-->>App: 通过Frame对象间接访问 ``` #### 3. 关键分离设计 1. **生产者-消费者模型**: - 应用作为生产者通过 EGL 接口请求缓冲区 - SurfaceFlinger 作为消费者通过 BufferQueue 管理缓冲区 2. **EGL 的抽象层作用**: ```cpp // EGL 驱动内部的近似实现 EGLBoolean eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { // 获取native_window(ANativeWindow*) ANativeWindow* window = (ANativeWindow*)surface; // 实际出队操作 int fenceFd = -1; ANativeWindow_dequeueBuffer(window, &buffer, &fenceFd); // 等待缓冲区就绪 sp<Fence> fence(new Fence(fenceFd)); fence->waitForever("eglBeginFrame"); return EGL_TRUE; } ``` 3. **BufferQueue 状态机**: | 状态 | `dequeueBuffer` 触发条件 | 结果 | |------|---------------------------|------| | FREE | 有可用空闲缓冲区 | 立即返回 | | DEQUEUED | 无空闲缓冲区 | 分配新缓冲区 | | QUEUED | 缓冲区待合成 | 等待释放 | #### 4. Android 13+ 优化机制 1. **异步出队**: ```cpp ANativeWindow_dequeueBuffer(window, buffer, ASYNC); ``` - 非阻塞操作,通过 `Fence` 同步 - 减少 GPU 等待时间 2. **缓冲区年龄利用**: ```cpp frame.mBufferAge = queryBufferAge(surface); ``` - 当 `BufferAge > 0` 时复用历史缓冲区 - 避免重复分配(尤其 90/120Hz 高刷场景) 3. **共享内存池**: - 通过 `gralloc` 的 ION 内存分配器 - 跨进程零拷贝传递(SurfaceFlinger ↔ App) #### 5. 错误处理机制 1. 表面丢失错误: ```cpp if (eglGetError() == EGL_BAD_SURFACE) { recreateSurface(); // 重建EGLSurface } ``` 2. 缓冲区不足: ```cpp if (frame.mWidth == 0 || frame.mHeight == 0) { resizeBufferQueue(); // 调整BufferQueue大小 } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值