From 28a25108c6d2f58f76a0cc7f8f31886723000e41 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 09:48:48 +0000 Subject: [PATCH] Run the same-device Vulkan OpenXR backend on desktop Linux Lepton's Vulkan driver has no external memory or sync fd extensions, so the Steam Frame gets a native SteamOS build instead. Its backend is the PC's: OpenXR creates Dawn's own Vulkan instance and device, and the eyes are copied into the swapchain on Dawn's queue, with nothing shared between devices. - openxr_vulkan_win32 and Aurora's vulkan_win32_interop now compile on desktop Linux. Dawn links statically there, so the bridge calls the patched package's hooks directly, and exists only when the package's aurora-dawn.json declares the Vulkan hook ABI (AuroraDawnProvider turns AuroraVulkanAbi into AURORA_DAWN_VULKAN_HOOKS); a stock Dawn keeps the stubs and VR falls back to the desktop. - openxr_integration.cpp gains a Linux branch: XR_KHR_vulkan_enable2, the timespec clock, refresh rate, performance level, the Frame controllers and eye gaze; fragment density maps are requested on Linux as on the Quest (fdm.cpp and gpu.cpp, AURORA_FDM=0/1 overrides). - Controller motion uses XR_KHR_convert_timespec_time on any Linux. - CMake: Vulkan headers are fetched for Linux OpenXR builds, the headset option is MKW_HEADSET on Android and Linux, and Linux aarch64 builds take MKW_LINUX_CPU (cortex-x4 for the Steam Frame, else native). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3 --- android/app/build.gradle.kts | 4 +- aurora-main/cmake/AuroraDawnProvider.cmake | 9 +++++ aurora-main/cmake/aurora_core.cmake | 8 ++++ aurora-main/lib/webgpu/fdm.cpp | 8 ++-- aurora-main/lib/webgpu/gpu.cpp | 7 ++++ .../lib/webgpu/vulkan_win32_interop.cpp | 38 +++++++++++++++++-- docs/steam-frame.md | 2 +- runtime/CMakeLists.txt | 35 +++++++++-------- runtime/cmake/PublicProducts.cmake | 9 ++++- runtime/include/vr/openxr_vulkan_win32.h | 6 ++- runtime/src/vr/openxr_input.cpp | 9 ++++- runtime/src/vr/openxr_integration.cpp | 30 +++++++++++++-- runtime/src/vr/openxr_vulkan_win32.cpp | 8 +++- 13 files changed, 137 insertions(+), 36 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index b2c42d6..beccbc4 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -187,7 +187,7 @@ android { } // Valve's Steam Frame, through Lepton (SteamOS's Android layer) and SteamVR's Android // OpenXR runtime. Its manifest (src/steamFrame) drops the Horizon OS entries, and - // MKW_ANDROID_HEADSET gives the runtime the Frame's defaults (runtime_config.h). + // MKW_HEADSET gives the runtime the Frame's defaults (runtime_config.h). create("steamFrame") { dimension = "headset" // Horizon OS only; src/steamFrame/AndroidManifest.xml removes the entry. @@ -199,7 +199,7 @@ android { cmake { arguments += listOf( "-DMKW_ANDROID_CPU=${headsetCpus.getValue("steamFrame")}", - "-DMKW_ANDROID_HEADSET=steam_frame", + "-DMKW_HEADSET=steam_frame", ) } } diff --git a/aurora-main/cmake/AuroraDawnProvider.cmake b/aurora-main/cmake/AuroraDawnProvider.cmake index 0a649dc..35214a0 100644 --- a/aurora-main/cmake/AuroraDawnProvider.cmake +++ b/aurora-main/cmake/AuroraDawnProvider.cmake @@ -268,7 +268,10 @@ elseif (_aurora_dawn_provider STREQUAL "package") # A package built with Aurora's patches (android/Build-QuestDawn.ps1) describes them in # aurora-dawn.json. Only such a package has the fragment density map ABI # (include/aurora/dawn_fdm_abi.h); aurora_core compiles its callers against it. + # The Vulkan hook ABI (include/aurora/dawn_vulkan_abi.h) is the other half: a package built by + # Launcher/build-dawn-linux.sh carries it, and the Linux OpenXR backend needs it. set(AURORA_DAWN_FDM_ABI 0 PARENT_SCOPE) + set(AURORA_DAWN_VULKAN_HOOKS_ABI 0 PARENT_SCOPE) if (EXISTS "${_dawn_pkg_dir}/aurora-dawn.json") file(READ "${_dawn_pkg_dir}/aurora-dawn.json" _aurora_dawn_manifest) string(JSON _aurora_dawn_fdm_abi ERROR_VARIABLE _aurora_dawn_manifest_error @@ -277,6 +280,12 @@ elseif (_aurora_dawn_provider STREQUAL "package") set(AURORA_DAWN_FDM_ABI ${_aurora_dawn_fdm_abi} PARENT_SCOPE) message(STATUS "aurora: Dawn package carries the fragment density map ABI ${_aurora_dawn_fdm_abi}") endif () + string(JSON _aurora_dawn_vulkan_abi ERROR_VARIABLE _aurora_dawn_manifest_error + GET "${_aurora_dawn_manifest}" AuroraVulkanAbi) + if (NOT _aurora_dawn_manifest_error AND _aurora_dawn_vulkan_abi GREATER 0) + set(AURORA_DAWN_VULKAN_HOOKS_ABI ${_aurora_dawn_vulkan_abi} PARENT_SCOPE) + message(STATUS "aurora: Dawn package carries the Vulkan hook ABI ${_aurora_dawn_vulkan_abi}") + endif () endif () _aurora_dawn_set_platform_backends() diff --git a/aurora-main/cmake/aurora_core.cmake b/aurora-main/cmake/aurora_core.cmake index 5ad4c73..f8f979d 100644 --- a/aurora-main/cmake/aurora_core.cmake +++ b/aurora-main/cmake/aurora_core.cmake @@ -39,6 +39,14 @@ if (AURORA_ENABLE_GX) # of producing unresolved interop references. if (CMAKE_SYSTEM_NAME STREQUAL Windows) target_sources(aurora_core PRIVATE lib/webgpu/d3d12_interop.cpp lib/webgpu/vulkan_win32_interop.cpp) + elseif (CMAKE_SYSTEM_NAME STREQUAL Linux) + # The same-device Vulkan bridge, for the desktop Linux OpenXR backend (SteamOS on the Steam + # Frame). Static Dawn on Linux is linked directly, so the bridge is compiled in only against + # a package with Aurora's Vulkan hooks; otherwise the file is its C ABI stubs. + target_sources(aurora_core PRIVATE lib/webgpu/vulkan_win32_interop.cpp) + if (AURORA_DAWN_VULKAN_HOOKS_ABI) + target_compile_definitions(aurora_core PRIVATE AURORA_DAWN_VULKAN_HOOKS=${AURORA_DAWN_VULKAN_HOOKS_ABI}) + endif () endif () # Android/Vulkan counterpart: the AHardwareBuffer stereo bridge. The file # compiles to C ABI stubs on every other platform so the runtime's OpenXR diff --git a/aurora-main/lib/webgpu/fdm.cpp b/aurora-main/lib/webgpu/fdm.cpp index 0e647c9..f48c5a0 100644 --- a/aurora-main/lib/webgpu/fdm.cpp +++ b/aurora-main/lib/webgpu/fdm.cpp @@ -6,8 +6,10 @@ #include #include -// AURORA_DAWN_FDM carries the ABI version of the Dawn package's patches (AuroraDawnProvider.cmake). -#if defined(__ANDROID__) && defined(AURORA_DAWN_FDM) +// AURORA_DAWN_FDM carries the ABI version of the Dawn package's patches (AuroraDawnProvider.cmake): +// the Quest's (android/Build-QuestDawn.ps1) or desktop Linux's (Launcher/build-dawn-linux.sh), both +// linked statically. +#if (defined(__ANDROID__) || defined(__linux__)) && defined(AURORA_DAWN_FDM) #include static_assert(AURORA_DAWN_FDM == AURORA_DAWN_FDM_ABI, "The Dawn package's fragment density map ABI does not match include/aurora/dawn_fdm_abi.h"); @@ -53,7 +55,7 @@ void device_created() noexcept { #else if (g_requested) { Log.warn("Fragment density maps: unavailable; this build links a Dawn without Aurora's patches " - "(android/Build-QuestDawn.ps1)"); + "(android/Build-QuestDawn.ps1, Launcher/build-dawn-linux.sh)"); } #endif } diff --git a/aurora-main/lib/webgpu/gpu.cpp b/aurora-main/lib/webgpu/gpu.cpp index 02c0690..0876bb6 100644 --- a/aurora-main/lib/webgpu/gpu.cpp +++ b/aurora-main/lib/webgpu/gpu.cpp @@ -830,6 +830,13 @@ bool initialize(AuroraBackend auroraBackend) { const int fdmOverride = android_debug::property_int("debug.wiicompiled.fdm", -1); fdm::request(g_backendType == wgpu::BackendType::Vulkan && (fdmOverride >= 0 ? fdmOverride == 1 : g_config.xrFragmentDensityMap)); +#elif defined(__linux__) + // Desktop Linux (SteamOS on the Steam Frame), with a Dawn built by Launcher/build-dawn-linux.sh. + // AURORA_FDM=0 or 1 overrides the settings, as debug.wiicompiled.fdm does on the Quest. + const char* fdmOverride = std::getenv("AURORA_FDM"); + fdm::request(g_backendType == wgpu::BackendType::Vulkan && + (fdmOverride != nullptr && *fdmOverride != '\0' ? std::strcmp(fdmOverride, "1") == 0 + : g_config.xrFragmentDensityMap)); #endif const auto future = g_adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly, diff --git a/aurora-main/lib/webgpu/vulkan_win32_interop.cpp b/aurora-main/lib/webgpu/vulkan_win32_interop.cpp index 45dba21..93f44f6 100644 --- a/aurora-main/lib/webgpu/vulkan_win32_interop.cpp +++ b/aurora-main/lib/webgpu/vulkan_win32_interop.cpp @@ -4,8 +4,27 @@ #include "../stereo.hpp" #include "../stereo_overlay.hpp" #include "gpu.hpp" -#if defined(_WIN32) && defined(WEBGPU_DAWN) && defined(DAWN_ENABLE_BACKEND_VULKAN) +// Windows loads the patched webgpu_dawn.dll's exports at run time. Desktop Linux (SteamOS on the +// Steam Frame) links Dawn statically, so there the bridge exists only against a package built with +// the hooks (AURORA_DAWN_VULKAN_HOOKS, from its aurora-dawn.json) and calls them directly. +#if defined(WEBGPU_DAWN) && defined(DAWN_ENABLE_BACKEND_VULKAN) && \ + (defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__) && defined(AURORA_DAWN_VULKAN_HOOKS))) +#if defined(_WIN32) #include +#else +static_assert(AURORA_DAWN_VULKAN_HOOKS == AURORA_DAWN_VULKAN_ABI, + "The Dawn package's Vulkan hook ABI does not match include/aurora/dawn_vulkan_abi.h"); +extern "C" { +uint32_t AuroraDawnVulkanVersion(void); +int AuroraDawnVulkanConfigure(const AuroraDawnVulkanHooks* hooks); +int AuroraDawnVulkanGetHandles(void* device, AuroraDawnVulkanHandles* handles); +void* AuroraDawnVulkanWrap(void* device, const void* textureDescriptor, uint64_t image); +int AuroraDawnVulkanRelease(void* device, void* const* textures, uint32_t count); +void* AuroraDawnVulkanLock(void* device); +void AuroraDawnVulkanUnlock(void* guard); +int AuroraDawnVulkanDrain(void* device); +} +#endif #include #include #include @@ -23,6 +42,17 @@ struct Api { AuroraDawnVulkanUnlockFn unlock = nullptr; AuroraDawnVulkanDrainFn drain = nullptr; bool Load() { +#if !defined(_WIN32) + if (AuroraDawnVulkanVersion() != AURORA_DAWN_VULKAN_ABI) return false; + configure = &AuroraDawnVulkanConfigure; + handles = &AuroraDawnVulkanGetHandles; + wrap = &AuroraDawnVulkanWrap; + release = &AuroraDawnVulkanRelease; + lock = &AuroraDawnVulkanLock; + unlock = &AuroraDawnVulkanUnlock; + drain = &AuroraDawnVulkanDrain; + return true; +#else HMODULE module = GetModuleHandleW(L"webgpu_dawn.dll"); if (!module) return false; auto version = reinterpret_cast(GetProcAddress(module, "AuroraDawnVulkanVersion")); @@ -37,6 +67,7 @@ struct Api { LOAD(drain, AuroraDawnVulkanDrainFn, "AuroraDawnVulkanDrain"); #undef LOAD return true; +#endif } } api; int64_t VkFormat(wgpu::TextureFormat format) { @@ -222,8 +253,9 @@ void* aurora_vulkan_win32_lock_queue() { void aurora_vulkan_win32_unlock_queue(void* guard) { aurora::vulkan_win32::api.unlock(guard); } #else // C ABI stubs keep the runtime's OpenXR integration linkable on Windows GX -// builds whose Dawn has no Vulkan backend; the backend then reports that the -// bridge is unavailable and the game falls back to the desktop renderer. +// builds whose Dawn has no Vulkan backend, and on Linux against a Dawn without +// Aurora's hooks; the backend then reports that the bridge is unavailable and +// the game falls back to the desktop renderer. bool aurora_vulkan_win32_configure(const AuroraDawnVulkanHooks*) { return false; } bool aurora_vulkan_win32_get_handles(AuroraDawnVulkanHandles* handles, int64_t* format) { if (handles) *handles = {}; diff --git a/docs/steam-frame.md b/docs/steam-frame.md index eb92dcf..c12bfce 100644 --- a/docs/steam-frame.md +++ b/docs/steam-frame.md @@ -22,7 +22,7 @@ to a single shared device first. | | Quest flavours | `steamFrame` | | --- | --- | --- | | CPU target (`kit.json` `androidCpu`) | `cortex-a77` (`kryo` on Quest 1) | `cortex-x4` | -| `MKW_ANDROID_HEADSET` | `quest` | `steam_frame` (defines `MKW_HEADSET_STEAM_FRAME`) | +| `MKW_HEADSET` | empty | `steam_frame` (defines `MKW_HEADSET_STEAM_FRAME`) | | Library entry | `LauncherActivity` (Quest 1: `QuestActivity`) | `FrameEntryActivity` | | Horizon OS manifest entries | present | removed | | `[vr] refresh_rate` default | `0` (the headset's own) | `120` | diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index bbda761..e977a35 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -37,18 +37,19 @@ option(MKW_BUILD_PRODUCTS "Build translated WiiCompiled product targets" ON) # aurora-main and its third-party tree stay unaffected. set(MKW_PROJECT_COMPILE_DEFINITIONS NOMINMAX) -# The Android headset family the build is for (the Gradle flavour passes it). The -# Steam Frame build runs under SteamVR's Android runtime rather than Horizon OS and -# takes its own defaults (runtime_config.h) through MKW_HEADSET_STEAM_FRAME. The -# definition reaches every target below, so the game kit's recorded compile flags -# carry it too. -if(MKW_PLATFORM_ANDROID) - set(MKW_ANDROID_HEADSET "quest" CACHE STRING "Android headset family: quest (Meta Quest, any flavour) or steam_frame") - set_property(CACHE MKW_ANDROID_HEADSET PROPERTY STRINGS quest steam_frame) - if(MKW_ANDROID_HEADSET STREQUAL "steam_frame") +# The headset a standalone build is for: the Android app's Gradle flavour passes +# it, and so does a native SteamOS build for the Steam Frame +# (Launcher/local-build.sh --headset steam_frame). The Steam Frame takes its own +# defaults (runtime_config.h) through MKW_HEADSET_STEAM_FRAME. The definition +# reaches every target below, so the Quest game kit's recorded compile flags carry +# it too. +if(MKW_PLATFORM_ANDROID OR MKW_PLATFORM_LINUX) + set(MKW_HEADSET "" CACHE STRING "Headset the build targets: empty (a Meta Quest on Android, any PC headset elsewhere) or steam_frame") + set_property(CACHE MKW_HEADSET PROPERTY STRINGS "" steam_frame) + if(MKW_HEADSET STREQUAL "steam_frame") list(APPEND MKW_PROJECT_COMPILE_DEFINITIONS MKW_HEADSET_STEAM_FRAME=1) - elseif(NOT MKW_ANDROID_HEADSET STREQUAL "quest") - message(FATAL_ERROR "MKW_ANDROID_HEADSET must be quest or steam_frame, not '${MKW_ANDROID_HEADSET}'") + elseif(NOT MKW_HEADSET STREQUAL "") + message(FATAL_ERROR "MKW_HEADSET must be empty or steam_frame, not '${MKW_HEADSET}'") endif() endif() @@ -148,10 +149,10 @@ set(MKW_TRANSLATED_COMPILE_JOBS 0 CACHE STRING Scheduling only - never affects output bytes, so it is deliberately outside the canonical flag fingerprint.") set(MKW_CPPWINRT_INCLUDE_DIR "" CACHE PATH "Optional self-contained C++/WinRT include directory") -# The functional backend is currently Windows D3D12, so only Windows defaults -# to carrying the loader. Linux keeps the capability-gated Vulkan scaffold -# available to explicit developer builds without making a nonfunctional SDK -# fetch part of every normal build. +# Windows and Android carry the loader by default. Desktop Linux has the +# same-device Vulkan backend too, but it needs a Dawn built with Aurora's patches +# (Launcher/build-dawn-linux.sh), so it stays opt-in: Launcher/local-build.sh +# --openxr, as the Steam Frame's native SteamOS build uses it. set(MKW_ENABLE_OPENXR_DEFAULT OFF) if(MKW_PLATFORM_WINDOWS OR MKW_PLATFORM_ANDROID) set(MKW_ENABLE_OPENXR_DEFAULT ON) @@ -301,7 +302,9 @@ if(MKW_PROJECT_COMPILE_DEFINITIONS) add_compile_definitions(${MKW_PROJECT_COMPILE_DEFINITIONS}) endif() -if(MKW_PLATFORM_WINDOWS AND MKW_ENABLE_OPENXR) +# The same-device Vulkan backend (openxr_vulkan_win32.cpp, on Windows and desktop +# Linux) includes vulkan.h; the pin keeps it independent of the build machine. +if((MKW_PLATFORM_WINDOWS OR MKW_PLATFORM_LINUX) AND MKW_ENABLE_OPENXR) include(FetchContent) FetchContent_Declare(vulkan_headers URL "https://github.com/KhronosGroup/Vulkan-Headers/archive/015e25c3c91b70eb1a754d36fb14c4ba6ad9b0b9.tar.gz" diff --git a/runtime/cmake/PublicProducts.cmake b/runtime/cmake/PublicProducts.cmake index 4cc7ba2..3769473 100644 --- a/runtime/cmake/PublicProducts.cmake +++ b/runtime/cmake/PublicProducts.cmake @@ -452,7 +452,14 @@ elseif(MKW_PLATFORM_ANDROID) set(MKW_ANDROID_CPU "cortex-a77" CACHE STRING "AArch64 -mcpu target for the Android products") set(MKW_BASELINE_ARCH_FLAG -mcpu=${MKW_ANDROID_CPU}) elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$") - set(MKW_BASELINE_ARCH_FLAG -mcpu=native) + # A cross-build names its CPU; the Steam Frame's Snapdragon 8 Gen 3 exposes SVE (cortex-x4). + if(MKW_HEADSET STREQUAL "steam_frame") + set(_mkw_linux_cpu_default "cortex-x4") + else() + set(_mkw_linux_cpu_default "native") + endif() + set(MKW_LINUX_CPU "${_mkw_linux_cpu_default}" CACHE STRING "AArch64 -mcpu target for the Linux products") + set(MKW_BASELINE_ARCH_FLAG -mcpu=${MKW_LINUX_CPU}) else() set(MKW_BASELINE_ARCH_FLAG "") endif() diff --git a/runtime/include/vr/openxr_vulkan_win32.h b/runtime/include/vr/openxr_vulkan_win32.h index 638290e..98c4be1 100644 --- a/runtime/include/vr/openxr_vulkan_win32.h +++ b/runtime/include/vr/openxr_vulkan_win32.h @@ -2,7 +2,9 @@ #pragma once -#if defined(MKW_ENABLE_OPENXR) && defined(_WIN32) +// Despite the name, also the desktop Linux backend (SteamOS on the Steam Frame): nothing in it is +// Windows-specific. The Quest has its own two-device backend (openxr_vulkan.h). +#if defined(MKW_ENABLE_OPENXR) && (defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__))) #include "vr/openxr_backend.h" #include "vr/openxr_runtime.h" @@ -112,4 +114,4 @@ private: } // namespace mkw::vr -#endif // defined(MKW_ENABLE_OPENXR) && defined(_WIN32) +#endif // defined(MKW_ENABLE_OPENXR) && (defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__))) diff --git a/runtime/src/vr/openxr_input.cpp b/runtime/src/vr/openxr_input.cpp index 3436ba3..8929226 100644 --- a/runtime/src/vr/openxr_input.cpp +++ b/runtime/src/vr/openxr_input.cpp @@ -35,6 +35,8 @@ #if defined(__ANDROID__) #include +#endif +#if defined(__linux__) #include #endif @@ -275,6 +277,9 @@ bool Injected(const char*) { return false; } #if defined(_WIN32) using ConvertNowToXrTime = XrResult(XRAPI_PTR*)(XrInstance, const LARGE_INTEGER*, XrTime*); +#elif defined(__linux__) +// Desktop Linux (SteamOS on the Steam Frame); Android declares it with its injection above. +using ConvertNowToXrTime = XrResult(XRAPI_PTR*)(XrInstance, const struct timespec*, XrTime*); #endif #endif @@ -666,7 +671,7 @@ void OpenXRInput::LoadInputClock() { if (enabled("XR_KHR_win32_convert_performance_counter_time")) { m_runtime->GetInstanceProcAddress("xrConvertWin32PerformanceCounterToTimeKHR", &function); } -#elif defined(__ANDROID__) +#elif defined(__linux__) if (enabled("XR_KHR_convert_timespec_time")) { m_runtime->GetInstanceProcAddress("xrConvertTimespecTimeToTimeKHR", &function); } @@ -692,7 +697,7 @@ XrTime OpenXRInput::InputSampleTime(XrTime predicted_display_time) const { &counter, &now))) { return predicted_display_time; } -#elif defined(__ANDROID__) +#elif defined(__linux__) timespec spec{}; if (clock_gettime(CLOCK_MONOTONIC, &spec) != 0 || XR_FAILED(reinterpret_cast(m_convert_now_to_xr_time)(m_runtime->Instance(), diff --git a/runtime/src/vr/openxr_integration.cpp b/runtime/src/vr/openxr_integration.cpp index be5f9c9..695b73b 100644 --- a/runtime/src/vr/openxr_integration.cpp +++ b/runtime/src/vr/openxr_integration.cpp @@ -48,6 +48,14 @@ #define XR_USE_TIMESPEC #include #define MKW_OPENXR_GRAPHICS_BACKEND 1 +#elif defined(__linux__) +// Desktop Linux, SteamOS on the Steam Frame above all: the PC's same-device Vulkan backend, with +// Dawn's own device bound to the session. +#include "vr/openxr_vulkan_win32.h" +#include +#define XR_USE_TIMESPEC +#include +#define MKW_OPENXR_GRAPHICS_BACKEND 1 #else #define MKW_OPENXR_GRAPHICS_BACKEND 0 #endif @@ -85,9 +93,12 @@ void ConfigurePolicy(bool enabled) noexcept { #if defined(_WIN32) using GraphicsBackend = OpenXRWindowsBackend; -#else +#elif defined(__ANDROID__) using GraphicsBackend = OpenXRVulkanBackend; inline constexpr const char* kGraphicsBackendName = "Vulkan"; +#else +using GraphicsBackend = OpenXRWindowsVulkanBackend; +inline constexpr const char* kGraphicsBackendName = "Vulkan"; #endif // Whether the immersive window's eyes can be aimed through the window, so that only the window is @@ -412,6 +423,16 @@ public: "XR_FB_display_refresh_rate", "XR_EXT_performance_settings", "XR_VALVE_frame_controller_interaction"}; AddHandMeshExtensions(config); +#elif defined(__linux__) && !defined(__ANDROID__) + // As the PC's Vulkan binding, plus the clock conversion Linux uses and the Steam Frame's + // eye gaze for eye-tracked foveation. + config.required_extensions = {"XR_KHR_vulkan_enable2"}; + config.optional_extensions = {"XR_KHR_convert_timespec_time", "XR_FB_display_refresh_rate", + "XR_EXT_performance_settings", "XR_VALVE_frame_controller_interaction"}; + if (RuntimeConfigFile::VrEyeTrackedFoveation()) { + config.optional_extensions.push_back("XR_EXT_eye_gaze_interaction"); + } + AddHandMeshExtensions(config); #else // Either Vulkan binding extension is acceptable; the backend picks // whichever the runtime enabled, preferring enable2. @@ -718,9 +739,10 @@ private: void ApplyGraphicsRequirements(AuroraConfig& aurora_config) { aurora_config.desiredBackend = kRequiredAuroraBackend; aurora_config.xrInterop = true; -#if defined(__ANDROID__) - // Foveated rendering: fragment density maps are decided with the device. They put a flag on - // every render pipeline, so a session launched with foveation off does without them. +#if !defined(_WIN32) + // Foveated rendering (the Quest's and desktop Linux's patched Dawn): fragment density maps + // are decided with the device. They put a flag on every render pipeline, so a session + // launched with foveation off does without them. aurora_config.xrFragmentDensityMap = RuntimeConfigFile::VrFoveation() != "off"; #endif #if defined(_WIN32) diff --git a/runtime/src/vr/openxr_vulkan_win32.cpp b/runtime/src/vr/openxr_vulkan_win32.cpp index 1515df8..bd6da99 100644 --- a/runtime/src/vr/openxr_vulkan_win32.cpp +++ b/runtime/src/vr/openxr_vulkan_win32.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#if defined(MKW_ENABLE_OPENXR) && defined(_WIN32) +#if defined(MKW_ENABLE_OPENXR) && (defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__))) // OpenXR's Vulkan structures are selected when openxr_platform.h is parsed. #define XR_USE_GRAPHICS_API_VULKAN @@ -180,7 +180,11 @@ public: if (XR_FAILED(get(runtime.Instance(), runtime.SystemId(), &requirements))) return Fail("Vulkan requirements query failed"); requirements_ = {requirements.minApiVersionSupported, requirements.maxApiVersionSupported}; AuroraDawnVulkanHooks hooks{this, CreateInstance, CreateDevice, GetPhysical}; +#if defined(_WIN32) if (!aurora_vulkan_win32_configure(&hooks)) return Fail("PC Vulkan OpenXR requires the custom Dawn library with Aurora Vulkan ABI 1; rebuild/install it with Launcher/Build-DawnVulkan.ps1"); +#else + if (!aurora_vulkan_win32_configure(&hooks)) return Fail("Linux Vulkan OpenXR requires a Dawn built with Aurora's patches (Aurora Vulkan ABI 1); build it with Launcher/build-dawn-linux.sh"); +#endif hooks_installed_ = true; { std::lock_guard lock(submission_mutex_); @@ -1315,4 +1319,4 @@ const std::string& OpenXRWindowsVulkanBackend::LastError() const { return m_impl } // namespace mkw::vr -#endif // defined(MKW_ENABLE_OPENXR) && defined(_WIN32) +#endif // defined(MKW_ENABLE_OPENXR) && (defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)))