从零开始的跨平台渲染引擎(零)——基础架构分析与设计

Posted 董小虫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始的跨平台渲染引擎(零)——基础架构分析与设计相关的知识,希望对你有一定的参考价值。

本文同时发布于本人的知乎专栏:https://zhuanlan.zhihu.com/p/403395505

前言

近几年我的工作主要集中在渲染引擎方面。随着工作时间的增长,接触和学习到的相关知识也逐渐增多,并且有着渐渐变杂的趋势。我也想着我所掌握的对相关知识进行总结,将这些知识串联起来,形成体系。于是,从本篇文章开始,我打算从零开始,一步步搭建一个跨平台的渲染引擎,并就着代码逐步总结。

本篇文章将对跨平台渲染引擎做了简要的需求分析,以及基础架构的简要设计。

需求分析

  1. 跨平台:需要符合“一次编写,随处运行”的主旨;在现在初始阶段,首先选择移动端的两大主流平台androidios来实现,后续会考虑适配Windows、WASM。(MacOS和iOS的实现比较接近,可以在完成iOS端的同时进行适配)
  2. 嵌入式:非独立运行的应用,由其他应用以控件的方式将引擎嵌入;
  3. 提供脚本运行时环境:对上层开发者(如游戏开发者等)提供脚本接口,如javascript或Lua等。

技术分析

有了大概的引擎需求,现在我们开始分析一下需要什么样的技术栈来实现这些需求。

整体的技术架构

渲染引擎的整体架构如下图所示:

  • 底层的Platform层对应着各个系统平台,如Android、iOS、Web、Windows等。
  • Container层对应着系统平台容器层。其作用是抹平系统API差异,并使引擎可以运行与各个平台上。
  • Graphic Engine层和Graphic Wrapper层对应着图形引擎及其包装。其作用是底层的图形绘制,以及向上提供绘制接口。图形引擎包括但不限于:OpenGL、Metal、Vulkan等。
  • Script Engine和Runtime Wrapper层对应脚本引擎及其包装。其作用是解析运行脚本,向上提供脚本运行接口及扩展API能力。脚本引擎包括但不限于:V8、JSCore、WASM、QuickJS等。
  • Framework层是引擎的核心层,其作用是物理引擎模拟、Action计算、渲染指令生成等。
  • Export API是向上层开发者提供的API接口。上层开发者可用脚本语言,利用这些API进行业务逻辑开发。

下面对各层进行详细分析。

图形引擎

首先,底层的图形引擎我优先选择使用OpenGL,因为OpenGL在平台的适配性上优势比较明显。尤其在初期版本,搭建引擎整体架构时,需要尽量减少适配性的工作。

当然,现在Metal、Vulkan等引擎也在逐步的代替古老的OpenGL。所以,在后续版本迭代中,会将适配不同的图形引擎。这也是架构中Graphic Wrapper层存在的意义。

OpenGL

OpenGL全称为Open Graphic Library,是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。

OpenGL接口类似C语言函数,但其实是语言独立的。因此,OpenGL会有很多不同语言的绑定,如:Android提供Java和C绑定、iOS提供C绑定,甚至JavaScript绑定的WebGL。OpenGL不仅语言无关,而且平台无关。规范只字未提获得和管理OpenGL上下文相关的内容,而是将这些作为细节交给底层的窗口系统。

针对手机、PDA和游戏主机等嵌入式设备,Khronos组织又从OpenGL中裁剪定制了OpenGL ES(全称为OpenGL for Embedded Systems)。其中OpenGL ES 2.0是参照 OpenGL 2.0 规范定义的,引入了对可编程管线的支持。

由于OpenGL依赖于硬件的支持,所以在OpenGL版本选择的考虑中,为了适配较老的硬件机型,这里选择使用OpenGL/OpenGL ES 2.0版本。同时也应该考虑适配3.0版本,以用于后续更新。

Metal

Metal是苹果公司于2014年推出的新的图形引擎,其拥有更高效的图形渲染性能。Metal引擎对软硬件有一些要求,如果使用Metal引擎,要求系统版本不低于iOS 8或MacOS 10.11,硬件则是需要A7芯片以上。

苹果曾在18年的WWDC大会上宣布在MacOS 10.14、iOS 12等版本之后,将逐步废弃OpenGL。虽然当前仍然可以使用OpenGL,不过不再继续更新,并且不推荐使用。所以在后续的适配工作中,也会考虑到在iOS平台上使用Metal等情况。

Vulkan

Vulkan是由Khronos组织在2015年推出的一种高级图形API。Vulkan的设计初衷是为了避免在上层API出现更高的开销。同时,还希望Vulkan成为一个比其他图形API更跨平台的API,它不仅针对高端系统,还针对低端移动设备。

同样的,Vulkan也有最低软硬件要求。软件上,Android 7.0开发者预览版开始,Google在系统平台中添加了对Vulkan的API支持。硬件上,则需要AMD Radeon Software Crimson 版 16.3 及更新版本在 Windows® 7、Window® 8.1、Windows® 10 和 Linux® 中支持基于次世代图形核心架构的以下 AMD APU 和 Radeon™ 显卡。

平台容器控件

平台容器控件用于承载引擎本身,并起到引擎和系统之间的桥梁作用。

Android

在Android系统中,存在两种控件可以满足自定义渲染能力,分别是:SufaceViewTextureView

SurfaceViewTextureView都继承于View,可以作为普通的View嵌入到视图层次结构中。但和普通的View区别在于,他们拥有自己独立的Surface。而这个Surface就是我们引擎渲染绘制的载体。这两种View的区别在于,SurfaceView会在SurfaceFlinger中开启一个独立的Layer来绘制。而TextureView则和DecoView处于同一个Layer中,所以画面更新是和视图结构同步进行的。在实际使用中,则需要根据实际情况来选择其中一个View来作为视图载体。

Surface在Android的EGL环境中可以通过ANativeWindow_fromSurface创建ANativeWindow。并在此window基础上,创建EGLSurfaceEGLContext,作为OpenGL的绘制环境。

iOS

在iOS平台上,直接继承UIView即可,只需要在类型实现中添加:

+ (Class)layerClass {
  return [CAEAGLLayer class];
}

用于告诉系统当前的ViewLayer将作为OpenGL的绘制层。执行下列代码,将此Layer绑定到渲染线程的EAGLContext上。

[EAGLContext setCurrentContext:context];
// ...
[context renderbufferStorage:GL_RENDERBUFFER
                fromDrawable:layer];

脚本运行时

跨平台引擎的脚本语言,较常用的是JavaScript和Lua。在脚本语言的选择问题上,这里选择使用JavaScript。原因有一下几点:

  1. Lua更容易上手,但是JavaScript则拥有更大的用户基数和完善的生态环境;
  2. JavaScirpt有TypeScript这个超集,开发效率更高;
  3. JS是Web端的原生语言,在向Web端适配时更容易;
  4. 在iOS平台上,JS可以利用其内置的JavaScriptCore实现JIT。

主流的JavaScript引擎主要有V8、JavaScriptCore以及前两年由Fabrice Bellard大神写出的QuickJs,这几种JS引擎各有优劣。可以从一下几个角度来对各种引擎考量:

  • 性能
  • 体积
  • 语法支持度
  • 调试便捷性
  • 应用市场平台规范

此处可参考文章:🤔 移动端 JS 引擎哪家强?美国硅谷找…这里就不再过多赘述。

根据各项条件,这里选择在iOS平台使用JavaScriptCore,在Android平台使用QuickJs。

  1. iOS平台选择JavaScriptCore这点基本没什么可犹豫的,因其主场优势,可直接依赖系统级框架,减小包体积,开发难度也减小很多。当然,也存在一些缺陷,JSC在三方应用直接引用时,JIT因安全原因是默认关闭的。而且因为是系统级框架,语法支持度则是根据系统版本来决定的,例如:iOS 9以下是不支持ArrayBuffer的。
  2. Android平台选择QuickJs。其实从性能的角度来说,在Android平台上使用V8是最好的。但是作为嵌入式引擎,包体积是一个很重要的考量标准。我在编译V8的8.x版本时发现arm64架构下的动态库甚至可以达到14M,这对于一个嵌入式引擎来说,有些巨大。所以这里选择使用QuickJs,虽然没有JIT,但是如果没有太多的CPU密集计算的话,性能上是可以接受的。

Framework和接口

Framework是渲染引擎的核心,承担起物理模拟、布局排版、渲染指令生成等任务。Export API则是向上层开发者提供的API接口。上层开发者可用脚本语言,利用这些API进行业务逻辑开发。
这两层的更新迭代频率会比较高,基本上会在引擎迭代过程中不断的变化。所以我打算在后续引擎搭建的过程中,再对这两块进行详细的分析。

总结

本篇文章主要对跨平台渲染引擎做了简要的需求分析,以及基础架构的简要设计。后续文章将结合实际代码,一步步从零开始搭建起整个引擎。

以上是关于从零开始的跨平台渲染引擎(零)——基础架构分析与设计的主要内容,如果未能解决你的问题,请参考以下文章

从零开始的跨平台渲染引擎(零)——基础架构分析与设计

从零开始的跨平台渲染引擎——OpenGL基础环境搭建

从零开始的跨平台渲染引擎——OpenGL基础环境搭建

从零开始的跨平台渲染引擎——OpenGL基础环境搭建

从零开始的跨平台渲染引擎(番外一)——光和相机

从零开始的跨平台渲染引擎(番外一)——光和相机