Fix iOS14 bad performance on Browsers/WebView (Solved)

Hi,

I also wanted to raise a forum thread on the English forums about iOS14 bad performance when running on at least web-mobile.

Link to Chinese forum thread:

I think this is a very serious concern now since ios14 was released 16.9.2020 and every game live now is affected by it.

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?

I’ll ask engineering for their thoughts
This is just games running in Safari on mobile?

Could be related to OpenGL performance which is now a second level citizen in iOS, and have been reimplemented through Metal, I think Apple is gradually degrading the support of OpenGL.

That’s just a guess, we are trying to verify

This is affecting all browsers in iOS14. I’ve tested Safari, Chrome, Edge

Oh, That I didn’t know. Thanks for the input, that could make sense. I did some super quick research and saw that they want to deprecate the old openGL and move to use Apple’s Metal like you said. Feels like apple is doing the “headphone-jack” solution again, just get rid of it and let everyone adapt :smiley: :upside_down_face:

What does this mean for the Cocos Engine? Will it be a huge re-work to support?

1 Like

Cocos’s native platform renderer is rewriting for Metal / Vulkan to make it as powerful as possible. But in Browser, every engine can only render upon WebGL standard, which is implemented by the browser. Cosos is not able to hack it. But we are introducing some kind of GPU driven rendering solutions such as GPU particle, GPU instancing, GPU animation… as workaround to the WebGL degradation.

1 Like

yeah, confirmed, our all games faced the same performance issue on iOS 14.

To dev team , we are expected your super HOT FIX, wish you a good luck =)

Thank you!

Hi,

Thank you for your response @jare!
Isn’t it crazy by Apple to degrade openGL when browsers are relying on it and are not supporting their Metal standard?

I’m very glad to hear that you are working on a solution around this and I’m here to assist if there is anything I can help with!

Keep us updated! :slightly_smiling_face:

1 Like

Same problem here.

I have debugged this problem with Safari Dev Tools, and found that after upgrading to iOS 14, the self time of drawElements() usually costs more that 60ms. However before iOS 14, the self time of drawElements() is under 10ms.

Cocos 2dx on iOS 14:

Cocos 2dx Before iOS 14 ↓:

BTW, I also test it with a game that running on Egret engine, and everything goes as well as before.

Egret engine on iOS 14↓

2 Likes

After a day of debugging, we found that the problem lies in the sharing of vb and ib. For the sake of performance optimization, Creator will share the same vb and ib between multiple drawcalls, each drawcall will use an offset to find the rendered data in the shared vb and ib, but after testing, we found that sharing vb and ib will cause serious performance degradation on iOS 14.

So the way of fix is switch vb and ib after committing drawcall.

details:

> 2.2

Customize the engine and manually merge this pr.

If you don’t want to customize the engine you can also override the methods of MeshBuffer manually in the outermost layer of your project script.

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

The 2.1.x version is similar, you still need to override the 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();
        }
    };    
}

However, forwardIndiceStartToOffset is not implemented in 2.1.x, so you need the following additional operations.
Customize engine and find model-batcher.js and replaces the last three lines in the _flush method likes the following code.

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

Customize the engine and use this file mesh-buffer.zip (1.5 KB)
to replace mesh-buffer.js in the engine. Then, do the same process likes the 2.1.x version above.


Note: You need to recompile the engine after customizing the engine in order to take effect. It is recommended to use different phones for full testing after these changes.

6 Likes

@SantyWang What a champ! :star_struck:
I will run test against our game immediately and report back!

Thanks a lot

@SantyWang
I used the Manual overwrite solution for CC 2.3.3

Works like a charm! I did comparions with before and after and this fixes it for me completely. I also did a comparison of iOS14 to iOS13 with this update and the games run smoothly on both versions of iOS.

I’m extremely satisfied with how fast you have been able to help in this matter, since this was such a critical thing for the performance of our games.

A thousand thank you to the whole Cocos Creator team! :heart:

4 Likes

Thanks a lot !

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.