1.Android 14 HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
2.skia发展历史
3.opengl和skia哪个快
4.Flutter 新一代图形渲染器 Impeller
5.Skia 编译及踩坑实践
Android 14 HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
HUWUI是源码Android系统中负责应用可视化元素绘制的核心组件,其架构主要在C++层实现,源码从Java层接收View绘制信息,源码通过唯一的源码渲染线程使用skia技术完成渲染任务。整体上,源码从应用程序到UI线程,源码作业部落源码再到渲染线程,源码形成了清晰的源码层级关系。
HUWUI的源码构建主要包括三个核心类,它们分别是源码:RecordingCanvas、Canvas、源码RenderNode、源码RenderProxy、源码RenderThread、源码CanvasContext、源码IRenderPipeline。在Java层,主要涉及两类Canvas,RecordingCanvas用于记录绘制指令,Canvas则是直接用于渲染。RecordingCanvas在构造时创建,而Canvas在调用时创建。这两个类在C++层分别对应SkiaRecordingCanvas和SkiaCanvas,后者直接引用SkCanvas。
在全局循环中,UI线程与渲染线程之间的协同操作至关重要。具体流程包括:新创建Activity后,附着到对应的PhoneWindow,然后调用PhoneWindow的jmockit源码setContentView方法,将View添加到DecorView作为子节点。接着,DecorView与ViewRootImpl对接,完成View的更新与渲染。整个过程包含了measure、layout和draw等复杂子流程。
渲染线程创建与核心对象紧密关联,主要包括RenderProxy、RenderThread和DrawFrameTask。RenderProxy负责Java层信息的衔接,RenderThread作为进程唯一的渲染线程,持有DrawFrameTask和CanvasContext,完成一帧的绘制任务。指令记录流程的核心在于使用C++层的RecordingCanvas将View属性和绘制信息记录到DisplayList中,进而完成指令的渲染。
Surface、ANativeWindow、EGLSurface的创建流程在ViewRootImpl的performTraversals函数中初始化。ReliableSurface的封装和EGL与Skia环境的创建主要在RenderThread的requireGlContext函数中实现。从源码分析,这一过程通常在三个地方调用。
View树与RenderNode树之间的协作关系明确,一个Application进程对应多个Activity,每个Activity与一个PhoneWindow绑定,PhoneWindow持有DecorView,DecorView对应一个ViewRootImpl,而ViewRootImpl与ThreadedRender模块对接。ThreadedRender与C++层的shellinabox源码RenderProxy一一对应,RenderProxy持有关键对象,如RenderThread、CanvasContext、DrawFrameTask等。RenderThread是单例模式,进程唯一,负责一帧绘制的逻辑。
在RenderPipeline模块中,关键操作包括makeCurrent、draw和swapBuffers。Native Canvas在这一过程中扮演了桥梁角色,接收Java API调用,而RecordingCanvas完成Op记录,最终DisplayListData存储这些Op。
skia的核心资源主要在三个使用场景中发挥作用,具体细节需深入分析,这些资源对于实现高效、稳定的渲染效果至关重要。
skia发展历史
自年Google购入Skia以来,这个项目一直保持着相对低调的姿态。转折点发生在年初,Skia的GL相关源代码首次公开,它成为了Google Android平台的关键图形引擎。随后,Google Chrome浏览器也接纳了Skia的技术,使其在浏览器渲染中发挥重要作用。随着Android和Chrome开源计划(Android的开源版本称为Chromium)的展开,Skia的linuxrc源码原始源代码也随之公开,采用的是Apache License v2的许可协议,这与GPLv2有所不同。在Android和Chrome的源代码库中,都包含了对Skia的定制版本,如Chrome项目下的"chrome/trunk/src/skia",这些定制主要针对各自平台的需求,如Android通过Linux Framebuffer接口与系统集成,而Chrome在开发中的Linux版本则使用Gtk+。
值得注意的是,Skia本身并不直接处理底层环境的细节,如Linux Framebuffer或Gtk+的衔接,这就导致了Android和Chrome需要针对其各自的环境需求进行相应的代码修改,以确保系统的兼容性和流畅运行。这些定制版的Skia在各自的项目中扮演了不可或缺的角色,但原始的Skia项目本身并未涉及这些底层整合工作。[1]
opengl和skia哪个快
从Honeycomb[3.x]版本起,Andorid便支持GPU加速,但目前Android并没有使用Skia GPU进行Webkit渲染。Skia GPU使用OpenGL进行后台加速渲染,未来也许会代替Skia。
很多人觉得,即使Android成功使用了GPU加速Webkit渲染,在访问浏览如雅虎等一般的网站时,用户也感觉不到太大的差异。因为Webkit的资源大多数消耗在了Javascript脚本和布局定位上。
我们觉得Webkit使用GPU加速渲染的最大意义无非是HTML5 Canvas[HTML5的动态绘图效果]。Android渲染Canvas动画实在太慢,导致Web开发者根本无法在Android上用Canvas开发网页游戏[要注意的fulao源码是,目前很多手机和平板的应用程序以HTML5做为界面,并使用Webkit工作,这也是很多应用在Android系统上感觉不流畅的重要因素。
Android Webkit开发平台[NDK]使用Skia GPU加速测试
我们对Android系统使用Skia GPU加速的Webkit进行了测试。我们手上已经有Android Webkit NDK的WAC2.0版本,我使用了某个提交版本的Skia源码,并开启Skia GPU加速将其编译进NDK中。
我并没有使用Canvas加速,因为这还要增加修改GraphicsContextSkia API的工作,所以并未测试Canvas渲染的性能。
为了使用Skia GPU加速,我做了以下两点:
1,新增了一个使用GLSurfaceView的eglContext内容。
2,在WebView.cpp中使用SkGpuCanvas代替SkCanvas。 我在系统版本为2.3.2的Nexus S上测试,并禁用了屏幕合成加速和Webkit后备缓存,结果出乎意料,Skia GPU反而降低了绘图性能,比Skia使用CPU渲染的时候慢了两倍以上。
当用户滚动雅虎网站页面的时候,每一帧都会使Webkit对页面元素进行重绘。页面元素包括%的文本,%的矩形和%的图像,Skia GPU加速渲染时候反而慢了五倍。
你看到图表后也许会觉得Skia GPU渲染SVG动画时是要比CPU快那么一丁点了。不过Webkit在渲染SVG动画的时候出了一些问题,它绝大多数时间花在了定位布局SVG元素上,而不是渲染SVG元素。所以我不敢确定Skia使用GPU加速时是不是真的变快了。
Skia在栅格化文本的时候使用的是CPU而不是GPU,它将文本缓存为材质贴图。因此Skia GPU加速并不会增加滚动文本时的速度。
我一开始觉得Skia GPU加速会在绘制飞舞的浏览器图标时理应能速度更快了,毕竟那是位图动画,是GPU的强项。结果,Skia GPU渲染慢了倍由于还没有得到详细结果,所以我们需要做进一步的研究,以找到问题的原因。
当你构建Skia的时候,你会得到一个跑分程序,运行之后,你会看到使用CPU和GPU渲染时的性能差异。下面是一些测试得分中的重点项目。
Flutter 新一代图形渲染器 Impeller
Flutter在年的Roadmap中提出需重新考虑着色器使用方式,计划重写图像渲染后端。此计划的初步成果是名为Impeller的渲染后端,本文将探讨Impeller解决的问题、目标、架构和渲染细节。
背景部分, Flutter过去一年解决了不少Jank问题,但着色器编译导致的Jank问题一直没有解决。着色器编译Jank问题源于Flutter底层使用skia做2D图形渲染库,内部定义了SkSL(Skia shading language)。在光栅化阶段,skia生成SkSL着色器,再将其转换为特定后端(GLSL、GLSL ES 或 Metal SL)着色器,并在设备上编译,此过程可能耗时数百毫秒,导致数十帧丢失。通过在Flutter 1.版本中为GL后端实现SkSL预热机制,离线收集并保存应用程序中使用的SkSL着色器,进而提升性能。
Impeller架构部分,Impeller是专为Flutter设计的渲染器,目前处于早期原型阶段,仅支持iOS和Mac系统,依赖flutter fml和display list,并实现了display list dispatcher接口,便于替换skia。其核心目标是解决着色器编译Jank问题。
Impeller着色器离线编译部分,Impeller compiler模块是关键。在编译阶段,将compiler相关源码编译为host工具impellerc binary,利用impellerc compiler将所有着色器源码(包括顶点和片段着色器)编译为SPIR-V中间语言,再转换为特定后端的高级着色器语言(如Metal SL),并编译为shader library,同时生成C++ shader binding用于快速创建pipeline state objects。这样所有着色器在离线时被编译,运行时不需执行任何编译操作,提升首帧渲染性能。
Impeller渲染流程部分,通过继承IOSContext、IOSSurface和flow Surface实现IOSContextMetalImpeller、IOSSurfaceMetalImpeller和GPUSurfaceMetalImpeller结构,对接flutter flow子系统。光栅化阶段,通过DisplayListCanvasRecorder合成Layer Tree,将所有layer中的绘图命令转换为DLOps,并存储到DisplayList结构。随后,使用DisplayListDispatcher执行所有Ops,将信息转换为EntityPass结构。接着,使用RenderPass从Root EntityPass开始遍历,将每个Entity转换为Command结构,生成GPU Pipeline,设置顶点和片段着色器的数据,将顶点数据和颜色或纹理数据转换为GPU buffer。最后,开始渲染指令编码阶段,根据MTLCommandBuffer生成MTLRenderCommandEncoder,遍历所有Commands,设置PipelineState、Vertext Buffer和Fragment Buffer,提交command buffer。
总结部分,Impeller通过离线编译着色器、优化渲染流程等手段解决着色器编译Jank问题,显著提升渲染性能。Flutter重写图像渲染后端的决心可见一斑,期待Impeller能进一步提升Flutter的渲染性能。
Skia 编译及踩坑实践
了解并入门 Skia、OpenGL 和 Vulkan 的关键点:
Skia 是一个开源2D 图形库,提供跨硬件和软件平台工作的通用 API。OpenGL 是跨平台的图形 API,用于3D 图形处理硬件标准接口。Vulkan 是高性能跨平台 2D 和 3D 图形 API。
Android 支持多版 OpenGL ES 和 Vulkan。Skia 的常用后端包括自身、OpenGL、Vulkan、Metal 和 PDF。
实践涉及获取 Skia 源码,编译集成,使用 Skia 画出三角形。常见问题包括链接 libskia.so 时的 undefined symbol 错误,以及使用 SkData、SkImage、SkFont 时的指针异常导致闪退。最终选择稳定版本 flutter-3.2-candidate.4 分支。
改用 GPU 画图(Vulkan)能显著提升绘制效果,但需调整编译配置,注意 Vulkan 库位置,不同 Android 系统内置的 so 存在增删。初始 Vulkan 配置涉及创建 Vulkan 实例、物理设备、逻辑设备、队列族、队列、回调函数、上下文、Surface 和交换链等。
发现 Vulkan 性能不及预期,验证后发现 OpenGL 在电量、温度和帧率上表现更优。Skia 开启 OpenGL 后端绘制,相比 Vulkan 简化许多。
实际项目构建 Skia 运行出现问题时,不要轻易怀疑自己,可能真是 Skia 的问题。Vulkan 需要一定的图形基础,没有经验者慎用。
学习和实践 Skia、OpenGL 和 Vulkan 的过程需要耐心,遇到问题时不要放弃,可能只是 Skia 的问题。Vulkan 的配置和调优需要投入更多精力,但最终能实现高性能渲染。