# EmscriptenGLX 原生引擎接入

## 简介

游戏内所有 GL 指令调用需要经过三层转换：WASM(业务逻辑) -> WebGL JS胶水层(GL -> WebGL) -> 平台渲染，WASM 模块 与 JavaScript 之间的高频通信无疑带来了繁重的性能开销。

通过 EmscriptenGLX 渲染模式，可以实现在 WASM 内部完成 GL -> WebGL 指令的处理，将原本 WebGL JS胶水层 的代码下沉至业务逻辑(WASM)中，消除WASM 与 JS 之间高频调用链路。详细文档请查阅[EmscriptenGLX方案介绍](perf-emscriptenglx)

## 特别说明

+ EmscriptenGLX 目前仅支持在 **iOS高性能+模式** 以及 **Android平台** 下运行，其他运行时将会自动降级为原有的运行方案。
+ 基础库版本必须 `>= 3.8.12`，低版本基础库会使用原有的运行模式运行游戏。
+ 建议游戏 **充分验证后** 再进行发布。

## 接入指南

> 以下的所有流程的前提是游戏已基于WebAssembly技术将游戏转换至微信小游戏平台

### 1. 获取 libemscriptenglx.a 静态库

开发者可以通过如下两种方式下载 `libemscriptenglx.a` 的静态库文件

```javascript
// 方式一：指定EmscriptnGLX版本下载（如 0.1.11）
https://game.weixin.qq.com/cgi-bin/gamewxagwasmsplitwap/getunityplugininfo?download=1&biz_id=1&version=${version}

// 方式二：获取最新版本
https://game.weixin.qq.com/cgi-bin/gamewxagwasmsplitwap/getunityplugininfo?download=1&biz_id=1&version=latest
```

不论通过上面哪种方式，开发者通过 `GET` 获取到的构建产物为 `libs_emscriptenglx.zip` 的压缩包文件，压缩包内包含如下的文件

```javascript
// 文件列表
- version.txt   // 记录当前版本的版本号 及 构建时间
- libemscriptenglx_x.x.x.a  // 微信小游戏官网维护的emsdk版本对应的构建产物，目前支持 3.1.17, 3.1.74, 4.0.10 版本
```

*** 若有特定 emsdk 版本需要额外支持的，欢迎通过文末助手链接与我们联系***

### 2. 新增 emcc 编译参数

```javascript
-L<libPath>        \ 指定库的路径
-lemscriptenglx    \ 链接名为 emscriptenglx.a的静态库
-s EXPORTED_RUNTIME_METHODS=ccall,cwrap,stringToUTF8,lengthBytesUTF8 \ 新增运行时的函数
-s ERROR_ON_UNDEFINED_SYMBOLS=0    \ 忽略undefined的声明，保证编译

// for CMake
# 指定静态库路径
link_directories(/libPath)
# 添加可执行文件并链接库
add_executable(myapp, main.cpp)
target_link_libraries(myapp emscriptenglx)
```

### 3. 胶水层代码修改

由于EmscriptenGLX方案需要额外为 `context` 挂载相关的函数实现，故而在EmscriptenGLX方案下，需要在JS Glue中进行下面的代码修改。
建议可以在导出的胶水层代码中，新增 `ReplaceRules` 规则，进行字符串替换。

#### createContext

```javascript
// 若使用 webgl/webgl2，请将contextType修改为 wxwebgl/wxwebgl2
// [GL]对象 GL.createContext
createContext: function(canvas, webGLContextAttributes) {
  // code...

  // eg: webgl示例，可以通过 wx.env.isSupportEmscriptenGLX 判断当前运行时是否支持 EmscriptenGLX特性
  const ctx = canvas.getContext(wx.env.isSupportEmscriptenGLX ? 'wxwebgl' : 'webgl');
  // ① 调用EmscriptenGLX的初始化函数
  Module.ccall(
    'glxInit',
    null,
    ['bool'],
    [ctx.emscriptenGLX ? true : false]
  );
  // ② 初始化全局变量
  if (typeof Module['wxContextGlobal'] === 'undefined' && ctx.emscriptenGLX) {
      Module.wxContextGlobal = { ...ctx.emscriptenGLX };
      Module.ccall(
        'glxInitBufferDataAndGlState',
        null,
        ['number', 'number'],
        [ctx.emscriptenGLX.isWebGL2 ? 2 : 1, ctx.emscriptenGLX.platform]
      );
  }
  // code...
}
```

#### makeCurrentContext

在渲染上下文切换时，调用 `glxUpdateContextId` 更新当前的 `contextid` 信息

```javascript
// makeCurrentContext 为示例函数名，具体根据游戏引擎名称而定
makeCurrentContext: function() {
  // code
  if (Module.ctx.emscriptenGLX) {
    // ③ 每次切换上下文时，都需要传入当前的contextId信息
    Module.ccall(
        'glxUpdateContextId',
        'number',
        ['number'],
        [Module.ctx.emscriptenGLX.ctxid]
    );
  }
}
```

## 更新日志

EmscriptenGLX特性更新，详见[更新日志](perf-emscriptenglx-changelog)

## 常见问题Q&A

请前往 [EmscriptenGLX Q&A](perf-emscriptenglx#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98-Q-A)

## 联系我们

如有任何问题，欢迎[联系小助手](https://res8.wxqcloud.qq.com.cn/wxdoc/0d0a621f-a50f-4a89-8454-2ef762b4fbf0.png)咨询。
