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 canvas
’ width
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).
Thanks!
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
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.
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?
working on a fix. thanks for the repro