关于图形技术栈的一些想法

关于图形技术栈的一些想法

当初 React 发布一个一个新消息的时候, 大家鼓吹也激动过, Virtual DOM 本来只能渲染 DOM, 渐渐有人用来渲染 Canvas 渲染 WebGL, 还有人渲染 Terminal Simulator, 也看到有渲染 TV 的. 后面 Facebook 放出大招, React Native 可以同时写 Android 跟 iOS, 感觉一下子打开了新世界的大门.

最近搞出新闻的是 Weex, 距离 Weex 第一次露面已经挺长时间了, Weex 的目标是三端一致, 不仅仅是 Facebook 那样停留在降低开发者门槛那种程度. 而现在这样的目标, 显然也面临着更大的难度, 首先就会有大量跨平台不一致性的问题需要处理. 主要就是 iOS, Android 和 Web.

这次参加 Weex Conf 听到了不少新东西, 觉得有很多值得思考. 比如说经历了这么多 Native UI 动态化的尝试, 现在的 Weex 能否达成最初的目的. 或者说怎样形成生态和标准, 以便更快对用户体验做大的提升. 而且假如 Weex 走得更远, 能否在 React Native 基础上做竞争甚至更进一步? 未来有很多可能性.

这里我觉得另一个视角可以观察, 如果 LLVM 生态有所了解的话, 图形渲染引擎领域发生的变化, 某种程度上可以进行借鉴. LLVM 是 Apple 使用并开源的一个编译器中间层, 并且是一种 IR. 有了 LLVM 之后, 编程语言可以先编译到 LLVM 的 IR, 然后由 LLVM 进行优化, 最终编译到各种平台. 实现了编译器前后端的解耦.

好处是编译器被模块化, 容易进行复用. 当前端需要扩展, 或者后端需要扩展, 工作量可以大大减少. 同时, 如果对于局部的模块不满意, 可以编写代码替换掉模块, 而不是整个做一遍语言. 同时 LLVM 自身会提供强大的优化, 提高生成的代码的效率值. 现在我们已经看到除了 Swift, 也有 Julia 和 Rust 这两种语言直接基于 LLVM 设计. 已有的语言比如 Haskell 也提供基于 LLVM 的编译后端, 使用 LLVM 进行优化. 可以说 LLVM 激活了一个不小的生态.

回过头再看 Virtual DOM, 对于 Virtual DOM 来说, 它的后端也是多样化, 能做到解耦, 它的性能优化主要由渲染引擎来实现. Virtual DOM 的前端目前主要是 React 及其方言, 但是从广义上说, Vue 使用 snabbdom 也算是 Virtual DOM, 其他比如 virtual-dom, deku 之类的 Virtual DOM 方案也是数不胜数, 只是说没有整合进入生态. 那么, 是否有着可能性, 通过我们制定可靠的 Virtual DOM 的 Spec, 最终形成渲染引擎技术栈的生态?

接下来说一个我的想法, 假如说抛开 DOM 重新构造技术栈, 从 OpenGL/WebGL 标准一直到实现 Business, 会是什么样子? 参考已有的技术方案, 我画了一个我理解的层级. 对于浏览器来说, 性能最高的 API 是 WebGL, 而对于互联网公司来说的, 主要是和 Framework 打交道, 将 Framework 封装为为公司所需要的工具链和开发习惯. 在中间, 则提供 Virtual DOM 和 OOP APIs 两层, 前者便于 Framework 开发者生成 Virtual DOM, 后者便于渲染引擎开发者针对具体的渲染指令进行实现.

当然这个划分过于理想化了, 比如 Business 当中可能直接基于 Virtual DOM 开始封装, 跟 Framework 没有多少差别; 或者在 Framework 当中为了处理性能以及 edge case 的情况会去直接调用 OOP APIs 甚至 WebGL 当中的代码, 也未可知. 而且目前而言, 以 real DOM 作为 IR 的场景反倒更清晰一些, 比如在 Android 当中模拟一个 DOM tree 出来, 而 Virtual DOM 更多是一种理论当中的 IR.

这里也许会有个误解, 就是 React 明明导向一个 FP 风格的方案, 为什么下面写了个 OOP? 我的看法是这样, FP 风格代码提供高级的抽象同时会形成较大的内存开销, 间接影响到性能. 在这个位置的 API 主要目的还是为了简单直接地对 WebGL API 进行语法封装, 由于 WebGL 是命令式的繁琐操作, 并涉及到各种内存状态的修改, 用 OOP 风格代码较轻而且对性能影响小. 当然这也仅限 API 的语法风格, 而不是整体的代码的架构.

其实最重要的还是怎样优化性能. 使用 FP 是寻求整体架构的清晰, 具体到实现当中还是会引入缓存方案以减少重复计算和内存消耗, 从而减少性能的开销. 包括 Framework, 除了提供语法糖的便利和随之带来的模块复用的好处之外, 也需要提供足够的性能. 当然, 这里说的性能, 是复杂应用的性能, 因为简单的应用某种程度上不需要抽象, 只有对于通用的渲染逻辑的抽象进行优化, 在这个场景里才有意义.

最后回到 Weex, 对照这个分层, Weex 主要覆盖了 Framework 和 Virtual DOM 两层的内容, 而在 OOP APIs 方面, 继续对 Android 和 iOS 做了一定的封装. OOP APIs 成了 IR. React 方案当中, IR 的范围还可以扩大到 Virtual DOM, 这在 Weex 和 Rax 方案中似乎由于 Framework 的差异并不是复用. 当然简单地讨论整个分层有多少复用的部分没有多大意义, 我主要是认为这样一套分层的思路可以为生态当中职能的划分提供一些参考, 以便我们从关键节点触发连接好生态当中各个片段. 以前端每年新的 Framework 抢风头的周期的, 贺老(@贺师俊)对 Weex 的三年过气的担忧我觉得触发的可能性很大. 而 Virtual DOM 这一层影响较小, 底层 OOP APIs 会受到系统影响, 但不会像开发者口味那么善变.

还是那句话, 画图形出来往往是理想化的, 实际场景当中复杂太多了. 虽然我喜欢把渲染引擎跟虚拟机进行类比, 但是图形界面自身的特点也是蛮多的, 最终搬过来也只有 IR 这么个法说罢了.

编辑于 2017-01-16 16:42