Ahao's Technical Blog Ahao's Technical Blog
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

阿豪讲Framework

不积跬步无以至千里
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础篇

  • 玩转AOSP篇

  • 学穿Binder篇

  • 基础组件篇

  • 系统启动过程分析

  • Hal开发入门与实践

  • 显示系统

    • 如何调试 SurfaceFlinger
    • HWC 接口分析
    • SurfaceFlinger 概述
    • 启动过程总览
    • SurfaceFlinger 初始化
    • RenderEnginge 初始化
      • 1. RenderEngine 在 SurfaceFlinger 中的角色
      • 2. 什么是图形编程,为什么需要图形编程
      • 3. Android 平台图形编程接口
      • 4. Android 平台上的 OpenGL ES
      • 5. RenderEngine 初始化过程
      • 参考资料
  • Framework
  • 显示系统
阿豪
2025-10-05
目录

RenderEnginge 初始化

# 1. RenderEngine 在 SurfaceFlinger 中的角色

HWC (硬件合成器) 是 Android 图形系统中负责高效合成图层(Layer)的硬件模块,但它并非万能。其处理能力受限于​​特定的硬件实现​​。当遇到超出其硬件能力或设计范围的复杂操作时,HWC 便会将这些图层标记为需要由 GPU(通过 SurfaceFlinger 的 RenderEngine)来处理.

以下是常见的 HWC 无法处理或处理效率较低,从而需要回退到 GPU 进行合成的常见图层操作:

  1. ​​超出叠加层数量限制​​ 每款 SOC 硬件支持的叠加层(Overlay)数量是有限的。虽然 Android 要求至少支持 ​​4 个叠加层​​(例如状态栏、系统栏、应用界面和壁纸),但当屏幕上的图层数量超过这个限制时,超出的图层就无法通过 HWC 直接合成,需要由 GPU 处理。

  2. ​​复杂的 Alpha 混合与透明度处理​​

    • ​每像素 Alpha 混合 (Per-Pixel Alpha Blending)​​:当图层需要根据每个像素的 Alpha 值进行精细的透明度混合时(例如不规则形状的遮罩或渐变透明),这对 HWC 来说计算量较大,可能无法高效处理。
    • 多层透明度叠加​​:若多个半透明图层相互叠加(例如一个半透明的 UI 控件叠加在另一个半透明图层上),混合计算会变得复杂,HWC 可能无法胜任。
  3. ​​受保护的内容​​:出于数字版权保护(如 DRM)的要求,播放受保护视频等内容时,往往需要​​专用的安全硬件路径​​。如果 HWC 硬件没有提供这样的路径,这些受保护的图层就无法通过 HWC 合成,必须由支持安全渲染的 GPU 来处理

  4. ​​复杂的几何变换与定位​​

    • ​​旋转和缩放​​:某些 HWC 实现可能对图层的旋转角度(非 90° 的倍数)和缩放比例支持有限。
    • 不规则定位和重叠​​:当图层之间存在复杂的重叠关系,尤其是结合了透明度混合时,HWC
  5. ​​高阶视觉特效​​:许多常见的 UI 特效超出了 HWC 的标准处理能力,例如:

    • ​​模糊 (Blur)​​:实时模糊效果需要读取下方图层的内容并进行像素处理,HWC 通常无法直接支持。
    • 圆角 (Round Corners)​​:尤其是当多个带有圆角的图层相互重叠时,GPU 处理起来更加灵活。
    • ​色彩效果​​:如色相调整、饱和度调整、灰阶等,这些通常由 GPU 处理。
  6. ​​其他特定操作​​

    • ​色彩格式​​:如果图层使用了 HWC 不支持的色彩格式(某些特殊的 YUV 格式或 RGB 压缩格式),也无法直接通过 HWC 处理。
    • ​边带流 (Sideband Streams)​​:用于持续更新的视频流等场景,需要 HWC 具备 HWC2_CAPABILITY_SIDEBAND_STREAM能力,否则无法处理()

小结:

  • RenderEngine 是 Android 图形合成系统 SurfaceFlinger 中的核心软件渲染引擎,主要负责处理那些无法通过硬件合成器 (HWC) 直接完成的图层操作,确保复杂图形效果能够被正确且高效地渲染到屏幕上。
  • RenderEngine 在执行复杂图层的合成时,会使用到图形编程技术来进行图层的合成操作,在分析 RenderEngine 源码之前,我们需要先了解图形编程相关的基础。

# 2. 什么是图形编程,为什么需要图形编程

早期的计算机,屏幕上只能显示简单的命令行界面,随着计算机系统的发展,屏幕上可以展示出复杂的图形和动画了,这些图形和动画都依赖于图形编程。图形编程可以简单理解为通过编写代码来组织和下达指令,最终由 GPU 执行指令,生成显示 buffer,显示 buffer 发送给屏幕后,屏幕就会根据 buffer 中的数据进行显示。

图形编程历史上经历了几个阶段:

  1. 手工作坊式的硬件编程​​:早期(如 IBM PC 及其兼容机),开发者常需​​直接操作图形硬件​​。他们要了解不同图形卡(如 CGA、EGA、Hercules)的内存布局、调色板等细节,编写代码将数据移动到显存特定位置来显示内容。这个过程繁琐且易出错,虽然能榨取硬件极限性能,但​​开发效率低,代码可移植性差​。

  2. ​​专用 API 与图形库的萌芽​​:为简化开发,一些专用 API 和图形库出现:

    • IRIS GL​​:Silicon Graphics: SGI 公司为其工作站开发的专有图形 API,在 90 年代初是事实上的行业标准 3D 图形库。但它与 SGI 平台紧密耦合,​​移植性差​​,且包含非图形功能(如窗口、键盘、鼠标API)。
    • ​Glide​(不是图片加载库 glide)​:3Dfx 公司为其 Voodoo 显卡设计的 API,​​性能极高​​,一度是游戏开发首选。但它是​​厂商锁定​​的,随着 3Dfx 衰落而消失。
    • ​​Borland Graphics Interface (BGI)​​:Borland 公司为其经典的 DOS 时代编程工具(如 ​​Turbo Pascal​​ 和 ​​Turbo C/C++​​)开发的一套​​图形编程库​​。它极大地简化了在 DOS 环境下进行图形编程的复杂度,是许多早期程序员接触图形学的启蒙工具
  3. OpenGL 时代来临:在 OpenGL 诞生之前,图形硬件制造商众多,每家都可能提供自己的编程接口或库。开发者若想应用/游戏支持更多硬件,就需​​为不同硬件编写不同代码​​,或依赖硬件商提供的库,工作量巨大。SGI 公司为了推动一个​​真正开放、跨平台的标准​​,并应对市场上其他图形硬件供应商(如Sun、HP、IBM 等通过扩展 PHIGS 进入市场)带来的竞争压力,决定在 IRIS GL 的基础上,开发一个​​全新的、开放的、与硬件无关的图形接口​​,这就是 OpenGL,在 1992 年 7 月推出了 1.0 版本。OpenGL(Open Graphics Library,开放图形库)的推出是图形计算领域发展的一个重要里程碑。解决了图形编程碎片化问题​​。它通过定义一个​​跨平台、开放的标准图形接口​​,将开发者从为各种硬件编写特定驱动和代码的繁重工作中解放出来。硬件厂商则负责根据 OpenGL 标准实现自己的驱动程序。这种分工大大推动了 3D 图形应用的发展和平民化。

  4. 随着对性能要求的提升,更底层的 API 应运而生。它们将更多控制权交给开发者,以减少驱动开销,更好地利用多核 CPU 和现代 GPU 架构。

    • Vulkan​​:由 Khronos Group 制定,是 OpenGL 的继任者。它提供​​低层次控制 API、支持多线程优化、更高效的资源管理​​,性能强大,适合高性能应用和多线程,但使用复杂,学习曲线陡峭。是许多 3A 游戏和高级图形应用的选择。
    • DirectX 12​​:微软开发的​​高性能图形和计算API​​,支持​​低层次控制和硬件加速​​。仅限 Windows 和 Xbox 平台。其集成度较高,控制较少
    • Metal​​:苹果公司为其生态系统打造的图形 API,为 Apple 硬件进行了深度优化,旨在提供低开销和高性能,是开发 macOS 和 iOS 平台高性能应用的首选。

简单理解,图形编程就是利用一个行业通用的接口标准(OpenGL Vulkan 等)编写代码,这些代码向 GPU 下达指令,GPU 收到指令后,根据指令绘制显示 Buffer。OpenGL Vulkan 只是一些接口,具体的实现需要 GPU 硬件厂商去做实现。厂商通常会提供相应的 so 库给到客户。

# 3. Android 平台图形编程接口

Android 平台上的图形渲染主要由 ​​OpenGL ES​​ 、​​Vulkan 和 Skia​​ 这三项技术协同支撑。

  1. 在应用层,主要使用 ​​OpenGL ES​​,Vulkan 进行图像编程,应用的类型主要有:

    • 游戏类应用,通常使用了游戏引擎(如 Unity、Unreal Engine),游戏引擎会使用 OpenGL ES 或 Vulkan 进行图形渲染。
    • 增强现实 (AR) 与虚拟现实 (VR)
      • 当你使用 AR 测量工具将虚拟尺子“放在”真实桌面上,或使用宜家 App 预览虚拟家具时,OpenGL ES 在背后做这两件事:1. 渲染虚拟的 3D 模型;2. 通过算法将模型准确地“锚定”在摄像头画面的特定位置和角度上,并处理遮挡关系(如虚拟椅子被真实桌腿挡住)。
      • VR 游戏或观影应用需要为每只眼睛渲染一个略有不同的视图,并保证极高的帧率以避免眩晕。OpenGL ES 的高性能渲染能力是提供沉浸式体验的基础。
    • 数据与科学可视化
    • 医学影像​​:渲染 MRI、CT 扫描得到的​​三维体数据​​,让医生能进行虚拟“剖切”和旋转观察,辅助诊断。
    • 地理信息与工程​​:渲染数字高程模型、复杂的 3D 建筑模型、机械设计图,允许工程师从任意角度审查和交互。
    • ​科学研究​​:可视化分子结构、流体动力学模拟结果等。这些场景通常涉及​​数百万个点、线或三角形​​,OpenGL ES 的 GPU 加速能力是唯一能实时、流畅渲染它们的途径。例如,有开发者使用 OpenGL ES 来原生渲染雷达点云数据
    • 视频与图像处理,许多你觉得“炫酷”的图像效果,背后都是 OpenGL ES 在实时计算。
    • ​美颜相机​​:“磨皮”(肤色平滑)、“大眼”、“瘦脸”这些特效,本质上是对图像进行实时滤镜处理和顶点变形,这些都通过编写 OpenGL ES 的​​着色器​​ 程序在 GPU 上完成,速度极快。
    • 专业滤镜 App​​:各种复杂的色彩滤镜、艺术效果(如素描、油画),也是通过片段着色器对每个像素的颜色进行数学运算来实现。
    • 视频编辑软件​​:在预览和最终输出时,应用转场特效、字幕、调色等,都离不开 GPU 加速渲染。 <!-- * 系统 UI 与高级动画
  • 系统界面​​:虽然 Android 的高级 UI 绘制主要依赖 Skia,但在一些​​系统动画​​(如窗口过渡、模糊效果)和​​自定义控件​​中,开发者可能会使用 OpenGL ES 来实现更极致的性能和控制力。
  • 流畅动画​​:对于某些极其复杂或需要高度定制的动画效果,直接使用 OpenGL ES 可以绕过 View 系统的限制,实现 60fps 或更高刷新率的极致流畅体验。 --> 目前应用层处在 OpenGL ES 到 Vulkan 过渡的重要转型期。主流的 App 都在使用 OpenGL ES 来实现各类炫酷效果,Google 在不遗余力地推广 Vulkan。从 2025 年开始,Vulkan 将正式官宣成为 Android 上的官方图形 API。Vulkan 将正式作为 Android 的唯一 GPU 硬件抽象层 (HAL),Google 会要求所有应用和游戏都必须基于 Vulkan 来实现。虽然 Google 打算强制开发者使用 Vulkan ,但是让大家短期全部迁移明显不现实,毕竟 Vulkan 和 OpenGLES 的差异还是很大的,而这时候 ANGLE 就开始体现它作用。ANGLE 作为兼容层,它支持让 GLES 应用运行到 Vulkan ,ANGLE 通过将 GLES API 调用翻译为 Vulkan API 调用,从而让 GLES 能够兼容运行到 Vulkan。
  1. 在 Framework 层,在 HWUI 和 RenderEngine 模块中使用了 Skia:

    • HWUI 中使用 SKia 执行 UI 元素的渲染操作:Android 的视图系统(View System)将 UI 组件的绘制命令记录到 ​​显示列表(Display List)​​ 中。在渲染时,HWUI(硬件加速 UI)驱动 Skia 执行这些命令。Skia 通过其 SkCanvas 等 API 将矢量指令或位图数据​​光栅化​​(转换为像素),最终输出到屏幕或离屏缓冲区。
    • SurfaceFlinger 在执行 GPU 合成时,会通过 RenderEngine 调用 Skia 来执行图层和合成操作。Skia 是一个功能丰富的 2D 图形库,内置了对​​抗锯齿(Anti-aliasing)、多种混合模式、模糊滤镜(Blur)、阴影(Shadow)、颜色空间转换(Color Management)​​ 等高级特性的优化实现,当待合成的图层需要应用此类效果时,可以在 RenderEngine 中利用 Skia 提供的 API 来实现。

Skia 是一个开源的 ​​2D 图形库​​,由 Google 开发和维护。它是 ​​Android 系统 UI 渲染的基石​​,同时也被广泛应用于 Chrome、Flutter 等项目中。Skia 的核心作用是​​提供高层、易用的绘图 API​​。开发者常用的 Canvas 和 Paint 等对象就是由 Skia 实现的。Skia 主要负责​​文字渲染、路径绘制、图像处理(缩放、解码)、矢量图形(如 SVG)显示​​等任务。其设计理念是将图形绘制过程简化为在画布(Canvas)上通过画笔(Paint)绘制各种图形元素。

早期,Skia 使用 CPU 进行绘制,性能有限,复杂场景易导致卡顿。Android 自 3.0 (Honeycomb) 开始,​​默认开启硬件加速​​。此模式下,Skia 将其绘图指令转换为 OpenGL ES 或 Vulkan 命令,由 GPU 执行,大幅提升性能。几乎所有 Android 应用的 ​​UI 绘制​​,包括视图系统、文本显示、基本形状和图像,都依赖于 Skia 提供的绘图 API。

Skia 设计上解耦为前端与后端,Skia 的​​前端​​指的是一套提供给开发者的​​高级编程接口(API)​​,它隐藏了底层硬件的复杂性,让开发者能够以一种相对统一和直观的方式来描述和创建 2D 图形内容。你可以把它想象成一位​​画家手中的各种画笔、画板和调色板​​,提供了创作所需的所有工具。

为了让你快速了解 Skia 前端的主要组成部分,我用一个表格来汇总其核心组件:

20250917222138

Skia 的 ​​Ganesh​​ 和 ​​Graphite​​ 是其两大核心的​​硬件加速渲染后端:

  • Ganesh 基于 ​​OpenGL​​ 体系构建,围绕 GrContext 对象管理 GPU 状态和资源。​​Ganesh 后端同时支持 ​​OpenGL​​ 和 Vulkan,但 ​Ganesh 并非为 Vulkan 原生设计​​:Ganesh 的架构最初是基于 ​​OpenGL​​ 的状态机模型设计的。虽然其后扩展支持了 Vulkan 和 Metal,但在这些现代 API 上的性能和效率表现可能​​不如其专为这些 API 设计的新一代后端​​
  • Graphite 后端为 ​​Vulkan/Metal/D3D12​​ 等现代 API 从头设计,采用​显式控制​​和​​多线程优先​​的架构。它的设计目标是​​显著降低 CPU 开销​​,并​​更好地利用多核处理器和现代 GPU。

目前 Android16 平台, HWUI 与 RenderThread 均采用了 Ganesh + Opengl 进行渲染和合成操作,该组合具有良好的稳定性和广泛兼容性,预计在 Android17 上,HWUI 将采用 Ganesh + Vulkan 的组合, RenderThread 采用了 Graphite + Vulkan 进行合成操作。更后续版本中,HWUI 与 RenderThread 均将切换到 Graphite + Vulkan 组合,预计届时图形渲染性能会有较大幅度的提升,各类奇怪的 bug 会更多(活着就是折腾!)。

# 4. Android 平台上的 OpenGL ES

Android 平台上,Vulkan 还在起步中,Skia 基于 Opengl ES 实现,所用目前我们的关注点还是在 Opengl ES 上。

OpenGL ES(OpenGL for Embedded Systems)是专为移动设备和嵌入式系统优化的图形渲染 API,通过硬件加速实现高性能的 2D/3D 图形渲染。OpenGL ES 只提供了接口,Android 平台通过对应的 GLES 库实现了这些接口:

​* ​libGLESv1_CM/v2/v3 库​​:分别对应 OpenGL ES 1.x(固定管线)、2.0+(可编程着色器)及 3.0+(实例渲染等高级特性),OpenGL ES 接口实现

GLES 库不能单独使用,还需要 EGL 库与本地窗口系统之间建立连接,OpenGL ES 的平台无关性正是借助 EGL 实现的,EGL 屏蔽了不同平台的差异,

这些库的 AOSP 实现都在手机上的 /sytem/lib64 路径下:

# oepngl 库
cd /system/lib64 
find . -name "libGLES*"
./libGLESv1_CM.so
./libGLESv1_CM_angle.so
./libGLESv2.so
./libGLESv2_angle.so
./libGLESv3.so

# egl 库
find . -name "libEGL*"
./libEGL.so
./libEGL_angle.so
1
2
3
4
5
6
7
8
9
10
11
12
13

这些库对应的源码在 frameworks/native/opengl 目录下。AOSP 提供这些库用于保证系统有基本的图形编程能力,通常性能都不太理想。这些库其中有两个不一样的面孔 libGLESv2_angle 和 libEGL_angle,他俩为 OpenGL ES 提供基于 ​​Vulkan 的转译实现​​,旨在解决 Android 生态中长期存在的图形驱动碎片化问题,并提升图形渲染的兼容性与性能。

芯片厂商通常会提供自己的优化版本,通常为闭源实现(综合性能更好,优先使用)。一般在 /vendor/lib64 路径下:

cd /vendor/lib64
find . -name "libGLES*"
./egl/libGLESv2_angle.so
./egl/libGLESv2_adreno.so
./egl/libGLESv1_CM_adreno.so
./egl/libGLESv1_CM_angle.so
./libGLESv2_adreno.so

find . -name "libEGL*"
./egl/libEGL_angle.so
./egl/libEGL_adreno.so
./libEGL_adreno.so
1
2
3
4
5
6
7
8
9
10
11
12

这些库是什么时候加载的?通常在执行 opengl 环境初始化函数 eglGetDisplay 时,通过 dlopen 加载这些库.

# 5. RenderEngine 初始化过程

有了前面的基础我们就可以开始分析 RenderEngine 的初始化过程了:

初始化过程的调用栈如下:

SurfaceFlinger::init
    RenderEngineCreationArgs::Builder               # 使用 builder 模式构建
    chooseRenderEngineType                          # 根据系统的配置,选择 RenderEngine 类型
    RenderEngine::create                            # 根据配置创建 RenderEngine 实例
        SkiaGLRenderEngine::create                  
        RenderEngineThreaded::create
    CompositionEngine::setRenderEngine              # 与 CompositionEngine 关联
1
2
3
4
5
6
7

整个过程如下:

void SurfaceFlinger::init() {
    // ... existing code ...
    
    // 创建 RenderEngine 配置
    auto builder = renderengine::RenderEngineCreationArgs::Builder()
                           .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
                           .setImageCacheSize(maxFrameBufferAcquiredBuffers)
                           .setEnableProtectedContext(enable_protected_contents(false))
                           .setPrecacheToneMapperShaderOnly(false)
                           .setBlurAlgorithm(chooseBlurAlgorithm(mSupportsBlur))
                           .setContextPriority(/* ... */);
    
    chooseRenderEngineType(builder);
    
    // 创建 RenderEngine 实例
    mRenderEngine = renderengine::RenderEngine::create(builder.build());
    
    // 将 RenderEngine 设置到 CompositionEngine 的 mRenderEngine 成员变量中
    mCompositionEngine->setRenderEngine(mRenderEngine.get());
    
    // ... existing code ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

本节重点关注到 RenderEngine::create 过程:

// frameworks/native/libs/renderengine/RenderEngine.cpp
std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
    threaded::CreateInstanceFactory createInstanceFactory;

// TODO: b/341728634 - Clean up conditional compilation.
#if COMPILE_GRAPHITE_RENDERENGINE   // 目前还没有
    const RenderEngine::SkiaBackend actualSkiaBackend = args.skiaBackend;
#else
    if (args.skiaBackend == RenderEngine::SkiaBackend::GRAPHITE) {
        ALOGE("RenderEngine with Graphite Skia backend was requested, but Graphite was not "
              "included in the build. Falling back to Ganesh (%s)",
              args.graphicsApi == RenderEngine::GraphicsApi::GL ? "GL" : "Vulkan");
    }
    // 目前,大部分情况是这个
    const RenderEngine::SkiaBackend actualSkiaBackend = RenderEngine::SkiaBackend::GANESH;
#endif

    ALOGD("%sRenderEngine with %s Backend (%s)", args.threaded == Threaded::YES ? "Threaded " : "",
          args.graphicsApi == GraphicsApi::GL ? "SkiaGL" : "SkiaVK",
          actualSkiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite");

// TODO: b/341728634 - Clean up conditional compilation.
#if COMPILE_GRAPHITE_RENDERENGINE
    if (actualSkiaBackend == SkiaBackend::GRAPHITE) {
        createInstanceFactory = [args]() {
            return android::renderengine::skia::GraphiteVkRenderEngine::create(args);
        };
    } else
#endif
    { // GANESH
        if (args.graphicsApi == GraphicsApi::VK) {
            createInstanceFactory = [args]() {
                return android::renderengine::skia::GaneshVkRenderEngine::create(args);
            };
        } else { // GL
            // 关注点
            // 绝大部分手机,OpenGL ES + Ganesh 类型,走这个分支
            createInstanceFactory = [args]() {
                return android::renderengine::skia::SkiaGLRenderEngine::create(args);
            };
        }
    }

    // 线程化包装
    if (args.threaded == Threaded::YES) {
        return renderengine::threaded::RenderEngineThreaded::create(createInstanceFactory);
    } else {
        return createInstanceFactory();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

create 函数会根据前面 chooseRenderEngineType 函数选择的配置,选择对应的实现类:

  • Graphite + Vulkan : GraphiteVkRenderEngine::create()
  • Ganesh + Vulkan : GaneshVkRenderEngine::create()
  • Ganesh + OpenGL : SkiaGLRenderEngine::create() (大部分是这个)

可以通过 adb shell dumpsys SurfaceFlinger | grep -A 10 "RE " 命令查看 RenderEngine 类型:

  • ------------RE GLES (Ganesh)------------: (OpenGL ES + Ganesh)
  • ------------RE Vulkan (Ganesh)----------: (Vulkan + Ganesh)
  • ------------RE Vulkan (Graphite)----------: (Vulkan + Graphite)

目前主流仍然是 OpenGL ES + Ganesh 类型(稳如老狗),对应的创建 SkiaGLRenderEngine 对象。SkiaGLRenderEngine 初始化的过程如下:

// frameworks/native/libs/renderengine/skia/SkiaGLRenderEngine.cpp
std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
        const RenderEngineCreationArgs& args) {
    // initialize EGL for the default display
    // 步骤1,获取默认 EGL 显示器
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    // 每个 EGLDisplay 在使用前都需要初始化
    if (!eglInitialize(display, nullptr, nullptr)) {
        LOG_ALWAYS_FATAL("failed to initialize EGL");
    }

    // 步骤2,获取 EGL 版本和扩展信息
    // 获取 EGL 版本和扩展信息,用于后续功能检测
    const auto eglVersion = eglQueryString(display, EGL_VERSION);
    if (!eglVersion) {
        checkGlError(__FUNCTION__, __LINE__);
        LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
    }

    const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
    if (!eglExtensions) {
        checkGlError(__FUNCTION__, __LINE__);
        LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
    }

    // 初始化扩展功能
    auto& extensions = GLExtensions::getInstance();
    extensions.initWithEGLStrings(eglVersion, eglExtensions);

    // 步骤3,获取 EGLConfig 对象
    // The code assumes that ES2 or later is available if this extension is
    // supported.
    // - 配置选择策略 :如果扩展支持无配置上下文,则使用 EGL_NO_CONFIG_KHR ;否则调用 `chooseEglConfig` 选择合适的配置
    // - 优先级顺序 :优先尝试 OpenGL ES 3.0,失败后降级到 ES 2.0,最后尝试简化查询
    EGLConfig config = EGL_NO_CONFIG_KHR;
    if (!extensions.hasNoConfigContext()) {
        config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
    }

    /*
    步骤4. EGL 上下文创建
    - 受保护上下文 :如果启用受保护内容且硬件支持,创建受保护的 EGL 上下文
    - 普通上下文 :创建主要的 EGL 上下文,支持上下文优先级设置(实时、高、中、低)
    - 上下文属性配置 :
    - EGL_CONTEXT_CLIENT_VERSION :设置 OpenGL ES 版本
    - EGL_CONTEXT_PRIORITY_LEVEL_IMG :设置上下文优先级
    - EGL_PROTECTED_CONTENT_EXT :启用受保护内容支持
    */
    EGLContext protectedContext = EGL_NO_CONTEXT;
    const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
    if (args.enableProtectedContext && extensions.hasProtectedContent()) {
        protectedContext =
                createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
        ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
    }

    EGLContext ctxt =
            createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);

    // if can't create a GL context, we can only abort.
    LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");

    /*
    步骤5,创建 EGLSurface​
    在缺乏原生窗口的情况下,为你提供一个“画布”(EGLSurface),使得 OpenGL ES 渲染上下文(EGLContext)能够被安全地激活并进行一些必要的 GPU 操作占位表面创建
    - 普通占位表面 :如果不支持无表面上下文,创建 1x1 像素的 PBuffer 作为占位表面
    - 受保护占位表面 :为受保护上下文创建对应的受保护占位表面
    - 表面属性 :设置宽度、高度为 1,并根据需要启用受保护内容
    */
    EGLSurface placeholder = EGL_NO_SURFACE;
    if (!extensions.hasSurfacelessContext()) {
        // createPlaceholderEglPbufferSurface 函数会调用到 eglCreatePbufferSurface 创建一个像素缓冲区表面
        placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
                                                         Protection::UNPROTECTED);
        LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer");
    }

    /*
    步骤6,​​绑定上下文
    - 激活上下文 :调用 eglMakeCurrent() 激活 GL 上下文
    - 查询 GL 信息 :获取 GL 供应商、渲染器、版本和扩展信息
    - 初始化 GL 扩展 :通过 extensions.initWithGLStrings() 完成 GL 扩展检测
    */
    EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt);
    LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current");
    extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
                                 glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));

    EGLSurface protectedPlaceholder = EGL_NO_SURFACE;
    if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
        protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
                                                                  Protection::PROTECTED);
        ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE,
                 "can't create protected placeholder pbuffer");
    }

    // initialize the renderer while GL is current
    /*
    关注点,引擎实例创建
    - 构造 SkiaGLRenderEngine :使用收集的所有 EGL 资源创建引擎实例
    - 创建 Skia 上下文 :调用 ensureContextsCreated() 初始化 Skia GPU 上下文
    - 信息输出 :记录 OpenGL ES 和 EGL 的详细信息到日志
    */
    std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt,
                                                                      placeholder, **protectedContext**,
                                                                      protectedPlaceholder));
    engine->ensureContextsCreated();

    ALOGI("OpenGL ES informations:");
    ALOGI("vendor    : %s", extensions.getVendor());
    ALOGI("renderer  : %s", extensions.getRenderer());
    ALOGI("version   : %s", extensions.getVersion());
    ALOGI("extensions: %s", extensions.getExtensions());
    ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
    ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());

    return engine;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

SkiaGLRenderEngine 中会使用到 Opengl 的接口来合成图层,使用 opengl 前,需要搭建 EGL 环境。

EGL 作为 ​​OpenGL ES 等渲染 API 与底层原生平台窗口系统之间的接口​​。EGL 屏蔽了不同系统之间的差异,向 Opengl 提供了三个统一的窗口相关对象:

20250921122737

  • Display(EGLDisplay) 是对实际显示设备的抽象;
  • Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer(颜色缓冲区), Stencil Buffer(模板缓冲区) ,Depth Buffer(深度缓冲区);
  • Context (EGLContext) 存储 OpenGL ES 绘图的一些状态信息;

搭建 EGL 环境的一般的步骤如下:

  1. 获取 EGLDisplay​​:通过 eglGetDisplay(EGL_DEFAULT_DISPLAY) 获取默认的显示连接。
  2. ​​初始化 EGL​​:调用 eglInitialize() 来初始化与 EGLDisplay 的连接,并可获取 EGL 版本号。源码中使用的是 eglQueryString 完成该过程。
  3. ​选择 EGLConfig​​:使用 eglChooseConfig() 指定期望的渲染表面参数(如颜色深度、模板/深度缓冲区),系统会返回最匹配的配置。
  4. ​创建 EGLContext​​:通过 eglCreateContext() 创建渲染上下文。创建时可指定 OpenGL ES 的版本(如3.0)。
  5. 创建 EGLSurface​​:
    • 对于​​屏幕上渲染​​:使用 eglCreateWindowSurface() 并将 Android 的 NativeWindow(通常由 Surface 或 SurfaceTexture获取)传入。
    • 对于​​离屏渲染​​:使用 eglCreatePbufferSurface() 创建一个像素缓冲区表面。
  6. ​​绑定上下文​​:调用 eglMakeCurrent() 将 EGLDisplay、EGLSurface 和 EGLContext 与当前线程绑定。此后,该线程发出的 OpenGL ES 指令将会在指定的 Context 和 Surface 上生效。

完成 EGL 环境初始化以后就可以使用 OpenGL ES API 进行绘制操作了:

  1. ​渲染与交换缓冲区​​:
    • 使用 OpenGL ES API 进行绘制。
    • 对于 ​​Window Surface​​,绘制完成后需调用 eglSwapBuffers() 将后台缓冲区(Back Buffer)的内容交换到前台(Front Buffer)以显示到屏幕。
    • 对于 ​​PBuffer Surface​​(离屏渲染),通常不需要交换缓冲区,渲染结果可直接读回或绑定为纹理
  2. ​释放资源​​:在渲染结束后,需要调用 eglMakeCurrent() 解绑上下文,并依次销毁 EGLContext、EGLSurface,最后调用 eglTerminate() 终止与 EGLDisplay 的连接

EGLSurface 有多种创建方式:

  1. eglCreatePbufferSurface,创建离屏渲染表面 (此处使用该种方式)
// ... existing code ...
EGLSurface placeholder = EGL_NO_SURFACE;
if (!extensions.hasSurfacelessContext()) {
    placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
                                                     Protection::UNPROTECTED);
    LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer");
}
// ... existing code ...
1
2
3
4
5
6
7
8
  • 创建离屏渲染表面(off-screen surface)
  • 不与任何窗口关联
  • 主要用作占位符表面,当不支持无表面上下文时使用
  • 尺寸固定为1x1像素
  • 支持受保护内容(Protected Content)
  1. eglCreateWindowSurface,这是最常用的窗口表面创建方式:
EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, attrib_list);
1
  • 与原生窗口关联
  • 用于屏幕显示
  • 支持双缓冲
  • 可以调用 eglSwapBuffers 进行缓冲区交换
  • 尺寸由关联的窗口决定
  1. eglCreatePixmapSurface,创建像素映射表面
EGLSurface surface = eglCreatePixmapSurface(display, config, pixmap, attrib_list);
1
  • 与像素图关联
  • 用于离屏渲染到像素图
  • 在Android中已被标记为 @Deprecated
  • 实际实现中抛出 UnsupportedOperationException
  1. eglCreatePlatformWindowSurface (EGL 1.5)
EGLSurface surface = eglCreatePlatformWindowSurface(display, config, native_window, attrib_list);
1
  • EGL 1.5引入的新API
  • 提供更好的平台特定支持
  • 使用 EGLAttrib 而不是 EGLint 作为属性类型

EGL 环境初始化完成后,开始 SkiaGLRenderEngine 对象的初始化:

// frameworks/native/libs/renderengine/skia/SkiaGLRenderEngine.cpp
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
                                       EGLContext ctxt, EGLSurface placeholder,
                                       EGLContext protectedContext, EGLSurface protectedPlaceholder)
      : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat),
                         args.blurAlgorithm),
        mEGLDisplay(display),
        mEGLContext(ctxt),
        mPlaceholderSurface(placeholder),
        mProtectedEGLContext(protectedContext),
        mProtectedPlaceholderSurface(protectedPlaceholder) {}
1
2
3
4
5
6
7
8
9
10
11

调用父类构造函数,然后给一些成员赋值。父类 SkiaRenderEngine 构造函数实现如下:

// frameworks/native/libs/renderengine/skia/SkiaRenderEngine.cpp
    
enum class Threaded {
    NO,
    YES,
};

SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat,
                                   BlurAlgorithm blurAlgorithm)
      : RenderEngine(threaded), mDefaultPixelFormat(pixelFormat) {
    switch (blurAlgorithm) {
        case BlurAlgorithm::GAUSSIAN: {
            ALOGD("Background Blurs Enabled (Gaussian algorithm)");
            mBlurFilter = new GaussianBlurFilter();
            break;
        }
        case BlurAlgorithm::KAWASE: {
            ALOGD("Background Blurs Enabled (Kawase algorithm)");
            mBlurFilter = new KawaseBlurFilter();
            break;
        }
        case BlurAlgorithm::KAWASE_DUAL_FILTER: {
            ALOGD("Background Blurs Enabled (Kawase dual-filtering algorithm)");
            mBlurFilter = new KawaseBlurDualFilter();
            break;
        }
        default: {
            mBlurFilter = nullptr;
            break;
        }
    }

    mCapture = std::make_unique<SkiaCapture>();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

调用父类构造函数,然后初始化一些成员:

  • mBlurFilter:用于指定模糊算法
  • mDefaultPixelFormat:默认的像素格式,用于创建 Skia 渲染表面
  • mCapture:用于捕获发送到 Skia 的所有绘制命令

父类 RenderEngine 构造函数:

// frameworks/native/libs/renderengine/include/renderengine/RenderEngine.h
RenderEngine(Threaded threaded) : mThreaded(threaded) {}
1
2

初始化 mThreaded 成员变量,用于指定渲染引擎是否支持线程化。

SkiaGLRenderEngine 初始化完成后,接着调用 RenderEngineThreaded::create 进行线程化包装:


// frameworks/native/libs/renderengine/threaded/RenderEngineThreaded.cpp
std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
    return std::make_unique<RenderEngineThreaded>(std::move(factory));
}

RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory)
      : RenderEngine(Threaded::YES) {
    ATRACE_CALL();

    std::lock_guard lockThread(mThreadMutex);
    // 创建线程对象
    mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

关注线程的执行过程:

// frameworks/native/libs/renderengine/threaded/RenderEngineThreaded.h
std::atomic<bool> mRunning = true;
mutable std::queue<Work> mFunctionCalls GUARDED_BY(mThreadMutex);

// frameworks/native/libs/renderengine/threaded/RenderEngineThreaded.cpp
void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
    ATRACE_CALL();
#if ((defined MTK_SF_CPU_POLICY) || (defined MTK_SF_CPU_POLICY_FOR_LEGACY))
    if (foregroundEnabled()) {
        if (!SetTaskProfiles(0, {"ProcessCapacityNormal"})) {
            ALOGE("%s SetTaskProfiles error happened : ProcessCapacityNormal", __FUNCTION__);
        }
    } else
#endif
    if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
        ALOGW("Failed to set render-engine task profile!");
    }

    if (setSchedFifo(true) != NO_ERROR) {
        ALOGW("Couldn't set SCHED_FIFO");
    }

    mRenderEngine = factory(); // 返回 SkiaGLRenderEngine 对象
#if ((defined MTK_SF_CPU_POLICY) || (defined MTK_SF_CPU_POLICY_FOR_LEGACY))
    mTid = gettid();
#endif
    pthread_setname_np(pthread_self(), mThreadName);

    {
        std::scoped_lock lock(mInitializedMutex);
        mIsInitialized = true;
    }
    mInitializedCondition.notify_all();

    while (mRunning) {
        const auto getNextTask = [this]() -> std::optional<Work> {
            std::scoped_lock lock(mThreadMutex);
            if (!mFunctionCalls.empty()) {
                Work task = mFunctionCalls.front();
                mFunctionCalls.pop();
                return std::make_optional<Work>(task);
            }
            return std::nullopt;
        };

        const auto task = getNextTask();

        if (task) {
            (*task)(*mRenderEngine);
        }

        std::unique_lock<std::mutex> lock(mThreadMutex);
        mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
            return !mRunning || !mFunctionCalls.empty();
        });
    }

    // we must release the RenderEngine on the thread that created it
    mRenderEngine.reset();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

和大部分事件循环机制类似:获取 Task,执行 Task。

整体的类关系如下图所示:

20250921122403

# 参考资料

  • OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow (opens new window)
#显示系统
SurfaceFlinger 初始化

← SurfaceFlinger 初始化

最近更新
01
如何调试 SurfaceFlinger
10-05
02
SurfaceFlinger 概述
10-05
03
HWC 接口分析
10-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 AHao Framework | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式