Multiple scenes on different canvas using single engine

Hey guys,

I recently had an issues while creating two unique scenes, on two unique canvases, using the same Babylon engine, following the documentation here:


And so I followed those and it seemed like they were incomplete because it wouldn’t work as described for me. So I looked through many different playgrounds and noticed they don’t work for me as they were built to work.

I’m wondering if there is a bug causing these issues, and is subsequently causing issues for what I’m trying to do. Here are the PGs with bugs:
(the spheres don’t appear for me): New feature: Multi canvases rendering

(the second canvas just re-renders the first canvas): New feature: Multi canvases rendering

I’m basically trying to do something like this:

But with one engine.

The canvas of the engine is not resized to match the size of the current view canvas, that’s why it does not work.

@Deltakosh I can see the code to resize the engine is:

        const dimsChanged =
            canvas.width !== canvas.clientWidth ||
            canvas.height !== canvas.clientHeight;
        if (canvas.clientWidth && canvas.clientHeight && dimsChanged) {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            parent.width = canvas.clientWidth;
            parent.height = canvas.clientHeight;
            this.resize();
        }

Note that the current code does not work because width === clientWidth and height === clientHeight after creation time (maybe the browser implementation of canvas creation changed since this code has been done?).

It’s already something that had been changed from:

        if (canvas.clientWidth && canvas.clientHeight) {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            parent.width = canvas.clientWidth;
            parent.height = canvas.clientHeight;
            this.resize();
        }

I guess this one was not ok because it resized the engine for each view/each frame.

I wonder if the right code would not be:

        const dimsChanged =
            canvas.clientWidth !== parent.width ||
            canvas.clientHeight !== parent.height;
        if (canvas.clientWidth && canvas.clientHeight && dimsChanged) {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            parent.width = canvas.clientWidth;
            parent.height = canvas.clientHeight;
            this.resize(true);
        }

As we render in the canvas of the engine (parent), we only need to resize it if it is not already the size of the current view canvas.

1 Like

I tend to agree. We should try

PR is here:

Note that the size of the canvas should be set after calling registerView and not before, because the function is setting the canvas size with the default canvas size.

This PG should work after the PR is merged: https://playground.babylonjs.com/#LEIZ83#37

@Evgeni_Popov I’m having trouble with this in 4.2.0, although my problem is a bit different.

If I have multiple canvas views on the one page, but each canvas is a different size (inside a different sized container) then the resizing causes flickering that shows the different sized canvases in the one view.

If all canvases are the same size then it’s fine, but that’s not my use case.

@Evgeni_Popov works fine in 5.0.0-alpha.31 so Ignore my comment. :slight_smile: Thanks.

2 Likes

Hi,
I have a scenario similar to “inteja” (canvas of different size, flickering on each others after resizing) and, equally, moving to 5.0.0-alpha.40 the issue is perfectly fixed.
The point is that moving to alpha version the performance seems to drastically decrease compared to 4.2 version… May I ask help in order to investigate the issue on 4.2 version? Do you think that I need to open a new related topic?

PS this is my first comment about babylonjs…great framework, great community ! thanks all!

We will not address the issue in 4.2 as there are way too many things we would need to back port.

Nevertheless I am really scared about the perf regression you are seeing and I would like to address it ASAP. Would you have a repro for it ? Version 5 needs to be at least as fast if not faster than 4.2.

1 Like

@sim_isc thanks a lot in advance for your help investigating the perf issue :slight_smile:

cc @Deltakosh FYI

Agree we need to address that ASAP :slight_smile:

I see, no problem I’ll wait for release of 5.
Sure, I need some time in order to isolate and set up properly the part of code related to babylonjs, I’ll write back ASAP.
of course you are welcome!

Thanks a ton for your help, can not wait to fix those perf regressions.

FWIW I’ve been using 5.0.0-alpha.31 & 5.0.0-alpha.35 and haven’t noticed any performance regressions in our use cases. I know every application is different though.

1 Like

Sure, i completely agree… I did more tests on the main application and I confirm the performance degradation, but, in the meantime, I make a simple test application related only on babylonjs code and no performances degradation is present!
So the point seems to be related on some specific aspects of my application…

In any case, I write some details about my scenario, maybe they could be usefull…

First thing, when I speak about “performance decrease”, I mean an empiric evaluation, I just refer to “CPU usage” of google chrome, in one case (v 4.2) it stays stable in the range of 15-20% in the other it stays fixed to 98-100% (v 4.1 and 5alpha). So I haven’t made any fps measurament for now, but the difference seems to be not marginal and constant in time.

The application is written in angular 12 and is based on a dynamic dashboards structure, with the possibility to insert some widget related to different tasks; the babylonjs related widgets import some .stl file with “SceneLoader.ImportMesh” representing some tools machines and other stuff; after we move some components based on different factors and conditions (external info based on webstomp and other).I’ve attached “application example.png” just to give an idea of the concept.

Related to these widgets there is a dedicated angular service that handles the single babylonjs engine and all the related points as rendering and camera handling as described in the documentation of multi canvas and multi scenes.

The application is based on nx workspace, with the babylonjs code located in a dedicated lib, in attachment the package.json file.
Some critical package beyond babylonjs are:
angular-gridster2: for handling the dashboards structure
ng-dynamic-component: for handling the dynamic creation of the component inside the widget of every dashboards

Now the test! Off course, the code of the application is identical apart from babylonjs version.

  • File App_v42: Application with v 4.2 and very good performance
  • File App_1_v5: Application with v 5 in exactly same condition and poor performance (CPU usage)
  • File App_2_v5: Application with v 5 with “less stressful” condition and quite poor performance (CPU usage)
  • File App_3_v5: Application with v 5 with “less stressful” condition and quite poor performance (CPU usage)
  • File Test_v5: Test application with v 5 of babylonjs; the application reproduce the code of the lib related to babylonjs, with static defined canvas; so WITHOUT handling the dashboards structure and dynamic creation of the widgets. The performances are good with no significant difference compared to V4.2.

So I see that is not easy to understand the motivation of the performances change, it seems related to some specific aspects of the application…if you think that it is worth investigating the point more, I can gladly prepare a small test application with the dashboarding and widget modules integrated with the babylonjs code, in any case it could be interesting for someone…

w000000t, this definitely looks like a huge issue :frowning:

Could you share some perf capture of both so we could see what is eating the CPU ?

1 Like

Here a direct comparison between the two performances snap. It seems that the call “engine.setSize” is repeated lot of times in V5, and of course it takes lot of time…

Made the same analysis on test application based on V5 and with static canvas. In this case the performance are good and, as for V4.2, the call “engine.setSize” is not repeated lot of times. For some reasons, using the dynamic dashboard approach (gridster + ngdynamic) force the lib to call setSize lot of times.

I compare the part of code that check for rising and we have:

V 42:

const dimsChanged =
            canvas.width !== canvas.clientWidth ||
            canvas.height !== canvas.clientHeight;
        if (canvas.clientWidth && canvas.clientHeight && dimsChanged) {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            parent.width = canvas.clientWidth;
            parent.height = canvas.clientHeight;
            this.resize();
        }

V 5:

 var width = Math.floor(canvas.clientWidth / this._hardwareScalingLevel);
        var height = Math.floor(canvas.clientHeight / this._hardwareScalingLevel);
        var dimsChanged = width !== canvas.width || parent.width !== canvas.width ||
            height !== canvas.height || parent.height !== canvas.height;
        if (canvas.clientWidth && canvas.clientHeight && dimsChanged) {
            canvas.width = width;
            canvas.height = height;
            this.setSize(width, height);
        }

Shouldn’t we update the new values of height and width of parent even in V5? ex:

var width = Math.floor(canvas.clientWidth / this._hardwareScalingLevel);
        var height = Math.floor(canvas.clientHeight / this._hardwareScalingLevel);
        var dimsChanged = width !== canvas.width || parent.width !== canvas.width ||
            height !== canvas.height || parent.height !== canvas.height;
        if (canvas.clientWidth && canvas.clientHeight && dimsChanged) {
            canvas.width = width;
            canvas.height = height;
            parent.width = canvas.width ;
            parent.height = canvas.height ;
            this.setSize(width, height);
        }

And so I understand why the test application doesn’t have performance problems… the canvas were all equal! as soon as I change the canvas sizes, forcing them different from each other, the performance problem was present event in the test application with static canvas…

Unfortunately, it looks like it was a bug before as we were considering all renders being identical in sizes.

Now, despite being dead slow, this at least renders in a consistent and expected way. I wonder how we could work around this ?

Maybe @Deltakosh has an idea ? but not sure there is an easy fix here

Discussed with @Deltakosh offline :slight_smile:

I will add a callback you can set to override our default checks so that you could override the default behavior.

But did you not see any issue with the rendering being stretched in 4.2 ?

1 Like

Thank you very much.

yes, I confirm and the callback idea seems very good. I could handle the moment when the canvas are stretched (edit mode, window/widget resizing, etc…) with the default checks and then, in static condition, I force back the checks related to not equal canvas increasing performance.

I mean, I hope it will work but it looks promising

PS I’m going crazy with the “new user” limits for uploading and replying… :laughing: :laughing: :laughing:
ok, now I got trust level :sweat_smile:

I changed the code to allow you after you call register view to override the resize check function.

You ll be able to do:

var view = engine.registerView(targetCanvas);
view.customResize = (canvas) => {
    const dimsChanged =
            canvas.width !== canvas.clientWidth ||
            canvas.height !== canvas.clientHeight;
        if (canvas.clientWidth && canvas.clientHeight && dimsChanged) {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            const parent = engine.getRenderingCanvas();
            parent.width = canvas.clientWidth;
            parent.height = canvas.clientHeight;
            engine.resize();
        }
};
2 Likes