mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-17 16:02:33 +02:00
OpenXR: keep controller input in Flat Vulkan mode
This commit is contained in:
1 parent
3ec01541fd
commit
bec9d1beae
7 files changed
+59
-30
No files matched your search
@@ -149,7 +149,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
||||
// both the instance and device; without them xrCreateSession will crash.
|
||||
Vulkan::VulkanExtensionRequirements vr_ext_requirements;
|
||||
bool vr_extensions_queried = false;
|
||||
if (g_Config.VRSessionActive())
|
||||
if (g_Config.OpenXRSessionActive())
|
||||
{
|
||||
vr_extensions_queried = Vulkan::VulkanOpenXR::PreQueryVulkanExtensions(vr_ext_requirements);
|
||||
if (!vr_extensions_queried)
|
||||
@@ -396,10 +396,10 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
||||
static_cast<int>(g_ActiveConfig.stereo_mode),
|
||||
g_ActiveConfig.VRSessionActive() ? "YES" : "NO",
|
||||
g_ActiveConfig.vr_flat_screen ? "YES" : "NO");
|
||||
if (g_ActiveConfig.VRSessionActive())
|
||||
if (g_ActiveConfig.OpenXRSessionActive())
|
||||
{
|
||||
auto openxr = std::make_unique<Vulkan::VulkanOpenXR>();
|
||||
if (!openxr->Initialize())
|
||||
if (!openxr->Initialize(!g_ActiveConfig.VRSessionActive()))
|
||||
{
|
||||
WARN_LOG_FMT(VIDEO, "OpenXR initialization failed; continuing without VR.");
|
||||
}
|
||||
@@ -424,7 +424,7 @@ void VideoBackend::Shutdown()
|
||||
#ifdef ENABLE_VR
|
||||
// Whether VR actually ran this session. Captured before the OpenXR objects are torn down so the
|
||||
// persist decision below can tell a VR game stop from a plain non-VR shutdown.
|
||||
const bool vr_active = Vulkan::g_openxr_vk != nullptr;
|
||||
const bool vr_active = Vulkan::g_openxr_vk && !Vulkan::g_openxr_vk->IsInputOnly();
|
||||
|
||||
// The OpenXR pacing thread can be inside xrEndFrame(), where the runtime accesses our graphics
|
||||
// queue. Stop it before waiting on the Vulkan device: vkDeviceWaitIdle() must not race an
|
||||
|
||||
@@ -547,9 +547,10 @@ bool VulkanOpenXR::PreQueryVulkanExtensions(VulkanExtensionRequirements& out)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanOpenXR::Initialize()
|
||||
bool VulkanOpenXR::Initialize(bool input_only)
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "OpenXR Vulkan: Starting initialization...");
|
||||
m_input_only = input_only;
|
||||
|
||||
// If PreQueryVulkanExtensions() was called, VR::g_openxr already exists.
|
||||
if (!VR::g_openxr)
|
||||
@@ -615,6 +616,13 @@ bool VulkanOpenXR::Initialize()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input_only)
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "OpenXR Vulkan: Starting input-only session for Flat Vulkan controls.");
|
||||
VR::g_openxr->StartInputOnlyFrameThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
INFO_LOG_FMT(VIDEO, "OpenXR Vulkan: Creating swapchains...");
|
||||
if (!CreateSwapchains())
|
||||
{
|
||||
|
||||
@@ -104,10 +104,12 @@ public:
|
||||
// OpenXR runtime, and stores the OpenXRManager in VR::g_openxr for later reuse.
|
||||
static bool PreQueryVulkanExtensions(VulkanExtensionRequirements& out_requirements);
|
||||
|
||||
// Full initialization: creates Vulkan-bound XrSession, reference space, and
|
||||
// per-eye swapchains. If PreQueryVulkanExtensions() was called, reuses the
|
||||
// existing VR::g_openxr; otherwise creates a new one.
|
||||
bool Initialize();
|
||||
// Creates a Vulkan-bound XrSession and reference space. Normal VR also creates per-eye
|
||||
// swapchains; input_only starts an empty-layer pacing loop for controller input while the
|
||||
// game's Vulkan window surface remains the output. If PreQueryVulkanExtensions() was called,
|
||||
// reuses the existing VR::g_openxr; otherwise creates a new one.
|
||||
bool Initialize(bool input_only = false);
|
||||
bool IsInputOnly() const { return m_input_only; }
|
||||
|
||||
// Tears down swapchains and resets g_openxr.
|
||||
void Shutdown();
|
||||
@@ -205,6 +207,7 @@ private:
|
||||
// True when any swapchain framebuffer carries a fragment density map; PostProcessing
|
||||
// compiles matching foveated pipelines when set.
|
||||
bool m_foveated = false;
|
||||
bool m_input_only = false;
|
||||
|
||||
// Reused per-frame composition data (avoids per-frame heap allocation).
|
||||
std::array<XrCompositionLayerProjectionView, 2> m_projection_views{};
|
||||
|
||||
@@ -1073,19 +1073,26 @@ void OpenXRManager::StartFrameThread()
|
||||
INFO_LOG_FMT(OPENXR, "OpenXR: XR pacing thread started.");
|
||||
}
|
||||
|
||||
void OpenXRManager::StartInputOnlyFrameThread()
|
||||
{
|
||||
m_input_only_frame_thread.store(true, std::memory_order_release);
|
||||
StartFrameThread();
|
||||
}
|
||||
|
||||
void OpenXRManager::StopFrameThread()
|
||||
{
|
||||
if (!m_frame_thread.joinable())
|
||||
return;
|
||||
|
||||
m_frame_thread_should_exit.store(true, std::memory_order_release);
|
||||
if (m_frame_thread.joinable())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_publish_mutex);
|
||||
m_publish_cv.notify_all();
|
||||
m_frame_thread_should_exit.store(true, std::memory_order_release);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_publish_mutex);
|
||||
m_publish_cv.notify_all();
|
||||
}
|
||||
m_frame_thread.join();
|
||||
m_frame_thread_running.store(false, std::memory_order_release);
|
||||
INFO_LOG_FMT(OPENXR, "OpenXR: XR pacing thread stopped.");
|
||||
}
|
||||
m_frame_thread.join();
|
||||
m_frame_thread_running.store(false, std::memory_order_release);
|
||||
INFO_LOG_FMT(OPENXR, "OpenXR: XR pacing thread stopped.");
|
||||
m_input_only_frame_thread.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void OpenXRManager::ShutdownSession()
|
||||
@@ -1291,7 +1298,8 @@ void OpenXRManager::FrameThreadLoop()
|
||||
|
||||
while (!m_frame_thread_should_exit.load(std::memory_order_acquire))
|
||||
{
|
||||
const bool eager_heartbeat = g_ActiveConfig.vr_eager_heartbeat;
|
||||
const bool eager_heartbeat = m_input_only_frame_thread.load(std::memory_order_acquire) ||
|
||||
g_ActiveConfig.vr_eager_heartbeat;
|
||||
const u64 cycle_start_us = Common::Timer::NowUs();
|
||||
if (!PollEvents())
|
||||
break;
|
||||
|
||||
@@ -219,6 +219,9 @@ public:
|
||||
// Started by SetSwapchain() when UseXRPacingThread is enabled; when inactive, the
|
||||
// legacy synchronous flow (Presenter calling Wait/Begin/End inline) applies.
|
||||
void StartFrameThread();
|
||||
// Run the XR frame protocol without a graphics swapchain so OpenXR controller actions remain
|
||||
// live while Flat Vulkan mode renders only to its desktop surface.
|
||||
void StartInputOnlyFrameThread();
|
||||
void StopFrameThread();
|
||||
bool IsFrameThreadActive() const
|
||||
{
|
||||
@@ -637,6 +640,7 @@ private:
|
||||
std::thread m_frame_thread;
|
||||
std::atomic<bool> m_frame_thread_running{false};
|
||||
std::atomic<bool> m_frame_thread_should_exit{false};
|
||||
std::atomic<bool> m_input_only_frame_thread{false};
|
||||
std::mutex m_publish_mutex;
|
||||
std::condition_variable m_publish_cv;
|
||||
PublishedXRFrame m_published_frame; // guarded by m_publish_mutex
|
||||
|
||||
@@ -164,8 +164,7 @@ void VideoConfig::Refresh()
|
||||
color_correction.fHDRPaperWhiteNits = Config::Get(Config::GFX_CC_HDR_PAPER_WHITE_NITS);
|
||||
|
||||
stereo_mode = Config::Get(Config::GFX_STEREO_MODE);
|
||||
const OpenXRPresentationMode vr_presentation_mode =
|
||||
Config::Get(Config::GFX_VR_PRESENTATION_MODE);
|
||||
vr_presentation_mode = Config::Get(Config::GFX_VR_PRESENTATION_MODE);
|
||||
bool vr_openxr_enabled = Config::Get(Config::GFX_VR_ENABLE_OPENXR);
|
||||
vr_flat_screen = Config::Get(Config::GFX_VR_FLAT_SCREEN);
|
||||
vr_stereo_screen = Config::Get(Config::GFX_VR_STEREO_SCREEN);
|
||||
|
||||
@@ -388,12 +388,13 @@ struct VideoConfig final
|
||||
// them natively over each eye (like a Fullscreen override) instead of capturing them onto the
|
||||
// virtual screen. Downscaled copies always qualify; full-res copies only when blended.
|
||||
bool vr_auto_native_efb_effects = true;
|
||||
// Render the whole game as a flat mono panel in the VR scene (StereoMode::Off), while the
|
||||
// OpenXR session still runs. Used by the "Launch games in VR = off" cinema path on Quest.
|
||||
bool vr_flat_screen = false;
|
||||
// Render the game using classic SBS stereoscopy and submit it as a stereoscopic virtual screen
|
||||
// (two OpenXR quad layers). Bypasses the immersive per-eye reprojection path.
|
||||
bool vr_stereo_screen = false;
|
||||
// Render the whole game as a flat mono panel in the VR scene (StereoMode::Off), while the
|
||||
// OpenXR session still runs. Used by the "Launch games in VR = off" cinema path on Quest.
|
||||
bool vr_flat_screen = false;
|
||||
// Render the game using classic SBS stereoscopy and submit it as a stereoscopic virtual screen
|
||||
// (two OpenXR quad layers). Bypasses the immersive per-eye reprojection path.
|
||||
bool vr_stereo_screen = false;
|
||||
OpenXRPresentationMode vr_presentation_mode = OpenXRPresentationMode::Legacy;
|
||||
float vr_screen_distance = 1.5f;
|
||||
float vr_screen_size = 1.5f;
|
||||
float vr_hud_thickness = 0.0f; // World-space depth (m) spread across a 2D layer's ortho-Z (0 = flat)
|
||||
@@ -489,13 +490,19 @@ float vr_screen_distance = 1.5f;
|
||||
return bPreferVSForLinePointExpansion;
|
||||
}
|
||||
bool MultisamplingEnabled() const { return iMultisamples > 1; }
|
||||
// True whenever an OpenXR session must run: per-eye stereo (StereoMode::OpenXR), the flat
|
||||
// mono panel path, or the classic-SBS stereoscopic virtual-screen path. Use this for session
|
||||
// lifecycle gates; keep immersive stereo-specific rendering on stereo_mode == OpenXR.
|
||||
// True whenever game content is rendered/presented through OpenXR: immersive stereo, the flat
|
||||
// mono panel path, or the classic-SBS stereoscopic virtual-screen path. Flat Vulkan output can
|
||||
// separately run OpenXR for controller input without switching its renderer or presentation.
|
||||
bool VRSessionActive() const
|
||||
{
|
||||
return stereo_mode == StereoMode::OpenXR || vr_flat_screen || vr_stereo_screen;
|
||||
}
|
||||
// Flat Vulkan output still keeps an OpenXR session alive for Frame controller actions.
|
||||
// The session submits no composition layers, so rendering/presentation stays on the window.
|
||||
bool OpenXRSessionActive() const
|
||||
{
|
||||
return VRSessionActive() || vr_presentation_mode == OpenXRPresentationMode::Vulkan;
|
||||
}
|
||||
// Hold one head pose for every draw of a game frame, refreshed only at the XFB-copy
|
||||
// boundary. Required whenever ImmediateXFB is off: presentation then happens at VI
|
||||
// time, interleaved with the NEXT frame's draw stream, so a per-draw pose refresh
|
||||
|
||||
Reference in new issue
Block a user