开发者技术前线 ,汇集技术前线快讯和关注行业趋势,大厂干货,是开发者经历和成长的优秀指南。
爱奇艺自研UI引擎渲染技术分析
The following article is from 爱奇艺技术产品团队 Author Lyra team
点击上方“开发者技术前线”,选择“星标”
13点21分打卡 就是真爱
来源:爱奇艺技术产品团队 | 本期责编:可可
摘要
我们都知道,好的视频内容可以吸引更多用户。那么在好的内容基础上,我们希望用更好的架构及渲染技术,不断迭代出更轻、更炫、更平滑的客户端,保证用户有更好的体验。在此驱动下我们致力于开发一套高效的跨平台渲染引擎Lyra,为用户提供新的体验。本文主要详细阐述在PC客户端上引入的新渲染技术。
背景
爱奇艺PC客户端之前使用的UI引擎是公司内部开发的QuiLib。QuiLib是基于开源的uilib和DuiLib开发的轻量级的UI引擎。QuiLib使用了DirectUI框架,其主要思想就是控件无窗口化。虽然在很长一段时间内,QuiLib都发挥着至关重要的作用,但是随着渲染技术和计算机技术的发展,QuiLib暴露了它的很多缺点。
QuiLib的渲染的流程
QuiLib的动画Timer、业务逻辑、布局、渲染等均在主线程,这样每一个阶段的耗时都会导致整个流程的推迟或卡顿,其主要问题有:
(1)绘制性能差,如当UI元素进行alpha或者旋转变化时,paint慢且需要多次内存拷贝,效率很低;
(2)冗余layout,排版效率低,一次业务处理过程中若对某个节点多次操作,会导致该节点冗余layout;
(3)动画不流畅,每组动画都有独立的时间线,同时主线程负载太重,任何一环耗时,都会导致动画不平滑,用户体验差。
其次,在组件架构层面,QuiLib也存在一些欠缺。
组件架构图
从组件架构图可以看出:QuiLib基于Windows和GDI图形库;QuiLib 没有利用D3D等3D图形技术来给UI渲染提速,并且重度使用了GDI,无法做到渲染跨平台化。这样会导致如下问题:不能充分利用显卡的性能,不支持跨平台化。
技术引入
为了解决现在的框架(即Quilib)具有的以上问题,我们做出了以下的技术解决方案:
(1)3D渲染:充分利用显卡性能,加速图形渲染速度;
(2)异步渲染:把渲染流程细划为业务逻辑、样式变化、布局、生成渲染命令、执行渲染命令、合成渲染层和刷新到屏幕几个阶段;每个线程使用一个唯一的动画时间线,来驱动动画;
(3)分层渲染:根据情况,自动把某些频繁渲染的元素从主渲染层提升为单独的渲染层,这样每个层都进行单独的渲染流程,最后再把这些渲染结果合成并且上传到屏幕;
引入新的设计思想和渲染技术,能够让基于Lyra开发的PC应用运行更平滑,达到和mac系统上应用一样的平滑度。
技术实践
为了实践以上设计思想和渲染技术,我们开发了一个新的UI渲染引擎并且命名为Lyra(天琴座),寓意希望新的UI渲染引擎像天琴座一样灿烂。
跨平台和GPU渲染图形库
Lyra一开始就致力于跨平台和GPU加速渲染。通过调研和比较现在流行的图形库及游戏渲染引擎,Lyra最终选择了使用skia。
Lyra引擎组件架构图:阐述了新UI引擎的层级关系
skia具有如下这些优点:
(1)跨平台;
(2)高效基本图元渲染;
(3)高效的字体处理及文本渲染;
(4)丰富的图像格式支持;
(5)支持3D渲染,可以无缝切换为2D渲染。
得益于skia 3D & 2D backend的无缝切换,lyra可以针对不同的机器配置选择不同的渲染方式,优化Layered窗口这种需要渲染后处理的特殊情况,支持运行时渲染方式的自动fallback。也正是3D & 2D backend的无缝切换也导致skia资源占用很高,为了优化资源占用及命令序列化,也需要做很多侵占式的定制,在此不详述了。
异步渲染
Lyra使用了多线程渲染技术:UI主线程主要负责与用户交互,Render线程主要负责图形渲染。
Lyra新的渲染流程图
如上图:Lyra把渲染流程细分为更多的步骤:Event&Message、样式变化、Layout、Paint(生成绘制命令)、Execute(执行绘制命令)、合成、刷新到窗口。在渲染过程中,比较耗时的几个点就是用户逻辑处理、Layout和执行渲染命令、合成。
Lyra把Paint以及其之前和Execute以及其之后分为两大块,这两大块是并行运行。这样的好处是:执行渲染命令的耗时不会导致用户响应延迟,主线程的Timer等比较耗时的逻辑也不会导致渲染延迟;同时形成渲染流水,执行灵活的帧选择策略,最大化渲染效率,提高响应速度。
动画系统由基于线程的唯一Timer来驱动,保证一个线程一个时间线,这样减少了Timer资源的消耗,提高了动画的渲染效率。同时将动画分为主线程动画和render线程动画,主线程动画操作逻辑渲染节点,Render线程动画驱动layer层相关动画。动画系统自动将不同的动画附着到不同的线程。通过如此设计可以支持3D动效、复杂动效,各部件联动,最大化动画帧率。
分层渲染
我们知道一个UI程序,大部分时候只有个别元素在响应事件。比如旋转时,理论上我们只需要绘制正在旋转的这个元素就可以。所以基于这种理念,Lyra实现了分层渲染技术,针对于每个元素,Lyra内部会根据情况创建新层或者销毁多余的层。
分层渲染,需要解决以下问题:各种树之间的逻辑关系;何时创建新的层。
我们用一个示例来说明分层技术的实现原理。
只有主层
在这个示例中,UI第一次展示时,只有主层,如下图所示:
创建分层渲染中的所有树:
(1)解析xml生成一棵Control树,这棵树主要负责处理事件和消息,以及管理各个Control的生命周期;
(2)生成一棵RenderObject树,这棵树的RenderObject和Control树中的Control有一一映射关系,并且RenderObject生命周期的管理完全由Control负责;
(3)Lyra根据需要会生成一棵RenderLayer树,同时也会生成一棵GraphicsLayer树;GraphicsLayer树上的结点和RenderLayer树上的结点有一一映射关系。
构建新层
当用户旋转其中一个元素时,就需要创建新的层。旋转C-5时,树之间的关系以及层之间关系就有如下图新的变化:
这里Lyra根据需要会生成新的层RL-Rotation(对应于RO-5)和RL-Fake(对应于RO-6)。
异步绘制
示例中的3层在UI主线程生成绘制命令,并且交换到Render线程,下图展示了异步绘制:
RenderLayer树来负责驱动关联于它上的RenderObject的Paint,生成绘制命令。 GraphicsLayer对应于真正的LyraSurface,会驱动RenderLayer生成绘制命令,并且把绘制命令交换到渲染线程。
合成
渲染线程在接收到绘制命令后,填充屏幕像素,如下图所示:
Lyra合成器会驱动GraphicsLayer在渲染线程中执行绘制命令,并且合成绘制结果,最后刷新到屏幕上。
结语
本文主要简述了Lyra渲染技术相关的一些突破性工作:
(1)3D渲染、跨平台渲染;
(2)异步渲染,基于线程Timer驱动的动画系统;
(3)分层渲染。如果你想深入了解或者使用Lyra,可以直接在公众号上留言。