From 951879240ca1bcb518f19d085ae3260fed19841b Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sun, 26 Jan 2020 20:06:47 +0100 Subject: [PATCH] Simplified render timing by separating rendering from the openxr swapchain, and instead blitting and submitting separately --- apps/openmw/engine.cpp | 12 +- apps/openmw/mwvr/openxrinputmanager.cpp | 2 +- apps/openmw/mwvr/openxrmanager.cpp | 98 ++++---- apps/openmw/mwvr/openxrmanager.hpp | 73 +++--- apps/openmw/mwvr/openxrmanagerimpl.cpp | 286 ++++++++++-------------- apps/openmw/mwvr/openxrmanagerimpl.hpp | 67 +++--- apps/openmw/mwvr/openxrmenu.cpp | 8 +- apps/openmw/mwvr/openxrswapchain.cpp | 62 ++--- apps/openmw/mwvr/openxrswapchain.hpp | 7 +- apps/openmw/mwvr/openxrtexture.cpp | 140 ++++-------- apps/openmw/mwvr/openxrtexture.hpp | 24 +- apps/openmw/mwvr/openxrview.cpp | 35 ++- apps/openmw/mwvr/openxrview.hpp | 10 +- apps/openmw/mwvr/openxrviewer.cpp | 87 +++---- apps/openmw/mwvr/openxrviewer.hpp | 15 +- apps/openmw/mwvr/openxrworldview.cpp | 51 ++++- apps/openmw/mwvr/openxrworldview.hpp | 19 +- 17 files changed, 449 insertions(+), 547 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c4fe48cd4..376553f77 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -731,12 +731,12 @@ void OMW::Engine::go() mXRViewer->addChild(root); mViewer->setSceneData(mXRViewer); #ifndef _NDEBUG - mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::STAGE)); - mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::VIEW)); - mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::STAGE)); - mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::VIEW)); - mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::STAGE)); - mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::VIEW)); + //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::STAGE)); + //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::VIEW)); + //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::STAGE)); + //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::VIEW)); + //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::STAGE)); + //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::VIEW)); #endif #endif diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index 49bbfbcc7..b8a238d59 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -515,7 +515,7 @@ namespace MWVR OpenXRInputManagerImpl::updateHandTracking() { for (auto hand : { LEFT_HAND, RIGHT_HAND }) { - CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().mFrameState.predictedDisplayTime, &mHandSpaceLocation[hand])); + CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().predictedDisplayTime(OpenXRFrameIndexer::instance().updateIndex()), &mHandSpaceLocation[hand])); } } diff --git a/apps/openmw/mwvr/openxrmanager.cpp b/apps/openmw/mwvr/openxrmanager.cpp index efa3bf45d..d047fe6e0 100644 --- a/apps/openmw/mwvr/openxrmanager.cpp +++ b/apps/openmw/mwvr/openxrmanager.cpp @@ -67,10 +67,10 @@ namespace MWVR return impl().waitFrame(); } - void OpenXRManager::beginFrame(long long frameIndex) + void OpenXRManager::beginFrame() { if (realized()) - return impl().beginFrame(frameIndex); + return impl().beginFrame(); } void OpenXRManager::endFrame() @@ -85,12 +85,6 @@ namespace MWVR return impl().updateControls(); } - void OpenXRManager::updatePoses() - { - if (realized()) - return impl().updatePoses(); - } - void OpenXRManager::realize( osg::GraphicsContext* gc) @@ -111,14 +105,6 @@ namespace MWVR } } - - void OpenXRManager::addPoseUpdateCallback( - osg::ref_ptr cb) - { - if (realized()) - return impl().addPoseUpdateCallback(cb); - } - int OpenXRManager::eyes() { if (realized()) @@ -146,50 +132,58 @@ namespace MWVR } - void OpenXRManager::viewerBarrier() +//#ifndef _NDEBUG +// void PoseLogger::operator()(MWVR::Pose pose) +// { +// const char* limb = nullptr; +// const char* space = nullptr; +// switch (mLimb) +// { +// case TrackedLimb::HEAD: +// limb = "HEAD"; break; +// case TrackedLimb::LEFT_HAND: +// limb = "LEFT_HAND"; break; +// case TrackedLimb::RIGHT_HAND: +// limb = "RIGHT_HAND"; break; +// } +// switch (mSpace) +// { +// case TrackedSpace::STAGE: +// space = "STAGE"; break; +// case TrackedSpace::VIEW: +// space = "VIEW"; break; +// } +// +// //TODO: Use a different output to avoid spamming the debug log when enabled +// Log(Debug::Verbose) << space << "." << limb << ": " << pose; +// } +//#endif + + static OpenXRFrameIndexer g_OpenXRFrameIndexer; + + OpenXRFrameIndexer& OpenXRFrameIndexer::instance() { - if (realized()) - return impl().viewerBarrier(); + return g_OpenXRFrameIndexer; } - void OpenXRManager::registerToBarrier() + int64_t OpenXRFrameIndexer::advanceUpdateIndex() { - if (realized()) - return impl().registerToBarrier(); + std::unique_lock lock(mMutex); + mUpdateIndex++; + Log(Debug::Verbose) << "Advancing update index to " << mUpdateIndex; + assert(mUpdateIndex > mRenderIndex); + return mUpdateIndex; } - void OpenXRManager::unregisterFromBarrier() + int64_t OpenXRFrameIndexer::advanceRenderIndex() { - if (realized()) - return impl().unregisterFromBarrier(); + std::unique_lock lock(mMutex); + mRenderIndex++; + Log(Debug::Verbose) << "Advancing frame index to " << mRenderIndex; + if (!(mUpdateIndex >= mRenderIndex)) + mUpdateIndex = mRenderIndex - 1; + return mRenderIndex; } - -#ifndef _NDEBUG - void PoseLogger::operator()(MWVR::Pose pose) - { - const char* limb = nullptr; - const char* space = nullptr; - switch (mLimb) - { - case TrackedLimb::HEAD: - limb = "HEAD"; break; - case TrackedLimb::LEFT_HAND: - limb = "LEFT_HAND"; break; - case TrackedLimb::RIGHT_HAND: - limb = "RIGHT_HAND"; break; - } - switch (mSpace) - { - case TrackedSpace::STAGE: - space = "STAGE"; break; - case TrackedSpace::VIEW: - space = "VIEW"; break; - } - - //TODO: Use a different output to avoid spamming the debug log when enabled - Log(Debug::Verbose) << space << "." << limb << ": " << pose; - } -#endif } std::ostream& operator <<( diff --git a/apps/openmw/mwvr/openxrmanager.hpp b/apps/openmw/mwvr/openxrmanager.hpp index 1ae2ba7ef..a7cf8dccc 100644 --- a/apps/openmw/mwvr/openxrmanager.hpp +++ b/apps/openmw/mwvr/openxrmanager.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,23 @@ struct XrSwapchainSubImage; namespace MWVR { + struct Timer + { + Timer(std::string name) : mName(name) + { + mBegin = std::chrono::steady_clock::now(); + } + ~Timer() + { + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast>(end - mBegin); + Log(Debug::Verbose) << mName << "Elapsed: " << elapsed.count() << "s"; + } + + std::chrono::steady_clock::time_point mBegin; + std::string mName; + }; + //! Represents the pose of a limb in VR space. struct Pose { @@ -43,25 +61,32 @@ namespace MWVR VIEW //!< Track limb in the VR view space. Meaning a space with the head as origin and orientation. }; + struct OpenXRFrameIndexer + { + static OpenXRFrameIndexer& instance(); + + OpenXRFrameIndexer() = default; + ~OpenXRFrameIndexer() = default; + + int64_t advanceUpdateIndex(); + + int64_t renderIndex() { return mRenderIndex; } + + int64_t advanceRenderIndex(); + + int64_t updateIndex() { return mUpdateIndex; } + + std::mutex mMutex{}; + int64_t mUpdateIndex{ -1 }; + int64_t mRenderIndex{ -1 }; + }; + // Use the pimpl pattern to avoid cluttering the namespace with openxr dependencies. class OpenXRManagerImpl; class OpenXRManager : public osg::Referenced { - public: - class PoseUpdateCallback: public osg::Referenced - { - public: - PoseUpdateCallback(TrackedLimb limb, TrackedSpace space) - : mLimb(limb), mSpace(space){} - - virtual void operator()(MWVR::Pose pose) = 0; - - TrackedLimb mLimb; - TrackedSpace mSpace; - }; - public: class RealizeOperation : public osg::GraphicsOperation { @@ -96,24 +121,14 @@ namespace MWVR void handleEvents(); void waitFrame(); - void beginFrame(long long frameIndex); + void beginFrame(); void endFrame(); void updateControls(); - void updatePoses(); void realize(osg::GraphicsContext* gc); - void addPoseUpdateCallback(osg::ref_ptr cb); - int eyes(); - //! A barrier used internally to ensure all views have released their frames before endFrame can complete. - void viewerBarrier(); - //! Increments the target viewer counter of the barrier - void registerToBarrier(); - //! Decrements the target viewer counter of the barrier - void unregisterFromBarrier(); - OpenXRManagerImpl& impl() { return *mPrivate; } private: @@ -121,16 +136,6 @@ namespace MWVR std::mutex mMutex; using lock_guard = std::lock_guard; }; - -#ifndef _NDEBUG - class PoseLogger : public OpenXRManager::PoseUpdateCallback - { - public: - PoseLogger(TrackedLimb limb, TrackedSpace space) - : OpenXRManager::PoseUpdateCallback(limb, space) {}; - void operator()(MWVR::Pose pose) override; - }; -#endif } std::ostream& operator <<(std::ostream& os, const MWVR::Pose& pose); diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index d2bcf1aa2..fb0c07952 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -99,7 +99,6 @@ namespace MWVR XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO }; createInfo.next = &mGraphicsBinding; createInfo.systemId = mSystemId; - createInfo.createFlags; CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession)); assert(mSession); } @@ -117,13 +116,6 @@ namespace MWVR CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage)); } - { // Set up layers - - //mLayer.space = mReferenceSpaceStage; - //mLayer.viewCount = (uint32_t)mProjectionLayerViews.size(); - //mLayer.views = mProjectionLayerViews.data(); - } - { // Read and log graphics properties for the swapchain xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties); @@ -142,32 +134,32 @@ namespace MWVR uint32_t viewCount = 0; CHECK_XRCMD(xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewConfigType, 2, &viewCount, mConfigViews.data())); + + // OpenXR gives me crazy bananas high resolutions. Likely an oculus bug. + mConfigViews[0].recommendedImageRectHeight = 1200; + mConfigViews[1].recommendedImageRectHeight = 1200; + mConfigViews[0].recommendedImageRectWidth = 1080; + mConfigViews[1].recommendedImageRectWidth = 1080; + if (viewCount != 2) { std::stringstream ss; ss << "xrEnumerateViewConfigurationViews returned " << viewCount << " views"; Log(Debug::Verbose) << ss.str(); } - - // TODO: This, including the projection layer views, should be moved to openxrviewer - //for (unsigned i = 0; i < 2; i++) - //{ - // mEyes[i].reset(new OpenXRView(this, mConfigViews[i])); - - // mProjectionLayerViews[i].subImage.swapchain = mEyes[i]->mSwapchain; - // mProjectionLayerViews[i].subImage.imageRect.offset = { 0, 0 }; - // mProjectionLayerViews[i].subImage.imageRect.extent = { mEyes[i]->mWidth, mEyes[i]->mHeight }; - //} } } inline XrResult CheckXrResult(XrResult res, const char* originator, const char* sourceLocation) { if (XR_FAILED(res)) { - Log(Debug::Error) << sourceLocation << ": OpenXR[" << to_string(res) << "]: " << originator; + std::stringstream ss; + ss << sourceLocation << ": OpenXR[" << to_string(res) << "]: " << originator; + Log(Debug::Error) << ss.str(); + throw std::runtime_error(ss.str().c_str()); } else { - Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator; + // Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator; } return res; @@ -265,123 +257,59 @@ namespace MWVR void OpenXRManagerImpl::waitFrame() { - if (!mSessionRunning) - return; + Timer timer("waitFrame()"); + + // In some implementations xrWaitFrame might not return immediately when it should. + // So i let it wait in a separate thread. xrBeginFrame() should wait on xrWaitFrame() + // and xrWaitFrame() doesn't happen again until xrEndFrame() so synchronization is not necessary. + std::thread([this]() { + static std::mutex waitFrameMutex; + std::unique_lock lock(waitFrameMutex); + + if (!mSessionRunning) + return; + + XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; + XrFrameState frameState{ XR_TYPE_FRAME_STATE }; + + CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); + + + mTimeKeeper.progressToNextFrame(frameState); + + }).detach(); - XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; - XrFrameState frameState{ XR_TYPE_FRAME_STATE }; - CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); - mFrameState = frameState; } void - OpenXRManagerImpl::beginFrame(long long frameIndex) + OpenXRManagerImpl::beginFrame() { - Log(Debug::Verbose) << "frameIndex = " << frameIndex; - if (!mSessionRunning) - return; - - std::unique_lock lock(mFrameStatusMutex); - - // We need to wait for the frame to become idle or ready - // (There is no guarantee osg won't get us here before endFrame() returns) - while (mFrameStatus == OPENXR_FRAME_STATUS_ENDING || mFrameIndex < frameIndex) - mFrameStatusSignal.wait(lock); - - if (mFrameStatus == OPENXR_FRAME_STATUS_IDLE) - { - Log(Debug::Verbose) << "beginFrame()"; - handleEvents(); - waitFrame(); - - XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO }; - CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo)); - - updateControls(); - updatePoses(); - mFrameStatus = OPENXR_FRAME_STATUS_READY; - } - - assert(mFrameStatus == OPENXR_FRAME_STATUS_READY); - } - - void OpenXRManagerImpl::viewerBarrier() - { - std::unique_lock lock(mBarrierMutex); - mBarrier++; - - Log(Debug::Verbose) << "mBarrier=" << mBarrier << ", tid=" << std::this_thread::get_id(); - - if (mBarrier == mNBarrier) - { - std::unique_lock lock(mFrameStatusMutex); - mFrameStatus = OPENXR_FRAME_STATUS_ENDING; - mFrameStatusSignal.notify_all(); - //mBarrierSignal.notify_all(); - mBarrier = 0; - } - //else - //{ - // mBarrierSignal.wait(lock, [this]() { return mBarrier == mNBarrier; }); - //} - } - - void OpenXRManagerImpl::registerToBarrier() - { - std::unique_lock lock(mBarrierMutex); - mNBarrier++; - } - - void OpenXRManagerImpl::unregisterFromBarrier() - { - std::unique_lock lock(mBarrierMutex); - mNBarrier--; - assert(mNBarrier >= 0); + Timer timer("beginFrame"); + XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO }; + CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo)); } void OpenXRManagerImpl::endFrame() { + Timer timer("endFrame()"); if (!mSessionRunning) return; - std::unique_lock lock(mFrameStatusMutex); - while(mFrameStatus != OPENXR_FRAME_STATUS_ENDING) - mFrameStatusSignal.wait(lock); XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO }; - frameEndInfo.displayTime = mFrameState.predictedDisplayTime; + frameEndInfo.displayTime = mTimeKeeper.predictedDisplayTime(OpenXRFrameIndexer::instance().renderIndex()); frameEndInfo.environmentBlendMode = mEnvironmentBlendMode; - //frameEndInfo.layerCount = (uint32_t)1; - //frameEndInfo.layers = &mLayer_p; frameEndInfo.layerCount = mLayerStack.layerCount(); frameEndInfo.layers = mLayerStack.layerHeaders(); CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo)); - mFrameStatus = OPENXR_FRAME_STATUS_IDLE; - mFrameIndex++; - mFrameStatusSignal.notify_all(); } - std::array OpenXRManagerImpl::getStageViews() + std::array + OpenXRManagerImpl::getPredictedViews( + int64_t frameIndex, + TrackedSpace space) { - // Get the pose of each eye in the "stage" reference space. - // TODO: I likely won't ever use this, since it is only useful if the game world and the - // stage space have matching orientation, which is extremely unlikely. Instead we have - // to keep track of the head pose separately and move our world position based on progressive - // changes. - - // In more detail: - // When we apply yaw to the game world to allow free rotation of the character, the orientation - // of the stage space and our world space deviates which breaks free movement. - // - // If we align the orientations by yawing the head pose, that yaw will happen around the origin - // of the stage space rather than the character, which will not be comfortable (or make any sense) to the player. - // - // If we align the orientations by yawing the view poses, the yaw will happen around the character - // as intended, but physically walking will move the player in the wrong direction. - // - // The solution that solves both problems is to yaw the view pose *and* progressively track changes in head pose - // in the stage space and yaw that change before adding it to our separately tracked pose. std::array views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} }; XrViewState viewState{ XR_TYPE_VIEW_STATE }; @@ -389,34 +317,22 @@ namespace MWVR XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; viewLocateInfo.viewConfigurationType = mViewConfigType; - viewLocateInfo.displayTime = mFrameState.predictedDisplayTime; - - viewLocateInfo.space = mReferenceSpaceStage; + viewLocateInfo.displayTime = mTimeKeeper.predictedDisplayTime(frameIndex); + switch (space) + { + case TrackedSpace::STAGE: + viewLocateInfo.space = mReferenceSpaceStage; + break; + case TrackedSpace::VIEW: + viewLocateInfo.space = mReferenceSpaceView; + break; + } CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data())); return views; } - std::array OpenXRManagerImpl::getHmdViews() - { - // Eye poses relative to the HMD rarely change. But they might - // if the user reconfigures his or her hmd during runtime, so we - // re-read this every frame anyway. - std::array views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} }; - XrViewState viewState{ XR_TYPE_VIEW_STATE }; - uint32_t viewCount = 2; - - XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; - viewLocateInfo.viewConfigurationType = mViewConfigType; - viewLocateInfo.displayTime = mFrameState.predictedDisplayTime; - - viewLocateInfo.space = mReferenceSpaceView; - CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data())); - - return views; - } - - MWVR::Pose OpenXRManagerImpl::getLimbPose(TrackedLimb limb, TrackedSpace space) + MWVR::Pose OpenXRManagerImpl::getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space) { XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION }; XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY }; @@ -444,7 +360,7 @@ namespace MWVR referenceSpace = mReferenceSpaceView; break; } - CHECK_XRCMD(xrLocateSpace(limbSpace, referenceSpace, mFrameState.predictedDisplayTime, &location)); + CHECK_XRCMD(xrLocateSpace(limbSpace, referenceSpace, mTimeKeeper.predictedDisplayTime(frameIndex), &location)); if (!(velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT)) Log(Debug::Warning) << "Unable to acquire linear velocity"; return MWVR::Pose{ @@ -503,12 +419,13 @@ namespace MWVR switch (newState) { case XR_SESSION_STATE_READY: - case XR_SESSION_STATE_IDLE: + //case XR_SESSION_STATE_IDLE: { XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO }; beginInfo.primaryViewConfigurationType = mViewConfigType; CHECK_XRCMD(xrBeginSession(mSession, &beginInfo)); mSessionRunning = true; + waitFrame(); break; } case XR_SESSION_STATE_STOPPING: @@ -522,41 +439,12 @@ namespace MWVR } } - void OpenXRManagerImpl::updatePoses() + XrTime OpenXRManagerImpl::predictedDisplayTime(int64_t frameIndex) { - //auto oldHeadTrackedPose = mHeadTrackedPose; - //auto oldLefthandPose = mLeftHandTrackedPose; - //auto oldRightHandPose = mRightHandTrackedPose; - //mHeadTrackedPose = getLimbPose(TrackedLimb::HEAD); - //mLeftHandTrackedPose = getLimbPose(TrackedLimb::LEFT_HAND); - //mRightHandTrackedPose = getLimbPose(TrackedLimb::RIGHT_HAND); - - //for (auto& cb : mPoseUpdateCallbacks) - //{ - // switch (cb->mLimb) - // { - // case TrackedLimb::HEAD: - // (*cb)(mHeadTrackedPose); break; - // case TrackedLimb::LEFT_HAND: - // (*cb)(mLeftHandTrackedPose); break; - // case TrackedLimb::RIGHT_HAND: - // (*cb)(mRightHandTrackedPose); break; - // } - //} - - for (auto& cb : mPoseUpdateCallbacks) - (*cb)(getLimbPose(cb->mLimb, cb->mSpace)); + return mTimeKeeper.predictedDisplayTime(frameIndex); } - void OpenXRManagerImpl::addPoseUpdateCallback( - osg::ref_ptr cb) - { - mPoseUpdateCallbacks.push_back(cb); - } - - - const XrEventDataBaseHeader* OpenXRManagerImpl::nextEvent() { @@ -587,6 +475,62 @@ namespace MWVR { return XrPosef{ osg::toXR(pose.orientation), osg::toXR(pose.position) }; } + + XrTime OpenXRTimeKeeper::predictedDisplayTime(int64_t frameIndex) + { + std::unique_lock lock(mMutex); + + auto prediction = mPredictedFrameTime; + auto predictedPeriod = mPredictedPeriod; + + auto futureFrames = frameIndex - OpenXRFrameIndexer::instance().renderIndex(); + + prediction += ( 0 + futureFrames) * predictedPeriod; + + Log(Debug::Verbose) << "Predicted: displayTime[" << futureFrames << "]=" << prediction; + + return prediction; + } + + void OpenXRTimeKeeper::progressToNextFrame(XrFrameState frameState) + { + std::unique_lock lock(mMutex); + OpenXRFrameIndexer::instance().advanceRenderIndex(); + //XrDuration realPeriod = frameState.predictedDisplayPeriod; + //if(mFrameState.predictedDisplayTime != 0) + // realPeriod = frameState.predictedDisplayTime - mFrameState.predictedDisplayTime; + + auto now = clock::now(); + auto nanoseconds_elapsed = std::chrono::duration_cast(now - mLastFrame); + XrDuration realPeriod = nanoseconds_elapsed.count(); + mFrameState = frameState; + + mPredictedFrameTime = mFrameState.predictedDisplayTime; + mPredictedPeriod = mFrameState.predictedDisplayPeriod; + + // Real fps is lower than expected fps + // Adjust predictions + // (Really wish OpenXR would handle this!) + if (realPeriod > mFrameState.predictedDisplayPeriod) + { + // predictedDisplayTime refers to the midpoint of the display period + // The upjustment must therefore only be half the magnitude + // mPredictedFrameTime += (realPeriod - mFrameState.predictedDisplayPeriod); + // mPredictedPeriod = realPeriod; + } + + + + seconds elapsed = std::chrono::duration_cast(now - mLastFrame); + std::swap(now, mLastFrame); + double fps = 1. / elapsed.count(); + mFps = mFps * 0.8 + 0.2 * fps; + + + + Log(Debug::Verbose) << "Render progressed to next frame: elapsed=" << elapsed.count() << ", fps=" << fps << ", ImplementationPeriod=" << mFrameState.predictedDisplayPeriod << " realPeriod=" << realPeriod; + Log(Debug::Verbose) << "Render progressed to next frame: predictedDisplayTime=" << mFrameState.predictedDisplayTime << ", mPredictedFrameTime=" << mPredictedFrameTime << ", mFps=" << mFps; + } } namespace osg diff --git a/apps/openmw/mwvr/openxrmanagerimpl.hpp b/apps/openmw/mwvr/openxrmanagerimpl.hpp index c794edacb..753d7d217 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.hpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.hpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace osg { Vec3 fromXR(XrVector3f); @@ -43,10 +44,33 @@ namespace MWVR MWVR::Pose fromXR(XrPosef pose); XrPosef toXR(MWVR::Pose pose); + struct OpenXRTimeKeeper + { + using seconds = std::chrono::duration; + using nanoseconds = std::chrono::nanoseconds; + using clock = std::chrono::steady_clock; + using time_point = clock::time_point; + + OpenXRTimeKeeper() = default; + ~OpenXRTimeKeeper() = default; + + XrTime predictedDisplayTime(int64_t frameIndex); + void progressToNextFrame(XrFrameState frameState); + + private: + + XrFrameState mFrameState{ XR_TYPE_FRAME_STATE }; + std::mutex mMutex{}; + + double mFps{ 0. }; + time_point mLastFrame = clock::now(); + + XrTime mPredictedFrameTime{ 0 }; + XrDuration mPredictedPeriod{ 0 }; + }; + struct OpenXRManagerImpl { - using PoseUpdateCallback = OpenXRManager::PoseUpdateCallback; - OpenXRManagerImpl(void); ~OpenXRManagerImpl(void); @@ -56,17 +80,15 @@ namespace MWVR const XrEventDataBaseHeader* nextEvent(); void waitFrame(); - void beginFrame(long long frameIndex); + void beginFrame(); void endFrame(); - std::array getStageViews(); - std::array getHmdViews(); - MWVR::Pose getLimbPose(TrackedLimb limb, TrackedSpace space); + std::array getPredictedViews(int64_t frameIndex, TrackedSpace mSpace); + MWVR::Pose getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space); int eyes(); void handleEvents(); void updateControls(); - void updatePoses(); - void addPoseUpdateCallback(osg::ref_ptr cb); void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent); + XrTime predictedDisplayTime(int64_t frameIndex); bool initialized = false; long long mFrameIndex = 0; @@ -83,38 +105,11 @@ namespace MWVR XrSpace mReferenceSpaceView = XR_NULL_HANDLE; XrSpace mReferenceSpaceStage = XR_NULL_HANDLE; XrEventDataBuffer mEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER }; - //XrCompositionLayerProjection mLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION }; - //XrCompositionLayerBaseHeader const* mLayer_p = reinterpret_cast(&mLayer); - //std::array mProjectionLayerViews{ { {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW} } }; + OpenXRTimeKeeper mTimeKeeper{}; OpenXRLayerStack mLayerStack{}; - XrFrameState mFrameState{ XR_TYPE_FRAME_STATE }; XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN; bool mSessionRunning = false; - - //osg::Pose mHeadTrackedPose{}; - //osg::Pose mLeftHandTrackedPose{}; - //osg::Pose mRightHandTrackedPose{}; - - std::vector< osg::ref_ptr > mPoseUpdateCallbacks{}; - std::mutex mEventMutex{}; - - enum { - OPENXR_FRAME_STATUS_IDLE, //!< Frame is ready for initialization - OPENXR_FRAME_STATUS_READY, //!< Frame has been initialized and swapchains may be acquired - OPENXR_FRAME_STATUS_ENDING //!< All swapchains have been releazed, frame ready for presentation - } mFrameStatus{ OPENXR_FRAME_STATUS_IDLE }; - std::condition_variable mFrameStatusSignal{}; - std::mutex mFrameStatusMutex; - - int mBarrier{ 0 }; - int mNBarrier{ 0 }; - std::condition_variable mBarrierSignal{}; - std::mutex mBarrierMutex; - - void viewerBarrier(); - void registerToBarrier(); - void unregisterFromBarrier(); }; } diff --git a/apps/openmw/mwvr/openxrmenu.cpp b/apps/openmw/mwvr/openxrmenu.cpp index 2c6297024..0c66bbd37 100644 --- a/apps/openmw/mwvr/openxrmenu.cpp +++ b/apps/openmw/mwvr/openxrmenu.cpp @@ -6,7 +6,7 @@ namespace MWVR { OpenXRMenu::OpenXRMenu(osg::ref_ptr XR, osg::ref_ptr state, const std::string& title, int width, int height, osg::Vec2 extent_meters) - : OpenXRView(XR) + : OpenXRView(XR, title) , mTitle(title) { setWidth(width); @@ -27,7 +27,7 @@ namespace MWVR // Orientation needs a norm of 1 to be accepted by OpenXR, so we default it to 0,0,0,1 mLayer->pose.orientation.w = 1.f; - updatePosition(); + //updatePosition(); } OpenXRMenu::~OpenXRMenu() @@ -47,10 +47,12 @@ namespace MWVR return; if (!mXR->sessionRunning()) return; + if (OpenXRFrameIndexer::instance().updateIndex() == 0) + return; // Menus are position one meter in front of the player, facing the player. // Go via osg since OpenXR doesn't distribute a linear maths library - auto pose = mXR->impl().getLimbPose(TrackedLimb::HEAD, TrackedSpace::STAGE); + auto pose = mXR->impl().getPredictedLimbPose(OpenXRFrameIndexer::instance().updateIndex(), TrackedLimb::HEAD, TrackedSpace::STAGE); pose.position += pose.orientation * osg::Vec3(0, 0, -1); mLayer->pose.position = osg::toXR(pose.position); mLayer->pose.orientation = osg::toXR(-pose.orientation); diff --git a/apps/openmw/mwvr/openxrswapchain.cpp b/apps/openmw/mwvr/openxrswapchain.cpp index c2d61d605..654404c0a 100644 --- a/apps/openmw/mwvr/openxrswapchain.cpp +++ b/apps/openmw/mwvr/openxrswapchain.cpp @@ -26,21 +26,20 @@ namespace MWVR { OpenXRSwapchainImpl(osg::ref_ptr XR, osg::ref_ptr state, OpenXRSwapchain::Config config); ~OpenXRSwapchainImpl(); - osg::ref_ptr prepareNextSwapchainImage(); - void releaseSwapchainImage(); void beginFrame(osg::GraphicsContext* gc); void endFrame(osg::GraphicsContext* gc); osg::ref_ptr mXR; XrSwapchain mSwapchain = XR_NULL_HANDLE; std::vector mSwapchainImageBuffers{}; - std::vector > mTextureBuffers{}; + //std::vector > mTextureBuffers{}; XrSwapchainSubImage mSubImage{}; int32_t mWidth = -1; int32_t mHeight = -1; int32_t mSamples = -1; int64_t mSwapchainColorFormat = -1; - OpenXRTextureBuffer* mCurrentBuffer = nullptr; + uint32_t mFBO = 0; + OpenXRTextureBuffer* mRenderBuffer = nullptr; }; OpenXRSwapchainImpl::OpenXRSwapchainImpl( @@ -99,8 +98,9 @@ namespace MWVR { mSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR }); CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast(mSwapchainImageBuffers.data()))); - for (const auto& swapchainImage : mSwapchainImageBuffers) - mTextureBuffers.push_back(new OpenXRTextureBuffer(state, swapchainImage.image, mWidth, mHeight, 0)); + //for (const auto& swapchainImage : mSwapchainImageBuffers) + // mTextureBuffers.push_back(new OpenXRTextureBuffer(state, swapchainImage.image, mWidth, mHeight, 0)); + mRenderBuffer = new OpenXRTextureBuffer(state, mWidth, mHeight, 0); mSubImage.swapchain = mSwapchain; mSubImage.imageRect.offset = { 0, 0 }; @@ -113,11 +113,18 @@ namespace MWVR { CHECK_XRCMD(xrDestroySwapchain(mSwapchain)); } - osg::ref_ptr - OpenXRSwapchainImpl::prepareNextSwapchainImage() + void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc) { + mRenderBuffer->beginFrame(gc); + } + + void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc) + { + Timer timer("Swapchain::endFrame"); + // Blit frame to swapchain + if (!mXR->sessionRunning()) - return nullptr; + return; XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; uint32_t swapchainImageIndex = 0; @@ -127,32 +134,11 @@ namespace MWVR { waitInfo.timeout = XR_INFINITE_DURATION; CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo)); - return mTextureBuffers[swapchainImageIndex]; - } - - void - OpenXRSwapchainImpl::releaseSwapchainImage() - { - if (!mXR->sessionRunning()) - return; + mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[swapchainImageIndex].image); XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)); - //mCurrentBuffer = nullptr; - } - void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc) - { - mCurrentBuffer = prepareNextSwapchainImage(); - if (mCurrentBuffer) - mCurrentBuffer->beginFrame(gc); - } - - void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc) - { - if (mCurrentBuffer) - mCurrentBuffer->endFrame(gc); - releaseSwapchainImage(); } OpenXRSwapchain::OpenXRSwapchain( @@ -165,16 +151,6 @@ namespace MWVR { { } - osg::ref_ptr OpenXRSwapchain::prepareNextSwapchainImage() - { - return impl().prepareNextSwapchainImage(); - } - - void OpenXRSwapchain::releaseSwapchainImage() - { - impl().releaseSwapchainImage(); - } - void OpenXRSwapchain::beginFrame(osg::GraphicsContext* gc) { impl().beginFrame(gc); @@ -206,8 +182,8 @@ namespace MWVR { return impl().mSamples; } - OpenXRTextureBuffer* OpenXRSwapchain::current() + OpenXRTextureBuffer* OpenXRSwapchain::renderBuffer() { - return impl().mCurrentBuffer; + return impl().mRenderBuffer; } } diff --git a/apps/openmw/mwvr/openxrswapchain.hpp b/apps/openmw/mwvr/openxrswapchain.hpp index 02af024da..90e70e81b 100644 --- a/apps/openmw/mwvr/openxrswapchain.hpp +++ b/apps/openmw/mwvr/openxrswapchain.hpp @@ -26,11 +26,6 @@ namespace MWVR ~OpenXRSwapchain(); public: - //! Get the next color buffer. - //! \return The GL texture ID of the now current swapchain image - osg::ref_ptr prepareNextSwapchainImage(); - //! Release current color buffer. Do not forget to call this after rendering to the color buffer. - void releaseSwapchainImage(); //! Prepare for render (set FBO) void beginFrame(osg::GraphicsContext* gc); //! Finalize render @@ -44,7 +39,7 @@ namespace MWVR //! Samples of the view surface int samples(); //! Get the current texture - OpenXRTextureBuffer* current(); + OpenXRTextureBuffer* renderBuffer(); //! Get the private implementation OpenXRSwapchainImpl& impl() { return *mPrivate; } //! Get the private implementation diff --git a/apps/openmw/mwvr/openxrtexture.cpp b/apps/openmw/mwvr/openxrtexture.cpp index 8f118ead9..deb09b0a9 100644 --- a/apps/openmw/mwvr/openxrtexture.cpp +++ b/apps/openmw/mwvr/openxrtexture.cpp @@ -15,85 +15,57 @@ namespace MWVR { OpenXRTextureBuffer::OpenXRTextureBuffer( osg::ref_ptr state, - uint32_t XRColorBuffer, std::size_t width, std::size_t height, uint32_t msaaSamples) : mState(state) , mWidth(width) , mHeight(height) - , mXRColorBuffer(XRColorBuffer) - , mMSAASamples(msaaSamples) + , mSamples(msaaSamples) { auto* gl = osg::GLExtensions::Get(state->getContextID(), false); - glBindTexture(GL_TEXTURE_2D, XRColorBuffer); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLint w = 0; - GLint h = 0; - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WIDTH, &w); - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_HEIGHT, &h); + gl->glGenFramebuffers(1, &mBlitFBO); + gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mBlitFBO); gl->glGenFramebuffers(1, &mFBO); + glGenTextures(1, &mDepthBuffer); + glGenTextures(1, &mColorBuffer); - if (mMSAASamples == 0) - { - glGenTextures(1, &mDepthBuffer); - glBindTexture(GL_TEXTURE_2D, mDepthBuffer); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); - gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0); - gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, mDepthBuffer, 0); - - if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) - throw std::runtime_error("Failed to create OpenXR framebuffer"); - } + if (mSamples == 0) + mTextureTarget = GL_TEXTURE_2D; else - { - gl->glGenFramebuffers(1, &mMSAAFBO); + mTextureTarget = GL_TEXTURE_2D_MULTISAMPLE; - // Create MSAA color buffer - glGenTextures(1, &mMSAAColorBuffer); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer); - gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_RGBA, mWidth, mHeight, false); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0); + glBindTexture(mTextureTarget, mColorBuffer); + if (mSamples == 0) + glTexImage2D(mTextureTarget, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_INT, nullptr); + else + gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_RGBA, mWidth, mHeight, false); + glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB); + glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB); + glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0); - // Create MSAA depth buffer - glGenTextures(1, &mMSAADepthBuffer); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer); - gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0); + glBindTexture(mTextureTarget, mDepthBuffer); + if (mSamples == 0) + glTexImage2D(mTextureTarget, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + else + gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false); + glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0); - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mMSAAFBO); - gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer, 0); - gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer, 0); - if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) - throw std::runtime_error("Failed to create MSAA framebuffer"); - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); - gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0); - gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); - if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) - throw std::runtime_error("Failed to create OpenXR framebuffer"); - } + gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); + gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, mTextureTarget, mColorBuffer, 0); + gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, mTextureTarget, mDepthBuffer, 0); + if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) + throw std::runtime_error("Failed to create OpenXR framebuffer"); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); @@ -117,55 +89,37 @@ namespace MWVR auto* gl = osg::GLExtensions::Get(state->getContextID(), false); if (mFBO) gl->glDeleteFramebuffers(1, &mFBO); - if (mMSAAFBO) - gl->glDeleteFramebuffers(1, &mMSAAFBO); } - else if(mFBO || mMSAAFBO) + else if(mFBO) // Without access to opengl methods, i'll just let the FBOs leak. - Log(Debug::Warning) << "destroy() called without a State. Leaking FBOs"; + Log(Debug::Warning) << "destroy() called without a State. Leaking FBO"; if (mDepthBuffer) glDeleteTextures(1, &mDepthBuffer); - if (mMSAAColorBuffer) - glDeleteTextures(1, &mMSAAColorBuffer); - if (mMSAADepthBuffer) - glDeleteTextures(1, &mMSAADepthBuffer); + if (mColorBuffer) + glDeleteTextures(1, &mColorBuffer); - mFBO = mMSAAFBO = mDepthBuffer = mMSAAColorBuffer = mMSAADepthBuffer = 0; + mFBO = mDepthBuffer = mColorBuffer; } void OpenXRTextureBuffer::beginFrame(osg::GraphicsContext* gc) { auto state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); - - - if (mMSAASamples == 0) - { - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); - } - else - { - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mMSAAFBO); - } + gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); } - void OpenXRTextureBuffer::endFrame(osg::GraphicsContext* gc) + void OpenXRTextureBuffer::endFrame(osg::GraphicsContext* gc, uint32_t blitTarget) { auto* state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); - if (mMSAASamples == 0) - { - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); - } - else - { - gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mMSAAFBO); - gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mFBO); - gl->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); - gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); - gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); - } + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mBlitFBO); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mFBO); + gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, blitTarget, 0); + gl->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); } void OpenXRTextureBuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h) diff --git a/apps/openmw/mwvr/openxrtexture.hpp b/apps/openmw/mwvr/openxrtexture.hpp index cd8e2470e..bc3a33295 100644 --- a/apps/openmw/mwvr/openxrtexture.hpp +++ b/apps/openmw/mwvr/openxrtexture.hpp @@ -13,22 +13,23 @@ namespace MWVR class OpenXRTextureBuffer : public osg::Referenced { public: - OpenXRTextureBuffer(osg::ref_ptr state, uint32_t XRColorBuffer, std::size_t width, std::size_t height, uint32_t msaaSamples); + OpenXRTextureBuffer(osg::ref_ptr state, std::size_t width, std::size_t height, uint32_t msaaSamples); ~OpenXRTextureBuffer(); void destroy(osg::State* state); auto width() const { return mWidth; } auto height() const { return mHeight; } - auto msaaSamples() const { return mMSAASamples; } + auto msaaSamples() const { return mSamples; } void beginFrame(osg::GraphicsContext* gc); - void endFrame(osg::GraphicsContext* gc); + void endFrame(osg::GraphicsContext* gc, uint32_t blitTarget); void writeToJpg(osg::State& state, std::string filename); uint32_t fbo(void) const { return mFBO; } + //! Blit to region in currently bound draw fbo void blit(osg::GraphicsContext* gc, int x, int y, int w, int h); private: @@ -39,18 +40,13 @@ namespace MWVR std::size_t mWidth = 0; std::size_t mHeight = 0; - // Swapchain buffer - uint32_t mXRColorBuffer = 0; - - // FBO target for swapchain buffer - uint32_t mDepthBuffer = 0; + // Render Target uint32_t mFBO = 0; - - // Render targets for MSAA - uint32_t mMSAASamples = 0; - uint32_t mMSAAFBO = 0; - uint32_t mMSAAColorBuffer = 0; - uint32_t mMSAADepthBuffer = 0; + uint32_t mBlitFBO = 0; + uint32_t mDepthBuffer = 0; + uint32_t mColorBuffer = 0; + uint32_t mSamples = 0; + uint32_t mTextureTarget = 0; }; } diff --git a/apps/openmw/mwvr/openxrview.cpp b/apps/openmw/mwvr/openxrview.cpp index c6785cbde..274fe9784 100644 --- a/apps/openmw/mwvr/openxrview.cpp +++ b/apps/openmw/mwvr/openxrview.cpp @@ -16,22 +16,22 @@ namespace MWVR { OpenXRView::OpenXRView( - osg::ref_ptr XR) + osg::ref_ptr XR, + std::string name) : mXR(XR) , mSwapchain(nullptr) , mSwapchainConfig{} + , mName(name) { mSwapchainConfig.requestedFormats = { GL_RGBA8, GL_RGBA8_SNORM, }; - mXR->registerToBarrier(); } OpenXRView::~OpenXRView() { - mXR->unregisterFromBarrier(); } osg::Camera* OpenXRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc) @@ -48,7 +48,10 @@ namespace MWVR { camera->setGraphicsContext(gc); camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback()); - camera->setPreDrawCallback(new OpenXRView::PredrawCallback(camera.get(), this)); + + mPredraw = new OpenXRView::PredrawCallback(camera.get(), this); + + camera->setPreDrawCallback(mPredraw); camera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(camera.get(), this)); return camera.release(); @@ -71,18 +74,28 @@ namespace MWVR { void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo) { - Log(Debug::Verbose) << "prerenderCallback"; - mXR->beginFrame(mFrameIndex); - if(mSwapchain) + Log(Debug::Verbose) << mName << ": prerenderCallback"; + if (mSwapchain) + { mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext()); + } } void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo) { - if (mSwapchain) - mSwapchain->endFrame(renderInfo.getState()->getGraphicsContext()); - mXR->viewerBarrier(); - mFrameIndex++; + // osg will sometimes call this without a corresponding prerender. + Log(Debug::Verbose) << mName << ": postrenderCallback"; + Log(Debug::Verbose) << renderInfo.getCurrentCamera()->getName() << ": " << renderInfo.getCurrentCamera()->getPreDrawCallback(); + if (renderInfo.getCurrentCamera()->getPreDrawCallback() != mPredraw) + { + // It seems OSG will sometimes overwrite the predraw callback. + // Undocumented behaviour? + renderInfo.getCurrentCamera()->setPreDrawCallback(mPredraw); + Log(Debug::Warning) << ("osg overwrote predraw"); + } + //if (mSwapchain) + // mSwapchain->endFrame(renderInfo.getState()->getGraphicsContext()); + } bool OpenXRView::realize(osg::ref_ptr state) diff --git a/apps/openmw/mwvr/openxrview.hpp b/apps/openmw/mwvr/openxrview.hpp index 53a7b40d3..09a8e6b93 100644 --- a/apps/openmw/mwvr/openxrview.hpp +++ b/apps/openmw/mwvr/openxrview.hpp @@ -1,6 +1,7 @@ #ifndef OPENXR_VIEW_HPP #define OPENXR_VIEW_HPP +#include #include "openxrmanager.hpp" #include "openxrswapchain.hpp" @@ -8,6 +9,7 @@ struct XrSwapchainSubImage; namespace MWVR { + class OpenXRView : public osg::Referenced { public: @@ -49,7 +51,7 @@ namespace MWVR }; protected: - OpenXRView(osg::ref_ptr XR); + OpenXRView(osg::ref_ptr XR, std::string name); virtual ~OpenXRView(); void setWidth(int width); void setHeight(int height); @@ -66,14 +68,14 @@ namespace MWVR OpenXRSwapchain& swapchain(void) { return *mSwapchain; } //! Create the view surface bool realize(osg::ref_ptr state); - //! Current frame being rendered - long long frameIndex() { return mFrameIndex; }; protected: osg::ref_ptr mXR; std::unique_ptr mSwapchain; OpenXRSwapchain::Config mSwapchainConfig; - long long mFrameIndex{ 0 }; + std::string mName{}; + bool mRendering{ false }; + osg::ref_ptr mPredraw{ nullptr }; }; } diff --git a/apps/openmw/mwvr/openxrviewer.cpp b/apps/openmw/mwvr/openxrviewer.cpp index f999d7c49..d77596e7e 100644 --- a/apps/openmw/mwvr/openxrviewer.cpp +++ b/apps/openmw/mwvr/openxrviewer.cpp @@ -18,20 +18,6 @@ namespace MWVR , mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}) { mViewer->setRealizeOperation(mRealizeOperation); - //auto* mainContext = mViewer->getCamera()->getGraphicsContext(); - //assert(mainContext); - - //auto* mainTraits = mainContext->getTraits(); - - //osg::ref_ptr traits = new osg::GraphicsContext::Traits(*mainTraits); - //traits->sharedContext = mainContext; - - //mViewerGW = new SDLUtil::GraphicsWindowSDL2(traits); - //if (!mViewerGW->valid()) throw std::runtime_error("Failed to create GraphicsContext"); - //mLeftGW = new SDLUtil::GraphicsWindowSDL2(traits); - //if (!mLeftGW->valid()) throw std::runtime_error("Failed to create GraphicsContext"); - //mRightGW = new SDLUtil::GraphicsWindowSDL2(traits); - //if (!mRightGW->valid()) throw std::runtime_error("Failed to create GraphicsContext"); } OpenXRViewer::~OpenXRViewer(void) @@ -50,7 +36,7 @@ namespace MWVR const XrCompositionLayerBaseHeader* OpenXRViewer::layer() { - auto stageViews = mXR->impl().getStageViews(); + auto stageViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().mRenderIndex, TrackedSpace::STAGE); mCompositionLayerProjectionViews[0].pose = stageViews[0].pose; mCompositionLayerProjectionViews[1].pose = stageViews[1].pose; mCompositionLayerProjectionViews[0].fov = stageViews[0].fov; @@ -61,6 +47,8 @@ namespace MWVR void OpenXRViewer::traversals() { Log(Debug::Verbose) << "Pre-Update"; + mXR->handleEvents(); + OpenXRFrameIndexer::instance().advanceUpdateIndex(); mViewer->updateTraversal(); Log(Debug::Verbose) << "Post-Update"; Log(Debug::Verbose) << "Pre-Rendering"; @@ -89,12 +77,14 @@ namespace MWVR // Use the main camera to render any GUI to the OpenXR GUI quad's swapchain. // (When swapping the window buffer we'll blit the mirror texture to it instead.) mMainCamera->setCullMask(MWRender::Mask_GUI); - mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this)); osg::Vec4 clearColor = mMainCamera->getClearColor(); - mViews[OpenXRWorldView::LEFT_VIEW] = new OpenXRWorldView(mXR, context->getState(), mMetersPerUnit, OpenXRWorldView::LEFT_VIEW); - mViews[OpenXRWorldView::RIGHT_VIEW] = new OpenXRWorldView(mXR, context->getState(), mMetersPerUnit, OpenXRWorldView::RIGHT_VIEW); + if (!mXR->realized()) + mXR->realize(context); + + mViews[OpenXRWorldView::LEFT_VIEW] = new OpenXRWorldView(mXR, "LeftEye", context->getState(), mMetersPerUnit, OpenXRWorldView::LEFT_VIEW); + mViews[OpenXRWorldView::RIGHT_VIEW] = new OpenXRWorldView(mXR, "RightEye", context->getState(), mMetersPerUnit, OpenXRWorldView::RIGHT_VIEW); mLeftCamera = mViews[OpenXRWorldView::LEFT_VIEW]->createCamera(OpenXRWorldView::LEFT_VIEW, clearColor, context); mRightCamera = mViews[OpenXRWorldView::RIGHT_VIEW]->createCamera(OpenXRWorldView::RIGHT_VIEW, clearColor, context); @@ -112,8 +102,8 @@ namespace MWVR mViewer->addSlave(mLeftCamera, mViews[OpenXRWorldView::LEFT_VIEW]->projectionMatrix(), mViews[OpenXRWorldView::LEFT_VIEW]->viewMatrix(), true); mViewer->addSlave(mRightCamera, mViews[OpenXRWorldView::RIGHT_VIEW]->projectionMatrix(), mViews[OpenXRWorldView::RIGHT_VIEW]->viewMatrix(), true); - mViewer->getSlave(OpenXRWorldView::LEFT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::LEFT_VIEW], context); - mViewer->getSlave(OpenXRWorldView::RIGHT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::RIGHT_VIEW], context); + mViewer->getSlave(OpenXRWorldView::LEFT_VIEW)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::LEFT_VIEW], context); + mViewer->getSlave(OpenXRWorldView::RIGHT_VIEW)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::RIGHT_VIEW], context); mViewer->setLightingMode(osg::View::SKY_LIGHT); mViewer->setReleaseContextAtEndOfFrameHint(false); @@ -147,18 +137,24 @@ namespace MWVR mMirrorTextureSwapchain.reset(new OpenXRSwapchain(mXR, context->getState(), config)); mXRMenu.reset(new OpenXRMenu(mXR, context->getState(), "MainMenu", config.width, config.height, osg::Vec2(1.f, 1.f))); - mMainCamera->setPreDrawCallback(new OpenXRView::PredrawCallback(mMainCamera, mXRMenu.get())); - mMainCamera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(mMainCamera, mXRMenu.get())); + mMenuCamera = mXRMenu->createCamera(2, clearColor, context); + mMenuCamera->setCullMask(MWRender::Mask_GUI); + mMenuCamera->setName("MenuCamera"); + mViewer->addSlave(mMenuCamera, true); + //mMenuCamera->setPreDrawCallback(new OpenXRView::PredrawCallback(mMenuCamera, mXRMenu.get())); + //mMenuCamera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(mMenuCamera, mXRMenu.get())); mXR->impl().mLayerStack.setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, mXRMenu.get()); + mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this)); + mMainCamera->setGraphicsContext(nullptr); mConfigured = true; } void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) const { mMirrorTextureSwapchain->beginFrame(gc); - mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); - mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().current()->blit(gc, mMirrorTextureSwapchain->width() / 2, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); + mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); + mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().renderBuffer()->blit(gc, mMirrorTextureSwapchain->width() / 2, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); //mXRMenu->swapchain().current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); } @@ -172,45 +168,30 @@ namespace MWVR void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc) { + Timer timer("swapBuffers"); + + Log(Debug::Verbose) << "SwapBuffers()"; std::unique_lock lock(mMutex); if (!mConfigured) return; auto* state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); - blitEyesToMirrorTexture(gc); + + + mXR->beginFrame(); + //blitEyesToMirrorTexture(gc); + mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().endFrame(gc); + mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().endFrame(gc); + mXRMenu->swapchain().endFrame(gc); mXR->endFrame(); - gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); + mXR->waitFrame(); + //gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); - mMirrorTextureSwapchain->current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); + //mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); - mMirrorTextureSwapchain->releaseSwapchainImage(); + //mMirrorTextureSwapchain->endFrame(gc); gc->swapBuffersImplementation(); - - } - - void - OpenXRViewer::UpdateSlaveCallback::updateSlave( - osg::View& view, - osg::View::Slave& slave) - { - mXR->handleEvents(); - if (!mXR->sessionRunning()) - return; - - - - auto* camera = slave._camera.get(); - auto name = camera->getName(); - - Log(Debug::Verbose) << "Updating camera " << name; - mXR->beginFrame(mView->frameIndex()); - - auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix(); - auto projMatrix = mView->projectionMatrix(); - - camera->setViewMatrix(viewMatrix); - camera->setProjectionMatrix(projMatrix); } void diff --git a/apps/openmw/mwvr/openxrviewer.hpp b/apps/openmw/mwvr/openxrviewer.hpp index 9d4151162..4277155d6 100644 --- a/apps/openmw/mwvr/openxrviewer.hpp +++ b/apps/openmw/mwvr/openxrviewer.hpp @@ -31,20 +31,6 @@ namespace MWVR osg::ref_ptr mViewer; }; - class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback - { - public: - UpdateSlaveCallback(osg::ref_ptr XR, osg::ref_ptr view, osg::GraphicsContext* gc) - : mXR(XR), mView(view), mGC(gc) - {} - - void updateSlave(osg::View& view, osg::View::Slave& slave) override; - - private: - osg::ref_ptr mXR; - osg::ref_ptr mView; - osg::ref_ptr mGC; - }; class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback { public: @@ -90,6 +76,7 @@ namespace MWVR //osg::ref_ptr mRightGW; osg::Camera* mMainCamera = nullptr; + osg::Camera* mMenuCamera = nullptr; osg::Camera* mLeftCamera = nullptr; osg::Camera* mRightCamera = nullptr; diff --git a/apps/openmw/mwvr/openxrworldview.cpp b/apps/openmw/mwvr/openxrworldview.cpp index 130fe097e..8397212f2 100644 --- a/apps/openmw/mwvr/openxrworldview.cpp +++ b/apps/openmw/mwvr/openxrworldview.cpp @@ -75,9 +75,10 @@ namespace MWVR return osg::Matrix(matrix); } + osg::Matrix OpenXRWorldView::projectionMatrix() { - auto hmdViews = mXR->impl().getHmdViews(); + auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW); float near = Settings::Manager::getFloat("near clip", "Camera"); float far = Settings::Manager::getFloat("viewing distance", "Camera") * mMetersPerUnit; @@ -88,11 +89,11 @@ namespace MWVR osg::Matrix OpenXRWorldView::viewMatrix() { osg::Matrix viewMatrix; - auto hmdViews = mXR->impl().getHmdViews(); + auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW); auto pose = hmdViews[mView].pose; osg::Vec3 position = osg::fromXR(pose.position); - auto stageViews = mXR->impl().getStageViews(); + auto stageViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::STAGE); auto stagePose = stageViews[mView].pose; // Comfort shortcut. @@ -115,8 +116,8 @@ namespace MWVR } OpenXRWorldView::OpenXRWorldView( - osg::ref_ptr XR, osg::ref_ptr state, float metersPerUnit, SubView view) - : OpenXRView(XR) + osg::ref_ptr XR, std::string name, osg::ref_ptr state, float metersPerUnit, SubView view) + : OpenXRView(XR, name) , mMetersPerUnit(metersPerUnit) , mView(view) { @@ -145,4 +146,44 @@ namespace MWVR renderer->setCameraRequiresSetUp(false); } } + + void OpenXRWorldView::prerenderCallback(osg::RenderInfo& renderInfo) + { + OpenXRView::prerenderCallback(renderInfo); + auto* view = renderInfo.getView(); + auto* camera = renderInfo.getCurrentCamera(); + auto name = camera->getName(); + + Log(Debug::Verbose) << "Updating camera " << name; + + auto viewMatrix = view->getCamera()->getViewMatrix() * this->viewMatrix(); + auto projectionMatrix = this->projectionMatrix(); + + camera->setViewMatrix(viewMatrix); + camera->setProjectionMatrix(projectionMatrix); + } + + void + OpenXRWorldView::UpdateSlaveCallback::updateSlave( + osg::View& view, + osg::View::Slave& slave) + { + mXR->handleEvents(); + if (!mXR->sessionRunning()) + return; + + + auto* camera = slave._camera.get(); + auto name = camera->getName(); + + Log(Debug::Verbose) << "Updating slave " << name; + + //auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix(); + //auto projMatrix = mView->projectionMatrix(); + + //camera->setViewMatrix(viewMatrix); + //camera->setProjectionMatrix(projMatrix); + + slave.updateSlaveImplementation(view); + } } diff --git a/apps/openmw/mwvr/openxrworldview.hpp b/apps/openmw/mwvr/openxrworldview.hpp index e56090a49..f21155f7d 100644 --- a/apps/openmw/mwvr/openxrworldview.hpp +++ b/apps/openmw/mwvr/openxrworldview.hpp @@ -15,10 +15,27 @@ namespace MWVR SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input. }; + class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback + { + public: + UpdateSlaveCallback(osg::ref_ptr XR, osg::ref_ptr view, osg::GraphicsContext* gc) + : mXR(XR), mView(view), mGC(gc) + {} + + void updateSlave(osg::View& view, osg::View::Slave& slave) override; + + private: + osg::ref_ptr mXR; + osg::ref_ptr mView; + osg::ref_ptr mGC; + }; + public: - OpenXRWorldView(osg::ref_ptr XR, osg::ref_ptr state, float metersPerUnit, SubView view); + OpenXRWorldView(osg::ref_ptr XR, std::string name, osg::ref_ptr state, float metersPerUnit, SubView view); ~OpenXRWorldView(); + //! Prepare for render (update matrices) + void prerenderCallback(osg::RenderInfo& renderInfo) override; //! Projection offset for this view osg::Matrix projectionMatrix(); //! View offset for this view