javascriptasynchronouscallbackwebxr

Why is the callback provided to `XRSession.requestAnimationFrame()` not getting called?


Here is a minimal example which reproduces a problem I have encountered in a more complex situation:

let session;

const dummyRenderLoop = (time, frame) => {
    if (!session) 
      return;

    console.log("Render callback");
    
    // Continue loop
    session.requestAnimationFrame(dummyRenderLoop);
}

window.addEventListener("load", async () => {
    if (!await navigator.xr.isSessionSupported("immersive-vr")) 
      return;
    
    // Obtain a WebXR session
    session = await navigator.xr.requestSession("immersive-vr");
    if (!session) 
      return;
    
    // Session creation succeeded
    console.log("Have an XR session");
    
    session.onend = () => {
      console.log("End session");
      session = undefined;
    };
    
    // Initiate animation loop
    session.requestAnimationFrame(dummyRenderLoop);
});

I've tried this in Chrome 129 with both a WebXR emulator and a real VR headset. In console, I get the output Have an XR session, but never Render callback. According to MDN, this usage pattern should be fine. So why is the animation frame callback never being called?


Solution

  • An XRSession will not call the animation frame callback if it has not been correctly configured to render. To configure the session correctly, you need to:

    const canvas = document.createElement("canvas");
    const gl     = canvas.getContext("webgl2", { xrCompatible: true });
    
    const glLayer = new XRWebGLLayer(session, gl);
    
    session.updateRenderState({ baseLayer: glLayer });
    

    Now the animation frame callback will be run. You don't even have to actually render anything!

    MDN has further information about XRSession.updateRenderState.