针对 iOS 14 Web 平台性能变差的优化方案

  • Creator 版本:2.0.10

  • 目标平台:iOS 14beta 6 Safari

  • 重现方式:Sprite + Label 超過10個

  • 之前哪个版本是正常的:iOS 13

  • 手机型号: iPhone SE2

  • 手机浏览器: Safari

  • 重现概率: 100%

iOS 13 版本 FPS 還可以維持在 60
iOS 14 beta 就 40以下了
Frame Time 都超過 20ms

不知道有沒有同伴出現一樣的問題, 可以協助一下
不然就要等 iOS 14 Release 版本了


Jare 补充:感谢反馈,已在 15 楼给出解决方案

1赞

更新到 iOS14 beta 7 FPS 有提升了
不過還是比原本 iOS13 少了 5~6 幀

图呢????:3:

我在Beta 7版本中运行 example cases的项目时候仍然有很低帧数的情况。

遇到同样的问题,@jare

Creator 2.2.2 官方范例直接在iOS14上看 也是一样的情况

@jare

Hi @1111833 @jare,

I can confirm that the performance is way worse in iOS14 (even the released version) compared to iOS13.
I’ve debugged that it is mostly about the drawcalls. With iPhone 11 Pro and iOS13 you can easily run 150-200 drawcalls with any FPS drops (having full 60FPS), but running 200 drawcalls with the same phone on iOS14 you are down to about 5FPS.

Even getting down drawcalls in our games to around 20, it still has slightly worse performance than iOS13 with 150-200 drawcalls.

Could this be something in the engine causing this or some issues in the operating system? What do you think?

我们项目也遇到了,升级了ios14的手机掉帧严重。。。

1赞

I’m experiencing my FPS drops in web-mobile. Not sure about other platforms.

ios14修改了JavaScriptcore模块,问题很多,耐心等待吧

相同的问题。

我使用Safari 的dev tools跟踪了一下timeline,发现cocos在iOS 14下 draw()里面的 drawElements() 耗时会达到 60ms。造成js每一帧都非常长。

而iOS14 之前这个数值都是在10ms以内。

此外我测试了使用白鹭引擎的游戏,发现在iOS 14下,白鹭引擎一切正常。drawElements()耗时都在10ms以下。两者都使用的是webgl,而不是webgl2。

1赞

经过我们一天的调试,目前发现这个问题的症结在 vb 和 ib 的共享上, Creator 为了优化性能,多个drawcall 之间是会共享同一份 vb 和 ib 的,每个 drawcall 使用一个偏移值在共享的 vb 和 ib 中找到本次渲染的数据,但是经过我们测试后发现,共享 vb 和 ib 会导致在 iOS 14 上性能下降非常严重。

所以修复方式就是,在提交 drawcall 之后,切换 vb 和 ib

解决方案:

2.2 版本以上:

自定义引擎并手动合并此 pr :

https://github.com/cocos-creator/engine/pull/7415

如果不想自定义引擎的话你也可以在项目脚本最外层手动覆盖这个类型中的方法。

const isIOS14Device = cc.sys.os === cc.sys.OS_IOS && cc.sys.isBrowser && cc.sys.isMobile && /iPhone OS 14/.test(window.navigator.userAgent);
if (isIOS14Device) {
    cc.MeshBuffer.prototype.checkAndSwitchBuffer = function (vertexCount) {
        if (this.vertexOffset + vertexCount > 65535) {
            this.uploadData();
            this._batcher._flush();
        }
    };     
    cc.MeshBuffer.prototype.forwardIndiceStartToOffset = function () {
        this.uploadData();
        this.switchBuffer();
    }  
}

2.1.x 版本

2.1.x 版本原理类似,你依旧需要覆盖 checkAndSwitchBuffer

const isIOS14Device = cc.sys.os === cc.sys.OS_IOS && cc.sys.isBrowser && cc.sys.isMobile && /iPhone OS 14/.test(window.navigator.userAgent);
if (isIOS14Device) {
    cc.MeshBuffer.prototype.checkAndSwitchBuffer = function (vertexCount) {
        if (this.vertexOffset + vertexCount > 65535) {
            this.uploadData();
            this._batcher._flush();
        }
    };    
}

但 2.1.x 中没有实现 forwardIndiceStartToOffset,所以你需要以下额外操作:
自定义引擎找到 model-batcher.js,将 _flush 方法中的最后三行改为:

const isIOS14Device = cc.sys.os === cc.sys.OS_IOS && cc.sys.isBrowser && cc.sys.isMobile && /iPhone OS 14/.test(window.navigator.userAgent);

_flush () {
        let material = this.material,
            buffer = this._buffer,
            indiceStart = buffer.indiceStart,
            indiceOffset = buffer.indiceOffset,
            indiceCount = indiceOffset - indiceStart;
        if (!this.walking || !material || indiceCount <= 0) {
            return;
        }

        let effect = material.effect;
        if (!effect) return;

        // Generate ia
        let ia = this._iaPool.add();
        ia._vertexBuffer = buffer._vb;
        ia._indexBuffer = buffer._ib;
        ia._start = indiceStart;
        ia._count = indiceCount;
        
        // Generate model
        let model = this._modelPool.add();
        this._batchedModels.push(model);
        model.sortKey = this._sortKey++;
        model._cullingMask = this.cullingMask;
        model.setNode(this.node);
        model.setEffect(effect, this.customProperties);
        model.setInputAssembler(ia);
        
        this._renderScene.addModel(model);

        if (isIOS14Device) {
              buffer.uploadData();
              buffer.switchBuffer();
        }
        else {
              buffer.byteStart = buffer.byteOffset;
              buffer.indiceStart = buffer.indiceOffset;
              buffer.vertexStart = buffer.vertexOffset;
        }
  },

2.0.x 版本

自定义引擎,并用此文件mesh-buffer.zip (1.5 KB)覆盖引擎中的 mesh-buffer.js. 然后,使用和 2.1.x 版本相同的改动方式修改。


注意: 自定义引擎后需要重新编译引擎才能生效。建议合并后,使用不同手机进行全面测试。

9赞

@EndEvil
Awesome work!!

I will immediately start testing your solution on top of our games and report back!

2赞

We also replied in English forum

1赞

Works like a charm! I only tested the above 2.2 version on 2.3.3 verison of CC. I did comparions with before and after and compared iOS14 to iOS13 with this update and it is completely compareable.

I am extremely thankful for your swift action here and finding out this in such a short notice!
Full 5 stars :star: :star: :star: :star: :star: from me for the whole Cocos Creator team!

1.x版本呢

1赞

同问下大佬,1.0的解决方案

1.x 没这个问题

可以,经过修改,我们项目比之前流畅了:joy: