@covas-labs/electron-vr 0.5.2 → 0.7.0

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/README.md CHANGED
@@ -88,6 +88,10 @@ Use the same command with `enable`, `disable`, or `uninstall`. `npm install` doe
88
88
 
89
89
  Set `ELECTRON_VR_DISABLE_OPENVR=1` to prevent the OpenVR/SteamVR fallback while diagnosing OpenXR. When no usable OpenXR overlay path is available, the bridge selects `mock` and includes `openvr-disabled-by-env` in `probeMode`.
90
90
 
91
+ When Virtual Desktop VDXR is the active Windows OpenXR runtime, the bridge automatically avoids the OpenVR fallback and does not initialize OpenVR during probing, because that can start SteamVR and disrupt an OpenXR game. Install and enable the OpenXR API layer to use the OpenXR application-integration path, then restart the game.
92
+
93
+ OpenComposite does not implement the OpenVR overlay API required by this package. When OpenComposite is the registered OpenVR runtime, the bridge reports it in diagnostics but does not select the OpenVR backend; use a compatible OpenXR runtime with the API layer instead.
94
+
91
95
  Applications can manage the same per-user installation through the public API:
92
96
 
93
97
  ```ts
@@ -102,6 +106,8 @@ await VROverlay.uninstallOpenXRApiLayer();
102
106
 
103
107
  The methods return `Promise<OpenXRApiLayerStatus>` and reject when the platform is unsupported or the utility fails. Installation should require explicit user consent. It affects subsequently started OpenXR applications, so restart an already running game after installing, enabling, disabling, or uninstalling the layer. These methods support Linux x64 and Windows x64 and do not require administrator/root access for the normal per-user installation.
104
108
 
109
+ `getOpenXRApiLayerStatus()` reports `requiresUpdate: true` when the installed layer assets differ from those bundled with the package. Re-run `installOpenXRApiLayer()` after confirmation to update them.
110
+
105
111
  For product UI, prefer the combined readiness report instead of interpreting raw probe fields:
106
112
 
107
113
  ```ts
@@ -111,4 +117,4 @@ console.log(report.launch.verdict, report.launch.requiredActions);
111
117
 
112
118
  `report.launch.wouldWorkNow` answers whether launching a browser overlay now is expected to produce real VR output. Otherwise, the verdict distinguishes an actionable setup/runtime/host requirement from a fundamental incompatibility. The report also exposes detailed readiness states, actionable issues, and per-feature support. See `PRODUCT_INTEGRATION.md` in the repository for the recommended consent, IPC, restart, recovery, diagnostics, and production UI flow.
113
119
 
114
- `getRuntimeInfo()` also includes `openvrRuntimeInstalled`, `openvrRuntimePath`, `openxrMode`, API-layer installation and connection state, protocol version, and connected OpenXR host metadata. `probeMode` includes the backend-selection decision.
120
+ `getRuntimeInfo()` also includes `openvrRuntimeInstalled`, `openvrRuntimePath`, `openxrMode`, API-layer installation state, `openxrHostDetected`, companion connection state, protocol version, and OpenXR host metadata. `openxrHostDetected` can become true before the overlay companion connects. `probeMode` includes the backend-selection decision.
package/dist/bridge.d.ts CHANGED
@@ -22,11 +22,14 @@ export interface RuntimeInfo {
22
22
  openxrApiLayerEnabled: boolean;
23
23
  openxrApiLayerManifestPath: string;
24
24
  openxrCompanionConnected: boolean;
25
+ openxrHostDetected: boolean;
25
26
  openxrHostProcessId: number;
26
27
  openxrHostApplicationName: string;
27
28
  openxrHostGraphicsApi: string;
28
29
  openxrHostAdapterLuid: string;
29
30
  openxrProtocolVersion: number;
31
+ openxrSubmittedFrameSequence: number;
32
+ openxrConsumedFrameSequence: number;
30
33
  openvrAvailable: boolean;
31
34
  openvrRuntimeInstalled: boolean;
32
35
  openvrRuntimePath: string;
@@ -117,6 +120,7 @@ export declare class VrBridge {
117
120
  private linuxOpenVRVisibleTexture;
118
121
  private windowsReadbackInFlight;
119
122
  private windowsReadbackPending;
123
+ private windowsOpenXRTextures;
120
124
  private linuxReadbackInFlight;
121
125
  private linuxReadbackPending;
122
126
  private invalidationTimer;
package/dist/bridge.js CHANGED
@@ -116,11 +116,14 @@ function sanitizeRuntimeInfo(runtimeInfo) {
116
116
  openxrApiLayerEnabled: runtimeInfo.openxrApiLayerEnabled ?? false,
117
117
  openxrApiLayerManifestPath: runtimeInfo.openxrApiLayerManifestPath ?? "",
118
118
  openxrCompanionConnected: runtimeInfo.openxrCompanionConnected ?? false,
119
+ openxrHostDetected: runtimeInfo.openxrHostDetected ?? false,
119
120
  openxrHostProcessId: runtimeInfo.openxrHostProcessId ?? 0,
120
121
  openxrHostApplicationName: runtimeInfo.openxrHostApplicationName ?? "",
121
122
  openxrHostGraphicsApi: runtimeInfo.openxrHostGraphicsApi ?? "",
122
123
  openxrHostAdapterLuid: runtimeInfo.openxrHostAdapterLuid ?? "",
123
124
  openxrProtocolVersion: runtimeInfo.openxrProtocolVersion ?? 0,
125
+ openxrSubmittedFrameSequence: runtimeInfo.openxrSubmittedFrameSequence ?? 0,
126
+ openxrConsumedFrameSequence: runtimeInfo.openxrConsumedFrameSequence ?? 0,
124
127
  openvrSceneApplicationState: runtimeInfo.openvrSceneApplicationState ?? "",
125
128
  openvrSceneProcessId: runtimeInfo.openvrSceneProcessId ?? 0,
126
129
  openvrSceneApplicationKey: runtimeInfo.openvrSceneApplicationKey ?? "",
@@ -211,6 +214,7 @@ export class VrBridge {
211
214
  linuxOpenVRVisibleTexture = null;
212
215
  windowsReadbackInFlight = false;
213
216
  windowsReadbackPending = false;
217
+ windowsOpenXRTextures = [];
214
218
  linuxReadbackInFlight = false;
215
219
  linuxReadbackPending = false;
216
220
  invalidationTimer = null;
@@ -242,6 +246,9 @@ export class VrBridge {
242
246
  return;
243
247
  }
244
248
  this.linuxReadbackPending = false;
249
+ for (const retained of this.windowsOpenXRTextures)
250
+ releaseTexture(retained.texture);
251
+ this.windowsOpenXRTextures = [];
245
252
  const offscreenContents = this.attachedWindow.webContents;
246
253
  offscreenContents.removeListener("paint", this.onPaint);
247
254
  this.attachedWindow = null;
@@ -472,6 +479,19 @@ export class VrBridge {
472
479
  console.log(`VR overlay received first paint event (platform=${process.platform}, backend=${this.getSelectedBackend()}, hasTexture=${texture ? "yes" : "no"}, hasBitmap=${nativeImage ? "yes" : "no"}).`);
473
480
  }
474
481
  const runtimeInfo = this.getRuntimeInfo();
482
+ if (process.platform === "win32" && this.windowsOpenXRTextures.length > 0) {
483
+ const consumedSequence = runtimeInfo.openxrConsumedFrameSequence;
484
+ const pending = [];
485
+ for (const retained of this.windowsOpenXRTextures) {
486
+ if (retained.sequence <= consumedSequence) {
487
+ releaseTexture(retained.texture);
488
+ }
489
+ else {
490
+ pending.push(retained);
491
+ }
492
+ }
493
+ this.windowsOpenXRTextures = pending;
494
+ }
475
495
  if (process.platform === "linux" &&
476
496
  runtimeInfo.selectedBackend === "openvr" &&
477
497
  isTruthyEnvironmentVariable("ELECTRON_VR_OPENVR_VULKAN_SOFTWARE")) {
@@ -548,9 +568,22 @@ export class VrBridge {
548
568
  this.useWindowsSoftwareFallback(nativeImage, "Windows shared texture submission failed; falling back to software RGBA upload.");
549
569
  }
550
570
  }
551
- else if (!this.loggedFirstWindowsSubmit) {
552
- this.loggedFirstWindowsSubmit = true;
553
- console.log("VR overlay submitted first Windows frame to the native bridge.");
571
+ else {
572
+ if (runtimeInfo.selectedBackend === "openxr" && runtimeInfo.openxrMode === "api-layer" &&
573
+ runtimeInfo.openxrHostGraphicsApi === "d3d11") {
574
+ const updatedRuntimeInfo = this.getRuntimeInfo();
575
+ if (updatedRuntimeInfo.openxrSubmittedFrameSequence > runtimeInfo.openxrSubmittedFrameSequence) {
576
+ this.windowsOpenXRTextures.push({
577
+ sequence: updatedRuntimeInfo.openxrSubmittedFrameSequence,
578
+ texture
579
+ });
580
+ releaseCurrentTexture = false;
581
+ }
582
+ }
583
+ if (!this.loggedFirstWindowsSubmit) {
584
+ this.loggedFirstWindowsSubmit = true;
585
+ console.log("VR overlay submitted first Windows frame to the native bridge.");
586
+ }
554
587
  }
555
588
  }
556
589
  else if (this.getSelectedBackend() === "openvr") {
@@ -4,7 +4,7 @@ export type VRReadiness = "ready" | "setup-required" | "waiting-for-host" | "fal
4
4
  export type VRLaunchVerdict = "works-now" | "action-required" | "incompatible";
5
5
  export type VRFeatureSupport = "supported" | "unsupported" | "runtime-dependent";
6
6
  export type VRIssueSeverity = "info" | "warning" | "error";
7
- export type VRSetupAction = "install-openxr-api-layer" | "enable-openxr-api-layer" | "restart-openxr-apps" | "start-openxr-app" | "install-xr-runtime" | "use-supported-openxr-host" | "none";
7
+ export type VRSetupAction = "install-openxr-api-layer" | "reinstall-openxr-api-layer" | "enable-openxr-api-layer" | "restart-openxr-apps" | "start-openxr-app" | "install-xr-runtime" | "use-supported-openxr-host" | "none";
8
8
  export interface VRFeatureCompatibility {
9
9
  flatOverlay: VRFeatureSupport;
10
10
  curvature: VRFeatureSupport;
@@ -37,6 +37,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
37
37
  (context.architecture ?? process.arch) === "x64";
38
38
  const layerInstalled = apiLayer?.installed ?? runtime.openxrApiLayerInstalled;
39
39
  const layerEnabled = apiLayer?.enabled ?? runtime.openxrApiLayerEnabled;
40
+ const layerRequiresUpdate = apiLayer?.requiresUpdate ?? false;
40
41
  let readiness = "unavailable";
41
42
  let backendLabel = "No compatible VR backend";
42
43
  let summary = "No compatible XR runtime is ready.";
@@ -56,9 +57,15 @@ export function analyzeVRCompatibility(runtime, context = {}) {
56
57
  features = { ...realFeatures };
57
58
  compatibleHostGraphicsApis = runtime.platform === "win32" ? ["D3D11", "D3D12"] : ["Vulkan", "OpenGL Xlib/GLX"];
58
59
  backendLabel = "OpenXR application integration";
59
- if (runtime.openxrCompanionConnected) {
60
+ if (layerRequiresUpdate) {
61
+ readiness = "setup-required";
62
+ summary = "OpenXR application integration must be updated before overlays can appear.";
63
+ recommendedAction = "reinstall-openxr-api-layer";
64
+ issues.push(issue("openxr-api-layer-update-required", "warning", "Update OpenXR integration", "Reinstall the per-user integration and restart OpenXR applications.", "reinstall-openxr-api-layer"));
65
+ }
66
+ else if (runtime.openxrCompanionConnected || runtime.openxrHostDetected) {
60
67
  readiness = "ready";
61
- summary = `Connected to ${runtime.openxrHostApplicationName || "an OpenXR application"} using ${runtime.openxrHostGraphicsApi || "a supported graphics API"}.`;
68
+ summary = `${runtime.openxrCompanionConnected ? "Connected to" : "Detected"} ${runtime.openxrHostApplicationName || "an OpenXR application"} using ${runtime.openxrHostGraphicsApi || "a supported graphics API"}.`;
62
69
  }
63
70
  else if (!layerEnabled) {
64
71
  readiness = "setup-required";
@@ -185,7 +192,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
185
192
  canRenderOverlay,
186
193
  isRealVrBackend: runtime.selectedBackend === "openxr" || runtime.selectedBackend === "openvr",
187
194
  requiresApiLayer,
188
- requiresOpenXRAppRestart: requiresApiLayer && (!runtime.openxrCompanionConnected || !layerEnabled),
195
+ requiresOpenXRAppRestart: requiresApiLayer && (!(runtime.openxrHostDetected || runtime.openxrCompanionConnected) || !layerEnabled),
189
196
  apiLayer,
190
197
  compatibleHostGraphicsApis,
191
198
  features,
@@ -2,6 +2,7 @@ export interface OpenXRApiLayerStatus {
2
2
  installed: boolean;
3
3
  enabled: boolean;
4
4
  registered: boolean | null;
5
+ requiresUpdate: boolean;
5
6
  manifestPath: string;
6
7
  scope: string;
7
8
  }
@@ -84,6 +84,7 @@ export function parseOpenXRApiLayerStatus(output) {
84
84
  installed: values.get("installed") === "true",
85
85
  enabled: values.get("enabled") === "true",
86
86
  registered: values.has("registered") ? values.get("registered") === "true" : null,
87
+ requiresUpdate: values.get("requires_update") === "true",
87
88
  manifestPath: values.get("manifest") ?? "",
88
89
  scope: values.get("scope") ?? "current-user"
89
90
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@covas-labs/electron-vr",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "description": "Electron VR overlay bridge with OpenXR, OpenVR, and mock fallback backends.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,8 +22,8 @@
22
22
  "electron": ">=37"
23
23
  },
24
24
  "optionalDependencies": {
25
- "@covas-labs/electron-vr-prebuilt-linux-x64": "0.5.2",
26
- "@covas-labs/electron-vr-prebuilt-win32-x64": "0.5.2"
25
+ "@covas-labs/electron-vr-prebuilt-linux-x64": "0.7.0",
26
+ "@covas-labs/electron-vr-prebuilt-win32-x64": "0.7.0"
27
27
  },
28
28
  "publishConfig": {
29
29
  "registry": "https://registry.npmjs.org",