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 | | SQLite | `libs/sqlite3` | Move to vcpkg |
| GLAD | `libs/glad` | Move to vcpkg or generated backend target | | GLAD | `libs/glad` | Move to vcpkg or generated backend target |
| Catch2 | none yet | Add through vcpkg | | 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 | | OVR Platform/Mobile | `libs/ovr_platform`, `libs/ovr_mobile` | Retain initially |
| Wave SDK | `libs/wave_sdk` | Retain initially | | Wave SDK | `libs/wave_sdk` | Retain initially |
| Wacom WinTab | `libs/wacom` | 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 the `pp_app_core` `AppPreferenceServices` contract while retained settings
persistence, recording lifecycle, and legacy canvas/UI execution remain persistence, recording lifecycle, and legacy canvas/UI execution remain
tracked by `DEBT-0045`; the VR mode callbacks now call `App` wrappers that tracked by `DEBT-0045`; the VR mode callbacks now call `App` wrappers that
dispatch through `PlatformServices` before reaching the retained platform VR dispatch through `PlatformServices` before reaching the selected platform XR
bridge. 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 - `src/legacy_app_startup_services.*` is the current app-shell bridge for
startup preference/runtime execution. It keeps run-counter persistence, startup preference/runtime execution. It keeps run-counter persistence,
startup preference save, auto-timelapse startup, stored VR-controller state, 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. context.
Desktop VR drawing also consumes backend-owned scissor/depth/blend state, Desktop VR drawing also consumes backend-owned scissor/depth/blend state,
blend/depth state query-restore, depth clear masks, active texture unit blend/depth state query-restore, depth clear masks, active texture unit
dispatch, and fallback 2D texture unbind dispatch; VR SDK start/stop now dispatches dispatch, and fallback 2D texture unbind dispatch; XR SDK start/stop now
through `PlatformServices` while retaining the existing Windows OpenVR bridge dispatches through `PlatformServices` and the tested desktop runtime policy
shape. Its retained callback endpoints now share `legacy_ui_gl_dispatch` 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 with app startup, app clear, app UI viewport/scissor, and command-convert
renderer state callbacks, so those files no longer duplicate local raw GL renderer state callbacks, so those files no longer duplicate local raw GL
adapter clusters for capability, blend, clear, viewport, scissor, active 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 | | 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 | | 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 | | 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 | | Quest/OVR | Android Quest files | `pp_platform_android_quest` | Compile/package gate |
| Focus/Wave | Android Focus files | `pp_platform_android_wave` | 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` - `glad`
- `Catch2` - `Catch2`
- Keep vendored until proven: - Keep vendored until proven:
- OpenVR - OpenVR only as the temporary desktop compatibility fallback while OpenXR is
introduced behind `pp_platform_vr`
- OVR/Wave SDKs - OVR/Wave SDKs
- Wacom WinTab - Wacom WinTab
- AppCenter - 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 retained settings writes, recording lifecycle calls, and legacy canvas/UI
adapters continue. adapters continue.
VR mode start/stop now enters `App` platform wrappers that dispatch through VR mode start/stop now enters `App` platform wrappers that dispatch through
`PlatformServices`; Windows keeps the retained OpenVR bridge in `PlatformServices`; the desktop runtime-selection policy in `pp_platform_api`
`WindowsPlatformServices`, while the legacy fallback reports unsupported VR prefers OpenXR and marks OpenVR as a legacy fallback. Windows still reaches the
startup on non-Windows platforms until their shells own the service. 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, `pano_cli plan-about-menu` exposes app-core planning for About menu help,
about, what's-new, crash-test, and performance-test commands, including about, what's-new, crash-test, and performance-test commands, including
versioned what's-new labels, diagnostic gating, and no-canvas performance-test 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 current iOS Objective-C bridge in the legacy adapter while removing iOS branches
from `App::init_menu_tools` and `LegacyToolsMenuServices`. from `App::init_menu_tools` and `LegacyToolsMenuServices`.
App VR lifecycle start/stop now asks `PlatformServices`, preserving the current App VR lifecycle start/stop now asks `PlatformServices`, preserving the current
Windows OpenVR startup/shutdown bridge in `WindowsPlatformServices` while Windows OpenVR startup/shutdown bridge as the selected legacy fallback in
non-Windows fallback adapters keep the existing unsupported/no-op behavior. `WindowsPlatformServices` while non-Windows fallback adapters keep the existing
unsupported/no-op behavior.
Canvas image export publishing and explicit persistent-storage flushes now Canvas image export publishing and explicit persistent-storage flushes now
dispatch through `PlatformServices` too, preserving iOS photo-library export dispatch through `PlatformServices` too, preserving iOS photo-library export
publication and WebGL filesystem sync behavior in the legacy adapter while 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`. now share `legacy_ui_gl_dispatch`.
VR UI framebuffer viewport and scissor-test setup now also consumes those VR UI framebuffer viewport and scissor-test setup now also consumes those
`pp_renderer_gl` contracts, keeping desktop and VR UI rendering aligned while `pp_renderer_gl` contracts, keeping desktop and VR UI rendering aligned while
the retained OpenVR app path is split incrementally; its retained callback the desktop XR path moves from the retained OpenVR app path toward OpenXR; its
endpoints now reuse the shared UI GL bridge. retained callback endpoints now reuse the shared UI GL bridge.
VR draw blend/depth state snapshots, transitions, restore, and depth-buffer VR draw blend/depth state snapshots, transitions, restore, and depth-buffer
clears, active texture unit switches, and fallback 2D texture unbinds now use clears, active texture unit switches, and fallback 2D texture unbinds now use
generic tested `pp_renderer_gl` capability query/apply, clear, active-texture, 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. VR controllers.
- Platform services: clipboard, file picker, save picker, directory picker, - Platform services: clipboard, file picker, save picker, directory picker,
share/display file, keyboard show/hide, cursor visibility. 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. iOS/macOS, Linux, WebGL.
- Cloud/network: upload, download, browse, license/check flows. - Cloud/network: upload, download, browse, license/check flows.
- Recording/export: PBO readbacks, MP4 encoder, timelapse frames. - Recording/export: PBO readbacks, MP4 encoder, timelapse frames.

View File

@@ -23,6 +23,34 @@ PlatformFamily current_platform_family() noexcept
#endif #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 bool platform_deletes_recorded_files_on_clear(PlatformFamily family) noexcept
{ {
return family == PlatformFamily::ios || family == PlatformFamily::macos; return family == PlatformFamily::ios || family == PlatformFamily::macos;

View File

@@ -17,8 +17,25 @@ enum class PlatformFamily {
webgl, 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]] 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_deletes_recorded_files_on_clear(PlatformFamily family) noexcept;
[[nodiscard]] bool platform_publishes_exported_images(PlatformFamily family) noexcept; [[nodiscard]] bool platform_publishes_exported_images(PlatformFamily family) noexcept;
[[nodiscard]] bool platform_flushes_persistent_storage(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); 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 build_supported_files_filter(const std::vector<std::string>& types)
{ {
std::string filter = "Supported Files ("; std::string filter = "Supported Files (";
@@ -442,12 +451,33 @@ public:
[[nodiscard]] bool start_vr_mode() override [[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 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 void pick_image(pp::platform::PickedPathCallback callback) override
@@ -572,6 +602,9 @@ public:
(void)suggested_name; (void)suggested_name;
callback(std::string(path), false); 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)); 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) void platform_policy_preserves_document_browse_roots(pp::tests::Harness& harness)
{ {
const auto ios_roots = pp::platform::platform_document_browse_roots( const auto ios_roots = pp::platform::platform_document_browse_roots(
@@ -1053,6 +1086,9 @@ int main()
harness.run( harness.run(
"platform policy preserves recording and export storage rules", "platform policy preserves recording and export storage rules",
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 document browse roots", platform_policy_preserves_document_browse_roots);
harness.run( harness.run(
"platform policy preserves picker and prepared file rules", "platform policy preserves picker and prepared file rules",