Prefer OpenXR for desktop XR policy

This commit is contained in:
2026-06-05 16:06:52 +02:00
parent 308fb13075
commit bdd7a32ff5
8 changed files with 149 additions and 20 deletions

View File

@@ -44,7 +44,8 @@ dependencies until each platform triplet is proven.
| SQLite | `libs/sqlite3` | Move to vcpkg |
| GLAD | `libs/glad` | Move to vcpkg or generated backend target |
| Catch2 | none yet | Add through vcpkg |
| OpenVR | `libs/openvr` | Retain initially |
| OpenXR | Not wired yet | Target desktop XR backend; add SDK/package behind `pp_platform_vr` |
| OpenVR | `libs/openvr` | Retain only as a temporary desktop compatibility fallback; remove under `DEBT-0061` |
| OVR Platform/Mobile | `libs/ovr_platform`, `libs/ovr_mobile` | Retain initially |
| Wave SDK | `libs/wave_sdk` | Retain initially |
| Wacom WinTab | `libs/wacom` | Retain initially |
@@ -323,8 +324,10 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
the `pp_app_core` `AppPreferenceServices` contract while retained settings
persistence, recording lifecycle, and legacy canvas/UI execution remain
tracked by `DEBT-0045`; the VR mode callbacks now call `App` wrappers that
dispatch through `PlatformServices` before reaching the retained platform VR
bridge.
dispatch through `PlatformServices` before reaching the selected platform XR
runtime. `pp_platform_api` now prefers OpenXR in its tested desktop runtime
selection policy and marks the retained Windows OpenVR SDK path as a legacy
fallback under `DEBT-0061`.
- `src/legacy_app_startup_services.*` is the current app-shell bridge for
startup preference/runtime execution. It keeps run-counter persistence,
startup preference save, auto-timelapse startup, stored VR-controller state,
@@ -623,9 +626,10 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
context.
Desktop VR drawing also consumes backend-owned scissor/depth/blend state,
blend/depth state query-restore, depth clear masks, active texture unit
dispatch, and fallback 2D texture unbind dispatch; VR SDK start/stop now dispatches
through `PlatformServices` while retaining the existing Windows OpenVR bridge
shape. Its retained callback endpoints now share `legacy_ui_gl_dispatch`
dispatch, and fallback 2D texture unbind dispatch; XR SDK start/stop now
dispatches through `PlatformServices` and the tested desktop runtime policy
prefers OpenXR before falling back to the existing Windows OpenVR bridge. Its
retained callback endpoints now share `legacy_ui_gl_dispatch`
with app startup, app clear, app UI viewport/scissor, and command-convert
renderer state callbacks, so those files no longer duplicate local raw GL
adapter clusters for capability, blend, clear, viewport, scissor, active

View File

@@ -70,7 +70,7 @@ and validation command.
| Wacom pressure | `WacomTablet` | `pp_platform_windows` | Adapter smoke with fallback |
| Clipboard/file picker/share/display | `App` platform methods | `pp_app_core`, `pp_platform_api`, `pp_platform_*` | Clipboard read/write, share saved-path, picked-path, and display-file decision tests, platform service display/share/picker dispatch tests, platform smoke or mocked service |
| Virtual keyboard | `App`, platform entrypoints | `pp_app_core`, `pp_platform_api`, `pp_platform_*` | Keyboard visibility decision tests, platform service dispatch tests, platform smoke |
| OpenVR desktop | `HMD`, `Vive`, `app_vr` | `pp_platform_vr`, app | Compile gate and mocked pose tests |
| Desktop XR | `HMD`, `Vive`, `app_vr`, retained OpenVR bridge | `pp_platform_vr`, app with OpenXR backend | Runtime-selection policy tests, compile gate, and mocked pose tests |
| Quest/OVR | Android Quest files | `pp_platform_android_quest` | Compile/package gate |
| Focus/Wave | Android Focus files | `pp_platform_android_wave` | Compile/package gate |

File diff suppressed because one or more lines are too long

View File

@@ -336,7 +336,8 @@ Implementation tasks:
- `glad`
- `Catch2`
- Keep vendored until proven:
- OpenVR
- OpenVR only as the temporary desktop compatibility fallback while OpenXR is
introduced behind `pp_platform_vr`
- OVR/Wave SDKs
- Wacom WinTab
- AppCenter
@@ -735,9 +736,11 @@ VR mode, VR-controller, auto-timelapse, and cursor-mode side effects through
retained settings writes, recording lifecycle calls, and legacy canvas/UI
adapters continue.
VR mode start/stop now enters `App` platform wrappers that dispatch through
`PlatformServices`; Windows keeps the retained OpenVR bridge in
`WindowsPlatformServices`, while the legacy fallback reports unsupported VR
startup on non-Windows platforms until their shells own the service.
`PlatformServices`; the desktop runtime-selection policy in `pp_platform_api`
prefers OpenXR and marks OpenVR as a legacy fallback. Windows still reaches the
retained OpenVR bridge in `WindowsPlatformServices` until the OpenXR backend is
wired, while the legacy fallback reports unsupported VR startup on non-Windows
platforms until their shells own the service.
`pano_cli plan-about-menu` exposes app-core planning for About menu help,
about, what's-new, crash-test, and performance-test commands, including
versioned what's-new labels, diagnostic gating, and no-canvas performance-test
@@ -823,8 +826,9 @@ available and dispatches startup through the same service, preserving the
current iOS Objective-C bridge in the legacy adapter while removing iOS branches
from `App::init_menu_tools` and `LegacyToolsMenuServices`.
App VR lifecycle start/stop now asks `PlatformServices`, preserving the current
Windows OpenVR startup/shutdown bridge in `WindowsPlatformServices` while
non-Windows fallback adapters keep the existing unsupported/no-op behavior.
Windows OpenVR startup/shutdown bridge as the selected legacy fallback in
`WindowsPlatformServices` while non-Windows fallback adapters keep the existing
unsupported/no-op behavior.
Canvas image export publishing and explicit persistent-storage flushes now
dispatch through `PlatformServices` too, preserving iOS photo-library export
publication and WebGL filesystem sync behavior in the legacy adapter while
@@ -903,8 +907,8 @@ the live OpenGL call sequence. The retained viewport/scissor callback endpoints
now share `legacy_ui_gl_dispatch`.
VR UI framebuffer viewport and scissor-test setup now also consumes those
`pp_renderer_gl` contracts, keeping desktop and VR UI rendering aligned while
the retained OpenVR app path is split incrementally; its retained callback
endpoints now reuse the shared UI GL bridge.
the desktop XR path moves from the retained OpenVR app path toward OpenXR; its
retained callback endpoints now reuse the shared UI GL bridge.
VR draw blend/depth state snapshots, transitions, restore, and depth-buffer
clears, active texture unit switches, and fallback 2D texture unbinds now use
generic tested `pp_renderer_gl` capability query/apply, clear, active-texture,
@@ -2699,7 +2703,8 @@ Use this as the starting checklist for Phase 0 inventory.
VR controllers.
- Platform services: clipboard, file picker, save picker, directory picker,
share/display file, keyboard show/hide, cursor visibility.
- VR/platform variants: OpenVR desktop, Quest, Focus/Wave, Android standard,
- VR/platform variants: OpenXR desktop target with retained OpenVR fallback,
Quest, Focus/Wave, Android standard,
iOS/macOS, Linux, WebGL.
- Cloud/network: upload, download, browse, license/check flows.
- Recording/export: PBO readbacks, MP4 encoder, timelapse frames.

View File

@@ -23,6 +23,34 @@ PlatformFamily current_platform_family() noexcept
#endif
}
const char* xr_runtime_backend_name(XrRuntimeBackend backend) noexcept
{
switch (backend)
{
case XrRuntimeBackend::openxr:
return "openxr";
case XrRuntimeBackend::openvr:
return "openvr";
case XrRuntimeBackend::none:
default:
return "none";
}
}
XrRuntimeSelection select_desktop_xr_runtime(
bool openxr_available,
bool openvr_available,
bool allow_legacy_openvr_fallback) noexcept
{
if (openxr_available)
return { XrRuntimeBackend::openxr, false };
if (allow_legacy_openvr_fallback && openvr_available)
return { XrRuntimeBackend::openvr, true };
return {};
}
bool platform_deletes_recorded_files_on_clear(PlatformFamily family) noexcept
{
return family == PlatformFamily::ios || family == PlatformFamily::macos;

View File

@@ -17,8 +17,25 @@ enum class PlatformFamily {
webgl,
};
enum class XrRuntimeBackend {
none,
openxr,
openvr,
};
struct XrRuntimeSelection {
XrRuntimeBackend backend = XrRuntimeBackend::none;
bool uses_legacy_openvr_fallback = false;
};
[[nodiscard]] PlatformFamily current_platform_family() noexcept;
[[nodiscard]] const char* xr_runtime_backend_name(XrRuntimeBackend backend) noexcept;
[[nodiscard]] XrRuntimeSelection select_desktop_xr_runtime(
bool openxr_available,
bool openvr_available,
bool allow_legacy_openvr_fallback) noexcept;
[[nodiscard]] bool platform_deletes_recorded_files_on_clear(PlatformFamily family) noexcept;
[[nodiscard]] bool platform_publishes_exported_images(PlatformFamily family) noexcept;
[[nodiscard]] bool platform_flushes_persistent_storage(PlatformFamily family) noexcept;

View File

@@ -209,6 +209,15 @@ void ensure_directory(const std::string& path)
CreateDirectoryA(path.c_str(), NULL);
}
[[nodiscard]] pp::platform::XrRuntimeSelection select_windows_xr_runtime() noexcept
{
// DEBT-0061: OpenXR is the target backend; Windows only exposes the retained OpenVR bridge today.
return pp::platform::select_desktop_xr_runtime(
false,
true,
true);
}
std::string build_supported_files_filter(const std::vector<std::string>& types)
{
std::string filter = "Supported Files (";
@@ -442,12 +451,33 @@ public:
[[nodiscard]] bool start_vr_mode() override
{
return win32_vr_start();
const auto runtime = select_windows_xr_runtime();
if (runtime.backend == pp::platform::XrRuntimeBackend::openvr)
{
active_xr_runtime_backend_ = pp::platform::XrRuntimeBackend::none;
if (win32_vr_start())
active_xr_runtime_backend_ = runtime.backend;
return active_xr_runtime_backend_ == runtime.backend;
}
if (runtime.backend == pp::platform::XrRuntimeBackend::openxr)
LOG("OpenXR runtime selected but the Windows OpenXR backend is not wired yet");
return false;
}
void stop_vr_mode() override
{
win32_vr_stop();
auto runtime = active_xr_runtime_backend_;
if (runtime == pp::platform::XrRuntimeBackend::none)
runtime = select_windows_xr_runtime().backend;
if (runtime == pp::platform::XrRuntimeBackend::openvr)
win32_vr_stop();
else if (runtime == pp::platform::XrRuntimeBackend::openxr)
LOG("OpenXR runtime selected but the Windows OpenXR stop path is not wired yet");
active_xr_runtime_backend_ = pp::platform::XrRuntimeBackend::none;
}
void pick_image(pp::platform::PickedPathCallback callback) override
@@ -572,6 +602,9 @@ public:
(void)suggested_name;
callback(std::string(path), false);
}
private:
pp::platform::XrRuntimeBackend active_xr_runtime_backend_ = pp::platform::XrRuntimeBackend::none;
};
}

View File

@@ -913,6 +913,39 @@ void platform_policy_preserves_recording_and_export_storage_rules(pp::tests::Har
PP_EXPECT(harness, !pp::platform::platform_flushes_persistent_storage(pp::platform::PlatformFamily::windows));
}
void platform_policy_prefers_openxr_and_marks_openvr_fallback(pp::tests::Harness& harness)
{
const auto openxr = pp::platform::select_desktop_xr_runtime(
true,
true,
true);
PP_EXPECT(harness, openxr.backend == pp::platform::XrRuntimeBackend::openxr);
PP_EXPECT(harness, !openxr.uses_legacy_openvr_fallback);
PP_EXPECT(
harness,
std::string_view(pp::platform::xr_runtime_backend_name(openxr.backend)) == "openxr");
const auto retained_openvr = pp::platform::select_desktop_xr_runtime(
false,
true,
true);
PP_EXPECT(harness, retained_openvr.backend == pp::platform::XrRuntimeBackend::openvr);
PP_EXPECT(harness, retained_openvr.uses_legacy_openvr_fallback);
PP_EXPECT(
harness,
std::string_view(pp::platform::xr_runtime_backend_name(retained_openvr.backend)) == "openvr");
const auto unsupported = pp::platform::select_desktop_xr_runtime(
false,
true,
false);
PP_EXPECT(harness, unsupported.backend == pp::platform::XrRuntimeBackend::none);
PP_EXPECT(harness, !unsupported.uses_legacy_openvr_fallback);
PP_EXPECT(
harness,
std::string_view(pp::platform::xr_runtime_backend_name(unsupported.backend)) == "none");
}
void platform_policy_preserves_document_browse_roots(pp::tests::Harness& harness)
{
const auto ios_roots = pp::platform::platform_document_browse_roots(
@@ -1053,6 +1086,9 @@ int main()
harness.run(
"platform policy preserves recording and export storage rules",
platform_policy_preserves_recording_and_export_storage_rules);
harness.run(
"platform policy prefers OpenXR and marks OpenVR fallback",
platform_policy_prefers_openxr_and_marks_openvr_fallback);
harness.run("platform policy preserves document browse roots", platform_policy_preserves_document_browse_roots);
harness.run(
"platform policy preserves picker and prepared file rules",