Skip to content

Commit

Permalink
Workaround for PVR Home race condition stealing the session.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbucchia committed Sep 10, 2023
1 parent d86e39b commit 8c14751
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 24 deletions.
2 changes: 1 addition & 1 deletion pimax-openxr/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ namespace pimax_openxr {

// system.cpp
void fillDisplayDeviceInfo();
void ensurePvrSession();
bool ensurePvrSession();

// session.cpp
void updateSessionState(bool forceSendEvent = false);
Expand Down
7 changes: 3 additions & 4 deletions pimax-openxr/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const XrBaseInStructure*>(createInfo->next);
Expand Down Expand Up @@ -296,17 +298,14 @@ 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.
m_pvrSession->envh->pvr_dxgl_interface = nullptr;

pvr_destroySession(m_pvrSession);
m_pvrSession = nullptr;

ensurePvrSession();
}

return XR_SUCCESS;
Expand Down
69 changes: 50 additions & 19 deletions pimax-openxr/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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

0 comments on commit 8c14751

Please sign in to comment.