作者| 阿里文娱无线开发专家 风吟
一、背景
在当前直播和短视频领域,各家公司为了吸引用户和提升用户体验都在创新各种视频玩法, 例如美颜、美妆、虚拟形象等。这些酷炫的效果背后其实是强大的渲染技术。优酷和来疯也在 布局这一领域,我们亟需一个底层渲染框架来高效地支撑我们的业务迭代。于是,自主开发一个跨平台、可扩展、高性能的渲染引擎就被提上了日程。
二、引擎设计
1. 引擎特点
前面说了我们的目标是开发一个跨平台、可扩展、高性能的渲染引擎。怎么理解?下面详细阐述一下:
1)跨平台。
我们的业务场景涵盖了移动端和 PC 端。为了提高开发效率,我们要把平台无关的逻辑抽象成公共组件,例如渲染管线、后处理算法等,实现一份代码,多端共用。
2)可扩展。
跨平台解决了横向的平台差异问题,而可扩展就是解决业务迭代的纵向问题。 我们将渲染逻辑分解成一个一个的小“零件”,业务方可以像搭积木一样自由组合这些“零件”, 而算法同学也可以很容易的造“零件”。这样业务迭代就简化成了简单的加减法,效率大大提升。
3)高性能。
音视频渲染,尤其是视频渲染,是很耗费计算资源的。在端侧有限的计算资源 前提下,还要能实现快速、高质量的渲染效果,就需要我们厘清渲染链路中的每个环节,剔除 冗余逻辑,实现高效渲染。
2. 引擎架构
在以上思想的指导下,我将渲染引擎分成了两层: 引擎层( Engine )和业务封装层 (MediaSDK)。
1)引擎层是平台无关的,使用 C++开发,渲染后端使用了各平台都支持的 OpenGL 框架。 核心思想是把渲染链路抽象成有向无环图(DAG),最基础的组件抽象成插件(Plugin),所有 的数据源(Source)、算法(Filter)、输出终端(End)都是插件,再定义好插件的输入/输出协 议,只要上下游插件的数据交互满足协议就可以自由组合。引擎层的架构图如下:
2)业务封装层封装了引擎层的调用逻辑,在 Android、iOS 和 Windows 平台上对业务方提 供统一调用接口,方便业务同学使用,并提供日志、监控和资源管理等能力。业务封装层的架 构图如下:
三、实践
1. 技术挑战 实现过程有两个挑战,一个是跨平台,一个是高性能。
1)跨平台
a)引擎层虽然使用了 OpenGL 作为渲染后端,但 Android 和 iOS 系统提供的是 OpenGL ES, Windows 提供的是 OpenGL,这两个版本在 shader 语法上是有差别的,这就需要引擎和算法去 做适配。其次,要使用 OpenGL 需要在每个平台上都实现一个上下文环境,iOS 是 EAGL,Android 是 EGL,Windows 是 WGL。各平台虽然都有封装好的 GL 组件,但是都不太好用(有 Bug), 最好的办法还是自己调用底层 API,这就需要对 OpenGL 有一定的了解;
b)业务封装层封装了引擎层的底层接口,对外提供统一业务接口。而业务开发是平台相关 的,语言也不尽相同,Android 是 Java,iOS 是 OC,Windows 是 C++。并且像相机、编/解码器、 View 这些系统组件各平台实现并不统一。因此开发业务封装层需要对各语言和系统特性都有较 深入的理解,才能实现易用性和性能的统一。
2)高性能
a)一个思路是尽可能的使用 GPU,解放 CPU,因为 GPU 在视频处理方面有天然优势,引 擎里的计算基本上都放在了 GPU 侧,包括图像色彩空间转换、后处理等。而且在某些场景下还 会对图像做下采样,进一步降低 GPU 使用率;
b)即使算法都使用 GPU 实现,但是还要面对显存-内存数据互传的问题,从内存上载数据 到显存和从显存下载数据到内存都会阻塞 CPU,导致 CPU 占用率高。解决方法一是引擎内部 数据都使用纹理传递,尽可能减少显存-内存数据互传。二是使用双 PBO,异步传输数据,但这 个方案需要注意数据同步的问题。
2. 引擎落地
1)算法接入 由于引擎的算法实现都抽象成了插件,而且将渲染的公共逻辑都提取到基类,算法同学接入算法时只需要关注 shader 本身就可以了,接入是很方便的。理论上只要符合插件规范,任何算法甚至二方或者三方SDK 都是可以做成插件接入进来的。目前引擎接入的算法能力有:人脸 检测、美颜、滤镜、2D 贴纸,未来还将接入美妆、美体、3D 贴纸、Avatar 等。
2)业务接入
渲染引擎提供了多种输入,如相机采集、帧序列等;多种输出,如 UI、编码、帧序列等, 结合前面提到的算法能力,业务方只需要调用简单的几个接口就可以定制自己的渲染管线,从 而实现自己的业务需求。目前渲染引擎接入了来疯 iOS 和 Android 双端,来疯 PC 浏览器插件, 优来播 iOS 和 Android 双端。并且稳定迭代了若干个版本,在多个业务场景中使用,例如秀场直播、电台直播、录屏直播等。
四、总结与展望
1.沉淀
回顾整个渲染引擎的开发过程,其实是踩了很多坑,也是对渲染技术的一次深入挖掘。整个架构的设计经受住了业务的考验,也为今后类似 SDK 的开发提供了宝贵的经验。
2.引擎存在的不足
虽然渲染引擎在目前的应用场景中运行良好,但还是存在一些不足,主要有两点,一个是 OpenGL 天生的缺陷,即 OpenGL 上下文和线程是强相关的,导致渲染引擎设计上虽然是一个 DAG,但实际上管线的所有绘制操作都是串行的,不能充分发挥 GPU 性能;二是引擎内部数 据传输只支持 push 的方式,即只能上游推动下游消费数据,而不能下游主动从上游拉取数据消 费,这就制约了引擎的使用方式。
3.引擎 2.0
前面提到渲染引擎在低端机上性能不佳,主要原因是 OpenGL 渲染耗时。其实谷歌和苹果 都已经在最新的代码中将 OpenGL 标记为 deprecated,分别在推广新一代渲染框架,即 Vulkan 和 Metal,而微软也有自家的渲染框架,即 DirectX:
这些新框架思路大同小异,相比 OpenGL 的优点有:支持预编译 shader、支持多 CPU 线程、 支持直接访问 GPU CommandBuffer 等,对开发者更友好,运行效率更高,甚至能达到成倍性能提升。所以,未来渲染引擎的 2.0 版本将会支持多 backends,包括 Vulkan、Metal 和 DirectX, 以实现更好的性能。