@covas-labs/electron-vr 0.6.0 → 0.8.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
@@ -106,6 +106,8 @@ await VROverlay.uninstallOpenXRApiLayer();
106
106
 
107
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.
108
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
+
109
111
  For product UI, prefer the combined readiness report instead of interpreting raw probe fields:
110
112
 
111
113
  ```ts
@@ -115,4 +117,4 @@ console.log(report.launch.verdict, report.launch.requiredActions);
115
117
 
116
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.
117
119
 
118
- `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;
@@ -25,6 +25,7 @@ export interface VRCompatibilityIssue {
25
25
  export interface VRLaunchAssessment {
26
26
  verdict: VRLaunchVerdict;
27
27
  wouldWorkNow: boolean;
28
+ canStartNow: boolean;
28
29
  fundamentalIncompatibility: boolean;
29
30
  message: string;
30
31
  requiredActions: VRSetupAction[];
@@ -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";
@@ -131,6 +138,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
131
138
  launch = {
132
139
  verdict: "works-now",
133
140
  wouldWorkNow: true,
141
+ canStartNow: true,
134
142
  fundamentalIncompatibility: false,
135
143
  message: "Launching the browser overlay now is expected to produce real VR output.",
136
144
  requiredActions: []
@@ -143,6 +151,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
143
151
  launch = {
144
152
  verdict: "action-required",
145
153
  wouldWorkNow: false,
154
+ canStartNow: false,
146
155
  fundamentalIncompatibility: false,
147
156
  message: "The overlay is compatible, but setup must be completed before launching it.",
148
157
  requiredActions: actions
@@ -152,6 +161,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
152
161
  launch = {
153
162
  verdict: "action-required",
154
163
  wouldWorkNow: false,
164
+ canStartNow: true,
155
165
  fundamentalIncompatibility: false,
156
166
  message: `The overlay is ready, but real VR output requires a compatible ${compatibleHostGraphicsApis.join(" or ")} OpenXR application to be started or restarted.`,
157
167
  requiredActions: ["start-openxr-app"]
@@ -161,6 +171,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
161
171
  launch = {
162
172
  verdict: "action-required",
163
173
  wouldWorkNow: false,
174
+ canStartNow: false,
164
175
  fundamentalIncompatibility: false,
165
176
  message: "Launching now would not produce real VR output. Install and configure a supported OpenXR or SteamVR runtime first.",
166
177
  requiredActions: ["install-xr-runtime"]
@@ -170,6 +181,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
170
181
  launch = {
171
182
  verdict: "incompatible",
172
183
  wouldWorkNow: false,
184
+ canStartNow: false,
173
185
  fundamentalIncompatibility: true,
174
186
  message: "This platform or execution mode cannot provide a production VR overlay with the current implementation.",
175
187
  requiredActions: []
@@ -185,7 +197,7 @@ export function analyzeVRCompatibility(runtime, context = {}) {
185
197
  canRenderOverlay,
186
198
  isRealVrBackend: runtime.selectedBackend === "openxr" || runtime.selectedBackend === "openvr",
187
199
  requiresApiLayer,
188
- requiresOpenXRAppRestart: requiresApiLayer && (!runtime.openxrCompanionConnected || !layerEnabled),
200
+ requiresOpenXRAppRestart: requiresApiLayer && (!(runtime.openxrHostDetected || runtime.openxrCompanionConnected) || !layerEnabled),
189
201
  apiLayer,
190
202
  compatibleHostGraphicsApis,
191
203
  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
  }
@@ -52,7 +52,8 @@ function runCommand(command) {
52
52
  ...process.env,
53
53
  ELECTRON_RUN_AS_NODE: "1"
54
54
  },
55
- stdio: ["ignore", "pipe", "pipe"]
55
+ stdio: ["ignore", "pipe", "pipe"],
56
+ windowsHide: true
56
57
  });
57
58
  let stdout = "";
58
59
  let stderr = "";
@@ -84,6 +85,7 @@ export function parseOpenXRApiLayerStatus(output) {
84
85
  installed: values.get("installed") === "true",
85
86
  enabled: values.get("enabled") === "true",
86
87
  registered: values.has("registered") ? values.get("registered") === "true" : null,
88
+ requiresUpdate: values.get("requires_update") === "true",
87
89
  manifestPath: values.get("manifest") ?? "",
88
90
  scope: values.get("scope") ?? "current-user"
89
91
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@covas-labs/electron-vr",
3
- "version": "0.6.0",
3
+ "version": "0.8.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.6.0",
26
- "@covas-labs/electron-vr-prebuilt-win32-x64": "0.6.0"
25
+ "@covas-labs/electron-vr-prebuilt-linux-x64": "0.8.0",
26
+ "@covas-labs/electron-vr-prebuilt-win32-x64": "0.8.0"
27
27
  },
28
28
  "publishConfig": {
29
29
  "registry": "https://registry.npmjs.org",