Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGL context must be marked as XR compatible in order to use with an immersive XRSession #20715

Closed
ranbuch opened this issue Nov 19, 2020 · 12 comments · Fixed by #20754
Closed
Labels

Comments

@ranbuch
Copy link
Contributor

ranbuch commented Nov 19, 2020

When calling WebXRManager.setSession( value ) you sometimes might get this message:

Uncaught (in promise) DOMException: Failed to construct 'XRWebGLLayer': WebGL context must be marked as XR compatible in order to use with an immersive XRSession

To Reproduce

I am able to reproduce the issue only on my localhost environment. When serving my viewer from the server it's working fine.

The reason for this issue is a racing condition.

when creating an instance of XRWebGLLayer the WebGL context (gl) should already be XR compatible enabled.

The gl.makeXRCompatible() function returns a Promise.

Current setSession Code

	this.setSession = function ( value ) {

		session = value;

		if ( session !== null ) {

			session.addEventListener( 'select', onSessionEvent );
			session.addEventListener( 'selectstart', onSessionEvent );
			session.addEventListener( 'selectend', onSessionEvent );
			session.addEventListener( 'squeeze', onSessionEvent );
			session.addEventListener( 'squeezestart', onSessionEvent );
			session.addEventListener( 'squeezeend', onSessionEvent );
			session.addEventListener( 'end', onSessionEnd );

			const attributes = gl.getContextAttributes();

			if ( attributes.xrCompatible !== true ) {

				gl.makeXRCompatible();

			}

			const layerInit = {
				antialias: attributes.antialias,
				alpha: attributes.alpha,
				depth: attributes.depth,
				stencil: attributes.stencil,
				framebufferScaleFactor: framebufferScaleFactor
			};

			// eslint-disable-next-line no-undef
			const baseLayer = new XRWebGLLayer( session, gl, layerInit );

			session.updateRenderState( { baseLayer: baseLayer } );

			session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace );

			//

			session.addEventListener( 'inputsourceschange', updateInputSources );

		}

	};

Suggested setSession Code

	this.setSession = function ( value ) {

		session = value;

		if ( session !== null ) {

			session.addEventListener( 'select', onSessionEvent );
			session.addEventListener( 'selectstart', onSessionEvent );
			session.addEventListener( 'selectend', onSessionEvent );
			session.addEventListener( 'squeeze', onSessionEvent );
			session.addEventListener( 'squeezestart', onSessionEvent );
			session.addEventListener( 'squeezeend', onSessionEvent );
			session.addEventListener( 'end', onSessionEnd );

			const attributes = gl.getContextAttributes();

			const setSession = function() {
				const layerInit = {
					antialias: attributes.antialias,
					alpha: attributes.alpha,
					depth: attributes.depth,
					stencil: attributes.stencil,
					framebufferScaleFactor: framebufferScaleFactor
				};
	
				// eslint-disable-next-line no-undef
				const baseLayer = new XRWebGLLayer( session, gl, layerInit );
	
				session.updateRenderState( { baseLayer: baseLayer } );
	
				session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace );
	
				//
	
				session.addEventListener( 'inputsourceschange', updateInputSources );
			};

			if ( attributes.xrCompatible !== true ) {

				gl.makeXRCompatible( setSession );

			} else {

				setSession();

			}

		}

	};

Screenshots
image

Platform:

  • Device: Mobile (Samsung Galaxy S20 Ultra - Exynos variant)
  • OS: Android 11
  • Browser: Chrome Canary 89.0.4329.2
  • Three.js version: dev, r122

I can create a pull request myself, I just remember that the way to do is on this repo isn't so straghtfarward.

Fork dev?

Build and then push?

If someone wants to do it be my guest. If not, please provide a link or explains how to do it right.

Thanks.

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 19, 2020

Does setSession() has to be sync? If it is declared as async, it should be sufficient to change the call to:

await gl.makeXRCompatible();

@ranbuch
Copy link
Contributor Author

ranbuch commented Nov 20, 2020

setSession() is not currently declered as async.

If however you will change setSession() to async you should consider also this change:

	session.addEventListener( 'inputsourceschange', updateInputSources );

	await session.requestReferenceSpace( referenceSpaceType );

	onRequestReferenceSpace();

instead of this:

	session.updateRenderState( { baseLayer: baseLayer } );

	session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace );

	//

	session.addEventListener( 'inputsourceschange', updateInputSources );

XRSession.requestReferenceSpace does return a Promise.

In general, we don't have to detect when the session is ready because we are getting the frame from the WebXRManager.setAnimationLoop callback.

In case it's not ready the frame parameter is falsely.

@mrdoob
Copy link
Owner

mrdoob commented Nov 20, 2020

@toji what do you think?

@sjpt
Copy link

sjpt commented Nov 28, 2020

Is there a significant overhead in makeXRCompatible()?
Why not just call it when the renderer is created and context established?

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 28, 2020

Some context: #19275

@toji
Copy link
Contributor

toji commented Nov 30, 2020

The suggested code change for setSession() looks good. It definitely needs to wait for the makeXRCompatible() call to complete prior to creating the XRWebGLLayer() for correctness.

@toji
Copy link
Contributor

toji commented Nov 30, 2020

Is there a significant overhead in makeXRCompatible()?

It may cause context loss/restore, primarily on multi-GPU devices. That probably qualifies as "significant overhead"

Why not just call it when the renderer is created and context established?

You don't want to do it blindly because it could cause inappropriate GPU switches for non-XR pages that don't require it. I do think that it should be something that can be requested when creating the renderer, though, because if you know the page will be displaying XR content that's the less disruptive path.

@sjpt
Copy link

sjpt commented Nov 30, 2020

Is there a significant overhead in makeXRCompatible()?

It may cause context loss/restore, primarily on multi-GPU devices. That probably qualifies as "significant overhead"

Thank you for the clarification, makes complete sense.

@mat-lo
Copy link

mat-lo commented Dec 2, 2020

I get the same error on all the WebXR AR examples (cones, hittest and paint), I can't run any of them currently.
Pixel 4 XL on Chrome Canary 89.0.4336.0

Uncaught (in promise) DOMException: Failed to construct 'XRWebGLLayer': WebGL context must be marked as XR compatible in order to use with an immersive XRSession
    at WebXRManager.setSession (https://threejs.org/build/three.module.js:22258:22)
    at onSessionStarted (https://threejs.org/examples/jsm/webxr/ARButton.js:16:17)

@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 3, 2020

The first step to solve this issue is to get #20761 in. After that we can use async/await syntax in #20754.

@jpryne
Copy link
Contributor

jpryne commented Jan 21, 2021

Just upgraded to Chrome 88.0.4324.96 & I'm facing this issue starting a WebXR session in 𝚕̶𝚘̶𝚌̶𝚊̶𝚕̶𝚑̶𝚘̶𝚜̶𝚝̶ testing. Windows 10 64, Oculus Rift CV1

Correction: This is broken when loading the examples from the server too. As of updating to Chrome 88, I can no longer view ThreeJS-VR in Chrome. I can however still enter WebXR at https://immersive-web.github.io/webxr-samples/

ThreeJS Examples are working fine in Microsoft Edge.

@Mugen87
Copy link
Collaborator

Mugen87 commented Jan 21, 2021

@jpryne Please read #21126 for more details.

trusktr added a commit to lume/lume that referenced this issue Jun 3, 2021
At the moment it has an error loading the WebXR context for some reason. The error is like this one in the Three.js issue tracker: mrdoob/three.js#20715
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants