Engine resize issue with adaptToDeviceRatio when canvas clientWidth/clientHeight are 0

Hi Team,

I ran into an interesting issue this week where our application was losing the WebGL context occasionally when we called engine.resize(). Turns out, this was happening when we had some html elements layered on top of our babylon canvas causing the canvas’ clientWidth and clientHeight to be reported as zero.

When these values are zero, thinEngine uses the canvaswidth and height properties to calculate the new width and height, adjusting it by a factor of the devicePixelRatio. Actually, now every time resize is called, the canvas’ width and height will keep increasing. On an iOS device, this kills the webgl context after a certain point.

I’ve included a playground that reproduces the exploding canvas size. In chrome, you’ll need to use mobile mode with DPR set to something above 1. Press ‘r’ to start the problem and you may want to set a breakpoint to prevent completely out of control canvas size increase as the playground calls resize every frame (perhaps another bug there ? although the playground may just not support adaptToDeviceRatio).

I’m using the width and height styles to force the clientWidth and clientHeight to zero, but there may be other cases in which this happens. In our application, we were covering the canvas with a “modal” component with some more complex styling.

We’ve put in a workaround for now to not call resize() when our canvas’ clientWidth is 0, but I thought it would be good to see if there’s a solution on the babylon side. My initial thought was that it doesn’t make sense to apply the _hardwareScalingLevel when this._renderingCanvas.width is used, but I don’t fully understand the use cases for all the branches here.

There’s another topic I found here for which this could be the root cause as well. After context loss we were also seeing a page refresh on iOS, which can be misdiagnosed as a memory issue (actually this is what we initially thought as well).


1 Like

I’m not an HTML expert and I don’t really understand the code that handles the pixel ratio specifically, let’s ask @RaananW who wrote it.

Well, that makes perfect sense :slight_smile:

When we change the width/height properties, we actually trigger another resize, and this will run recursively.
clientWidth will be 0 if you use an inline element. Is that your case?
I just want to be able to reproduce your usecase. I do have the playground (thank you for that), but that might really be happening because of the line you pasted (which technically fits our playground usecase, but could be a bug under certain circumstances)

It’s a similar case, we have a ResizeObserver observing the canvas element that calls engine.resize() so we can respond to orientation changes. For some UX, we’re hiding our canvas temporarily using display:none on a parent div which causes the canvas’ clientWidth to be 0. The observer was triggered when the display:none css was applied, engine.resize() was called, then the canvas` size was growing by the DPR. On certain devices, that single resize was killing the WebGL context.

1 Like

I believe I found a solution to the issue - Use getBoundingClientRect instead of width by RaananW · Pull Request #13830 · BabylonJS/Babylon.js (github.com)

We could have used the bounding rect exclusively, but I prefer not to cause any irregularities. Taking the (defined) width and height is wrong when devicePixelRatio (or hardware scaling rate) is not 1.

1 Like

This pr will cause a problem. For example, I create a canvas. I just use this canvas to create a screenshot. I don’t need to insert it into the document. At this time, the data obtained by getBoundingClientRect is 0, so an error will occur when resizes , it turns out that the data is obtained from canvas.width, so no error occurs.

cc @RaananW

Thanks! Is it already causing a problem? If i udnerstand correctly - a resize on a canvas that is not attached to the DOM will not work as expected? I am not sure about the error though.

We can check if the canvas is attached to the document object before making the computation. Would that solve the issue?

1 Like

working on a fix. thanks for the repro

Create snapshot of a scene not using the main canvas fails by RaananW · Pull Request #13925 · BabylonJS/Babylon.js (github.com)