Rendering canvas is scaled twice when using EngineViews and hardwareScalingLevel !== 1 (with potential fix)

You can reproduce this simply by adding:

engine.setHardwareScalingLevel(0.5);

to the Multi Views demo at

https://www.babylonjs.com/demos/views/.

(Trying to port this demo to a playground didn’t work great for me as it uses a non-displayed working canvas and the playground got confused when I tried to achieve that in createEngine(). Perhaps this is why the original demo isn’t in a playground? If it is doable I’d love to see how.)

Looking deeper, I see that the rendering canvas (parent) is scaled here:

Then resize(true) is immediately called. This leads to the rendering canvas being scaled again here:

Which means that the rendering canvas has doubled dimensions when compared to the views. I have fixed this is in dev by not scaling the rendering canvas by the hardwareScalingLevel in view rendering, e.g. by doing:

        const width = Math.floor(canvas.clientWidth / this._hardwareScalingLevel);
        const height = Math.floor(canvas.clientHeight / this._hardwareScalingLevel);

        const 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.clientWidth; // Will get scaled later
            parent.height = canvas.clientHeight; // Will get scaled later

Is there a better place to fix this? Is there anything I’m missing? Thanks!

1 Like

Hello and welcome to BJS,
Just for my understanding (the ‘big brains’ of this forum will hopefully kick-in shortly), but what is it you are trying to achieve here?
KR,

Hi @mawa. Thanks for the welcome.

I’m just trying to use multiple views/canvases (via engine.registerView()) along with adaptToDeviceRatio=true with a devicePixelRatio > 1. This combination seems to be broken in general, including for the Multi View demo that I reference.

For quick reference, here’s what the demo looks like if you add engine.setHardwareScalingLevel(0.5) or set adaptToDeviceRatio=true on a device with devicePixelRatio > 1:

It also runs slowly, I assume because there is a translation going on from the render canvas size to the view canvas size.

Well, I’m not a specialist for this part, but for me, the demo does exactly what it’s supposed to do.
Each canvas is set to half of the view size so you see only half of the scene. This behavior seems ok to me. Honestly, I don’t know about setting hardware scaling straight to 0.5. Sounds a bit odd. I only know of things like (i.e.):
engine.setHardwareScalingLevel(1 / window.devicePixelRatio);

May be @RaananW , will be able to explain a bit and give you the solution. Beware though of 4K and mobile. Sry, this is all I can say. I know, it doesn’t help a lot…

No worries. FWIW you can get the same effect with adaptToDeviceRatio=true on a device with devicePixelRatio > 1. I’m pretty sure it’s not desirable behavior to have that setting cause the display to be cropped and the render canvas and view canvas to have different dimensions (not to mention the extra CPU that gets used when those dimensions are different).

I’ll await further feedback!

I also see that @Deltakosh and @Evgeni_Popov have discussed this exact code area in recent months and am curious if they have any thoughts on this. Thanks all!

Yes, I know and I’m still following. Had some issues recently when slowly making a closer approach to mobile and 4K, so I’m kind of interested;) Hope you will get the feedback you are looking for…

1 Like

I will have a look as soon as possible.

1 Like

This PR should fix the problem:

5 Likes

Thanks @Evgeni_Popov, that looks like it should do it. Confirmed locally in dev.