GCanvas渲染引擎的演进
作者:韦青 发布于:2019-12-02 11:59:46

GCanvas的定位是遵循w3c标准的跨平台的轻量级图形渲染引擎。有清晰的定位和目标,并且紧贴现有的业务,为业务提供丰富表现形式。


GCanvas发展


GCanvas引擎从早期的H5性能加速,到Weex业务落地,从小游戏的业务探索,到服务端渲染,再到小程序。经过几个阶段的发展后日渐成熟。


淘系无线架构的不断升级迭代,GCanvas随之保持着更新迭代的步调,在多个业务场景中使用,了解下一些应用案例。


应用案例


GCanvas的目标人群是业务开发者,满足业务的功能需求,对开发者也非常友好,尤其是前端开发者。熟悉H5 Canvas的同学,很容易上手,无任何学习成本。


  • Weex
    2017年双十一预热会场,GCanvas与魔影合作的版头动画
  • 天猫未来店
    天猫未来店的智能电子标签,基于GCanvas JSBinding的智能电子标签
  • 小游戏
    野生小伙伴,基于GCanvas小游戏应用
  • Sketch Render
    Demo+中的Sketch Render,基于GCanvas实现的服务端渲染Sketch文件
  • 小程序Canvas
    基于小程序同层渲染组件
  • 支付宝小程序诸葛找房 - 2D

  • 淘宝小程序AR试妆 - WebGL


架构演进与优化


以业务先赢的基本原则,保证业务的前提下,架构容器和升级变化过程中,GCanvas引擎也经历了演进和升级优化。


  • 2017年的架构
    以插件为主的实现,仅支持移动端
  • 最新架构
    提供标准接口,链路升级,API升级,不仅支持移动端还支持服务端

    架构变化主要有这几个方面。
  • 适配支持更多的JS框架和库
  • JS到Native调用通路,从模块路由反射升级到JSBinding
  • 渲染API支持Metal
  • 增加MacOS和Linux平台支持


内功修炼


在快速迭代过重中,保持修炼内功。为保证高性能这个根本,在链路、内核以及底层图形API等方面也都做了不少优化与升级。


  • JS到Native链路优化
    从Weex调用链路到JSBinding,Weex容器的JS到Native的通路采用模块路由和反射的调用方式调用具体的模块和组件。在UI和一些非高频的产经完全能满足需求。
    但是对于连续操作、连续动画等高频的JS到Native通讯的场景,链路上的耗时非常大,导致卡顿产生。这也是为什会BindingX和GCanvas的JSBinding的出现。BindingX是另一种解决频发通讯消耗的方案,有兴趣的可以看下BindingX
    GCanvas的JSBinding的实现
    通过链路调用的改造,整体帧率平均提升10帧左右。Android和iOS的JSBinding实现方案类似。以iOS举例说明。
    iOS尝试使用JSExport和全局方法,两种JSBinding方案。
  • 第一种方案,使用JSExport和JSExportAS
@import JavaScriptCore;

@protocol TestObjecJSExport <JSExport>
JSExportAs(foo, - (void)foo:(NSString *)msg);
@end
  • 第二种方案,使用C Export将方法和属性用JSStaticFunctionJSStaticValue进行绑定
//方法JSStaticFunction
typedef struct {
  const char* name;   //方法名
  JSObjectCallAsFunctionCallback callAsFunction; //Native方法实现
  JSPropertyAttributes attributes; //方法设置
} JSStaticFunction;

//属性JSStaticValue
typedef struct {
const char* name;  //属性名
JSObjectGetPropertyCallback getProperty; //Native属性Getter实现
JSObjectSetPropertyCallback setProperty; //Native属性Setter实现
JSPropertyAttributes attributes; //属性的读写设置
} JSStaticValue;

两种实现方案,经过测试对比第二种方案在性能更好。原因在于静态JS方法是通过方法名到Native函数的直接映射,而JSExport的方案则需要类型检查,协议校验,再调用Native方法中间经过额外的处理。


简单的耗时测试数据对比

方式耗时(ms/百万次)
JSExport1278
C Export452


  • JS到Native数据传输
    方法调用与属性访问之外,参数数据的传输也影响每帧耗时,尤其是在WebGL的场景,通常有很大顶 点数据需要处理,有几万-几十万字节,甚至更多。JS到Native的大数据传输避免内存拷贝。
  • JS与Native对象生命周期
    JSBinding的对象生命周期管理,JS对象与Native对象一一对应,在JS对象创建触发JSObjectInitializeCallback回调,创建Native对象,并将JS与Native建立关联。JS的GC回收对象触发JSObjectFinalizeCallback的回调中去释放对应Native对象。
  • 帧率优化
    除了调用链路对帧率的提升,单帧绘制的CPU和GPU耗时相关的优化点
  • 顶点数据计算,顶点数据合并提交
  • 优化缓存策略,优化文字相关纹理的缓存
  • 增加状态管理,减少GPU提交数据和频次
  • 优化多边形填充效率
  • 抗锯齿等耗时特性可选
  • w3c标准完善
  • 支持阴影
  • 支持虚线
  • 支持多Clip区域嵌套
  • 支持Winding Rule支持
  • 扩展能力,扩展一些非标接口支持Sketch渲染,
  • 阴影的扩散
  • 路径的图案填充
  • 路径的高斯模糊
  • 路径的内描边和外描边
  • 底层图形API升级
    在iOS12之后,苹果将OpenGL ES API设为废弃,在已支持的设备上OpenGL ES的调用都已映射到Metal相应的后端实现,Metal替换OpenGL势在必行。GCanvas也已投入开发Metal,可选择使用Metal作为渲染的后端。已完成了2D的绝大部分能力。
    选择Metal会带来以下方面的收益:
  • 内存数据使用更高效,内存数据可共享,
  • 尽可能的榨取更多GPU性能
  • 摆脱OpenGL的状态机,更友好的面向对象编程
  • 苹果后续的持续投入和更新
  • 丰富的调试工具,能精确到每个顶点数据和每个素点颜色
  • 便捷调试这着色器语言(Metal Shader Language)

在内核升级优化的过程中,也有很多同学积极参与其中来在此表示感谢。


稳定性


增加了API的自动化测试以及CI建立保障稳定性。


未来的方向


  • 更多纹理压缩格式的支持
  • Vulkan的持续演进
  • 更多平台的支持,IoT设备上应用
  • 与云端渲染的融合,提供Fass能力
  • WebGPU以及GPU计算方向探索
  • WebAssembly的应用


结束语


有图形相关的业务需求和问题可联系淘宝前端-无线架构组。