From 66fe70f80eb378fc9d5e9422815a05cb8a043165 Mon Sep 17 00:00:00 2001 From: Gareth Morgan Date: Sun, 26 Apr 2026 22:11:49 -0400 Subject: [PATCH 1/2] Add lint config files --- deps/cloudxr/webxr_client/.prettierignore | 7 ++ deps/cloudxr/webxr_client/.prettierrc | 9 +++ deps/cloudxr/webxr_client/eslint.config.js | 85 ++++++++++++++++++++++ deps/cloudxr/webxr_client/package.json | 15 +++- 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 deps/cloudxr/webxr_client/.prettierignore create mode 100644 deps/cloudxr/webxr_client/.prettierrc create mode 100644 deps/cloudxr/webxr_client/eslint.config.js diff --git a/deps/cloudxr/webxr_client/.prettierignore b/deps/cloudxr/webxr_client/.prettierignore new file mode 100644 index 000000000..02e8ae473 --- /dev/null +++ b/deps/cloudxr/webxr_client/.prettierignore @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +node_modules +build +dist +**/*.tgz +package-lock.json diff --git a/deps/cloudxr/webxr_client/.prettierrc b/deps/cloudxr/webxr_client/.prettierrc new file mode 100644 index 000000000..4fe916cd6 --- /dev/null +++ b/deps/cloudxr/webxr_client/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100, + "arrowParens": "avoid", + "endOfLine": "auto" +} diff --git a/deps/cloudxr/webxr_client/eslint.config.js b/deps/cloudxr/webxr_client/eslint.config.js new file mode 100644 index 000000000..06902de73 --- /dev/null +++ b/deps/cloudxr/webxr_client/eslint.config.js @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +// Minimal ESLint flat config derived from /gitlab/cloudxr-js (examples / React client rules). +// Use CommonJS so Node can load without ESM; paths are relative to this package root. +const typescriptEslint = require('@typescript-eslint/eslint-plugin'); +const typescriptParser = require('@typescript-eslint/parser'); +const reactPlugin = require('eslint-plugin-react'); +const reactHooksPlugin = require('eslint-plugin-react-hooks'); +const simpleImportSort = require('eslint-plugin-simple-import-sort'); + +module.exports = [ + { + linterOptions: { + reportUnusedDisableDirectives: true, + }, + }, + { + ignores: ['node_modules/**', 'build/**', 'dist/**'], + }, + { + files: ['src/**/*.{ts,tsx}', 'helpers/**/*.ts'], + languageOptions: { + parser: typescriptParser, + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + '@typescript-eslint': typescriptEslint, + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + 'simple-import-sort': simpleImportSort, + }, + rules: { + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', + 'react/jsx-uses-react': 'off', + 'react/jsx-uses-vars': 'error', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'no-console': ['warn', { allow: ['info', 'warn', 'error', 'debug'] }], + // `import/order` from eslint-plugin-import does not reliably --fix + // "empty line within import group" (see App.tsx + @helpers + TeleopProjects). + // simple-import-sort rewrites the block and fixes that on `eslint --fix`. + 'simple-import-sort/imports': [ + 'error', + { + groups: [ + // Side effect imports + ['^\\u0000'], + // `node:` + ['^node:'], + // External: not `../` and not the `@helpers/` alias + ['^(?!\\.|@helpers/)'], + // Internal alias + ['^@helpers/'], + // Relative + ['^\\.'], + ], + }, + ], + 'simple-import-sort/exports': 'error', + }, + settings: { + react: { + version: 'detect', + }, + }, + }, +]; diff --git a/deps/cloudxr/webxr_client/package.json b/deps/cloudxr/webxr_client/package.json index 48df5c991..c58fe6846 100644 --- a/deps/cloudxr/webxr_client/package.json +++ b/deps/cloudxr/webxr_client/package.json @@ -17,7 +17,13 @@ "dev": "webpack --config ./webpack.dev.js", "dev-server": "webpack serve --config ./webpack.dev.js --no-open", "dev-server:https": "cross-env HTTPS=true webpack serve --config ./webpack.dev.js --no-open", - "clean": "rimraf dist" + "clean": "rimraf dist", + "lint:check": "eslint \"src/**/*.{ts,tsx}\" \"helpers/**/*.ts\" --ignore-pattern \"**/build/**\" --ignore-pattern \"**/node_modules/**\" --ignore-pattern \"**/dist/**\" --quiet", + "lint": "eslint \"src/**/*.{ts,tsx}\" \"helpers/**/*.ts\" --ignore-pattern \"**/build/**\" --ignore-pattern \"**/node_modules/**\" --ignore-pattern \"**/dist/**\" --quiet --fix", + "prettier:check": "prettier --check \"src/**/*.{ts,tsx,html,css,json,md}\" \"helpers/**/*.ts\" \"webpack.*.js\" \"eslint.config.js\" \"*.{js,json,md}\"", + "prettier": "prettier --write \"src/**/*.{ts,tsx,html,css,json,md}\" \"helpers/**/*.ts\" \"webpack.*.js\" \"eslint.config.js\" \"*.{js,json,md}\"", + "format:check": "npm run lint:check && npm run prettier:check", + "format": "npm run lint && npm run prettier" }, "dependencies": { "@nvidia/cloudxr": "file:../nvidia-cloudxr-6.1.0.tgz", @@ -37,12 +43,19 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/three": "^0.172.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "@webxr-input-profiles/assets": "1.0.19", "copy-webpack-plugin": "^13.0.0", "cross-env": "^10.1.0", "css-loader": "^6.8.1", + "eslint": "9.27.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-simple-import-sort": "^12.1.1", "html-webpack-plugin": "^5.6.3", "iwer": "^2.2.1", + "prettier": "3.5.3", "rimraf": "^5.0.5", "style-loader": "^3.3.3", "ts-loader": "^9.5.1", From 26aa7d565a2ee7f02406c60493cda382c23e3edc Mon Sep 17 00:00:00 2001 From: Gareth Morgan Date: Sun, 26 Apr 2026 22:13:31 -0400 Subject: [PATCH 2/2] Run format --- .../webxr_client/helpers/WebGLStateApply.ts | 46 +- .../webxr_client/helpers/WebGLStateBinding.ts | 2 +- .../webxr_client/helpers/controlChannel.ts | 18 +- deps/cloudxr/webxr_client/src/App.tsx | 39 +- deps/cloudxr/webxr_client/src/CloudXR2DUI.tsx | 29 +- deps/cloudxr/webxr_client/src/CloudXRUI.tsx | 8 +- deps/cloudxr/webxr_client/src/index.html | 1591 +++++++++-------- 7 files changed, 936 insertions(+), 797 deletions(-) diff --git a/deps/cloudxr/webxr_client/helpers/WebGLStateApply.ts b/deps/cloudxr/webxr_client/helpers/WebGLStateApply.ts index 5ca26da74..dbc26ce6c 100644 --- a/deps/cloudxr/webxr_client/helpers/WebGLStateApply.ts +++ b/deps/cloudxr/webxr_client/helpers/WebGLStateApply.ts @@ -16,38 +16,38 @@ */ import { - WebGLState, + GL_MAX_COLOR_ATTACHMENTS, + GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, + GL_MAX_UNIFORM_BUFFER_BINDINGS, + GL_MAX_VERTEX_ATTRIBS, + GLUndefined, + isDefined, + WebGLBlendState, WebGLBufferState, - WebGLTextureState, - WebGLTextureUnitState, - WebGLProgramState, - WebGLFramebufferState, - WebGLVertexArrayState, - WebGLVertexAttribState, WebGLCapabilityState, - WebGLViewportState, WebGLClearState, - WebGLBlendState, - WebGLDepthState, - WebGLStencilState, WebGLColorState, WebGLCullingState, + WebGLDepthState, + WebGLFramebufferState, + WebGLIndexedBufferBinding, WebGLLineState, - WebGLPolygonOffsetState, - WebGLSampleState, WebGLPixelStoreState, - WebGLTransformFeedbackState, + WebGLPolygonOffsetState, + WebGLProgramState, + WebGLQueryState, WebGLRenderbufferState, WebGLSamplerState, - WebGLQueryState, - WebGLIndexedBufferBinding, - GL_MAX_VERTEX_ATTRIBS, - GL_MAX_UNIFORM_BUFFER_BINDINGS, - GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, - GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, - GL_MAX_COLOR_ATTACHMENTS, - GLUndefined, - isDefined, + WebGLSampleState, + WebGLState, + WebGLStencilState, + WebGLTextureState, + WebGLTextureUnitState, + WebGLTransformFeedbackState, + WebGLVertexArrayState, + WebGLVertexAttribState, + WebGLViewportState, } from './WebGLState'; /** diff --git a/deps/cloudxr/webxr_client/helpers/WebGLStateBinding.ts b/deps/cloudxr/webxr_client/helpers/WebGLStateBinding.ts index 5e8a19aa8..4be208a9a 100644 --- a/deps/cloudxr/webxr_client/helpers/WebGLStateBinding.ts +++ b/deps/cloudxr/webxr_client/helpers/WebGLStateBinding.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { WebGLStateTracker, WebGLState } from './WebGLState'; +import { WebGLState, WebGLStateTracker } from './WebGLState'; import { apply } from './WebGLStateApply'; /** diff --git a/deps/cloudxr/webxr_client/helpers/controlChannel.ts b/deps/cloudxr/webxr_client/helpers/controlChannel.ts index 7062ebb52..533419670 100644 --- a/deps/cloudxr/webxr_client/helpers/controlChannel.ts +++ b/deps/cloudxr/webxr_client/helpers/controlChannel.ts @@ -102,11 +102,7 @@ export class HeadsetControlChannel { ws = new WebSocket(this.opts.url); } catch (err) { if (this.disposed) return; - console.warn( - '[ControlChannel] WebSocket constructor failed for', - this.opts.url, - err - ); + console.warn('[ControlChannel] WebSocket constructor failed for', this.opts.url, err); this.ws = null; this._afterSocketClosed(); return; @@ -129,7 +125,7 @@ export class HeadsetControlChannel { this._startMetricsTimer(); }; - ws.onmessage = (ev) => { + ws.onmessage = ev => { if (typeof ev.data !== 'string') return; let msg: { type?: string; payload?: unknown }; try { @@ -169,17 +165,11 @@ export class HeadsetControlChannel { if (type === 'hello') { // hello to headset includes initial config - if ( - payload.config != null && - typeof payload.configVersion === 'number' - ) { + if (payload.config != null && typeof payload.configVersion === 'number') { this.opts.onConfig(payload.config as StreamConfig, payload.configVersion as number); } } else if (type === 'config') { - if ( - payload.config != null && - typeof payload.configVersion === 'number' - ) { + if (payload.config != null && typeof payload.configVersion === 'number') { this.opts.onConfig(payload.config as StreamConfig, payload.configVersion as number); } } else if (type === 'error') { diff --git a/deps/cloudxr/webxr_client/src/App.tsx b/deps/cloudxr/webxr_client/src/App.tsx index 5beda2619..f459c634a 100644 --- a/deps/cloudxr/webxr_client/src/App.tsx +++ b/deps/cloudxr/webxr_client/src/App.tsx @@ -30,7 +30,18 @@ * and disconnect when in XR mode. */ +import * as CloudXR from '@nvidia/cloudxr'; +import { getResolutionValidationError } from '@nvidia/cloudxr'; +import { computed, signal } from '@preact/signals-react'; +import { Canvas } from '@react-three/fiber'; +import { setPreferredColorScheme } from '@react-three/uikit'; +import { createXRStore, noEvents, PointerEvents, useXR, XR, XROrigin } from '@react-three/xr'; +import type { XRDevice } from 'iwer'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { v5 } from 'uuid'; + import { checkCapabilities } from '@helpers/BrowserCapabilities'; +import { HeadsetControlChannel } from '@helpers/controlChannel'; import { getDeviceProfile, resolveDeviceProfileId } from '@helpers/DeviceProfiles'; import { loadIWERIfNeeded } from '@helpers/LoadIWER'; import { overridePressureObserver } from '@helpers/overridePressureObserver'; @@ -38,19 +49,9 @@ import { kPerformanceOptions } from '@helpers/PerformanceProfiles'; import CloudXRComponent from '@helpers/react/CloudXRComponent'; import { SimpleEnvironment } from '@helpers/react/SimpleEnvironment'; import { getControlPanelPositionVector } from '@helpers/react/utils'; -import * as CloudXR from '@nvidia/cloudxr'; -import { getResolutionValidationError } from '@nvidia/cloudxr'; -import { signal, computed } from '@preact/signals-react'; -import { Canvas } from '@react-three/fiber'; -import { setPreferredColorScheme } from '@react-three/uikit'; -import { XR, createXRStore, noEvents, PointerEvents, XROrigin, useXR } from '@react-three/xr'; -import type { XRDevice } from 'iwer'; -import { useState, useMemo, useEffect, useRef } from 'react'; -import { v5 } from 'uuid'; import { CloudXR2DUI } from './CloudXR2DUI'; import CloudXR3DUI from './CloudXRUI'; -import { HeadsetControlChannel } from '@helpers/controlChannel'; // Performance metrics signals - raw numeric data, one per callback cadence. // Signals update their value without triggering React re-renders. @@ -83,7 +84,6 @@ overridePressureObserver(); setPreferredColorScheme('dark'); - const TELEOP_CHANNEL_UUID: Uint8Array = v5('teleop_command', v5.DNS, new Uint8Array(16)); type AvailableChannel = CloudXR.Session['availableMessageChannels'][number]; @@ -118,8 +118,7 @@ function buildOobHubWsUrlFromQuery(searchParams: URLSearchParams): string | null const portStr = searchParams.get('port')?.trim(); if (!serverIP || portStr === undefined || portStr === '') return null; if (!/^\d{1,5}$/.test(portStr)) return null; - const host = - serverIP.includes(':') && !serverIP.startsWith('[') ? `[${serverIP}]` : serverIP; + const host = serverIP.includes(':') && !serverIP.startsWith('[') ? `[${serverIP}]` : serverIP; return `wss://${host}:${portStr}/oob/v1/ws`; } @@ -337,10 +336,7 @@ function App() { ui.initialize(Object.keys(urlSeeds).length > 0 ? urlSeeds : undefined); const doConnect = async () => { const config = ui.getConfiguration(); - const resolutionError = getResolutionValidationError( - config.perEyeWidth, - config.perEyeHeight - ); + const resolutionError = getResolutionValidationError(config.perEyeWidth, config.perEyeHeight); if (resolutionError) { ui.updateConnectButtonState(); return; @@ -560,7 +556,6 @@ function App() { }, 1000); }; - const handleResetTeleop = async () => { console.info('Reset Teleop pressed'); @@ -674,7 +669,9 @@ function App() { }); channel.connect(); - return () => { channel.dispose(); }; + return () => { + channel.dispose(); + }; }, [cloudXR2DUI]); // Countdown configuration handlers (0-5 seconds) @@ -735,9 +732,7 @@ function App() { const uuidHex = Array.from(ch.uuid as Uint8Array) .map((b: number) => b.toString(16).padStart(2, '0')) .join(''); - console.info( - ` [${i}] uuid=${uuidHex} status=${ch.status}` - ); + console.info(` [${i}] uuid=${uuidHex} status=${ch.status}`); }); const channel = findChannelByUuid(channels, TELEOP_CHANNEL_UUID); diff --git a/deps/cloudxr/webxr_client/src/CloudXR2DUI.tsx b/deps/cloudxr/webxr_client/src/CloudXR2DUI.tsx index 367fe6942..76fa205d3 100644 --- a/deps/cloudxr/webxr_client/src/CloudXR2DUI.tsx +++ b/deps/cloudxr/webxr_client/src/CloudXR2DUI.tsx @@ -32,9 +32,18 @@ */ import { + getGridValidationError, + getGridValidationMessageForConnect, + getResolutionValidationError, + getResolutionValidationMessageForConnect, + validateDepthReprojectionGrid, + validatePerEyeResolution, +} from '@nvidia/cloudxr'; + +import { + type DeviceProfileId, getDeviceProfile, resolveDeviceProfileId, - type DeviceProfileId, } from '@helpers/DeviceProfiles'; import { ControlPanelPosition, @@ -49,14 +58,6 @@ import { setSelectValueIfAvailable, setupCertificateAcceptanceLink, } from '@helpers/utils'; -import { - getGridValidationError, - getGridValidationMessageForConnect, - getResolutionValidationError, - getResolutionValidationMessageForConnect, - validateDepthReprojectionGrid, - validatePerEyeResolution, -} from '@nvidia/cloudxr'; /** Full config: CloudXR connection settings + React UI options. */ type AppConfig = CloudXRConfig & ReactUIConfig; @@ -215,7 +216,11 @@ export class CloudXR2DUI { const v = seeds[paramKey]; if (v === undefined) continue; element.value = v; - try { localStorage.setItem(storageKey, v); } catch (_) { /* quota */ } + try { + localStorage.setItem(storageKey, v); + } catch (_) { + /* quota */ + } } } @@ -537,9 +542,7 @@ export class CloudXR2DUI { reprojectionGridCols, reprojectionGridRows ); - const combinedConnectMessage = [connectMessage, gridConnectMessage] - .filter(Boolean) - .join('\n'); + const combinedConnectMessage = [connectMessage, gridConnectMessage].filter(Boolean).join('\n'); if (combinedConnectMessage) { this.validationMessageText.textContent = combinedConnectMessage; this.validationMessageBox.className = 'validation-message-box show'; diff --git a/deps/cloudxr/webxr_client/src/CloudXRUI.tsx b/deps/cloudxr/webxr_client/src/CloudXRUI.tsx index 1579ca6ea..4ef0989d4 100644 --- a/deps/cloudxr/webxr_client/src/CloudXRUI.tsx +++ b/deps/cloudxr/webxr_client/src/CloudXRUI.tsx @@ -35,16 +35,17 @@ * back to the parent component through callback props. */ -import { useXRButton } from '@helpers/react/useXRButton'; import { ReadonlySignal } from '@preact/signals-react'; import { useFrame } from '@react-three/fiber'; import { Handle, HandleTarget } from '@react-three/handle'; -import { Container, Text, Image } from '@react-three/uikit'; +import { Container, Image, Text } from '@react-three/uikit'; import { Button } from '@react-three/uikit-default'; -import React, { useRef, useState, useEffect } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Color, Euler, Group, Mesh, MeshStandardMaterial, Quaternion, Vector3 } from 'three'; import { damp } from 'three/src/math/MathUtils.js'; +import { useXRButton } from '@helpers/react/useXRButton'; + // Face-camera rotation constants const FACE_CAMERA_DAMPING = 10; // Higher = faster rotation toward camera @@ -495,7 +496,6 @@ export default function CloudXR3DUI({ Minimize on play (compact controls) - {/* Right Column - Controls */} diff --git a/deps/cloudxr/webxr_client/src/index.html b/deps/cloudxr/webxr_client/src/index.html index 6d469fa5f..c74569d1a 100644 --- a/deps/cloudxr/webxr_client/src/index.html +++ b/deps/cloudxr/webxr_client/src/index.html @@ -1,4 +1,4 @@ - + - - - - - - + + + + + NVIDIA Isaac Teleop Web Client - + - + - +
-
-
-

NVIDIA Isaac Teleop Web Client

-
- -
- - -
-

Debug Settings

- -
- - - -
- Load recommended defaults for a target device. You can still override settings below. -
-
-
- -
- - -
- Show or hide WebXR controller model meshes in XR. Input remains active either way. -
-
- -
- - -
- Where the in-XR control panel appears when you enter the session: left, center, or right. -
-
- -
- - -
- If you hide the control panel, only a small control stays visible so you can show it again. -
-
- -
- - - -
- -
-
- -
- - - - - -
- Configure WebRTC media server address and port for NAT traversal. Only needed when the streaming server is behind NAT. Leave empty to use server-provided ICE information. -
-
- -
- - - -
- Select the target device frame rate for the XR session -
-
- -
- - - -
- Select the maximum streaming bitrate (in Megabits per second) for the XR session -
-
- -
- - -
- Select the preferred streaming codec. Availability depends on browser and server support. -
-
- -
- - - -
- - -
-
- Configure the per-eye resolution. Width must be a multiple of 16 (min 128); height must be a multiple of 64 (min 128). -
- - -
- - -
-
- Grid rules: leave either field blank to use factor mode, or set both to integers >= 2 for explicit mesh resolution. -
-
- -
- - -
- Select the preferred reference space for XR tracking. "Auto" uses fallback logic (local-floor → local → viewer). Other options will attempt to use the specified space only. -
- - - - - - - -
- Configure the XR reference space offset in centimeters. These values will be applied to the reference space transform to adjust the origin position of the XR experience. -
-
- -
- - -
- Enable or disable secondary smoothing on predicted positions to reduce jitter. This only affects position, not orientation. -
-
- -
- - -
- Scale the pose prediction horizon (0.0 = no prediction, 1.0 = full prediction). This multiplier affects both position and orientation prediction strength. -
-
- -
- - -
- Enable texSubImage2D for texture updates. Improves performance on Meta Quest devices but may cause issues on headsets with older Chromium forks. -
-
- -
- - -
- Enable Display P3 color space workaround. Recommended for Quest 3 Browser. -
-
-
-
+
+
+

NVIDIA Isaac Teleop Web Client

+
+ +
+ + +
+

Debug Settings

+ +
+ + + +
+ Load recommended defaults for a target device. You can still override settings below. +
+
+
+ +
+ + +
+ Show or hide WebXR controller model meshes in XR. Input remains active either way. +
+
+ +
+ + +
+ Where the in-XR control panel appears when you enter the session: left, center, or + right. +
+
+ +
+ + +
+ If you hide the control panel, only a small control stays visible so you can show it + again. +
+
+ +
+ + + +
+ +
+
+ +
+ + + + + +
+ Configure WebRTC media server address and port for NAT traversal. Only needed when the + streaming server is behind NAT. Leave empty to use server-provided ICE information. +
+
+ +
+ + + +
Select the target device frame rate for the XR session
+
+ +
+ + + +
+ Select the maximum streaming bitrate (in Megabits per second) for the XR session +
+
+ +
+ + +
+ Select the preferred streaming codec. Availability depends on browser and server + support. +
+
+ +
+ + + +
+ + +
+
+ Configure the per-eye resolution. Width must be a multiple of 16 (min 128); height + must be a multiple of 64 (min 128). +
+ + +
+ + +
+
+ Grid rules: leave either field blank to use factor mode, or set both to integers >= 2 + for explicit mesh resolution. +
+
+ +
+ + +
+ Select the preferred reference space for XR tracking. "Auto" uses fallback logic + (local-floor → local → viewer). Other options will attempt to use the specified space + only. +
+ + + + + + + +
+ Configure the XR reference space offset in centimeters. These values will be applied + to the reference space transform to adjust the origin position of the XR experience. +
+
+ +
+ + +
+ Enable or disable secondary smoothing on predicted positions to reduce jitter. This + only affects position, not orientation. +
+
+ +
+ + +
+ Scale the pose prediction horizon (0.0 = no prediction, 1.0 = full prediction). This + multiplier affects both position and orientation prediction strength. +
+
+ +
+ + +
+ Enable texSubImage2D for texture updates. Improves performance on Meta Quest devices + but may cause issues on headsets with older Chromium forks. +
+
+ +
+ + +
+ Enable Display P3 color space workaround. Recommended for Quest 3 Browser. +
+
+
+
- - +