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’
clientHeight to be reported as zero.
When these values are zero, thinEngine uses the
height properties to calculate the new
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
I’m using the
height styles to force the
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
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).
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.
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.
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