微信小游戏高性能模式与高性能+模式

背景

在现在的团队待了两年多,对接过的开发商名字颇攒了一些。

有件事说来意外,却确实普遍:很多开发者,不乏一些收益颇丰的团队,并不了解游戏在 iOS 上到底跑的是哪个模式。

帧率低了、卡顿率高了来咨询。我问高性能模式开了没有。回答或是不清楚什么是高性能模式,或是不知道有什么坑所以没开。

回答多了甚至有近似膝跳反射的感受,因此以个人视角写下这篇文章来介绍一下这三种模式,希望或多或少能帮助开发者。

如果读者非技术出身,可以直接跳到最后一章;如果读者是技术出身,那么建议逐章向下读;

历史的进程

普通模式的痛

时间拨回到好几年前,那会还是跳一跳刚上线的时候。

iOS 上 App 内嵌的网页都跑在 WKWebView 里,底层引擎是 JavaScriptCore。引擎本身其实支持 JIT,能把热点编译成机器码。

但苹果自有他安全方面的考量——不开给第三方(当然也包括了微信),只有 Safari 和系统级 WebKit 进程有 JIT 权限。

安卓上的微信没这问题,因为安卓微信的底层是 V8,默认 JIT 是支持的。

这样就解释了为什么远古时期,iOS版本的微信小游戏的性能远不如安卓。

又由于微信小游戏本身是跨平台的,这也因此就导致了微信小游戏在彼时难以重度化。

破局:高性能模式

吉德林法则:把难题清清楚楚地写出来,便已经解决了一半。

微信进程肯定不算系统 WebKit、拿不到 JIT,那也好办,就让微信在启动小游戏的时候,将其跑到一个新拉起的独立系统 WebKit 里,微信基础库 2.23.1 开始,高性能模式应运而生。

由于游戏逻辑和渲染都跑在一个独立的 WebContent 进程中。因为是进程级的 WebKit,系统把它视为系统 WebKit 进程,因此 JIT 通了。

很好用,性能很不错,能力启用和项目适配的方法也很简单,技术上颇为遥遥领先了一阵。

当然,友商们也很快跟进这个技术,配置方法也惊人的一致,祝贺他们。

性能的提升催生了游戏的重度化——随着游戏越来越重,问题自然也滚滚而来。

由于系统 WebContent 进程完全由苹果控制,在这个进程内,开发商或者平台都完全插不进手。

因此它也就带来了两个主要问题:

  1. 内存更敏感,因此容易OOM导致crash;

  2. WebGL2 的限制;

好消息:系统 WebContent 进程毕竟由微信派生,平台还是可以稍微插手的;

高性能+模式:不只是内存的优化

WebContent 进程的内存限制很严格,我们通常建议开发者将这个进程的内存占用限制在 1.2G 以内,当然越低越好。

但是很难的啦,一个进程既要运行基础库,还得运行游戏逻辑,还要编译 wasm,还要做JIT,还要负责渲染,你跺你也麻。

但是话说回来:渲染通常与业务逻辑是分离的,所以理论上来说,我们可以把渲染的逻辑挪回微信进程!

这样一来,显存就可以挪回微信进程。原本针对一个进程的内存限制,现在分担在微信和WebContent两个进程上。

并且,由于渲染挪回微信进程,而微信内的相关逻辑当然可以由我们自定义。这样一来,WebGL2 的限制也可以顺手解决。

好用,推荐用。

compare

需要注意的点

高性能/高性能+ 模式的内存观测

如果你的游戏开启了高性能/高性能+ 的模式,你大概率会发现后台上报的小游戏内存占用值很低,但与此同时游戏的内存异常退出概率还挺高的?

因为高性能/高性能+ 的模式下,主要的内存瓶颈在WebContent 进程。而现网运行时, WebContent 进程的内存占用无法被直接观测到。

所以没错,此时后台上报的小游戏内存占用值是不准的,它只能上报微信进程内的内存占用。

这里划重点,要考的:

高性能/高性能+ 模式的内存观测,你需要通过xcode、perfdog等工具,定位 WebContent 进程,而非微信进程。

perfdog

顺带一提,WebContent 进程可能有多个,找内存占用最大的那个。

WASM编译内存的占用

广义来说是WASM环境的游戏引擎,狭义来说,瞄准的就是 Unity 和 团结。

简单来说:

高性能/高性能+模式下的 WASM 编译内存是一个很头大的问题。由于 WebContent 进程完全由苹果控制,因此无论是平台还是开发者,都无法收集到 WASM 编译内存占用的详细信息。

但是总体来说,WASM 编译的内存占用主要分为瞬时的内存尖峰和持续性的内存占用。

对 OOM 影响最大的,还是瞬时的内存尖峰。当 WASM 被加载后将开始进行编译,此时会出现一个瞬时的尖峰。当 WASM 太大,尖峰将会非常高,很容易导致 OOM。

顺带一提,iOS 18.3 系统及之后,WASM 的编译的瞬间内存占用高了不少,如果不进行 WASM 分包,则大概率一进入游戏就会 OOM 闪退。

这也意味着 WASM 分包对 Unity 或者 团结的小游戏开发来说,已经变成了一个必须的流程。

再来划一个重点:

因为 WASM 在 iOS 上的编译内存实时占用目前在iOS体系下不好量化,因此不要迷信把所有逻辑都编到WASM中。很多时候解释性脚本(例如HybridCLR、Lua)是必要的。

XHR:高性能+ 模式的阿喀琉斯之踵

在某些时候,你可能会发现高性能+模式比高性能模式更卡顿,运行也更烫。

这倒不是错觉,因为高性能+模式下的渲染逻辑挪回微信进程,因此小游戏在高性能+模式下,多了一份 WebContent 和微信进程进行通信的开销。

这个开销可以通过 XHR 请求的形式来进行观测——在打开开发调试开关后,点击左上角的fps帧率面板,可以切换到 XHR 请求次数和带宽查看面板,可以查看 XHR 请求次数和带宽开销。
XHR

在高性能+模式下,建议将 XHR 次数控制在5以下,完美情况下,这个数字应该被控制在 1 ;而 XHR 带宽开销则建议控制在 500kb 以下。

如果你观察到你的游戏在高性能+模式下发烫、卡顿,在高性能模式下则无这个情况,那大概率问题就在 XHR 请求次数和带宽开销上。

尝试优化你的渲染流程,减少 XHR 请求次数和带宽开销。

锁屏或切到后台,再切回小游戏,提示OOM

这是开发者常见的疑惑,但是这个现象很正常。“很正常”的意思就是:不奇怪。

更明确一些的意思就是:一个系统 WebKit 进程在iOS系统的眼里,本质上就是一个网页。

当它进入被判定为后台进程,那么 iOS 对它的忍受程度会降低,因此 OOM的阈值会低一些。

所以,很正常,无论是开发商还是平台,都没办法解决这个问题。

懒人结论

我是否应该放弃普通模式?

只有你确定你的游戏是休闲游戏,那么建议考虑使用普通模式。

否则只要满足以下的任意一个条件,则建议直接放弃普通模式:

  1. 你的游戏包含大量计算,例如海量割草、复杂物理模拟等;
  2. 你的游戏对帧率、丝滑程度要求比较高;
  3. 你的引擎是 Unity 或者 团结, 并且你的游戏不是超休闲游戏(比休闲游戏还轻度的游戏);

我应该使用高性能还是高性能+模式?

满足以下条件时,建议使用高性能+模式:

  1. 你的游戏需要较大显存(说人话:贴图、模型等资产占比大);
  2. 你的游戏有很酷炫的视觉效果(使用了WebGL2的特性);
  3. 你的游戏的内存压力比较大(例如开启高性能模式后,后台观测到内存异常退出次数明显变高);
  4. 开启高性能+模式后, XHR 请求次数和带宽开销符合标准;

我选择了普通模式后,应该注意什么?

  1. 你应该格外关注帧率和卡顿率;
  2. 你应该关注玩家反馈的发热或者功耗问题;

我使用了高性能模式后,应该注意什么?

  1. 你应该格外关注后台上报的内存异常退出率;
  2. 如果是Unity 或者 团结引擎的小游戏,你要格外关注 WASM 包的大小,务必确保走分包流程;
  3. 绝对要格外关注内存的占用情况;
  4. 要关注你的项目中是否有高级渲染特性的需求;

我选择了高性能模式+后,应该注意什么?

  1. 确保你的贴图、模型等资产是被放到显存中的(例如Unity小游戏 的贴图,需要确保 isReadable 为 false);
  2. 时刻关注你的 XHR 请求次数和带宽开销;
  3. 关注卡顿率和帧率,如果不达预期并且切回高性能模式后变得丝滑,则大概率是 XHR 的问题;
  4. 也要关注 WASM 包的大小,务必确保走分包流程;
I am so cute, please give me money...