mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
101.98k stars 35.33k forks source link

GL Context Lost - preventDefault #11435

Closed leweaver closed 7 years ago

leweaver commented 7 years ago
Description of the problem

The updated WebVR 1.1 specification has provisions for multi-GPU systems, which rely on correct usage of the webglcontextlost and webglcontextrestored events, or, the page not handling the events at all.

Currently three.js handles the event webglcontextlost, calling event.preventDefault to inform the browser that it intends to handle the webglcontextrestored event, yet makes no attempt to rebuild the resources (nor handle that event). Further, resetGLState(), setDefaultGLState() etc should actually occur in a webglcontextrestored.

Until such a time as three.js can confidently handle the webglcontextrestored event (such as building on the method proposed in issue #5507) handling of context lost should be removed entirely - or at the very least, not make a call to event.preventDefault() in the event handler.

From three.js WebGLRenderer.js:538

    function onContextLost( event ) {
        // LW: Remove this until glcontextrestored is handled correctly
        event.preventDefault();

        // LW: Below here should actually be in glcontextrestored
        resetGLState();
        setDefaultGLState();

        properties.clear();
        objects.clear();

    }
Three.js version
Browser
OS
Hardware Requirements (graphics card, VR Device, ...)

Multi GPU system (such as a laptop, or a machine with motherboard integrated graphics and a discrete adapter) VR Headset plugged in to discrete GPU Monitor plugged in to integrated motherboard graphics

toji commented 7 years ago

To expand on what @leweaver said: We've also determined that the next revision of WebVR (which should be the one that sticks around) will have forced context loss as a core concept of using WebGL with a VR device. Pull request here.

The TL;DR version is that contexts used with WebVR devices will need to have a compatibility bit set on them. You can do this two ways. First, with a context creation arg, like so:

let gl = glCanvas.getContext("webgl", { compatibleVrDevice: vrDevice });

This won't cause context loss, but may not be appropriate if you don't know ahead of time that the user will be using a specific VRDevice. The second method is a bit more disruptive:

let gl = glCanvas.getContext("webgl");

// Some time later...
gl.setCompatibleVrDevice(vrDevice).then(() => {
    // If the VRDevice is connected to a different GPU than the one
    // the WebGL context was created with this would have forced
    // a context loss. The promise only resolves when the context
    // has been restored. If the page doesn't handle it the promise
    // is rejected.
});

Obviously the context loss case won't be triggered every single time, and many devices (such as mobile) may never see a context loss. But devices with multiple GPUs (even laptops with an integrated/discreet pair) could hit this path frequently.

Ideally Three.js can provide some mechanisms for dealing with context loss and promote it as something that developers need to pay attention to, otherwise you'll get a lot of devs who test their content on a single-GPU system and say "It Works!" and then wonder why things fail on some systems when their content is released in the wild.

mrdoob commented 7 years ago

Okay. Basic support added. Works in most of the cases but a few things break when restored (sprites, lensflares, etc). I'll be fixing them over the next days.

kenrussell commented 7 years ago

This is great news!

mrdoob commented 7 years ago

Ok, after b317394ef3ebee642cec0e5e38badcc9137ea266, 5e661c2b3022ba7d9313cd2ae2d0976ab3d6cf4d, 79c14c8fd0f01a123306fac15742871636305124 and f6392b404c19e560cff3f04a0f9c70d7df012fe3 this should now be working.

makc commented 7 years ago

@mrdoob let's say I have a cube map that is rendered once and then used somewhere; now if I subscribe to 'webglcontextrestored' on canvas - will I have all the resources ready in order to re-render the cube map? do I also need to render() the scene to handle needUpdate=true stuff 1st? give us tutorial :D

mrdoob commented 7 years ago

Good question. If you setup a jsfiddle I'll adapt it to make it work 😊

makc commented 7 years ago

@mrdoob https://jsfiddle.net/hfj7gm6t/4282/

mrdoob commented 7 years ago

@makc

Basically, the user will have to take care of some things:

renderer.domElement.addEventListener( 'webglcontextrestored', function () {

    cubeCamera.updateCubeMap( renderer, scene );

} );

https://jsfiddle.net/hfj7gm6t/4284/

leweaver commented 7 years ago

Thanks @mrdoob - just tested this out and it is working great!

mrdoob commented 7 years ago

@leweaver 🙌

makc commented 3 years ago

@mrdoob I tried to upgrade the fiddle https://jsfiddle.net/023byLzq/ but it does not seem to work any more 😭

Mugen87 commented 3 years ago

Is this better? https://jsfiddle.net/jfnvaxy7/

makc commented 3 years ago

@Mugen87 not for me, it just freezes on loss and never starts moving again

makc commented 3 years ago

I get this in the console, maybe it is relevant: Screen Shot 2021-08-29 at 16 36 22

Mugen87 commented 3 years ago

What system are you using?

On my Windows 10 notebook with Chrome 92.0.4515.159 I see no console warnings at all.

makc commented 3 years ago

@Mugen87 it used to work 4 years ago on the same system 😭 it is macosx + firefox

makc commented 3 years ago

it used to work 4 years ago

actually... I just tried with r94 that should have been used on July 19-th, 2017, and it also fails, so it must be a browser issue

edit: wrong year 😅 but again does not work any more, so I have to blame ff

makc commented 3 years ago

@Mugen87 I just tried this on win10 machine (specifically, your fiddle fork) and it does work without warnings for me as well (aside from restore handler taking too many ms) BUT, in most of the button clicks results the cube map comes out half-black. This is the same as what I see here in my "broken" firefox if I switch to another tab and then go back to jsfiddle :(

Mugen87 commented 3 years ago

I have tested again and indeed after a few tries it seems only parts of the cubemap are getting rendered. Sometimes the cubemap is completely black. Sometimes it works. 🤔 .

makc commented 3 years ago

@Mugen87 this tweak will probably completely fix it on windows. my mac chrome is happy too.

Mugen87 commented 3 years ago

That is of course the right fix! And it explains the different outcomes of the previous version. Depending on the torus knot's transformation, certain renderings of the cube camera were occluded by the mesh.