Android GL Architecture

本文详细介绍了WebView在使用GL渲染架构时的工作原理和技术细节。包括BaseLayer与LayerAndroid的层级结构,利用Tile进行渲染的过程,纹理分配机制以及如何处理BaseTile的无效化问题等。

(见WebCore/platform/graphics/android/GLWebViewState.h)

/////////////////////////////////////////////////////////////////////////////////

// GL Architecture
/////////////////////////////////////////////////////////////////////////////////
//
// To draw things, WebView use a tree of layers. The root of that tree is a
// BaseLayerAndroid, which may have numerous LayerAndroid over it. The content
// of those layers are SkPicture, the content of the BaseLayer is an PictureSet.
//
// When drawing, we therefore have one large "surface" that is the BaseLayer,
// and (possibly) additional surfaces (usually smaller), which are the
// LayerAndroids. The BaseLayer usually corresponds to the normal web page
// content, the Layers are used for some parts such as specific divs (e.g. fixed
// position divs, or elements using CSS3D transforms, or containing video,
// plugins, etc.).
//
// *** NOTE: The GL drawing architecture only paints the BaseLayer for now.
//
// The rendering model is to use tiles to display the BaseLayer (as obviously a
// BaseLayer's area can be arbitrarly large). The idea is to compute a set of
// tiles covering the viewport's area, paint those tiles using the webview's
// content (i.e. the BaseLayer's PictureSet), then display those tiles.
// We check which tile we should use at every frame.
//
// Overview
// ---------
//
// The tiles are grouped into a TiledPage -- basically a map of tiles covering
// the BaseLayer's surface. When drawing, we ask the TiledPage to prepare()
// itself then draw itself on screen. The prepare() function is the one
// that schedules tiles to be painted -- i.e. the subset of tiles that intersect
// with the current viewport. When they are ready, we can display
// the TiledPage.
//
// Note that BaseLayerAndroid::drawGL() will return true to the java side if
// there is a need to be called again (i.e. if we do not have up to date
// textures or a transition is going on).
//
// Tiles are implemented as a BaseTile. It knows how to paint itself with the
// PictureSet, and to display itself. A GL texture is usually associated to it.
//
// We also works with two TiledPages -- one to display the page at the
// current scale factor, and another we use to paint the page at a different
// scale factor. I.e. when we zoom, we use TiledPage A, with its tiles scaled
// accordingly (and therefore possible loss of quality): this is fast as it's
// purely a hardware operation. When the user is done zooming, we ask for
// TiledPage B to be painted at the new scale factor, covering the
// viewport's area. When B is ready, we swap it with A.
//
// Texture allocation
// ------------------
//
// Obviously we cannot have every BaseTile having a GL texture -- we need to
// get the GL textures from an existing pool, and reuse them.
//
// The way we do it is that when we call TiledPage::prepare(), we group the
// tiles we need (i.e. in the viewport and dirty) into a TilesSet and call
// BaseTile::reserveTexture() for each tile (which ensures there is a specific
// GL textures backing the BaseTiles).
//
// reserveTexture() will ask the TilesManager for a texture. The allocation
// mechanism goal is to (in order):
// - prefers to allocate the same texture as the previous time
// - prefers to allocate textures that are as far from the viewport as possible
// - prefers to allocate textures that are used by different TiledPages
//
// Note that to compute the distance of each tile from the viewport, each time
// we prepare() a TiledPage. Also during each prepare() we compute which tiles
// are dirty based on the info we have received from webkit.
//
// BaseTile Invalidation
// ------------------
//
// We do not want to redraw a tile if the tile is up-to-date. A tile is
// considered to be dirty an in need of redrawing in the following cases
//  - the tile has acquires a new texture
//  - webkit invalidates all or part of the tiles contents
//
// To handle the case of webkit invalidation we store two ids (counters) of the
// pictureSets in the tile.  The first id (A) represents the pictureSet used to
// paint the tile and the second id (B) represents the pictureSet in which the
// tile was invalidated by webkit. Thus, if A < B then tile is dirty.
//
// Since invalidates can occur faster than a full tiled page update, the tiled
// page is protected by a 'lock' (m_baseLayerUpdate) that is set to true to
// defer updates to the background layer, giving the foreground time to render
// content instead of constantly flushing with invalidates. See
// lockBaseLayerUpdate() & unlockBaseLayerUpdate().
//
// Painting scheduling
// -------------------
//
// The next operation is to schedule this TilesSet to be painted
// (TilesManager::schedulePaintForTilesSet()). TexturesGenerator
// will get the TilesSet and ask the BaseTiles in it to be painted.
//
// BaseTile::paintBitmap() will paint the texture using the BaseLayer's
// PictureSet (calling TiledPage::paintBaseLayerContent() which in turns
// calls GLWebViewState::paintBaseLayerContent()).
//
// Note that TexturesGenerator is running in a separate thread, the textures
// are shared using EGLImages (this is necessary to not slow down the rendering
// speed -- updating GL textures in the main GL thread would slow things down).
//
/////////////////////////////////////////////////////////////////////////////////


<think>我们正在讨论Android渲染流程图。根据用户需求,我们需要提供Android渲染流程的图示和说明。同时,用户提供了几个引用,但我们需要关注用户上一次的问题(即获取Android渲染流程图)和之前的回答(已经概述了渲染流程的几个阶段)。 根据引用[1]中的描述,有一个Android图形管线的示意图(Figure 2. Graphic data flow through Android)。我们可以基于此构建流程图,并结合之前的回答。 另外,引用[2]提到了Android的2D Canvas渲染管线,它是基于Skia的,因此我们也可以考虑将Skia纳入流程图中。 引用[3]是关于颜色空间的,虽然与渲染有关,但可能不是流程图的核心,所以可以忽略。 因此,我们将结合Android官方文档(引用[1])和Skia(引用[2])来构建一个更详细的渲染流程图。 注意:由于我们无法实际绘制图形,我们将用文本描述流程图,并尽可能用文字和字符图来表示。 以下是Android渲染管线的详细步骤(基于Android图形管线): 1. **应用层(Application Layer)**: - 应用使用View系统或Canvas API绘制UI。 - 对于View系统,会执行measure、layout、draw过程,生成绘制命令(Display List)。 - 对于直接使用Canvas,应用调用Canvas的draw方法(底层是Skia库)。 2. **框架层(Framework Layer)**: - 在Android 5.0(API 21)及以上,使用RenderThread进行渲染。 - 绘制命令被记录到DisplayList中,然后由RenderThread处理。 3. **系统层(System Layer)**: - **Skia**:2D图形库,处理Canvas的绘制命令,生成OpenGL ES命令(如果使用硬件加速)或直接使用CPU软件渲染(生成位图)。 - **OpenGL ES / Vulkan**:如果使用硬件加速,Skia通过OpenGL ES或Vulkan将命令发送给GPU。 4. **原生层(Native Layer)**: - **硬件加速路径**:OpenGL ES命令被送到GPU驱动,由GPU进行栅格化(将矢量图形转换为像素)。 - **软件路径**:如果使用软件渲染,Skia在CPU上执行栅格化,生成位图。 5. **SurfaceFlinger**: - 无论硬件还是软件渲染,最终都会将帧缓冲区(包含位图)提交给SurfaceFlinger。 - SurfaceFlinger是系统服务,负责将多个应用的Surface(即帧缓冲区)合成,并发送到显示设备。 6. **显示设备(Display)**: - 显示控制器(Display Controller)读取合成后的帧缓冲区,并在屏幕上显示。 同时,整个流程由VSync信号驱动: - VSync信号由显示设备发出,表示开始新的一帧。 - Choreographer接收到VSync信号,触发UI线程的绘制(measure, layout, draw)。 - 绘制完成后,将绘制命令同步到RenderThread,RenderThread在下一个VSync信号到来之前完成栅格化并提交帧。 因此,我们可以绘制一个字符流程图: ``` +---------------------+ +---------------------+ +---------------------+ | Application | | Framework | | System | | | | | | | | View.draw(Canvas) | ----> | Record DisplayList | ----> | RenderThread | | or Canvas.drawX() | | | | (Skia/OpenGL ES) | +---------------------+ +----------+----------+ +----------+----------+ | | v v +---------------------+ +----------+----------+ +----------+----------+ | Native | | SurfaceFlinger | | Display | | | | | | | | GPU (Rasterize) | <---- | Compositing | <---- | VSync Signal | | or CPU (Rasterize) | | Layers | | | +---------------------+ +---------------------+ +---------------------+ ``` 但是,为了更符合实际数据流,我们可以这样描述: 1. 应用层生成绘制命令(通过View或Canvas)。 2. 框架层将绘制命令记录到DisplayList,并传递给RenderThread。 3. RenderThread使用Skia(通过OpenGL ES或Vulkan)将绘制命令转换为GPU指令(硬件加速)或使用Skia的CPU渲染(软件加速)。 4. GPU执行栅格化(硬件加速)或CPU执行栅格化(软件加速),生成帧缓冲区。 5. 在下一个VSync信号到来时,将帧提交给SurfaceFlinger。 6. SurfaceFlinger合成多个层的帧(例如状态栏、应用界面、导航栏等)。 7. 合成后的帧被送到显示控制器,在屏幕上显示。 注意:在硬件加速路径中,Skia生成的是GPU命令,由GPU执行栅格化;在软件路径中,Skia直接在CPU上生成位图(栅格化)。 因此,我们可以将流程图表示为: ``` +-------------------+ +-------------------+ +-------------------+ | Application | | Framework | | RenderThread | | (UI Thread) | | | | (Rendering) | | - measure | | - DisplayList | | - Skia (OpenGL) | | - layout |---->| Recording |---->| or Skia (CPU) | | - draw | | | | - Rasterization | +-------------------+ +-------------------+ +---------+---------+ | | (Submit Frame) v +-------------------+ +-------------------+ +---------+---------+ | SurfaceFlinger | | VSync | | Display | | - Compositing |<----| (Signal every |---->| - Refresh Screen | | - Output to | | 16ms) | | | | Display | | | | | +-------------------+ +-------------------+ +-------------------+ ``` 但是,实际上VSync信号是驱动整个流程的,所以更准确的流程图应该体现VSync信号的作用: 1. VSync信号触发UI线程开始绘制。 2. UI线程完成绘制后,将绘制任务交给RenderThread。 3. RenderThread在下一个VSync信号到来之前完成栅格化并提交帧。 4. SurfaceFlinger在下一个VSync信号到来时进行合成并显示。 因此,我们重新调整: ``` +----------------+ VSync (Start) +----------------+ Record and +----------------+ | | -----------------> | | Process Commands| | | UI Thread | | RenderThread | ----------------->| Rasterize | | | <--Deferred-------| | | (GPU/CPU) | | (Draw) | Processing | | | | +----------------+ +----------------+ +-------+--------+ | | v +----------------+ VSync (Deadline) +----------------+ Compositing +----------------+ | | ------------------> | | <----------------| | | SurfaceFlinger | | Display | | Frame Buffer | | | <-------------------| | -----------------| | | (Composite) | Display Frame | | Output +----------------+ +----------------+ +----------------+ ``` 然而,为了更清晰,我们采用线性流程,并标注关键点: ``` +----------------+ +----------------+ +----------------+ +----------------+ | | | | | | | | | App: UI | | Framework: | | System: | | Surface | | Thread |---->| Record |---->| RenderThread |---->| Flinger | | (Draw) | | DisplayList | | (Skia/GL) | | (Composite) | +----------------+ +----------------+ +-------+--------+ +-------+--------+ | | | | v v +----------------+ +----------------+ +-------+--------+ +-------+--------+ | | | | | | | | | VSync |---->| VSync |---->| VSync |---->| Display | | (App) | | (Render) | | (Surface) | | (Refresh) | +----------------+ +----------------+ +----------------+ +----------------+ ``` 注意:实际上VSync信号是同时触发的,但被不同的组件(UI线程、RenderThread、SurfaceFlinger)接收并处理。 更准确地说,Android的渲染流程分为两个阶段(两个VSync间隔): - 第一个VSync:触发UI线程进行measure、layout、draw(记录DisplayList)。 - 第二个VSync:RenderThread完成栅格化,SurfaceFlinger完成合成并显示。 因此,整个流程需要两个VSync周期(约32ms)才能显示出来。 考虑到这些,我们给出一个更符合实际的流程图: ``` VSync信号1(t0): -> UI线程:开始绘制(measure, layout, draw) -> 记录DisplayList -> 同步到RenderThread VSync信号2(t0+16ms): -> RenderThread:处理DisplayList(Skia/OpenGL) -> 栅格化 -> 提交帧到SurfaceFlinger -> SurfaceFlinger:等待下一个VSync信号3(t0+32ms)到来时合成并显示 VSync信号3(t0+32ms): -> SurfaceFlinger:将合成后的帧送显 -> 同时,UI线程可能开始下一帧的绘制(如果被VSync信号2触发的话,但通常UI线程的下一帧绘制是在VSync信号2之后?) 注意:实际机制是双缓冲或三缓冲,这里简化。 ``` 因此,字符流程图如下: ``` +-------------------+ +-------------------+ +-------------------+ | VSync (t0) | | VSync (t0+16ms) | | VSync (t0+32ms) | | | | | | | | UI Thread: | | RenderThread: | | SurfaceFlinger: | | - Draw Frame n | | - Process Frame n| | - Composite | | | | and Rasterize | | Frame n | | | | - Submit to SF | | - Display | +-------+-----------+ +---------+---------+ +---------+---------+ | | | | | | v v v +-------+-----------+ +---------+---------+ +---------+---------+ | Record DisplayList | | Rasterization | | Display Frame n | | for Frame n | | (GPU/CPU) | | | +-------------------+ +-------------------+ +-------------------+ ``` 但是,这个流程图没有体现SurfaceFlinger在VSync信号2时做了什么?实际上,在VSync信号2时,SurfaceFlinger会开始合成操作(如果所有层的帧都准备好),并在下一个VSync(信号3)显示。 因此,我们调整: ``` +-------------------+ +-------------------+ +-------------------+ | VSync (t0) | | VSync (t0+16ms) | | VSync (t0+32ms) | | | | | | | | UI Thread: | | RenderThread: | | SurfaceFlinger: | | - Draw Frame n | | - Rasterize | | - Composite | | | | Frame n | | and Display | | | | - Submit to SF | | Frame n | +-------+-----------+ +---------+---------+ +-------------------+ | | | | v v +-------+-----------+ +---------+---------+ | Record DisplayList | | Rasterization | | for Frame n | | (GPU/CPU) | +-------------------+ +-------------------+ ``` 注意:在VSync(t0+16ms)时,RenderThread完成栅格化并提交给SurfaceFlinger,然后SurfaceFlinger在VSync(t0+32ms)时进行合成和显示。 同时,在VSync(t0+16ms)时,UI线程可能已经开始绘制下一帧(Frame n+1)了。 综上所述,我们给出Android渲染流程的文本描述和字符流程图。由于用户要求的是流程图,我们尽量用字符图表示,但更复杂的图建议参考官方文档(引用[1]的图2)。 最后,我们根据用户要求,在回答末尾添加相关问题。 注意:引用[1]中的图2是官方图形数据流图,我们无法在此展示,但可以描述其关键组件: - 应用:生成图形缓冲区(通过Canvas或OpenGL ES)。 - SurfaceFlinger:合成多个图形缓冲区。 - 硬件混合渲染器(HWC):决定如何合成(使用GPU还是专门的硬件合成器)。 - 显示:最终输出。 因此,我们结合官方流程和上述分析,给出最终回答。</think>以下是Android渲染流程的详细图解和说明,结合了系统级渲染管线和关键组件的作用: ### Android渲染流程图解 ```mermaid graph LR A[应用层] --> B[View系统] B --> C[Canvas/Skia] C --> D[RenderThread] D --> E[OpenGL ES/Vulkan] E --> F[GPU驱动] F --> G[栅格化] G --> H[SurfaceFlinger] H --> I[Hardware Composer] I --> J[显示控制器] J --> K[屏幕显示] ``` ### 详细渲染流程说明 1. **应用层生成绘制指令** - 应用通过`View`系统执行`measure`→`layout`→`draw`流程 - 生成绘制命令树(DisplayList),包含矢量图形、文本等元素[^2] 2. **Canvas/Skia处理** - Android的2D Canvas API调用Skia图形库 - 将绘制命令转换为平台无关的图形操作 - 支持硬件加速(OpenGL/Vulkan)和软件渲染双路径[^2] 3. **RenderThread处理** ```mermaid graph TB R[RenderThread] --> S1[同步DisplayList] R --> S2[构建渲染节点树] R --> S3[计算属性变化] R --> S4[栅格化预处理] ``` - 独立于UI线程的渲染线程 - 处理动画、变换等GPU密集型操作 - 避免主线程阻塞导致丢帧 4. **GPU栅格化** - 通过OpenGL ES/Vulkan API发送指令到GPU - GPU执行片段着色器完成矢量到像素的转换: $$ \text{Rasterization} = f(\text{Vertex Data}, \text{Texture}, \text{Shaders}) $$ - 输出帧缓冲区(FrameBuffer)数据[^1] 5. **SurfaceFlinger合成** ```mermaid graph LR SF[SurfaceFlinger] --> C1[接收各层Surface] SF --> C2[应用窗口变换] SF --> C3[混合透明度] SF --> C4[调用HWC] ``` - 系统服务管理所有图层的合成 - 使用硬件混合器(HWC)优化合成效率 - 处理Z-order叠加和透明度混合[^1] 6. **显示输出** - 硬件混合器通过显示控制器(Display Controller) - 按VSync信号(每16ms)刷新屏幕 - 最终像素数据输出到物理显示器 ### 关键同步机制:VSync ```mermaid timeline title 双缓冲VSync同步机制 section 帧N VSync信号1 : UI线程开始绘制 16ms : RenderThread完成栅格化 section 帧N+1 VSync信号2 : SurfaceFlinger合成 16ms : 屏幕刷新显示 ``` - VSync确保每帧在16ms内完成(60fps) - 三缓冲机制防止Jank(帧丢失) - Choreographer协调UI线程与渲染时序 ### 性能优化要点 1. **减少过度绘制** - 使用GPU渲染模式分析Overdraw - 优化视图层级(merge标签,减少嵌套) 2. **避免主线程阻塞** - 耗时操作移到工作线程 - 使用`Systrace`检测UI线程卡顿 3. **高效资源使用** - 纹理压缩(ASTC/ETC2) - 避免频繁内存分配(对象池化) > 完整官方渲染管线图示可参考:[Android Graphics Architecture](https://source.android.com/docs/core/graphics) [^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值