Unexpected mutation with Engine.registerView(..)

There is an immediate workaround, but the current behavior is one that could be considered suboptimal.

The multiview feature introduced in version 4.1 is very exciting, and I’ve begun to write an API around it.

If I create a canvas:

<canvas id='renderCanvas' width='500' height='500' />

for later rendering, and then proceed to create a ‘working’ canvas to start the Engine, and later register the renderCanvas as view, roughly as follows:

const workCanvas = document.createElement('canvas') 
const canvas = document.getElementById('renderCanvas');
const engine = new B.Engine(workCanvas, true)

const [camera, scene] = createScene(canvas, engine)
engine.inputElement = canvas
let view = engine.registerView(canvas, camera)

You can assume createScene includes all needed setup code, as per the online example.

Then, to my surprise, the canvas that originally had a width and height of 500 is mutated within registerView and now contains a height and width of 0, which crashes the program.

This can then be traced to lines 66-70 here, where the current canvas attributes are used to mutate the canvas-to-be-registered attributes. As in my example, I only created the element, the width and height are set to 0. A ‘working’ canvas is created just like this within the online example, but there it seems the CSS ensures that the width and height is not 0 when the mutation step occurs. My code is not relying on CSS here, and may often rely on external methods, like React, for setting width/height. I imagine this will be similar for many users down the road.

A simple fix is to follow up the function call with:

canvas.setAttribute('width', '500')
canvas.setAttribute('height', '500')

Once I made these changes, the code functioned as expected.

I’m concerned that this is going to lead to multiple difficulties down the road, especially if the canvas needs to resize.

Can the code be modified so that if the canvas-to-be-registered has an existing width and height, it is not unexpectedly mutated? I did not submit an issue on GitHub as I cannot follow any of the existing protocols, (i.e., unsure how to create a playground demonstrating this), and it suggests to post on the forum as well. This seemed a natural way to get a conversation going.


This is great you use the forum first for this.

@Deltakosh who did the registerView might have more insights on why and he is coming back next Monday, so we could double check with him what the best approach would be ?

Great. I’ll be sure to use the forum again then if GitHub doesn’t seem appropriate. That will likely be the vast majority of the time.

Awesome. Yes, thank you. We can begin analysis Monday, or whenever next available.

1 Like

The idea behind that code is to align the size of the canvases to avoid perf issues when we will blit from main canvas to child canvases (if they are not of the same size the system will have to rescale and it is expensive).

I could add a warning maybe to tell the user that the main canvas must have a dimension or we can also test if main canvas has a size before copying it. I tend to think plan b seems the better here


Ah, interesting. I see. I am glad I asked about this then. Thank you for the explanation.

I agree, checking for existence of size (or that it’s nonzero) seems the cleanest implementation.

A follow-up question: is it a significant performance hit? Can this roughly be understood without performance testing? My interest in this latest development is to have several canvases distributed throughout a web page with a single Engine controlling them. These canvases will nearly always have different sizes. I thought I’d do something like having an IntersectionObserver check which one was in view and then registerView as appropriate.

If this second question becomes too in-depth, I’ll open another thread so we can document the best-practice for future users who may search for this.

Thanks again.

You mean, if we scale between different canvas sizes? It is not a big problem most of the time so you could definitely live with it. But from the framework standpoint we need to be the fastest by default :wink:

1 Like

Okay, great. So I’ll proceed with that idea.

I fully agree! And I’m glad BabylonJS adopts this philosophy. We should start with peak performance, even if initially restrictive, and enable possibly performance-worsening customizability thereafter. It is easy to customize in this case via the two follow-up canvas.setAttribute calls.