Babylon resizing causing iOS webkit jetsam memory leak event

In my company, we have built 3D configurator using Babylon engine. After the update to iOS 17.x.x we are noticing notorious jetsam event caused by a possible memory leak, when resizing the canvas. The problem seems to be non existing on older ios versions, but I cannot confirm it in 100%. Has anyone spotted a similar issue?

I’ve seen some similar posts from the past and since I’m quite new in the team, I only know that our devs had similar issues in the past and resolved them by limiting the size of the meshes. But after the latest ios update, the problem returned. I wonder if it’s a webkit bug, or maybe we have missed something in the latest app updates on our side.

oh, apple…

Do you experience this when running scenes in the playground as well, or is it only your experience? Are you doing anything special during resize, or just calling engine.resize() ?

In case you call engine.resize on every window.resize event … Did you try throttling?

It’s hard to recreate our scenes in the playground since it’s a 3D configurator. About resize, as for scene, we are doing only engine.resize(). There is of course other logic, but completely not relevant in context of the Babylon engine

Looks like it could totally be a webkit bug :frowning: Could you try to check on their bug tracker.

I am not able to repro in the playground, is it happening for you there as well ?

We have created codepen with our embed. I will try throttling in a second, seems like a good idea.

Added throttling for resize logic, no change, still getting jetsam event.

During the time you are checking with the Safari team on their bug tracker, I wonder if you have any post processes / shadows in place ? if yes could you try to disable them to see how big would the impact be ?

@sebavan we do not use the post process shadows, we do all pre-baked AO Maps and the ground shadow is just a texture with opacity material

Ok so only the canvas is resizing meaning a leak at the canvas level which unfortunately we are not in control of at the Babylon level :frowning:

Noted thanks @sebavan we will stop all dynamic canvas resizing on iOS till safari fix this. We noticed that in 17.1 it was partially fixed in that when we switch from one size to another it’s now ok but with page scroll and bottom bar causing the canvas to change size it’s crashing. Seems like each iOS version they do something fun for us to implement short term fixes for. :disappointed:

Thanks for the guidance we will check back and update this post when we know it’s fixed so others can benefit from this information.

tbf, YES AND I HATE IT !!! :slight_smile:

Hi, we were able to resolve this issue after all. At least in some capacity.

After some debugging we narrowed it down to issue with resize. Since our canvas is responsive we observe both window resize as well as canvas wrapper resize.

What we find out was that whenever ios was hiding bar we were triggering multiple resizes. And it wouldn’t be issue in itself. But there was some issue with resize method itself.

To add some context. We use multiple canvases in our project. However for most of the time only one of them is visible. Also usually all of them have different dimensions. We also use different cameras and effects (mostly for taking screenshots) at some views. In result we cannot use our main canvas as rendering canvas. Instead we use detached canvas. And we add all other canvases as views.

Apparently it came to bite us here. Since we use detached rendering canvas both clientWidth/clientHeight as well as boundingClientRect dimensions are 0’s. In result resize use this._renderingCanvas as source of width and height for each resize. And since this size is multiplied by hardware scaling it’s going up with each resize and call setSize method with bigger and bigger numbers.

We were able to overcome it by patching resize method with similar logic to one used as fallback below

const boundingRect = this._renderingCanvas.getBoundingClientRect
                    ? this._renderingCanvas.getBoundingClientRect()
                    : {
                        // fallback to last solution in case the function doesn't exist
                        width: this._renderingCanvas.width * this._hardwareScalingLevel,
                        height: this._renderingCanvas.height * this._hardwareScalingLevel,

adding multiplication by hardwareScalingLevel to rendering canvas dimensions

width = this._renderingCanvas.clientWidth || boundingRect.width || (this._renderingCanvas.width * this._hardwareScalingLevel) || 100;
height = this._renderingCanvas.clientHeight || boundingRect.height || (this._renderingCanvas.height * this._hardwareScalingLevel) || 100;