@plasius/gpu-xr 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +28 -0
- package/dist/index.cjs +225 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +218 -8
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/src/index.d.ts +60 -0
- package/src/index.js +274 -7
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,44 @@ All notable changes to this project will be documented in this file.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
- **Added**
|
|
14
|
+
- (placeholder)
|
|
15
|
+
|
|
16
|
+
- **Changed**
|
|
17
|
+
- (placeholder)
|
|
18
|
+
|
|
19
|
+
- **Fixed**
|
|
20
|
+
- (placeholder)
|
|
21
|
+
|
|
22
|
+
- **Security**
|
|
23
|
+
- (placeholder)
|
|
24
|
+
|
|
25
|
+
## [0.1.7] - 2026-03-14
|
|
26
|
+
|
|
27
|
+
- **Added**
|
|
28
|
+
- Added XR frame-rate capability helpers for current, supported, and
|
|
29
|
+
updatable runtime targets.
|
|
30
|
+
- Added XR performance hints that align immersive sessions with `render` /
|
|
31
|
+
`dag` worker-budget metadata for adaptive performance.
|
|
32
|
+
- Added XR manager methods and store state for observing and updating active
|
|
33
|
+
session frame targets.
|
|
34
|
+
- Added ADR, TDR, and design docs for XR adaptive-performance integration.
|
|
35
|
+
|
|
36
|
+
- **Changed**
|
|
37
|
+
- Clarified README guidance for negotiated XR frame targets and worker-budget
|
|
38
|
+
alignment.
|
|
39
|
+
- Updated GitHub Actions workflows to run JavaScript actions on Node 24,
|
|
40
|
+
refreshed core workflow action versions, and switched Codecov uploads to
|
|
41
|
+
the Codecov CLI.
|
|
42
|
+
|
|
43
|
+
- **Fixed**
|
|
44
|
+
- (placeholder)
|
|
45
|
+
|
|
46
|
+
- **Security**
|
|
47
|
+
- (placeholder)
|
|
48
|
+
|
|
49
|
+
## [0.1.6] - 2026-03-04
|
|
50
|
+
|
|
13
51
|
- **Added**
|
|
14
52
|
- (placeholder)
|
|
15
53
|
|
|
@@ -65,3 +103,5 @@ All notable changes to this project will be documented in this file.
|
|
|
65
103
|
- (placeholder)
|
|
66
104
|
[0.1.1]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.1
|
|
67
105
|
[0.1.2]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.2
|
|
106
|
+
[0.1.6]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.6
|
|
107
|
+
[0.1.7]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.7
|
package/README.md
CHANGED
|
@@ -39,14 +39,39 @@ const session = await xr.enterVr({
|
|
|
39
39
|
await xr.exitSession();
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Adaptive Performance Integration
|
|
43
|
+
|
|
44
|
+
`@plasius/gpu-xr` now exposes XR runtime frame-rate hints so
|
|
45
|
+
`@plasius/gpu-performance` can negotiate platform-native frame targets while
|
|
46
|
+
`@plasius/gpu-renderer` and `@plasius/gpu-worker` stay aligned on the `xr`
|
|
47
|
+
worker budget profile.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import { createXrManager } from "@plasius/gpu-xr";
|
|
51
|
+
|
|
52
|
+
const xr = createXrManager();
|
|
53
|
+
await xr.enterVr();
|
|
54
|
+
|
|
55
|
+
const hint = xr.getPerformanceHint({ preferredFrameRates: [90, 72] });
|
|
56
|
+
console.log(hint.targetFrameRate, hint.workerBudget);
|
|
57
|
+
|
|
58
|
+
await xr.setTargetFrameRate(hint.targetFrameRate);
|
|
59
|
+
```
|
|
60
|
+
|
|
42
61
|
## API
|
|
43
62
|
|
|
44
63
|
- `isXrModeSupported(mode, options)`
|
|
45
64
|
- `requestXrSession(options)`
|
|
65
|
+
- `readXrFrameRateCapabilities(session, options)`
|
|
66
|
+
- `createXrPerformanceHint(options)`
|
|
67
|
+
- `updateXrTargetFrameRate(session, frameRate)`
|
|
46
68
|
- `createXrStore(initialState)`
|
|
47
69
|
- `createXrManager(options)`
|
|
48
70
|
- `mergeXrSessionInit(base, override)`
|
|
49
71
|
- `defaultVrSessionInit`
|
|
72
|
+
- `xrWorkerQueueClass`
|
|
73
|
+
- `xrWorkerSchedulerMode`
|
|
74
|
+
- `defaultXrWorkerBudgetProfile`
|
|
50
75
|
|
|
51
76
|
## Demo
|
|
52
77
|
|
|
@@ -72,5 +97,8 @@ npm run pack:check
|
|
|
72
97
|
## Files
|
|
73
98
|
|
|
74
99
|
- `src/index.js`: XR runtime/session manager and store.
|
|
100
|
+
- `src/index.d.ts`: public API typings.
|
|
75
101
|
- `tests/package.test.js`: Unit tests for support probing and lifecycle handling.
|
|
76
102
|
- `docs/adrs/*`: XR architecture decisions.
|
|
103
|
+
- `docs/tdrs/*`: XR technical direction records.
|
|
104
|
+
- `docs/design/*`: XR integration design notes.
|
package/dist/index.cjs
CHANGED
|
@@ -20,19 +20,30 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
22
|
createXrManager: () => createXrManager,
|
|
23
|
+
createXrPerformanceHint: () => createXrPerformanceHint,
|
|
23
24
|
createXrStore: () => createXrStore,
|
|
24
25
|
defaultVrSessionInit: () => defaultVrSessionInit,
|
|
26
|
+
defaultXrWorkerBudgetProfile: () => defaultXrWorkerBudgetProfile,
|
|
25
27
|
isXrModeSupported: () => isXrModeSupported,
|
|
26
28
|
mergeXrSessionInit: () => mergeXrSessionInit,
|
|
29
|
+
readXrFrameRateCapabilities: () => readXrFrameRateCapabilities,
|
|
27
30
|
requestXrSession: () => requestXrSession,
|
|
31
|
+
updateXrTargetFrameRate: () => updateXrTargetFrameRate,
|
|
28
32
|
xrReferenceSpaceTypes: () => xrReferenceSpaceTypes,
|
|
29
|
-
xrSessionModes: () => xrSessionModes
|
|
33
|
+
xrSessionModes: () => xrSessionModes,
|
|
34
|
+
xrWorkerQueueClass: () => xrWorkerQueueClass,
|
|
35
|
+
xrWorkerSchedulerMode: () => xrWorkerSchedulerMode
|
|
30
36
|
});
|
|
31
37
|
module.exports = __toCommonJS(index_exports);
|
|
32
38
|
var DEFAULT_VR_SESSION_INIT = Object.freeze({
|
|
33
39
|
requiredFeatures: ["local-floor"],
|
|
34
40
|
optionalFeatures: ["bounded-floor", "hand-tracking", "layers"]
|
|
35
41
|
});
|
|
42
|
+
var DEFAULT_MODE_FRAME_RATES = Object.freeze({
|
|
43
|
+
inline: 60,
|
|
44
|
+
"immersive-vr": 90,
|
|
45
|
+
"immersive-ar": 72
|
|
46
|
+
});
|
|
36
47
|
var xrSessionModes = Object.freeze([
|
|
37
48
|
"inline",
|
|
38
49
|
"immersive-vr",
|
|
@@ -45,6 +56,9 @@ var xrReferenceSpaceTypes = Object.freeze([
|
|
|
45
56
|
"bounded-floor",
|
|
46
57
|
"unbounded"
|
|
47
58
|
]);
|
|
59
|
+
var xrWorkerQueueClass = "render";
|
|
60
|
+
var xrWorkerSchedulerMode = "dag";
|
|
61
|
+
var defaultXrWorkerBudgetProfile = "xr";
|
|
48
62
|
function toStringArray(values) {
|
|
49
63
|
if (!Array.isArray(values)) {
|
|
50
64
|
return [];
|
|
@@ -54,6 +68,31 @@ function toStringArray(values) {
|
|
|
54
68
|
function dedupeStrings(values) {
|
|
55
69
|
return [...new Set(toStringArray(values))];
|
|
56
70
|
}
|
|
71
|
+
function readPositiveNumber(value) {
|
|
72
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
73
|
+
}
|
|
74
|
+
function normalizeFrameRates(values) {
|
|
75
|
+
if (!values || typeof values === "string") {
|
|
76
|
+
return Object.freeze([]);
|
|
77
|
+
}
|
|
78
|
+
let collected;
|
|
79
|
+
try {
|
|
80
|
+
collected = Array.from(values);
|
|
81
|
+
} catch {
|
|
82
|
+
return Object.freeze([]);
|
|
83
|
+
}
|
|
84
|
+
return Object.freeze(
|
|
85
|
+
[...new Set(collected.map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0))].sort(
|
|
86
|
+
(left, right) => right - left
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
function getDefaultFrameRateForMode(mode) {
|
|
91
|
+
return DEFAULT_MODE_FRAME_RATES[mode] ?? DEFAULT_MODE_FRAME_RATES["immersive-vr"];
|
|
92
|
+
}
|
|
93
|
+
function getWorkerBudgetProfileForMode(mode) {
|
|
94
|
+
return mode === "inline" ? "realtime" : defaultXrWorkerBudgetProfile;
|
|
95
|
+
}
|
|
57
96
|
function readNavigator(navigatorOverride) {
|
|
58
97
|
const currentNavigator = navigatorOverride ?? globalThis.navigator;
|
|
59
98
|
if (!currentNavigator || typeof currentNavigator !== "object") {
|
|
@@ -137,6 +176,101 @@ async function requestXrSession(options = {}) {
|
|
|
137
176
|
const init = mergeXrSessionInit(baseSessionInit, sessionInit);
|
|
138
177
|
return xr.requestSession(mode, init);
|
|
139
178
|
}
|
|
179
|
+
function readXrFrameRateCapabilities(session, options = {}) {
|
|
180
|
+
const {
|
|
181
|
+
mode = "immersive-vr",
|
|
182
|
+
fallbackFrameRates = [],
|
|
183
|
+
defaultFrameRate
|
|
184
|
+
} = options;
|
|
185
|
+
assertSessionMode(mode);
|
|
186
|
+
const sessionFrameRate = readPositiveNumber(session?.frameRate);
|
|
187
|
+
const supportedFrameRates = normalizeFrameRates(session?.supportedFrameRates);
|
|
188
|
+
const fallbackRates = normalizeFrameRates(fallbackFrameRates);
|
|
189
|
+
const mergedSupported = supportedFrameRates.length ? [...supportedFrameRates] : [...fallbackRates];
|
|
190
|
+
if (sessionFrameRate && !mergedSupported.includes(sessionFrameRate)) {
|
|
191
|
+
mergedSupported.push(sessionFrameRate);
|
|
192
|
+
mergedSupported.sort((left, right) => right - left);
|
|
193
|
+
}
|
|
194
|
+
const refreshRateHz = sessionFrameRate ?? mergedSupported[0] ?? readPositiveNumber(defaultFrameRate) ?? getDefaultFrameRateForMode(mode);
|
|
195
|
+
return Object.freeze({
|
|
196
|
+
mode,
|
|
197
|
+
currentFrameRate: sessionFrameRate,
|
|
198
|
+
supportedFrameRates: Object.freeze(mergedSupported),
|
|
199
|
+
refreshRateHz,
|
|
200
|
+
canUpdateTargetFrameRate: Boolean(session) && typeof session.updateTargetFrameRate === "function"
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function createXrPerformanceHint(options = {}) {
|
|
204
|
+
const {
|
|
205
|
+
session = null,
|
|
206
|
+
mode = "immersive-vr",
|
|
207
|
+
preferredFrameRates = [],
|
|
208
|
+
fallbackFrameRates = [],
|
|
209
|
+
defaultFrameRate
|
|
210
|
+
} = options;
|
|
211
|
+
const capabilities = readXrFrameRateCapabilities(session, {
|
|
212
|
+
mode,
|
|
213
|
+
fallbackFrameRates,
|
|
214
|
+
defaultFrameRate
|
|
215
|
+
});
|
|
216
|
+
const filteredPreferredFrameRates = normalizeFrameRates(preferredFrameRates).filter(
|
|
217
|
+
(frameRate) => capabilities.supportedFrameRates.length === 0 || capabilities.supportedFrameRates.includes(frameRate)
|
|
218
|
+
);
|
|
219
|
+
const derivedPreferredFrameRates = filteredPreferredFrameRates.length ? filteredPreferredFrameRates : capabilities.supportedFrameRates.length ? capabilities.supportedFrameRates : Object.freeze([capabilities.refreshRateHz]);
|
|
220
|
+
const targetFrameRate = derivedPreferredFrameRates[0] ?? capabilities.refreshRateHz;
|
|
221
|
+
const rationale = [];
|
|
222
|
+
if (capabilities.currentFrameRate) {
|
|
223
|
+
rationale.push(
|
|
224
|
+
`XR session reports a current frame rate of ${capabilities.currentFrameRate}Hz.`
|
|
225
|
+
);
|
|
226
|
+
} else {
|
|
227
|
+
rationale.push("XR session does not expose a current frame rate; using defaults.");
|
|
228
|
+
}
|
|
229
|
+
if (capabilities.supportedFrameRates.length) {
|
|
230
|
+
rationale.push(
|
|
231
|
+
`XR runtime exposes supported frame rates: ${capabilities.supportedFrameRates.join(", ")}Hz.`
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
rationale.push("XR runtime does not expose supported frame rates; using fallback targets.");
|
|
235
|
+
}
|
|
236
|
+
if (filteredPreferredFrameRates.length) {
|
|
237
|
+
rationale.push("Preferred XR frame rates were filtered against runtime-supported values.");
|
|
238
|
+
} else {
|
|
239
|
+
rationale.push("XR target frame rate defaults to the highest available runtime target.");
|
|
240
|
+
}
|
|
241
|
+
return Object.freeze({
|
|
242
|
+
...capabilities,
|
|
243
|
+
preferredFrameRates: Object.freeze([...derivedPreferredFrameRates]),
|
|
244
|
+
targetFrameRate,
|
|
245
|
+
targetFrameTimeMs: 1e3 / targetFrameRate,
|
|
246
|
+
workerBudget: Object.freeze({
|
|
247
|
+
queueClass: xrWorkerQueueClass,
|
|
248
|
+
schedulerMode: xrWorkerSchedulerMode,
|
|
249
|
+
profile: getWorkerBudgetProfileForMode(mode)
|
|
250
|
+
}),
|
|
251
|
+
rationale: Object.freeze(rationale)
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async function updateXrTargetFrameRate(session, frameRate) {
|
|
255
|
+
if (!session || typeof session !== "object") {
|
|
256
|
+
throw new Error("XR session is required to update target frame rate.");
|
|
257
|
+
}
|
|
258
|
+
const requestedFrameRate = readPositiveNumber(frameRate);
|
|
259
|
+
if (!requestedFrameRate) {
|
|
260
|
+
throw new Error("XR target frame rate must be a finite number greater than zero.");
|
|
261
|
+
}
|
|
262
|
+
if (typeof session.updateTargetFrameRate !== "function") {
|
|
263
|
+
throw new Error("XR session does not support updateTargetFrameRate(frameRate).");
|
|
264
|
+
}
|
|
265
|
+
const supportedFrameRates = normalizeFrameRates(session.supportedFrameRates);
|
|
266
|
+
if (supportedFrameRates.length > 0 && !supportedFrameRates.includes(requestedFrameRate)) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`XR target frame rate ${requestedFrameRate}Hz is not supported by the active session.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
await session.updateTargetFrameRate(requestedFrameRate);
|
|
272
|
+
return requestedFrameRate;
|
|
273
|
+
}
|
|
140
274
|
function createXrStore(initialState = {}) {
|
|
141
275
|
const listeners = /* @__PURE__ */ new Set();
|
|
142
276
|
let state = {
|
|
@@ -145,6 +279,11 @@ function createXrStore(initialState = {}) {
|
|
|
145
279
|
isEntering: false,
|
|
146
280
|
lastError: null,
|
|
147
281
|
supportedModes: {},
|
|
282
|
+
currentFrameRate: null,
|
|
283
|
+
targetFrameRate: null,
|
|
284
|
+
supportedFrameRates: [],
|
|
285
|
+
canUpdateTargetFrameRate: false,
|
|
286
|
+
workerBudgetProfile: null,
|
|
148
287
|
...initialState
|
|
149
288
|
};
|
|
150
289
|
const notify = () => {
|
|
@@ -176,6 +315,11 @@ function createXrStore(initialState = {}) {
|
|
|
176
315
|
isEntering: false,
|
|
177
316
|
lastError: null,
|
|
178
317
|
supportedModes: {},
|
|
318
|
+
currentFrameRate: null,
|
|
319
|
+
targetFrameRate: null,
|
|
320
|
+
supportedFrameRates: [],
|
|
321
|
+
canUpdateTargetFrameRate: false,
|
|
322
|
+
workerBudgetProfile: null,
|
|
179
323
|
...initialState
|
|
180
324
|
};
|
|
181
325
|
notify();
|
|
@@ -205,7 +349,12 @@ function createXrManager(options = {}) {
|
|
|
205
349
|
store.set({
|
|
206
350
|
activeSession: null,
|
|
207
351
|
mode: null,
|
|
208
|
-
isEntering: false
|
|
352
|
+
isEntering: false,
|
|
353
|
+
currentFrameRate: null,
|
|
354
|
+
targetFrameRate: null,
|
|
355
|
+
supportedFrameRates: [],
|
|
356
|
+
canUpdateTargetFrameRate: false,
|
|
357
|
+
workerBudgetProfile: null
|
|
209
358
|
});
|
|
210
359
|
if (typeof onSessionEnd === "function") {
|
|
211
360
|
onSessionEnd();
|
|
@@ -220,6 +369,46 @@ function createXrManager(options = {}) {
|
|
|
220
369
|
};
|
|
221
370
|
const getState = () => store.getSnapshot();
|
|
222
371
|
const subscribe = (listener) => store.subscribe(listener);
|
|
372
|
+
const getFrameRateCapabilities = (options2 = {}) => {
|
|
373
|
+
const state = store.getSnapshot();
|
|
374
|
+
return readXrFrameRateCapabilities(
|
|
375
|
+
options2.session ?? state.activeSession,
|
|
376
|
+
{
|
|
377
|
+
mode: options2.mode ?? state.mode ?? defaultMode,
|
|
378
|
+
fallbackFrameRates: options2.fallbackFrameRates ?? state.supportedFrameRates,
|
|
379
|
+
defaultFrameRate: options2.defaultFrameRate ?? state.targetFrameRate ?? state.currentFrameRate ?? void 0
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
};
|
|
383
|
+
const getPerformanceHint = (options2 = {}) => {
|
|
384
|
+
const state = store.getSnapshot();
|
|
385
|
+
return createXrPerformanceHint({
|
|
386
|
+
session: options2.session ?? state.activeSession,
|
|
387
|
+
mode: options2.mode ?? state.mode ?? defaultMode,
|
|
388
|
+
preferredFrameRates: options2.preferredFrameRates ?? (state.targetFrameRate ? [state.targetFrameRate] : []),
|
|
389
|
+
fallbackFrameRates: options2.fallbackFrameRates ?? state.supportedFrameRates,
|
|
390
|
+
defaultFrameRate: options2.defaultFrameRate ?? state.targetFrameRate ?? state.currentFrameRate ?? void 0
|
|
391
|
+
});
|
|
392
|
+
};
|
|
393
|
+
const syncSessionPerformanceState = (session, mode, preferredFrameRates = []) => {
|
|
394
|
+
const hint = createXrPerformanceHint({
|
|
395
|
+
session,
|
|
396
|
+
mode,
|
|
397
|
+
preferredFrameRates
|
|
398
|
+
});
|
|
399
|
+
store.set({
|
|
400
|
+
activeSession: session,
|
|
401
|
+
mode,
|
|
402
|
+
isEntering: false,
|
|
403
|
+
lastError: null,
|
|
404
|
+
currentFrameRate: hint.currentFrameRate,
|
|
405
|
+
targetFrameRate: hint.targetFrameRate,
|
|
406
|
+
supportedFrameRates: hint.supportedFrameRates,
|
|
407
|
+
canUpdateTargetFrameRate: hint.canUpdateTargetFrameRate,
|
|
408
|
+
workerBudgetProfile: hint.workerBudget.profile
|
|
409
|
+
});
|
|
410
|
+
return hint;
|
|
411
|
+
};
|
|
223
412
|
const probeSupport = async (modes = [defaultMode]) => {
|
|
224
413
|
const supportedModes = {};
|
|
225
414
|
for (const mode of modes) {
|
|
@@ -246,12 +435,7 @@ function createXrManager(options = {}) {
|
|
|
246
435
|
navigator: navigatorOverride
|
|
247
436
|
});
|
|
248
437
|
attachSessionEndHandler(session);
|
|
249
|
-
|
|
250
|
-
activeSession: session,
|
|
251
|
-
mode,
|
|
252
|
-
isEntering: false,
|
|
253
|
-
lastError: null
|
|
254
|
-
});
|
|
438
|
+
syncSessionPerformanceState(session, mode);
|
|
255
439
|
if (typeof onSessionStart === "function") {
|
|
256
440
|
onSessionStart(session, mode);
|
|
257
441
|
}
|
|
@@ -268,6 +452,29 @@ function createXrManager(options = {}) {
|
|
|
268
452
|
const enterVr = async (sessionInit = {}) => {
|
|
269
453
|
return enterSession("immersive-vr", sessionInit);
|
|
270
454
|
};
|
|
455
|
+
const setTargetFrameRate = async (frameRate) => {
|
|
456
|
+
const state = store.getSnapshot();
|
|
457
|
+
const activeSession = state.activeSession;
|
|
458
|
+
if (!activeSession) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
"Cannot update XR target frame rate without an active XR session."
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
const appliedFrameRate = await updateXrTargetFrameRate(
|
|
465
|
+
activeSession,
|
|
466
|
+
frameRate
|
|
467
|
+
);
|
|
468
|
+
syncSessionPerformanceState(activeSession, state.mode ?? defaultMode, [
|
|
469
|
+
appliedFrameRate
|
|
470
|
+
]);
|
|
471
|
+
return appliedFrameRate;
|
|
472
|
+
} catch (error) {
|
|
473
|
+
const message = error instanceof Error ? error.message : String(error ?? "Unknown XR error");
|
|
474
|
+
store.set({ lastError: message });
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
271
478
|
const exitSession = async () => {
|
|
272
479
|
const { activeSession } = store.getSnapshot();
|
|
273
480
|
if (!activeSession) {
|
|
@@ -291,8 +498,11 @@ function createXrManager(options = {}) {
|
|
|
291
498
|
getState,
|
|
292
499
|
subscribe,
|
|
293
500
|
probeSupport,
|
|
501
|
+
getFrameRateCapabilities,
|
|
502
|
+
getPerformanceHint,
|
|
294
503
|
enterSession,
|
|
295
504
|
enterVr,
|
|
505
|
+
setTargetFrameRate,
|
|
296
506
|
exitSession,
|
|
297
507
|
dispose
|
|
298
508
|
};
|
|
@@ -301,12 +511,18 @@ var defaultVrSessionInit = DEFAULT_VR_SESSION_INIT;
|
|
|
301
511
|
// Annotate the CommonJS export names for ESM import in node:
|
|
302
512
|
0 && (module.exports = {
|
|
303
513
|
createXrManager,
|
|
514
|
+
createXrPerformanceHint,
|
|
304
515
|
createXrStore,
|
|
305
516
|
defaultVrSessionInit,
|
|
517
|
+
defaultXrWorkerBudgetProfile,
|
|
306
518
|
isXrModeSupported,
|
|
307
519
|
mergeXrSessionInit,
|
|
520
|
+
readXrFrameRateCapabilities,
|
|
308
521
|
requestXrSession,
|
|
522
|
+
updateXrTargetFrameRate,
|
|
309
523
|
xrReferenceSpaceTypes,
|
|
310
|
-
xrSessionModes
|
|
524
|
+
xrSessionModes,
|
|
525
|
+
xrWorkerQueueClass,
|
|
526
|
+
xrWorkerSchedulerMode
|
|
311
527
|
});
|
|
312
528
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.js"],"sourcesContent":["const DEFAULT_VR_SESSION_INIT = Object.freeze({\n requiredFeatures: [\"local-floor\"],\n optionalFeatures: [\"bounded-floor\", \"hand-tracking\", \"layers\"],\n});\n\nexport const xrSessionModes = Object.freeze([\n \"inline\",\n \"immersive-vr\",\n \"immersive-ar\",\n]);\n\nexport const xrReferenceSpaceTypes = Object.freeze([\n \"viewer\",\n \"local\",\n \"local-floor\",\n \"bounded-floor\",\n \"unbounded\",\n]);\n\nfunction toStringArray(values) {\n if (!Array.isArray(values)) {\n return [];\n }\n return values\n .filter((value) => typeof value === \"string\")\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction dedupeStrings(values) {\n return [...new Set(toStringArray(values))];\n}\n\nfunction readNavigator(navigatorOverride) {\n const currentNavigator = navigatorOverride ?? globalThis.navigator;\n if (!currentNavigator || typeof currentNavigator !== \"object\") {\n throw new Error(\n \"WebXR navigator unavailable. Provide a browser navigator with navigator.xr.\"\n );\n }\n return currentNavigator;\n}\n\nfunction readXrSystem(navigatorOverride) {\n const currentNavigator = readNavigator(navigatorOverride);\n const xr = currentNavigator.xr;\n if (!xr || typeof xr !== \"object\") {\n throw new Error(\n \"WebXR runtime unavailable. navigator.xr is missing in this environment.\"\n );\n }\n return xr;\n}\n\nfunction assertSessionMode(mode) {\n if (!xrSessionModes.includes(mode)) {\n const available = xrSessionModes.join(\", \");\n throw new Error(\n `Unknown XR session mode \"${mode}\". Available modes: ${available}.`\n );\n }\n}\n\nexport function mergeXrSessionInit(base = {}, override = {}) {\n const requiredFeatures = dedupeStrings([\n ...toStringArray(base.requiredFeatures),\n ...toStringArray(override.requiredFeatures),\n ]);\n\n const optionalFeatures = dedupeStrings([\n ...toStringArray(base.optionalFeatures),\n ...toStringArray(override.optionalFeatures),\n ]);\n\n const merged = {\n ...base,\n ...override,\n requiredFeatures,\n optionalFeatures,\n };\n\n if (requiredFeatures.length === 0) {\n delete merged.requiredFeatures;\n }\n\n if (optionalFeatures.length === 0) {\n delete merged.optionalFeatures;\n }\n\n return merged;\n}\n\nexport async function isXrModeSupported(\n mode = \"immersive-vr\",\n options = {}\n) {\n assertSessionMode(mode);\n\n const { navigator: navigatorOverride } = options;\n let xr;\n try {\n xr = readXrSystem(navigatorOverride);\n } catch {\n return false;\n }\n\n if (typeof xr.isSessionSupported !== \"function\") {\n return false;\n }\n\n try {\n return Boolean(await xr.isSessionSupported(mode));\n } catch {\n return false;\n }\n}\n\nexport async function requestXrSession(options = {}) {\n const {\n mode = \"immersive-vr\",\n sessionInit = {},\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n navigator: navigatorOverride,\n } = options;\n\n assertSessionMode(mode);\n\n const xr = readXrSystem(navigatorOverride);\n\n if (typeof xr.requestSession !== \"function\") {\n throw new Error(\"WebXR requestSession API unavailable.\");\n }\n\n const init = mergeXrSessionInit(baseSessionInit, sessionInit);\n return xr.requestSession(mode, init);\n}\n\nexport function createXrStore(initialState = {}) {\n const listeners = new Set();\n let state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n ...initialState,\n };\n\n const notify = () => {\n for (const listener of listeners) {\n listener(state);\n }\n };\n\n return {\n getSnapshot() {\n return state;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n set(partialState) {\n state = {\n ...state,\n ...partialState,\n };\n notify();\n },\n reset() {\n state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n ...initialState,\n };\n notify();\n },\n };\n}\n\nexport function createXrManager(options = {}) {\n const {\n navigator: navigatorOverride,\n defaultMode = \"immersive-vr\",\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n onSessionStart,\n onSessionEnd,\n } = options;\n\n assertSessionMode(defaultMode);\n\n const store = createXrStore();\n let activeSessionEndHandler = null;\n\n const detachSessionEndHandler = () => {\n const { activeSession } = store.getSnapshot();\n if (\n activeSession &&\n activeSessionEndHandler &&\n typeof activeSession.removeEventListener === \"function\"\n ) {\n activeSession.removeEventListener(\"end\", activeSessionEndHandler);\n }\n activeSessionEndHandler = null;\n };\n\n const handleSessionEnded = () => {\n detachSessionEndHandler();\n store.set({\n activeSession: null,\n mode: null,\n isEntering: false,\n });\n if (typeof onSessionEnd === \"function\") {\n onSessionEnd();\n }\n };\n\n const attachSessionEndHandler = (session) => {\n if (!session || typeof session.addEventListener !== \"function\") {\n return;\n }\n activeSessionEndHandler = handleSessionEnded;\n session.addEventListener(\"end\", activeSessionEndHandler);\n };\n\n const getState = () => store.getSnapshot();\n\n const subscribe = (listener) => store.subscribe(listener);\n\n const probeSupport = async (modes = [defaultMode]) => {\n const supportedModes = {};\n for (const mode of modes) {\n assertSessionMode(mode);\n supportedModes[mode] = await isXrModeSupported(mode, {\n navigator: navigatorOverride,\n });\n }\n store.set({ supportedModes });\n return supportedModes;\n };\n\n const enterSession = async (mode = defaultMode, sessionInit = {}) => {\n assertSessionMode(mode);\n\n const existing = store.getSnapshot().activeSession;\n if (existing) {\n return existing;\n }\n\n store.set({ isEntering: true, lastError: null });\n\n try {\n const session = await requestXrSession({\n mode,\n sessionInit,\n baseSessionInit,\n navigator: navigatorOverride,\n });\n\n attachSessionEndHandler(session);\n\n store.set({\n activeSession: session,\n mode,\n isEntering: false,\n lastError: null,\n });\n\n if (typeof onSessionStart === \"function\") {\n onSessionStart(session, mode);\n }\n\n return session;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error ?? \"Unknown XR error\");\n store.set({\n isEntering: false,\n lastError: message,\n });\n throw error;\n }\n };\n\n const enterVr = async (sessionInit = {}) => {\n return enterSession(\"immersive-vr\", sessionInit);\n };\n\n const exitSession = async () => {\n const { activeSession } = store.getSnapshot();\n if (!activeSession) {\n return false;\n }\n\n if (typeof activeSession.end === \"function\") {\n await activeSession.end();\n }\n\n // Fallback for test fakes or runtimes that do not emit an end event.\n if (store.getSnapshot().activeSession) {\n handleSessionEnded();\n }\n\n return true;\n };\n\n const dispose = async () => {\n await exitSession();\n detachSessionEndHandler();\n store.reset();\n };\n\n return {\n store,\n getState,\n subscribe,\n probeSupport,\n enterSession,\n enterVr,\n exitSession,\n dispose,\n };\n}\n\nexport const defaultVrSessionInit = DEFAULT_VR_SESSION_INIT;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.js"],"sourcesContent":["const DEFAULT_VR_SESSION_INIT = Object.freeze({\n requiredFeatures: [\"local-floor\"],\n optionalFeatures: [\"bounded-floor\", \"hand-tracking\", \"layers\"],\n});\nconst DEFAULT_MODE_FRAME_RATES = Object.freeze({\n inline: 60,\n \"immersive-vr\": 90,\n \"immersive-ar\": 72,\n});\n\nexport const xrSessionModes = Object.freeze([\n \"inline\",\n \"immersive-vr\",\n \"immersive-ar\",\n]);\n\nexport const xrReferenceSpaceTypes = Object.freeze([\n \"viewer\",\n \"local\",\n \"local-floor\",\n \"bounded-floor\",\n \"unbounded\",\n]);\nexport const xrWorkerQueueClass = \"render\";\nexport const xrWorkerSchedulerMode = \"dag\";\nexport const defaultXrWorkerBudgetProfile = \"xr\";\n\nfunction toStringArray(values) {\n if (!Array.isArray(values)) {\n return [];\n }\n return values\n .filter((value) => typeof value === \"string\")\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction dedupeStrings(values) {\n return [...new Set(toStringArray(values))];\n}\n\nfunction readPositiveNumber(value) {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n ? value\n : null;\n}\n\nfunction normalizeFrameRates(values) {\n if (!values || typeof values === \"string\") {\n return Object.freeze([]);\n }\n\n let collected;\n try {\n collected = Array.from(values);\n } catch {\n return Object.freeze([]);\n }\n\n return Object.freeze(\n [...new Set(collected.map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0))].sort(\n (left, right) => right - left\n )\n );\n}\n\nfunction getDefaultFrameRateForMode(mode) {\n return DEFAULT_MODE_FRAME_RATES[mode] ?? DEFAULT_MODE_FRAME_RATES[\"immersive-vr\"];\n}\n\nfunction getWorkerBudgetProfileForMode(mode) {\n return mode === \"inline\" ? \"realtime\" : defaultXrWorkerBudgetProfile;\n}\n\nfunction readNavigator(navigatorOverride) {\n const currentNavigator = navigatorOverride ?? globalThis.navigator;\n if (!currentNavigator || typeof currentNavigator !== \"object\") {\n throw new Error(\n \"WebXR navigator unavailable. Provide a browser navigator with navigator.xr.\"\n );\n }\n return currentNavigator;\n}\n\nfunction readXrSystem(navigatorOverride) {\n const currentNavigator = readNavigator(navigatorOverride);\n const xr = currentNavigator.xr;\n if (!xr || typeof xr !== \"object\") {\n throw new Error(\n \"WebXR runtime unavailable. navigator.xr is missing in this environment.\"\n );\n }\n return xr;\n}\n\nfunction assertSessionMode(mode) {\n if (!xrSessionModes.includes(mode)) {\n const available = xrSessionModes.join(\", \");\n throw new Error(\n `Unknown XR session mode \"${mode}\". Available modes: ${available}.`\n );\n }\n}\n\nexport function mergeXrSessionInit(base = {}, override = {}) {\n const requiredFeatures = dedupeStrings([\n ...toStringArray(base.requiredFeatures),\n ...toStringArray(override.requiredFeatures),\n ]);\n\n const optionalFeatures = dedupeStrings([\n ...toStringArray(base.optionalFeatures),\n ...toStringArray(override.optionalFeatures),\n ]);\n\n const merged = {\n ...base,\n ...override,\n requiredFeatures,\n optionalFeatures,\n };\n\n if (requiredFeatures.length === 0) {\n delete merged.requiredFeatures;\n }\n\n if (optionalFeatures.length === 0) {\n delete merged.optionalFeatures;\n }\n\n return merged;\n}\n\nexport async function isXrModeSupported(\n mode = \"immersive-vr\",\n options = {}\n) {\n assertSessionMode(mode);\n\n const { navigator: navigatorOverride } = options;\n let xr;\n try {\n xr = readXrSystem(navigatorOverride);\n } catch {\n return false;\n }\n\n if (typeof xr.isSessionSupported !== \"function\") {\n return false;\n }\n\n try {\n return Boolean(await xr.isSessionSupported(mode));\n } catch {\n return false;\n }\n}\n\nexport async function requestXrSession(options = {}) {\n const {\n mode = \"immersive-vr\",\n sessionInit = {},\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n navigator: navigatorOverride,\n } = options;\n\n assertSessionMode(mode);\n\n const xr = readXrSystem(navigatorOverride);\n\n if (typeof xr.requestSession !== \"function\") {\n throw new Error(\"WebXR requestSession API unavailable.\");\n }\n\n const init = mergeXrSessionInit(baseSessionInit, sessionInit);\n return xr.requestSession(mode, init);\n}\n\nexport function readXrFrameRateCapabilities(session, options = {}) {\n const {\n mode = \"immersive-vr\",\n fallbackFrameRates = [],\n defaultFrameRate,\n } = options;\n\n assertSessionMode(mode);\n\n const sessionFrameRate = readPositiveNumber(session?.frameRate);\n const supportedFrameRates = normalizeFrameRates(session?.supportedFrameRates);\n const fallbackRates = normalizeFrameRates(fallbackFrameRates);\n const mergedSupported = supportedFrameRates.length\n ? [...supportedFrameRates]\n : [...fallbackRates];\n\n if (sessionFrameRate && !mergedSupported.includes(sessionFrameRate)) {\n mergedSupported.push(sessionFrameRate);\n mergedSupported.sort((left, right) => right - left);\n }\n\n const refreshRateHz =\n sessionFrameRate ??\n mergedSupported[0] ??\n readPositiveNumber(defaultFrameRate) ??\n getDefaultFrameRateForMode(mode);\n\n return Object.freeze({\n mode,\n currentFrameRate: sessionFrameRate,\n supportedFrameRates: Object.freeze(mergedSupported),\n refreshRateHz,\n canUpdateTargetFrameRate:\n Boolean(session) && typeof session.updateTargetFrameRate === \"function\",\n });\n}\n\nexport function createXrPerformanceHint(options = {}) {\n const {\n session = null,\n mode = \"immersive-vr\",\n preferredFrameRates = [],\n fallbackFrameRates = [],\n defaultFrameRate,\n } = options;\n\n const capabilities = readXrFrameRateCapabilities(session, {\n mode,\n fallbackFrameRates,\n defaultFrameRate,\n });\n\n const filteredPreferredFrameRates = normalizeFrameRates(preferredFrameRates).filter(\n (frameRate) =>\n capabilities.supportedFrameRates.length === 0 ||\n capabilities.supportedFrameRates.includes(frameRate)\n );\n\n const derivedPreferredFrameRates = filteredPreferredFrameRates.length\n ? filteredPreferredFrameRates\n : capabilities.supportedFrameRates.length\n ? capabilities.supportedFrameRates\n : Object.freeze([capabilities.refreshRateHz]);\n const targetFrameRate =\n derivedPreferredFrameRates[0] ?? capabilities.refreshRateHz;\n const rationale = [];\n\n if (capabilities.currentFrameRate) {\n rationale.push(\n `XR session reports a current frame rate of ${capabilities.currentFrameRate}Hz.`\n );\n } else {\n rationale.push(\"XR session does not expose a current frame rate; using defaults.\");\n }\n\n if (capabilities.supportedFrameRates.length) {\n rationale.push(\n `XR runtime exposes supported frame rates: ${capabilities.supportedFrameRates.join(\", \")}Hz.`\n );\n } else {\n rationale.push(\"XR runtime does not expose supported frame rates; using fallback targets.\");\n }\n\n if (filteredPreferredFrameRates.length) {\n rationale.push(\"Preferred XR frame rates were filtered against runtime-supported values.\");\n } else {\n rationale.push(\"XR target frame rate defaults to the highest available runtime target.\");\n }\n\n return Object.freeze({\n ...capabilities,\n preferredFrameRates: Object.freeze([...derivedPreferredFrameRates]),\n targetFrameRate,\n targetFrameTimeMs: 1000 / targetFrameRate,\n workerBudget: Object.freeze({\n queueClass: xrWorkerQueueClass,\n schedulerMode: xrWorkerSchedulerMode,\n profile: getWorkerBudgetProfileForMode(mode),\n }),\n rationale: Object.freeze(rationale),\n });\n}\n\nexport async function updateXrTargetFrameRate(session, frameRate) {\n if (!session || typeof session !== \"object\") {\n throw new Error(\"XR session is required to update target frame rate.\");\n }\n\n const requestedFrameRate = readPositiveNumber(frameRate);\n if (!requestedFrameRate) {\n throw new Error(\"XR target frame rate must be a finite number greater than zero.\");\n }\n\n if (typeof session.updateTargetFrameRate !== \"function\") {\n throw new Error(\"XR session does not support updateTargetFrameRate(frameRate).\");\n }\n\n const supportedFrameRates = normalizeFrameRates(session.supportedFrameRates);\n if (\n supportedFrameRates.length > 0 &&\n !supportedFrameRates.includes(requestedFrameRate)\n ) {\n throw new Error(\n `XR target frame rate ${requestedFrameRate}Hz is not supported by the active session.`\n );\n }\n\n await session.updateTargetFrameRate(requestedFrameRate);\n return requestedFrameRate;\n}\n\nexport function createXrStore(initialState = {}) {\n const listeners = new Set();\n let state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n currentFrameRate: null,\n targetFrameRate: null,\n supportedFrameRates: [],\n canUpdateTargetFrameRate: false,\n workerBudgetProfile: null,\n ...initialState,\n };\n\n const notify = () => {\n for (const listener of listeners) {\n listener(state);\n }\n };\n\n return {\n getSnapshot() {\n return state;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n set(partialState) {\n state = {\n ...state,\n ...partialState,\n };\n notify();\n },\n reset() {\n state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n currentFrameRate: null,\n targetFrameRate: null,\n supportedFrameRates: [],\n canUpdateTargetFrameRate: false,\n workerBudgetProfile: null,\n ...initialState,\n };\n notify();\n },\n };\n}\n\nexport function createXrManager(options = {}) {\n const {\n navigator: navigatorOverride,\n defaultMode = \"immersive-vr\",\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n onSessionStart,\n onSessionEnd,\n } = options;\n\n assertSessionMode(defaultMode);\n\n const store = createXrStore();\n let activeSessionEndHandler = null;\n\n const detachSessionEndHandler = () => {\n const { activeSession } = store.getSnapshot();\n if (\n activeSession &&\n activeSessionEndHandler &&\n typeof activeSession.removeEventListener === \"function\"\n ) {\n activeSession.removeEventListener(\"end\", activeSessionEndHandler);\n }\n activeSessionEndHandler = null;\n };\n\n const handleSessionEnded = () => {\n detachSessionEndHandler();\n store.set({\n activeSession: null,\n mode: null,\n isEntering: false,\n currentFrameRate: null,\n targetFrameRate: null,\n supportedFrameRates: [],\n canUpdateTargetFrameRate: false,\n workerBudgetProfile: null,\n });\n if (typeof onSessionEnd === \"function\") {\n onSessionEnd();\n }\n };\n\n const attachSessionEndHandler = (session) => {\n if (!session || typeof session.addEventListener !== \"function\") {\n return;\n }\n activeSessionEndHandler = handleSessionEnded;\n session.addEventListener(\"end\", activeSessionEndHandler);\n };\n\n const getState = () => store.getSnapshot();\n\n const subscribe = (listener) => store.subscribe(listener);\n\n const getFrameRateCapabilities = (options = {}) => {\n const state = store.getSnapshot();\n return readXrFrameRateCapabilities(\n options.session ?? state.activeSession,\n {\n mode: options.mode ?? state.mode ?? defaultMode,\n fallbackFrameRates:\n options.fallbackFrameRates ?? state.supportedFrameRates,\n defaultFrameRate:\n options.defaultFrameRate ??\n state.targetFrameRate ??\n state.currentFrameRate ??\n undefined,\n }\n );\n };\n\n const getPerformanceHint = (options = {}) => {\n const state = store.getSnapshot();\n return createXrPerformanceHint({\n session: options.session ?? state.activeSession,\n mode: options.mode ?? state.mode ?? defaultMode,\n preferredFrameRates:\n options.preferredFrameRates ??\n (state.targetFrameRate ? [state.targetFrameRate] : []),\n fallbackFrameRates:\n options.fallbackFrameRates ?? state.supportedFrameRates,\n defaultFrameRate:\n options.defaultFrameRate ??\n state.targetFrameRate ??\n state.currentFrameRate ??\n undefined,\n });\n };\n\n const syncSessionPerformanceState = (session, mode, preferredFrameRates = []) => {\n const hint = createXrPerformanceHint({\n session,\n mode,\n preferredFrameRates,\n });\n\n store.set({\n activeSession: session,\n mode,\n isEntering: false,\n lastError: null,\n currentFrameRate: hint.currentFrameRate,\n targetFrameRate: hint.targetFrameRate,\n supportedFrameRates: hint.supportedFrameRates,\n canUpdateTargetFrameRate: hint.canUpdateTargetFrameRate,\n workerBudgetProfile: hint.workerBudget.profile,\n });\n\n return hint;\n };\n\n const probeSupport = async (modes = [defaultMode]) => {\n const supportedModes = {};\n for (const mode of modes) {\n assertSessionMode(mode);\n supportedModes[mode] = await isXrModeSupported(mode, {\n navigator: navigatorOverride,\n });\n }\n store.set({ supportedModes });\n return supportedModes;\n };\n\n const enterSession = async (mode = defaultMode, sessionInit = {}) => {\n assertSessionMode(mode);\n\n const existing = store.getSnapshot().activeSession;\n if (existing) {\n return existing;\n }\n\n store.set({ isEntering: true, lastError: null });\n\n try {\n const session = await requestXrSession({\n mode,\n sessionInit,\n baseSessionInit,\n navigator: navigatorOverride,\n });\n\n attachSessionEndHandler(session);\n syncSessionPerformanceState(session, mode);\n\n if (typeof onSessionStart === \"function\") {\n onSessionStart(session, mode);\n }\n\n return session;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error ?? \"Unknown XR error\");\n store.set({\n isEntering: false,\n lastError: message,\n });\n throw error;\n }\n };\n\n const enterVr = async (sessionInit = {}) => {\n return enterSession(\"immersive-vr\", sessionInit);\n };\n\n const setTargetFrameRate = async (frameRate) => {\n const state = store.getSnapshot();\n const activeSession = state.activeSession;\n if (!activeSession) {\n throw new Error(\n \"Cannot update XR target frame rate without an active XR session.\"\n );\n }\n\n try {\n const appliedFrameRate = await updateXrTargetFrameRate(\n activeSession,\n frameRate\n );\n syncSessionPerformanceState(activeSession, state.mode ?? defaultMode, [\n appliedFrameRate,\n ]);\n return appliedFrameRate;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error ?? \"Unknown XR error\");\n store.set({ lastError: message });\n throw error;\n }\n };\n\n const exitSession = async () => {\n const { activeSession } = store.getSnapshot();\n if (!activeSession) {\n return false;\n }\n\n if (typeof activeSession.end === \"function\") {\n await activeSession.end();\n }\n\n // Fallback for test fakes or runtimes that do not emit an end event.\n if (store.getSnapshot().activeSession) {\n handleSessionEnded();\n }\n\n return true;\n };\n\n const dispose = async () => {\n await exitSession();\n detachSessionEndHandler();\n store.reset();\n };\n\n return {\n store,\n getState,\n subscribe,\n probeSupport,\n getFrameRateCapabilities,\n getPerformanceHint,\n enterSession,\n enterVr,\n setTargetFrameRate,\n exitSession,\n dispose,\n };\n}\n\nexport const defaultVrSessionInit = DEFAULT_VR_SESSION_INIT;\n"],"mappings":"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","names":["options"]}
|