From 8c147519321a0ae13e2c238be33ff4b233849e0a Mon Sep 17 00:00:00 2001 From: Matthieu Bucchianeri Date: Sun, 10 Sep 2023 14:45:02 -0700 Subject: [PATCH] Workaround for PVR Home race condition stealing the session. --- pimax-openxr/runtime.h | 2 +- pimax-openxr/session.cpp | 7 ++-- pimax-openxr/system.cpp | 69 +++++++++++++++++++++++++++++----------- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/pimax-openxr/runtime.h b/pimax-openxr/runtime.h index 85453a5..5b6b229 100644 --- a/pimax-openxr/runtime.h +++ b/pimax-openxr/runtime.h @@ -362,7 +362,7 @@ namespace pimax_openxr { // system.cpp void fillDisplayDeviceInfo(); - void ensurePvrSession(); + bool ensurePvrSession(); // session.cpp void updateSessionState(bool forceSendEvent = false); diff --git a/pimax-openxr/session.cpp b/pimax-openxr/session.cpp index 28004cc..1a3c190 100644 --- a/pimax-openxr/session.cpp +++ b/pimax-openxr/session.cpp @@ -59,6 +59,8 @@ namespace pimax_openxr { return XR_ERROR_LIMIT_REACHED; } + CHECK_MSG(ensurePvrSession(), "PVR session was lost"); + // Get the graphics device and initialize the necessary resources. bool hasGraphicsBindings = false; const XrBaseInStructure* entry = reinterpret_cast(createInfo->next); @@ -296,8 +298,7 @@ namespace pimax_openxr { m_sessionExiting = false; // Workaround: PVR ties the last use D3D device to the PVR session, and therefore we must teardown the previous - // PVR session to clear that state. Because OpenComposite tends to call many API in unconventional order, we - // reset the session here. + // PVR session to clear that state. { // Workaround: the environment doesn't appear to be cleared when re-initializing PVR. Clear the one pointer // we care about. @@ -305,8 +306,6 @@ namespace pimax_openxr { pvr_destroySession(m_pvrSession); m_pvrSession = nullptr; - - ensurePvrSession(); } return XR_SUCCESS; diff --git a/pimax-openxr/system.cpp b/pimax-openxr/system.cpp index fe71369..9bfa1a4 100644 --- a/pimax-openxr/system.cpp +++ b/pimax-openxr/system.cpp @@ -51,29 +51,49 @@ namespace pimax_openxr { return XR_ERROR_FORM_FACTOR_UNSUPPORTED; } - // Create the PVR session. - if (!m_pvrSession) { - const auto result = pvr_createSession(m_pvr, &m_pvrSession); + pvrHmdStatus status{}; + bool isStatusValid = false; - // This is the error returned when pi_server is not running. We pretend the HMD is not found. - if (result == pvrResult::pvr_rpc_failed) { - return XR_ERROR_FORM_FACTOR_UNAVAILABLE; + // Workaround for PVR Home race condition upon destroying a session while an app (X-Plane 12) might be polling + // the XrSystem. + if (m_pvrSession) { + CHECK_PVRCMD(pvr_getHmdStatus(m_pvrSession, &status)); + TraceLoggingWrite(g_traceProvider, + "PVR_HmdStatus", + TLArg(!!status.ServiceReady, "ServiceReady"), + TLArg(!!status.HmdPresent, "HmdPresent"), + TLArg(!!status.HmdMounted, "HmdMounted"), + TLArg(!!status.IsVisible, "IsVisible"), + TLArg(!!status.DisplayLost, "DisplayLost"), + TLArg(!!status.ShouldQuit, "ShouldQuit")); + + // If PVR Home took over the active session, then force re-creating our session in order to continue. + if (status.ShouldQuit) { + pvr_destroySession(m_pvrSession); + m_pvrSession = nullptr; + } else { + isStatusValid = true; } + } - CHECK_PVRCMD(result); + // Create the PVR session if needed. + if (!ensurePvrSession()) { + m_cachedHmdInfo = {}; + return XR_ERROR_FORM_FACTOR_UNAVAILABLE; } // Check for HMD presence. - pvrHmdStatus status{}; - CHECK_PVRCMD(pvr_getHmdStatus(m_pvrSession, &status)); - TraceLoggingWrite(g_traceProvider, - "PVR_HmdStatus", - TLArg(!!status.ServiceReady, "ServiceReady"), - TLArg(!!status.HmdPresent, "HmdPresent"), - TLArg(!!status.HmdMounted, "HmdMounted"), - TLArg(!!status.IsVisible, "IsVisible"), - TLArg(!!status.DisplayLost, "DisplayLost"), - TLArg(!!status.ShouldQuit, "ShouldQuit")); + if (!isStatusValid) { + CHECK_PVRCMD(pvr_getHmdStatus(m_pvrSession, &status)); + TraceLoggingWrite(g_traceProvider, + "PVR_HmdStatus", + TLArg(!!status.ServiceReady, "ServiceReady"), + TLArg(!!status.HmdPresent, "HmdPresent"), + TLArg(!!status.HmdMounted, "HmdMounted"), + TLArg(!!status.IsVisible, "IsVisible"), + TLArg(!!status.DisplayLost, "DisplayLost"), + TLArg(!!status.ShouldQuit, "ShouldQuit")); + } if (!(status.ServiceReady && status.HmdPresent)) { m_cachedHmdInfo = {}; return XR_ERROR_FORM_FACTOR_UNAVAILABLE; @@ -416,6 +436,8 @@ namespace pimax_openxr { // Retrieve some information from PVR needed for graphic/frame management. void OpenXrRuntime::fillDisplayDeviceInfo() { + CHECK_MSG(ensurePvrSession(), "PVR session was lost"); + pvrDisplayInfo info{}; CHECK_PVRCMD(pvr_getEyeDisplayInfo(m_pvrSession, pvrEye_Left, &info)); TraceLoggingWrite(g_traceProvider, @@ -439,12 +461,21 @@ namespace pimax_openxr { memcpy(&m_adapterLuid, &info.luid, sizeof(LUID)); } - void OpenXrRuntime::ensurePvrSession() { + bool OpenXrRuntime::ensurePvrSession() { if (!m_pvrSession) { - CHECK_PVRCMD(pvr_createSession(m_pvr, &m_pvrSession)); + const auto result = pvr_createSession(m_pvr, &m_pvrSession); + + // This is the error returned when pi_server is not running. We pretend the HMD is not found. + if (result == pvrResult::pvr_rpc_failed) { + return false; + } + + CHECK_PVRCMD(result); CHECK_PVRCMD(pvr_setIntConfig(m_pvrSession, "view_rotation_fix", m_useParallelProjection)); CHECK_PVRCMD(pvr_setTrackingOriginType(m_pvrSession, pvrTrackingOrigin_EyeLevel)); } + + return true; } } // namespace pimax_openxr