Add per-container OpenComposite support - #1853
TheReal-Flo wants to merge 5 commits into
Conversation
- Persist the OpenComposite setting in container configuration - Add UI controls and update Windows VR payload setup
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
2 issues found across 12 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/src/main/java/app/gamenative/ui/screen/xr/windows/WindowsVrPayloadManager.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/screen/xr/windows/WindowsVrPayloadManager.kt:112">
P3: These new OpenComposite options are written identically for every container, and the previous `enableAudio=false` is silently dropped. The per-container config (`WindowsVrRuntimeConfig`) only stores an `openCompositeEnabled` boolean, so the ini flags can't be varied per container/game. Dropping `enableAudio=false` silently re-enables audio redirection to its default for any game where audio was muted by the prior payload. Either drive these options from the container config or keep `enableAudio=false` if disabling redirect was intentional.</violation>
</file>
<file name="tools/stage-opencomposite.ps1">
<violation number="1" location="tools/stage-opencomposite.ps1:51">
P2: Each Windows modern-XR asset preparation now performs a full OpenComposite configure/build, requiring Git, CMake, Visual Studio, and network access on every invocation. Cache the pinned source/build or declare task inputs and outputs so rebuilding occurs only when the commit, patch, or toolchain changes.
(Based on your team's feedback about fragile build dependencies.)</violation>
</file>
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
| Invoke-Checked { & $libTool.FullName /nologo "/def:$(Join-Path $vulkan 'vulkan-1.def')" /machine:x64 "/out:$(Join-Path $vulkan 'Lib\vulkan-1.lib')" } "Vulkan import library" | ||
|
|
||
| Invoke-Checked { cmake -S $source -B $build -G "Visual Studio 17 2022" -A x64 -DERROR_ON_WARNING=OFF "-DOC_VERSION=$commit-gamenative-wine-background" } "OpenComposite configure" | ||
| Invoke-Checked { cmake --build $build --config Release --target OCOVR --parallel } "OpenComposite build" |
There was a problem hiding this comment.
P2: Each Windows modern-XR asset preparation now performs a full OpenComposite configure/build, requiring Git, CMake, Visual Studio, and network access on every invocation. Cache the pinned source/build or declare task inputs and outputs so rebuilding occurs only when the commit, patch, or toolchain changes.
(Based on your team's feedback about fragile build dependencies.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/stage-opencomposite.ps1, line 51:
<comment>Each Windows modern-XR asset preparation now performs a full OpenComposite configure/build, requiring Git, CMake, Visual Studio, and network access on every invocation. Cache the pinned source/build or declare task inputs and outputs so rebuilding occurs only when the commit, patch, or toolchain changes.
(Based on your team's feedback about fragile build dependencies.) </comment>
<file context>
@@ -2,14 +2,56 @@ $ErrorActionPreference = "Stop"
+Invoke-Checked { & $libTool.FullName /nologo "/def:$(Join-Path $vulkan 'vulkan-1.def')" /machine:x64 "/out:$(Join-Path $vulkan 'Lib\vulkan-1.lib')" } "Vulkan import library"
+
+Invoke-Checked { cmake -S $source -B $build -G "Visual Studio 17 2022" -A x64 -DERROR_ON_WARNING=OFF "-DOC_VERSION=$commit-gamenative-wine-background" } "OpenComposite configure"
+Invoke-Checked { cmake --build $build --config Release --target OCOVR --parallel } "OpenComposite build"
+$built = Join-Path $build "bin\Release\vrclient_x64.dll"
+if (!(Test-Path -LiteralPath $built -PathType Leaf)) { throw "OpenComposite build output is missing" }
</file context>
| writeIfChanged(owner, "2\n".toByteArray()) | ||
| writeIfChanged(target, adapter) | ||
| writeIfChanged(ini, "enableAudio=false\nlogEnabled=true\n".toByteArray()) | ||
| writeIfChanged(ini, "initUsingVulkan=false\nlogAllOpenVRCalls=false\n".toByteArray()) |
There was a problem hiding this comment.
P3: These new OpenComposite options are written identically for every container, and the previous enableAudio=false is silently dropped. The per-container config (WindowsVrRuntimeConfig) only stores an openCompositeEnabled boolean, so the ini flags can't be varied per container/game. Dropping enableAudio=false silently re-enables audio redirection to its default for any game where audio was muted by the prior payload. Either drive these options from the container config or keep enableAudio=false if disabling redirect was intentional.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/ui/screen/xr/windows/WindowsVrPayloadManager.kt, line 112:
<comment>These new OpenComposite options are written identically for every container, and the previous `enableAudio=false` is silently dropped. The per-container config (`WindowsVrRuntimeConfig`) only stores an `openCompositeEnabled` boolean, so the ini flags can't be varied per container/game. Dropping `enableAudio=false` silently re-enables audio redirection to its default for any game where audio was muted by the prior payload. Either drive these options from the container config or keep `enableAudio=false` if disabling redirect was intentional.</comment>
<file context>
@@ -109,7 +109,7 @@ class WindowsVrPayloadManager(
writeIfChanged(owner, "2\n".toByteArray())
writeIfChanged(target, adapter)
- writeIfChanged(ini, "enableAudio=false\nlogEnabled=true\n".toByteArray())
+ writeIfChanged(ini, "initUsingVulkan=false\nlogAllOpenVRCalls=false\n".toByteArray())
openCompositeDirectories += directory
diagnostics.record("opencomposite", "installed path=${target.path}")
</file context>
| writeIfChanged(ini, "initUsingVulkan=false\nlogAllOpenVRCalls=false\n".toByteArray()) | |
| writeIfChanged(ini, "enableAudio=false\ninitUsingVulkan=false\nlogAllOpenVRCalls=false\n".toByteArray()) |
There was a problem hiding this comment.
All reported issues were addressed across 12 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
1 existing issue remains and no new issues found across 2 files (changes from recent commits).
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
10 issues found across 25 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveSessionOwnership.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveSessionOwnership.kt:46">
P2: When an XR activity instance has no `app_id`, `release(null)` clears the current owner's state because `null` is treated as a wildcard. A malformed or externally launched instance can therefore make `MainActivity` tear down another game's environment during its handoff; require a non-null matching app ID before releasing ownership.</violation>
</file>
<file name="tools/patches/opencomposite-gamenative-native-backend.patch">
<violation number="1" location="tools/patches/opencomposite-gamenative-native-backend.patch:102">
P1: When `GAMENATIVE_BACKEND` is enabled, the new backend still creates and owns a Windows OpenXR session before using the GameNative transport. Remove the `DrvOpenXR::CreateOpenXRBackend` compatibility-session dependency and provide input/session handling that does not create an OpenXR session.</violation>
</file>
<file name="app/src/main/java/app/gamenative/MainActivity.kt">
<violation number="1" location="app/src/main/java/app/gamenative/MainActivity.kt:384">
P2: If the launcher is destroyed before `ImmersiveXrActivity.onCreate` runs, this condition suppresses `ActivityDestroyed` cleanup while ownership remains `LAUNCHING`. Clear abandoned launch ownership on task removal or with a launch timeout.</violation>
</file>
<file name="tools/prepare-native-openvr-dev.ps1">
<violation number="1" location="tools/prepare-native-openvr-dev.ps1:37">
P2: When an existing checkout’s `origin` or `upstream` points to a fork, this script fetches the pinned commit from that fork instead of the declared upstream repository. Set `upstream`’s fetch URL to `$upstream` before disabling push.</violation>
</file>
<file name="app/build.gradle.kts">
<violation number="1" location="app/build.gradle.kts:299">
P1: On non-Windows builds, the `androidXr` APK omits `libxrimmersive.so`, so launching `ImmersiveXrActivity` fails with `UnsatisfiedLinkError`. Include `src/modernXr/jniLibs` in this source set or otherwise package the prebuilt library.</violation>
</file>
<file name="app/src/main/windows/openxr_runtime/gamenative_openxr_runtime.c">
<violation number="1" location="app/src/main/windows/openxr_runtime/gamenative_openxr_runtime.c:2188">
P2: When aim tracking is valid independently of grip tracking, `xrLocateSpace` returns the aim pose with velocity flags derived from the grip pose. Track velocity per pose, or clear the velocity flags when the selected pose has no corresponding velocity.</violation>
</file>
<file name="app/src/main/windows/openvr_client/DrvGameNative.cpp">
<violation number="1" location="app/src/main/windows/openvr_client/DrvGameNative.cpp:125">
P2: When an OpenVR submission uses inverted V bounds, `store()` normalizes the range and copies it without flipping the pixels, so the eye image is submitted upside down. Apply a vertical transform during the copy or preserve the inversion through the submission path instead of treating it as informational.</violation>
<violation number="2" location="app/src/main/windows/openvr_client/DrvGameNative.cpp:485">
P2: After one successful view lookup, a later lookup failure leaves the previous validity flag and poses active. Clear the current-frame validity on failure and submit only when this frame’s view snapshot succeeded.</violation>
</file>
<file name="app/src/test/java/app/gamenative/AndroidXrLaunchTest.kt">
<violation number="1" location="app/src/test/java/app/gamenative/AndroidXrLaunchTest.kt:32">
P3: This test only covers the true path for the new openxr feature branch. Add a companion negative case that stubs both system features to false and asserts `isHeadset` returns false, so an always-true regression or a broken short-circuit would be caught.</violation>
</file>
<file name="tools/build-native-openvr-dev.ps1">
<violation number="1" location="tools/build-native-openvr-dev.ps1:11">
P3: Developer builds into app\build\native-openvr-dev are never cleaned between runs, so a stale CMake cache and incremental object files persist across NDK/compiler/commit changes. The managed (CI) path in stage-opencomposite.ps1 explicitly wipes its build directory before each build, but this wrapper always passes a non-empty -SourceDirectory, which forces stage into its developer branch and bypasses that cleanup. Clear the directory (or have stage do it for the same app/build path) before configuring so repeat builds don't silently produce stale OpenComposite payloads.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
|
||
| // TODO seperate this from the rest of dllmain | ||
| + #ifdef GAMENATIVE_BACKEND | ||
| + IBackend* backend = DrvGameNative::CreateGameNativeBackend(pStartupInfo); |
There was a problem hiding this comment.
P1: When GAMENATIVE_BACKEND is enabled, the new backend still creates and owns a Windows OpenXR session before using the GameNative transport. Remove the DrvOpenXR::CreateOpenXRBackend compatibility-session dependency and provide input/session handling that does not create an OpenXR session.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/patches/opencomposite-gamenative-native-backend.patch, line 102:
<comment>When `GAMENATIVE_BACKEND` is enabled, the new backend still creates and owns a Windows OpenXR session before using the GameNative transport. Remove the `DrvOpenXR::CreateOpenXRBackend` compatibility-session dependency and provide input/session handling that does not create an OpenXR session.</comment>
<file context>
@@ -0,0 +1,108 @@
+
+ // TODO seperate this from the rest of dllmain
++ #ifdef GAMENATIVE_BACKEND
++ IBackend* backend = DrvGameNative::CreateGameNativeBackend(pStartupInfo);
++ #else
+ IBackend* backend = DrvOpenXR::CreateOpenXRBackend(pStartupInfo);
</file context>
| srcDirs("src/modern/assets", "src/main/assets", "build/generated/xrPayload/modernXr") | ||
| } | ||
| jniLibs { | ||
| setSrcDirs(listOf("src/modern/jniLibs", "build/generated/xrNative/modernXr")) |
There was a problem hiding this comment.
P1: On non-Windows builds, the androidXr APK omits libxrimmersive.so, so launching ImmersiveXrActivity fails with UnsatisfiedLinkError. Include src/modernXr/jniLibs in this source set or otherwise package the prebuilt library.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/build.gradle.kts, line 299:
<comment>On non-Windows builds, the `androidXr` APK omits `libxrimmersive.so`, so launching `ImmersiveXrActivity` fails with `UnsatisfiedLinkError`. Include `src/modernXr/jniLibs` in this source set or otherwise package the prebuilt library.</comment>
<file context>
@@ -274,6 +288,17 @@ android {
+ srcDirs("src/modern/assets", "src/main/assets", "build/generated/xrPayload/modernXr")
+ }
+ jniLibs {
+ setSrcDirs(listOf("src/modern/jniLibs", "build/generated/xrNative/modernXr"))
+ }
+ }
</file context>
| setSrcDirs(listOf("src/modern/jniLibs", "build/generated/xrNative/modernXr")) | |
| setSrcDirs(listOf("src/modern/jniLibs", "src/modernXr/jniLibs", "build/generated/xrNative/modernXr")) |
| fun release(appId: String?) { | ||
| var released = false | ||
| synchronized(lock) { | ||
| if (appId == null || state.appId == appId) { |
There was a problem hiding this comment.
P2: When an XR activity instance has no app_id, release(null) clears the current owner's state because null is treated as a wildcard. A malformed or externally launched instance can therefore make MainActivity tear down another game's environment during its handoff; require a non-null matching app ID before releasing ownership.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveSessionOwnership.kt, line 46:
<comment>When an XR activity instance has no `app_id`, `release(null)` clears the current owner's state because `null` is treated as a wildcard. A malformed or externally launched instance can therefore make `MainActivity` tear down another game's environment during its handoff; require a non-null matching app ID before releasing ownership.</comment>
<file context>
@@ -0,0 +1,70 @@
+ fun release(appId: String?) {
+ var released = false
+ synchronized(lock) {
+ if (appId == null || state.appId == appId) {
+ state = Snapshot()
+ released = true
</file context>
| if (appId == null || state.appId == appId) { | |
| if (appId != null && state.appId == appId) { |
| // emit before super so Compose DisposableEffects (which unregister | ||
| // listeners during super.onDestroy's lifecycle transition) still fire | ||
| if (!isChangingConfigurations) { | ||
| if (ImmersiveSessionOwnership.shouldLauncherHandleDestruction(isChangingConfigurations)) { |
There was a problem hiding this comment.
P2: If the launcher is destroyed before ImmersiveXrActivity.onCreate runs, this condition suppresses ActivityDestroyed cleanup while ownership remains LAUNCHING. Clear abandoned launch ownership on task removal or with a launch timeout.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/MainActivity.kt, line 384:
<comment>If the launcher is destroyed before `ImmersiveXrActivity.onCreate` runs, this condition suppresses `ActivityDestroyed` cleanup while ownership remains `LAUNCHING`. Clear abandoned launch ownership on task removal or with a launch timeout.</comment>
<file context>
@@ -372,7 +381,7 @@ class MainActivity : ComponentActivity() {
// emit before super so Compose DisposableEffects (which unregister
// listeners during super.onDestroy's lifecycle transition) still fire
- if (!isChangingConfigurations) {
+ if (ImmersiveSessionOwnership.shouldLauncherHandleDestruction(isChangingConfigurations)) {
PluviaApp.events.emit(AndroidEvent.ActivityDestroyed)
</file context>
| if (!(@(& git -c "safe.directory=$source" -C $source remote) -contains "upstream")) { | ||
| Invoke-Checked { git -c "safe.directory=$source" -C $source remote add upstream $upstream } "OpenComposite upstream remote" | ||
| } | ||
| Invoke-Checked { git -c "safe.directory=$source" -C $source remote set-url --push upstream DISABLED } "OpenComposite upstream push protection" |
There was a problem hiding this comment.
P2: When an existing checkout’s origin or upstream points to a fork, this script fetches the pinned commit from that fork instead of the declared upstream repository. Set upstream’s fetch URL to $upstream before disabling push.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/prepare-native-openvr-dev.ps1, line 37:
<comment>When an existing checkout’s `origin` or `upstream` points to a fork, this script fetches the pinned commit from that fork instead of the declared upstream repository. Set `upstream`’s fetch URL to `$upstream` before disabling push.</comment>
<file context>
@@ -0,0 +1,71 @@
+if (!(@(& git -c "safe.directory=$source" -C $source remote) -contains "upstream")) {
+ Invoke-Checked { git -c "safe.directory=$source" -C $source remote add upstream $upstream } "OpenComposite upstream remote"
+}
+Invoke-Checked { git -c "safe.directory=$source" -C $source remote set-url --push upstream DISABLED } "OpenComposite upstream push protection"
+
+& git -c "safe.directory=$source" -C $source cat-file -e "$commit^{commit}" 2>$null
</file context>
| * legacy actions were synced, which placed both controllers at the stage origin. */ | ||
| if (!gn_hands[hand].active) return 0; | ||
| if (comp == GN_COMP_AIM_POSE) { | ||
| if (!gn_hands[hand].aim_active) return 0; |
There was a problem hiding this comment.
P2: When aim tracking is valid independently of grip tracking, xrLocateSpace returns the aim pose with velocity flags derived from the grip pose. Track velocity per pose, or clear the velocity flags when the selected pose has no corresponding velocity.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/windows/openxr_runtime/gamenative_openxr_runtime.c, line 2188:
<comment>When aim tracking is valid independently of grip tracking, `xrLocateSpace` returns the aim pose with velocity flags derived from the grip pose. Track velocity per pose, or clear the velocity flags when the selected pose has no corresponding velocity.</comment>
<file context>
@@ -2176,8 +2180,19 @@ static int gn_space_absolute_pose(
+ * legacy actions were synced, which placed both controllers at the stage origin. */
+ if (!gn_hands[hand].active) return 0;
+ if (comp == GN_COMP_AIM_POSE) {
+ if (!gn_hands[hand].aim_active) return 0;
+ gn_write_pose(&tracked, gn_hands[hand].aim);
+ } else if (comp == GN_COMP_GRIP_POSE) {
</file context>
| if (!transport_.locateViews(views_.data(), &flags)) { | ||
| OOVR_LOG("DrvGameNative: view snapshot unavailable after frame wait"); | ||
| } else { | ||
| viewFlags_ = flags; |
There was a problem hiding this comment.
P2: After one successful view lookup, a later lookup failure leaves the previous validity flag and poses active. Clear the current-frame validity on failure and submit only when this frame’s view snapshot succeeded.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/windows/openvr_client/DrvGameNative.cpp, line 485:
<comment>After one successful view lookup, a later lookup failure leaves the previous validity flag and poses active. Clear the current-frame validity on failure and submit only when this frame’s view snapshot succeeded.</comment>
<file context>
@@ -0,0 +1,548 @@
+ if (!transport_.locateViews(views_.data(), &flags)) {
+ OOVR_LOG("DrvGameNative: view snapshot unavailable after frame wait");
+ } else {
+ viewFlags_ = flags;
+ }
+ }
</file context>
| sourceBox.right = static_cast<UINT>(std::ceil(uMax * sourceDesc.Width)); | ||
| sourceBox.top = static_cast<UINT>(std::floor(vMin * sourceDesc.Height)); | ||
| sourceBox.bottom = static_cast<UINT>(std::ceil(vMax * sourceDesc.Height)); | ||
| verticallyInverted = bounds->vMin > bounds->vMax; |
There was a problem hiding this comment.
P2: When an OpenVR submission uses inverted V bounds, store() normalizes the range and copies it without flipping the pixels, so the eye image is submitted upside down. Apply a vertical transform during the copy or preserve the inversion through the submission path instead of treating it as informational.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/windows/openvr_client/DrvGameNative.cpp, line 125:
<comment>When an OpenVR submission uses inverted V bounds, `store()` normalizes the range and copies it without flipping the pixels, so the eye image is submitted upside down. Apply a vertical transform during the copy or preserve the inversion through the submission path instead of treating it as informational.</comment>
<file context>
@@ -0,0 +1,548 @@
+ sourceBox.right = static_cast<UINT>(std::ceil(uMax * sourceDesc.Width));
+ sourceBox.top = static_cast<UINT>(std::floor(vMin * sourceDesc.Height));
+ sourceBox.bottom = static_cast<UINT>(std::ceil(vMax * sourceDesc.Height));
+ verticallyInverted = bounds->vMin > bounds->vMax;
+ }
+ if (sourceBox.right <= sourceBox.left || sourceBox.bottom <= sourceBox.top) return false;
</file context>
| whenever(packageManager.hasSystemFeature("android.hardware.vr.headtracking")).thenReturn(false) | ||
| whenever(packageManager.hasSystemFeature("android.software.xr.api.openxr")).thenReturn(true) | ||
|
|
||
| assertTrue(MainActivity.isHeadset(context)) |
There was a problem hiding this comment.
P3: This test only covers the true path for the new openxr feature branch. Add a companion negative case that stubs both system features to false and asserts isHeadset returns false, so an always-true regression or a broken short-circuit would be caught.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/test/java/app/gamenative/AndroidXrLaunchTest.kt, line 32:
<comment>This test only covers the true path for the new openxr feature branch. Add a companion negative case that stubs both system features to false and asserts `isHeadset` returns false, so an always-true regression or a broken short-circuit would be caught.</comment>
<file context>
@@ -0,0 +1,53 @@
+ whenever(packageManager.hasSystemFeature("android.hardware.vr.headtracking")).thenReturn(false)
+ whenever(packageManager.hasSystemFeature("android.software.xr.api.openxr")).thenReturn(true)
+
+ assertTrue(MainActivity.isHeadset(context))
+ }
+
</file context>
| $repository = Split-Path -Parent $PSScriptRoot | ||
| $workspace = Split-Path -Parent $repository | ||
| $source = if ($SourceDirectory) { [IO.Path]::GetFullPath($SourceDirectory) } else { Join-Path $workspace "OpenComposite-gamenative" } | ||
| $build = if ($BuildDirectory) { [IO.Path]::GetFullPath($BuildDirectory) } else { Join-Path $repository "app\build\native-openvr-dev" } |
There was a problem hiding this comment.
P3: Developer builds into app\build\native-openvr-dev are never cleaned between runs, so a stale CMake cache and incremental object files persist across NDK/compiler/commit changes. The managed (CI) path in stage-opencomposite.ps1 explicitly wipes its build directory before each build, but this wrapper always passes a non-empty -SourceDirectory, which forces stage into its developer branch and bypasses that cleanup. Clear the directory (or have stage do it for the same app/build path) before configuring so repeat builds don't silently produce stale OpenComposite payloads.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/build-native-openvr-dev.ps1, line 11:
<comment>Developer builds into app\build\native-openvr-dev are never cleaned between runs, so a stale CMake cache and incremental object files persist across NDK/compiler/commit changes. The managed (CI) path in stage-opencomposite.ps1 explicitly wipes its build directory before each build, but this wrapper always passes a non-empty -SourceDirectory, which forces stage into its developer branch and bypasses that cleanup. Clear the directory (or have stage do it for the same app/build path) before configuring so repeat builds don't silently produce stale OpenComposite payloads.</comment>
<file context>
@@ -0,0 +1,19 @@
+$repository = Split-Path -Parent $PSScriptRoot
+$workspace = Split-Path -Parent $repository
+$source = if ($SourceDirectory) { [IO.Path]::GetFullPath($SourceDirectory) } else { Join-Path $workspace "OpenComposite-gamenative" }
+$build = if ($BuildDirectory) { [IO.Path]::GetFullPath($BuildDirectory) } else { Join-Path $repository "app\build\native-openvr-dev" }
+$output = if ($Destination) { [IO.Path]::GetFullPath($Destination) } else { Join-Path $build "opencomposite_x64.dll" }
+$stage = Join-Path $PSScriptRoot "stage-opencomposite.ps1"
</file context>
Description
Further improve OpenComposite support, not working yet
Recording
Type of Change
Checklist
#code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.CONTRIBUTING.md.Summary by cubic
Adds a per-container OpenComposite toggle so VR compatibility can be enabled per game and persisted in container config. On Windows, background OpenVR clients are rejected to avoid competing sessions, and changes apply on next launch.
ContainerDataviaContainerUtils;WindowsVrRuntimeConfigowns the canonical keys/accessors, covered by a Robolectric test.GraphicsTabfor non-default containers with new strings.ImmersiveXrActivityreads/writes VR flags throughWindowsVrRuntimeConfig; controller buttons are remapped from the shared Xbox layout to per-hand OpenVR semantics, and gamepad events only go out when a valid X server handler exists; quick-menu and pointer-mode handling unchanged..gitattributeskeeps patch whitespace from breaking, and/app/.containersnapshots are git-ignored.WindowsVrPayloadManager: writesinitUsingVulkan=falseandlogAllOpenVRCalls=falsesince unknown keys are fatal in the pinned payload.stageOpenCompositetracks toolchain inputs, andtools/stage-opencomposite.ps1builds from a pinned commit, applies patches, and exports Vulkan symbols viatools/opencomposite-vulkan-x64.def.VRApplication_Backgroundon Windows (returnVRInitError_Init_NoServerForBackgroundApp), robust exe-name parsing, and removal of ATL dependencies.androidXrbuild flavor and a native OpenVR backend (DrvGameNative) so OpenVR titles bypass the OpenXR compositor and submit straight to the native XR transport; a design doc and dev scripts accompany them.ImmersiveSessionOwnershipandImmersiveRuntimeViewModelkeep the Wine environment and runtime service alive across the Android XR Home Space handoff, fixing a Quest launch where the flat launcher tore them down mid-start; covered by unit and Robolectric tests.To enable: toggle OpenComposite in the container's Graphics tab and relaunch the game.
For devs: run
tools/stage-opencomposite.ps1on Windows; needs git, CMake, Visual Studio C++ x64 tools, and an Android NDK. The Gradle task fingerprints the NDK/VS toolchain, so toolchain changes invalidate the cache and rebuild.Written for commit e894b26. Summary will update on new commits.