11import {
22 Color ,
3+ DEVICETYPE_WEBGL2 ,
34 Entity ,
45 Quat ,
56 Vec3 ,
7+ XrManager ,
68 type CameraComponent
79} from 'playcanvas' ;
810import { XrControllers } from 'playcanvas/scripts/esm/xr-controllers.mjs' ;
@@ -14,21 +16,31 @@ import { Global } from './types';
1416const initXr = ( global : Global ) => {
1517 const { app, events, state, camera, renderer } = global ;
1618
17- state . hasAR = app . xr . isAvailable ( 'immersive-ar' ) ;
18- state . hasVR = app . xr . isAvailable ( 'immersive-vr' ) ;
19-
20- // initialize ar/vr
21- app . xr . on ( 'available:immersive-ar' , ( available ) => {
22- state . hasAR = available ;
23- } ) ;
24- app . xr . on ( 'available:immersive-vr' , ( available ) => {
25- state . hasVR = available ;
26- } ) ;
19+ // Engine availability is backend-aware (2.20+): under WebGPU a session is only
20+ // reported available when it can start on the current device (browser exposes
21+ // XRGPUBinding, e.g. Safari on Apple Vision Pro). A session the WebGPU device
22+ // can't host may still run after reloading into WebGL, so keep the buttons
23+ // visible then — the UI offers that reload when the session can't start directly.
24+ let webglAR = false ;
25+ let webglVR = false ;
26+
27+ const updateAvailable = ( ) => {
28+ state . hasAR = app . xr . isAvailable ( 'immersive-ar' ) || webglAR ;
29+ state . hasVR = app . xr . isAvailable ( 'immersive-vr' ) || webglVR ;
30+ } ;
2731
28- // XR sessions require a WebGL device; under WebGPU we only expose availability so
29- // the UI can offer to reload the viewer in WebGL mode.
30- if ( renderer !== 'webgl' ) {
31- return ;
32+ updateAvailable ( ) ;
33+ app . xr . on ( 'available' , updateAvailable ) ;
34+
35+ if ( renderer === 'webgpu' ) {
36+ Promise . all ( [
37+ XrManager . isDeviceSupported ( DEVICETYPE_WEBGL2 , 'immersive-ar' ) ,
38+ XrManager . isDeviceSupported ( DEVICETYPE_WEBGL2 , 'immersive-vr' )
39+ ] ) . then ( ( [ ar , vr ] ) => {
40+ webglAR = ar ;
41+ webglVR = vr ;
42+ updateAvailable ( ) ;
43+ } ) ;
3244 }
3345
3446 const parent = camera . parent as Entity ;
0 commit comments