diff --git a/apps/openmw/mwvr/openxrmanager.cpp b/apps/openmw/mwvr/openxrmanager.cpp index c9a7f3b05..05cd61120 100644 --- a/apps/openmw/mwvr/openxrmanager.cpp +++ b/apps/openmw/mwvr/openxrmanager.cpp @@ -45,10 +45,11 @@ namespace MWVR return impl().handleEvents(); } - void OpenXRManager::waitFrame() + long long OpenXRManager::waitFrame() { if (realized()) return impl().waitFrame(); + return 0; } void OpenXRManager::beginFrame() diff --git a/apps/openmw/mwvr/openxrmanager.hpp b/apps/openmw/mwvr/openxrmanager.hpp index dd88c7a9d..ddbd1275d 100644 --- a/apps/openmw/mwvr/openxrmanager.hpp +++ b/apps/openmw/mwvr/openxrmanager.hpp @@ -45,7 +45,7 @@ namespace MWVR bool realized(); //! Forward call to xrWaitFrame() - void waitFrame(); + long long waitFrame(); //! Forward call to xrBeginFrame() void beginFrame(); diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index 0e33d9f7d..d6f583d61 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -317,7 +317,7 @@ namespace MWVR Log(Debug::Verbose) << ss.str(); } - void + long long OpenXRManagerImpl::waitFrame() { XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; @@ -325,6 +325,7 @@ namespace MWVR CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); mFrameState = frameState; + return frameState.predictedDisplayTime; } void diff --git a/apps/openmw/mwvr/openxrmanagerimpl.hpp b/apps/openmw/mwvr/openxrmanagerimpl.hpp index ed9aeb596..4b1021966 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.hpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.hpp @@ -55,7 +55,7 @@ namespace MWVR OpenXRManagerImpl(void); ~OpenXRManagerImpl(void); - void waitFrame(); + long long waitFrame(); void beginFrame(); void endFrame(int64_t displayTime, int layerCount, const std::array& layerStack); bool xrSessionRunning() const { return mSessionRunning; } diff --git a/apps/openmw/mwvr/vrsession.cpp b/apps/openmw/mwvr/vrsession.cpp index 4ae3d63e6..a27cd7f02 100644 --- a/apps/openmw/mwvr/vrsession.cpp +++ b/apps/openmw/mwvr/vrsession.cpp @@ -149,7 +149,7 @@ namespace MWVR layerStack[(int)Side::RIGHT_SIDE].fov = frameMeta->mPredictedPoses.view[(int)Side::RIGHT_SIDE].fov; Log(Debug::Debug) << frameMeta->mFrameNo << ": EndFrame " << std::this_thread::get_id(); - xr->endFrame(frameMeta->mPredictedDisplayTime, 1, layerStack); + xr->endFrame(frameMeta->mXrPredictedDisplayTime, 1, layerStack); xr->xrResourceReleased(); } @@ -161,6 +161,8 @@ namespace MWVR mLastFrameInterval = std::chrono::duration_cast(now - mLastRenderedFrameTimestamp); mLastRenderedFrameTimestamp = now; mLastRenderedFrame = getFrame(FramePhase::Swap)->mFrameNo; + mLastPredictedDisplayTime = getFrame(FramePhase::Swap)->mXrPredictedDisplayTime; + mLastPredictedDisplayPeriod = xr->getLastPredictedDisplayPeriod();; // Using this to track framerate over the course of gameplay, rather than just seeing the instantaneous auto seconds = std::chrono::duration_cast>(now - mStart).count(); @@ -206,10 +208,12 @@ namespace MWVR // For example, shadows do some draw calls during cull an as such phase should be "Cull" or earlier with shadows enabled. // But may be "Draw" without shadows. if (phase == mXrSyncPhase && getFrame(phase)->mShouldRender) - doFrameSync(); + { + getFrame(phase)->mXrPredictedDisplayTime = doFrameSync(); + } } - void VRSession::doFrameSync() + long long VRSession::doFrameSync() { auto begin = std::chrono::steady_clock::now(); { @@ -222,7 +226,7 @@ namespace MWVR auto condEnd = std::chrono::steady_clock::now(); auto* xr = Environment::get().getManager(); Log(Debug::Debug) << mFrames << ": WaitFrame " << std::this_thread::get_id(); - xr->waitFrame(); + auto predictedDisplayTime = xr->waitFrame(); Log(Debug::Debug) << mFrames << ": BeginFrame " << std::this_thread::get_id(); xr->beginFrame(); auto xrSyncEnd = std::chrono::steady_clock::now(); @@ -231,6 +235,7 @@ namespace MWVR auto xrSyncTime = std::chrono::duration_cast(xrSyncEnd - condEnd); Log(Debug::Debug) << "condTime: " << condTime.count() << ", xrSyncTime: " << xrSyncTime.count(); + return predictedDisplayTime; } std::unique_ptr& VRSession::getFrame(FramePhase phase) @@ -249,25 +254,27 @@ namespace MWVR xr->handleEvents(); auto epochTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - auto predictedDisplayTime = std::max(xr->getLastPredictedDisplayTime(), epochTime); - auto predictedDisplayPeriod = std::max(xr->getLastPredictedDisplayPeriod(), (long long)1000000); - float intervalsf = static_cast(mLastFrameInterval.count()) / static_cast(predictedDisplayPeriod); + auto predictedDisplayTime = mLastPredictedDisplayTime; + auto predictedDisplayPeriod = mLastPredictedDisplayPeriod; + double intervalsf = static_cast(mLastFrameInterval.count()) / static_cast(predictedDisplayPeriod); + int intervals = std::max((int)std::roundf(intervalsf), 1); + + if (predictedDisplayTime == 0) + predictedDisplayTime = epochTime; //////////////////////// OCULUS BUG //////////////////// Oculus will suddenly start increasing their predicted display time by precisely 1 second per frame //////////////////// regardless of real time passed, causing predictions to go crazy due to the time difference. //////////////////// Therefore, for the time being, i ignore oculus' predicted display time altogether. - if(mUseSteadyClock) - predictedDisplayTime = epochTime; - - if (mFrames > 1) + if (mUseSteadyClock) + { + predictedDisplayTime = epochTime + intervals * predictedDisplayPeriod; + } + else { - int intervals = std::max((int)std::roundf(intervalsf), 1); predictedDisplayTime = predictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * predictedDisplayPeriod; } - - PoseSet predictedPoses{}; xr->enablePredictions(); diff --git a/apps/openmw/mwvr/vrsession.hpp b/apps/openmw/mwvr/vrsession.hpp index f6e374b2b..1c7309378 100644 --- a/apps/openmw/mwvr/vrsession.hpp +++ b/apps/openmw/mwvr/vrsession.hpp @@ -44,6 +44,7 @@ namespace MWVR { long long mFrameNo{ 0 }; long long mPredictedDisplayTime{ 0 }; + long long mXrPredictedDisplayTime{ 0 }; PoseSet mPredictedPoses{}; bool mShouldRender{ false }; }; @@ -60,7 +61,7 @@ namespace MWVR void prepareFrame(); //! Synchronize with openxr - void doFrameSync(); + long long doFrameSync(); //! Angles to be used for overriding movement direction void movementAngles(float& yaw, float& pitch); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index a4b253130..c9f9ac7d8 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -926,4 +926,4 @@ flip mirror texture order = false # Work around for some preview openxr runtimes whose display time predictions do not work # Use this if tracking seems crazy. -use steady clock = true \ No newline at end of file +use steady clock = false \ No newline at end of file