RenderEnginge 初始化
# 1. RenderEngine 在 SurfaceFlinger 中的角色
HWC (硬件合成器) 是 Android 图形系统中负责高效合成图层(Layer)的硬件模块,但它并非万能。其处理能力受限于特定的硬件实现。当遇到超出其硬件能力或设计范围的复杂操作时,HWC 便会将这些图层标记为需要由 GPU(通过 SurfaceFlinger 的 RenderEngine)来处理.
以下是常见的 HWC 无法处理或处理效率较低,从而需要回退到 GPU 进行合成的常见图层操作:
超出叠加层数量限制 每款 SOC 硬件支持的叠加层(Overlay)数量是有限的。虽然 Android 要求至少支持 4 个叠加层(例如状态栏、系统栏、应用界面和壁纸),但当屏幕上的图层数量超过这个限制时,超出的图层就无法通过 HWC 直接合成,需要由 GPU 处理。
复杂的 Alpha 混合与透明度处理
- 每像素 Alpha 混合 (Per-Pixel Alpha Blending):当图层需要根据每个像素的 Alpha 值进行精细的透明度混合时(例如不规则形状的遮罩或渐变透明),这对 HWC 来说计算量较大,可能无法高效处理。
- 多层透明度叠加:若多个半透明图层相互叠加(例如一个半透明的 UI 控件叠加在另一个半透明图层上),混合计算会变得复杂,HWC 可能无法胜任。
受保护的内容:出于数字版权保护(如 DRM)的要求,播放受保护视频等内容时,往往需要专用的安全硬件路径。如果 HWC 硬件没有提供这样的路径,这些受保护的图层就无法通过 HWC 合成,必须由支持安全渲染的 GPU 来处理
复杂的几何变换与定位
- 旋转和缩放:某些 HWC 实现可能对图层的旋转角度(非 90° 的倍数)和缩放比例支持有限。
- 不规则定位和重叠:当图层之间存在复杂的重叠关系,尤其是结合了透明度混合时,HWC
高阶视觉特效:许多常见的 UI 特效超出了 HWC 的标准处理能力,例如:
- 模糊 (Blur):实时模糊效果需要读取下方图层的内容并进行像素处理,HWC 通常无法直接支持。
- 圆角 (Round Corners):尤其是当多个带有圆角的图层相互重叠时,GPU 处理起来更加灵活。
- 色彩效果:如色相调整、饱和度调整、灰阶等,这些通常由 GPU 处理。
其他特定操作
- 色彩格式:如果图层使用了 HWC 不支持的色彩格式(某些特殊的 YUV 格式或 RGB 压缩格式),也无法直接通过 HWC 处理。
- 边带流 (Sideband Streams):用于持续更新的视频流等场景,需要 HWC 具备 HWC2_CAPABILITY_SIDEBAND_STREAM能力,否则无法处理()
小结:
- RenderEngine 是 Android 图形合成系统 SurfaceFlinger 中的核心软件渲染引擎,主要负责处理那些无法通过硬件合成器 (HWC) 直接完成的图层操作,确保复杂图形效果能够被正确且高效地渲染到屏幕上。
- RenderEngine 在执行复杂图层的合成时,会使用到图形编程技术来进行图层的合成操作,在分析 RenderEngine 源码之前,我们需要先了解图形编程相关的基础。
# 2. 什么是图形编程,为什么需要图形编程
早期的计算机,屏幕上只能显示简单的命令行界面,随着计算机系统的发展,屏幕上可以展示出复杂的图形和动画了,这些图形和动画都依赖于图形编程。图形编程可以简单理解为通过编写代码来组织和下达指令,最终由 GPU 执行指令,生成显示 buffer,显示 buffer 发送给屏幕后,屏幕就会根据 buffer 中的数据进行显示。
图形编程历史上经历了几个阶段:
手工作坊式的硬件编程:早期(如 IBM PC 及其兼容机),开发者常需直接操作图形硬件。他们要了解不同图形卡(如 CGA、EGA、Hercules)的内存布局、调色板等细节,编写代码将数据移动到显存特定位置来显示内容。这个过程繁琐且易出错,虽然能榨取硬件极限性能,但开发效率低,代码可移植性差。
专用 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 环境下进行图形编程的复杂度,是许多早期程序员接触图形学的启蒙工具
OpenGL 时代来临:在 OpenGL 诞生之前,图形硬件制造商众多,每家都可能提供自己的编程接口或库。开发者若想应用/游戏支持更多硬件,就需为不同硬件编写不同代码,或依赖硬件商提供的库,工作量巨大。SGI 公司为了推动一个真正开放、跨平台的标准,并应对市场上其他图形硬件供应商(如Sun、HP、IBM 等通过扩展 PHIGS 进入市场)带来的竞争压力,决定在 IRIS GL 的基础上,开发一个全新的、开放的、与硬件无关的图形接口,这就是 OpenGL,在 1992 年 7 月推出了 1.0 版本。OpenGL(Open Graphics Library,开放图形库)的推出是图形计算领域发展的一个重要里程碑。解决了图形编程碎片化问题。它通过定义一个跨平台、开放的标准图形接口,将开发者从为各种硬件编写特定驱动和代码的繁重工作中解放出来。硬件厂商则负责根据 OpenGL 标准实现自己的驱动程序。这种分工大大推动了 3D 图形应用的发展和平民化。
随着对性能要求的提升,更底层的 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 这三项技术协同支撑。
在应用层,主要使用 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。
在 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 前端的主要组成部分,我用一个表格来汇总其核心组件:
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
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
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 关联
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 ...
}
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();
}
}
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;
}
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 提供了三个统一的窗口相关对象:
- Display(EGLDisplay) 是对实际显示设备的抽象;
- Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer(颜色缓冲区), Stencil Buffer(模板缓冲区) ,Depth Buffer(深度缓冲区);
- Context (EGLContext) 存储 OpenGL ES 绘图的一些状态信息;
搭建 EGL 环境的一般的步骤如下:
- 获取 EGLDisplay:通过 eglGetDisplay(EGL_DEFAULT_DISPLAY) 获取默认的显示连接。
- 初始化 EGL:调用 eglInitialize() 来初始化与 EGLDisplay 的连接,并可获取 EGL 版本号。源码中使用的是 eglQueryString 完成该过程。
- 选择 EGLConfig:使用 eglChooseConfig() 指定期望的渲染表面参数(如颜色深度、模板/深度缓冲区),系统会返回最匹配的配置。
- 创建 EGLContext:通过 eglCreateContext() 创建渲染上下文。创建时可指定 OpenGL ES 的版本(如3.0)。
- 创建 EGLSurface:
- 对于屏幕上渲染:使用 eglCreateWindowSurface() 并将 Android 的 NativeWindow(通常由 Surface 或 SurfaceTexture获取)传入。
- 对于离屏渲染:使用 eglCreatePbufferSurface() 创建一个像素缓冲区表面。
- 绑定上下文:调用 eglMakeCurrent() 将 EGLDisplay、EGLSurface 和 EGLContext 与当前线程绑定。此后,该线程发出的 OpenGL ES 指令将会在指定的 Context 和 Surface 上生效。
完成 EGL 环境初始化以后就可以使用 OpenGL ES API 进行绘制操作了:
- 渲染与交换缓冲区:
- 使用 OpenGL ES API 进行绘制。
- 对于 Window Surface,绘制完成后需调用 eglSwapBuffers() 将后台缓冲区(Back Buffer)的内容交换到前台(Front Buffer)以显示到屏幕。
- 对于 PBuffer Surface(离屏渲染),通常不需要交换缓冲区,渲染结果可直接读回或绑定为纹理
- 释放资源:在渲染结束后,需要调用 eglMakeCurrent() 解绑上下文,并依次销毁 EGLContext、EGLSurface,最后调用 eglTerminate() 终止与 EGLDisplay 的连接
EGLSurface 有多种创建方式:
- 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 ...
2
3
4
5
6
7
8
- 创建离屏渲染表面(off-screen surface)
- 不与任何窗口关联
- 主要用作占位符表面,当不支持无表面上下文时使用
- 尺寸固定为1x1像素
- 支持受保护内容(Protected Content)
- eglCreateWindowSurface,这是最常用的窗口表面创建方式:
EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, attrib_list);
- 与原生窗口关联
- 用于屏幕显示
- 支持双缓冲
- 可以调用 eglSwapBuffers 进行缓冲区交换
- 尺寸由关联的窗口决定
- eglCreatePixmapSurface,创建像素映射表面
EGLSurface surface = eglCreatePixmapSurface(display, config, pixmap, attrib_list);
- 与像素图关联
- 用于离屏渲染到像素图
- 在Android中已被标记为 @Deprecated
- 实际实现中抛出 UnsupportedOperationException
- eglCreatePlatformWindowSurface (EGL 1.5)
EGLSurface surface = eglCreatePlatformWindowSurface(display, config, native_window, attrib_list);
- 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) {}
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>();
}
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) {}
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);
}
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();
}
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。
整体的类关系如下图所示: