What's missing
IWER emulates XRWebGLLayer (the base, dual-pass layer) but not the WebXR Layers API — there's no XRWebGLBinding.createProjectionLayer({ textureType: 'texture-array' }). So an emulated session can only render dual-pass; the multiview path (one pass, gl_ViewID_OVR, a texture-array framebuffer via OVR_multiview2) is unreachable.
Why it matters
A whole class of XR bugs only shows up under multiview — shader compile errors from gl_ViewID_OVR / GL_OVR_multiview injects, per-eye uniform pressure, etc. Since no WebXR emulator renders multiview, none of it is catchable in headless CI. We just shipped two multiview-only regressions (a material plugin emitting gl_ViewID_OVR into a non-multiview compile, and a skinned mesh silently exceeding the vertex-uniform cap) that our IWER + puppeteer e2e ran straight past — because it ran dual-pass.
The ask
Emulate enough of WebGL multiview to render the path:
XRWebGLBinding + createProjectionLayer({ textureType: 'texture-array' })
- backed by an
OVR_multiview2 framebuffer (framebufferTextureMultiviewOVR, 2 layers)
- wired into
updateRenderState({ layers: [...] }) + the frame loop
Feasible headlessly: SwiftShader (what Playwright/puppeteer ship) exposes OVR_multiview2, so the emulated session can create a real multiview framebuffer and a renderer (Babylon, three, …) will take its multiview path — enough to catch the compile-error class in CI.
Happy to take a crack at a PR if there's interest and a pointer on where the layer/session plumbing should live.
What's missing
IWER emulates
XRWebGLLayer(the base, dual-pass layer) but not the WebXR Layers API — there's noXRWebGLBinding.createProjectionLayer({ textureType: 'texture-array' }). So an emulated session can only render dual-pass; the multiview path (one pass,gl_ViewID_OVR, atexture-arrayframebuffer viaOVR_multiview2) is unreachable.Why it matters
A whole class of XR bugs only shows up under multiview — shader compile errors from
gl_ViewID_OVR/GL_OVR_multiviewinjects, per-eye uniform pressure, etc. Since no WebXR emulator renders multiview, none of it is catchable in headless CI. We just shipped two multiview-only regressions (a material plugin emittinggl_ViewID_OVRinto a non-multiview compile, and a skinned mesh silently exceeding the vertex-uniform cap) that our IWER + puppeteer e2e ran straight past — because it ran dual-pass.The ask
Emulate enough of WebGL multiview to render the path:
XRWebGLBinding+createProjectionLayer({ textureType: 'texture-array' })OVR_multiview2framebuffer (framebufferTextureMultiviewOVR, 2 layers)updateRenderState({ layers: [...] })+ the frame loopFeasible headlessly: SwiftShader (what Playwright/puppeteer ship) exposes
OVR_multiview2, so the emulated session can create a real multiview framebuffer and a renderer (Babylon, three, …) will take its multiview path — enough to catch the compile-error class in CI.Happy to take a crack at a PR if there's interest and a pointer on where the layer/session plumbing should live.