glayout 介绍:界面绘制需要一块新的天空

上一篇文章 中,我介绍了自己的界面绘制库 glayout 。这篇文章会对我的设计思路做进一步的解读,来说明我为什么想要做 glayout ,以及现在这样设计的缘由和技术细节。

界面绘制需要一块新的天空

编写 glayout 的初衷是在网页上呈现简单的游戏和互动作品。以前我的游戏基本上基于两大类技术:DOM,比如 SVG 或者普通 DOM 节点操作;基于 Canvas ,使用游戏引擎。无论哪种方式都不是很让我满意。这类作品需要一个更加合适的编码框架。

现在我对 glayout 的想象已超出这个范畴。我希望 glayout 能画出常规的 2D 界面,具有足够好的性能,可以运行在网页和客户端两类环境中。至少,它能够给我们一些新的启发。

界面绘制需要一块新的天空!

Emscripten 带来的可能性

glayout 的工作方式基本就是在一块原始的画布上画上文字和图片。这种方式足够底层,可以使得 glayout 具有足够强大的能力。但这也使得 glayout 需要自己做很多事情,增加了代码量,对性能方面也就更加严苛。

常规的网页编程使用 JavaScript 语言。这是一门拥有高度 JIT 优化的语言,但它恐怕还不足以承担 glayout 的任务。一是它过于动态、不够严格,二是性能还并不足够优秀。幸好,asm.js 和 WebAssembly 技术可以让我们在网页环境中足够快地运行代码。现在也已经有了一个很成熟的工具 Emscripten ,它可以将 C、C++、rust 等语言的代码编译为 asm.js 和 WebAssembly 代码,让我们可以用这些语言来进行网页编程。

glayout 主体使用 rust 语言编写,用 Emscripten 编译器编译为网页代码。rust 是众多现代编程语言中一门设计上非常独特的语言。它具有良好的静态语言编程体验;它没有垃圾回收器,这使得它附加的运行时环境代码很小;它可以和 C 语言互调用,这也使得 glayout 可以被所有能调用 C 语言代码的编程语言所调用。换而言之,在编译到网页代码时,glayout 可以被 rust、C、C++ 和 JavaScript 等多门语言调用。

像游戏引擎一样绘制界面!

glayout 主要分为两层:绘制与布局。简而言之,“布局层”负责将诸如相对位置、字号等布局信息转换为画布坐标系上的坐标,“绘制层”负责将图片和文字画在画布的指定坐标上。

对于绘制层,传统的 2D 界面库更关心如何分层绘制和合成。而 glayout 从思路上就完全不同:我们为什么不像游戏引擎那样绘制界面呢?glayout 完全基于 WebGL 或 OpenGL 来绘制。这使得逻辑非常简单明晰:glayout 的绘制层努力将图片和文字上传给 GPU ,让 GPU 高效地画到画布上。

这个想法和 Mozilla WebRender 一致,感兴趣的小伙伴可以去了解一下这项惊人的绘制技术。它也是使用 rust 编写的,但它无法使用 Emscripten 编译——这也是为什么 glayout 的绘制层没有直接采用它。

glayout 的绘制层需要填上 WebGL 和 OpenGL 的差异。所以这里的代码也被分为两部分:一部分针对 WebGL 后端,使用 Emscripten 编译时将启用这部分代码;而另一部分则针对 OpenGL 后端,编译为常规客户端代码时会用到这一部分代码。

定制化的排版体系

布局层相对而言更加复杂,它的任务是“排版”。绘制层需要布局层告诉它:在哪个坐标上画一幅多大的画或者写一个多大的字。而绘制层拥有的信息则是:界面树结构上有哪些容器、一段文字字号是多少、一幅画相对于它的父级容器有多少偏移。由于这些信息非常多,而且从相对位置换算绝对位置的算法比较复杂,这部分的处理也就很不容易了。

从接口上看,glayout 维护一棵节点树,每个叶子节点表示一段文本、一幅画,非叶子节点表示装着很多文本和画“容器”。每个节点都可以有一些属性,如“字号”“相对父节点的偏移”。这个模型是仿照浏览器 DOM 设计的,很多属性名都一样,甚至可以使用简单的 CSS 样式表来描述这些属性。

事实上,我很不喜欢浏览器排版引擎的设计——他就像是一个奇形怪状的庞然大物,每当想到什么有用的属性,就往这个庞然大物里追加,最后它变成了一个还在不断变大的怪兽;而且,竟然只有浏览器可以追加样式属性,作为应用又没什么好办法添加。我既没法扔掉那些我根本用不到的东西,又没法优雅高效地往里面塞一个我需要的东西。

glayout 避免这样的设计。排版流程应当是可以被定制化的:如果不需要文本下划线,那可以完全移除它;如果需要支持双下划线,那么可以通过“插件”的形式插入到排版流程中。

目前状态

glayout 目前还处于早期的开发阶段。绘制层已经可用了,只是还有一些 bug 和一点关于性能的绘制策略问题。(现在可以毫无压力地 60 帧绘制!)

布局层则还比较原始,只是实现了制作游戏时常用的部分,诸如 position: absolute opacity: 0.5 color: #0cf 这样的样式属性。

因而,总体上看还需要比较多的工作。如果对这个想法感兴趣,欢迎以各种方式联系我!

asmjs