From 14039e5e256b5289158ad706722c4450fea4010b Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sun, 2 Feb 2020 13:12:53 +0100 Subject: [PATCH] Predictions work perfectly with this. Committing that and clean up later. --- apps/openmw/CMakeLists.txt | 2 + apps/openmw/mwvr/openxrinputmanager.cpp | 7 +- apps/openmw/mwvr/openxrlayer.cpp | 2 +- apps/openmw/mwvr/openxrlayer.hpp | 3 + apps/openmw/mwvr/openxrmanager.cpp | 149 ++++++++++------- apps/openmw/mwvr/openxrmanager.hpp | 54 +++---- apps/openmw/mwvr/openxrmanagerimpl.cpp | 163 ++++++++++--------- apps/openmw/mwvr/openxrmanagerimpl.hpp | 37 +---- apps/openmw/mwvr/openxrmenu.cpp | 69 ++++---- apps/openmw/mwvr/openxrmenu.hpp | 7 +- apps/openmw/mwvr/openxrsession.cpp | 166 +++++++++++++++++++ apps/openmw/mwvr/openxrsession.hpp | 79 +++++++++ apps/openmw/mwvr/openxrswapchain.cpp | 27 +++- apps/openmw/mwvr/openxrswapchain.hpp | 5 +- apps/openmw/mwvr/openxrview.cpp | 75 ++------- apps/openmw/mwvr/openxrview.hpp | 50 ++---- apps/openmw/mwvr/openxrviewer.cpp | 207 +++++++++++++++--------- apps/openmw/mwvr/openxrviewer.hpp | 55 +++++-- apps/openmw/mwvr/openxrworldview.cpp | 96 +++++------ apps/openmw/mwvr/openxrworldview.hpp | 18 +-- 20 files changed, 778 insertions(+), 493 deletions(-) create mode 100644 apps/openmw/mwvr/openxrsession.cpp create mode 100644 apps/openmw/mwvr/openxrsession.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index cfae024f3..f406ff39f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -124,6 +124,8 @@ if(BUILD_VR_OPENXR) mwvr/openxrmanagerimpl.cpp mwvr/openxrmenu.hpp mwvr/openxrmenu.cpp + mwvr/openxrsession.hpp + mwvr/openxrsession.cpp mwvr/openxrswapchain.hpp mwvr/openxrswapchain.cpp mwvr/openxrtexture.hpp diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index b8a238d59..cb6155526 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -514,9 +514,10 @@ namespace MWVR void OpenXRInputManagerImpl::updateHandTracking() { - for (auto hand : { LEFT_HAND, RIGHT_HAND }) { - CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().predictedDisplayTime(OpenXRFrameIndexer::instance().updateIndex()), &mHandSpaceLocation[hand])); - } + // TODO + //for (auto hand : { LEFT_HAND, RIGHT_HAND }) { + // CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().predictedDisplayTime(OpenXRFrameIndexer::instance().updateIndex()), &mHandSpaceLocation[hand])); + //} } XrPath diff --git a/apps/openmw/mwvr/openxrlayer.cpp b/apps/openmw/mwvr/openxrlayer.cpp index 2c0023467..32873664b 100644 --- a/apps/openmw/mwvr/openxrlayer.cpp +++ b/apps/openmw/mwvr/openxrlayer.cpp @@ -5,7 +5,7 @@ namespace MWVR { void OpenXRLayerStack::setLayer(Layer layer, OpenXRLayer* layerObj) { - mLayers[layer] = nullptr; + mLayers[layer] = layerObj->layer(); mLayerObjects[layer] = layerObj; } diff --git a/apps/openmw/mwvr/openxrlayer.hpp b/apps/openmw/mwvr/openxrlayer.hpp index f5fed549b..efb516330 100644 --- a/apps/openmw/mwvr/openxrlayer.hpp +++ b/apps/openmw/mwvr/openxrlayer.hpp @@ -16,6 +16,7 @@ namespace MWVR virtual ~OpenXRLayer(void) = default; virtual const XrCompositionLayerBaseHeader* layer() = 0; + virtual void swapBuffers(osg::GraphicsContext* gc) = 0; }; class OpenXRLayerStack @@ -32,9 +33,11 @@ namespace MWVR OpenXRLayerStack() = default; ~OpenXRLayerStack() = default; + void setLayer(Layer layer, OpenXRLayer* layerObj); int layerCount(); const XrCompositionLayerBaseHeader** layerHeaders(); + LayerObjectStack& layerObjects() { return mLayerObjects; }; private: LayerObjectStack mLayerObjects; diff --git a/apps/openmw/mwvr/openxrmanager.cpp b/apps/openmw/mwvr/openxrmanager.cpp index d047fe6e0..066db8bfb 100644 --- a/apps/openmw/mwvr/openxrmanager.cpp +++ b/apps/openmw/mwvr/openxrmanager.cpp @@ -16,11 +16,93 @@ #include #include +#include #include namespace MWVR { + + static std::vector stats; + static std::mutex statsMutex; + static std::thread statsThread; + static bool statsThreadRunning = false; + + static void statsThreadRun() + { + return; + while (statsThreadRunning) + { + std::stringstream ss; + for (auto& context : stats) + { + for (auto& measurement : *context.second) + { + double ms = static_cast(measurement.second) / 1000000.; + Log(Debug::Verbose) << context.first << "." << measurement.first << ": " << ms << "ms"; + } + } + + //Log(Debug::Verbose) << ss.str(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + + + Timer::Timer(const char* name) : mName(name) + { + mLastCheckpoint = mBegin = std::chrono::steady_clock::now(); + + std::unique_lock lock(statsMutex); + for (auto& m : stats) + { + if (m.first == mName) + mContext = m.second; + } + + if (mContext == nullptr) + { + mContext = new Measures(); + mContext->reserve(32); + stats.emplace_back(MeasurementContext(mName, mContext)); + } + + if (!statsThreadRunning) + { + statsThreadRunning = true; + statsThread = std::thread([] { statsThreadRun(); }); + } + } + Timer::~Timer() + { + //statsThreadRunning = false; + checkpoint("~"); + } + + void Timer::checkpoint(const char* name) + { + auto now = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now - mLastCheckpoint); + mLastCheckpoint = now; + + Measure* measure = nullptr; + for (auto& m : *mContext) + { + if (m.first == name) + { + measure = &m; + } + } + if (!measure) + { + mContext->push_back(Measure(name, elapsed.count())); + } + else { + measure->second = measure->second * 0.95 + elapsed.count() * 0.05; + } + } OpenXRManager::OpenXRManager() : mPrivate(nullptr) @@ -34,7 +116,7 @@ namespace MWVR } - bool + bool OpenXRManager::realized() { return !!mPrivate; @@ -73,10 +155,10 @@ namespace MWVR return impl().beginFrame(); } - void OpenXRManager::endFrame() + void OpenXRManager::endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack) { if (realized()) - return impl().endFrame(); + return impl().endFrame(displayTime, layerStack); } void OpenXRManager::updateControls() @@ -96,11 +178,11 @@ namespace MWVR try { mPrivate = std::make_shared(); } - catch (std::exception& e) + catch (std::exception & e) { Log(Debug::Error) << "Exception thrown by OpenXR: " << e.what(); osg::ref_ptr state = gc->getState(); - + } } } @@ -119,71 +201,18 @@ namespace MWVR mXR->realize(gc); } - bool + bool OpenXRManager::RealizeOperation::realized() { return mXR->realized(); } - void + void OpenXRManager::CleanupOperation::operator()( osg::GraphicsContext* gc) { } - -//#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() - { - return g_OpenXRFrameIndexer; - } - - int64_t OpenXRFrameIndexer::advanceUpdateIndex() - { - std::unique_lock lock(mMutex); - mUpdateIndex++; - Log(Debug::Verbose) << "Advancing update index to " << mUpdateIndex; - assert(mUpdateIndex > mRenderIndex); - return mUpdateIndex; - } - - int64_t OpenXRFrameIndexer::advanceRenderIndex() - { - std::unique_lock lock(mMutex); - mRenderIndex++; - Log(Debug::Verbose) << "Advancing frame index to " << mRenderIndex; - if (!(mUpdateIndex >= mRenderIndex)) - mUpdateIndex = mRenderIndex - 1; - return mRenderIndex; - } } std::ostream& operator <<( diff --git a/apps/openmw/mwvr/openxrmanager.hpp b/apps/openmw/mwvr/openxrmanager.hpp index a7cf8dccc..38a996aab 100644 --- a/apps/openmw/mwvr/openxrmanager.hpp +++ b/apps/openmw/mwvr/openxrmanager.hpp @@ -20,30 +20,29 @@ 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"; - } + using Measure = std::pair < const char*, int64_t >; + using Measures = std::vector < Measure >; + using MeasurementContext = std::pair < const char*, Measures* >; + + Timer(const char* name); + ~Timer(); + void checkpoint(const char* name); std::chrono::steady_clock::time_point mBegin; - std::string mName; + std::chrono::steady_clock::time_point mLastCheckpoint; + const char* mName = nullptr; + Measures* mContext = nullptr; }; //! Represents the pose of a limb in VR space. struct Pose { //! Position in VR space - osg::Vec3 position; + osg::Vec3 position{ 0,0,0 }; //! Orientation in VR space. - osg::Quat orientation; + osg::Quat orientation{ 0,0,0,1 }; //! Speed of movement in VR space, expressed in meters per second - osg::Vec3 velocity; + osg::Vec3 velocity{ 0,0,0 }; }; //! Describes what limb to track. @@ -57,31 +56,16 @@ namespace MWVR //! Describes what space to track the limb in enum class TrackedSpace { - STAGE, //!< Track limb in the VR stage space. Meaning a space with a floor level origin and fixed horizontal orientation. - VIEW //!< Track limb in the VR view space. Meaning a space with the head as origin and orientation. + STAGE=0, //!< Track limb in the VR stage space. Meaning a space with a floor level origin and fixed horizontal orientation. + VIEW=1 //!< Track limb in the VR view space. Meaning a space with the head as origin and orientation. }; - struct OpenXRFrameIndexer + enum class Chirality { - 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 }; + LEFT_HAND = 0, + RIGHT_HAND = 1 }; - // Use the pimpl pattern to avoid cluttering the namespace with openxr dependencies. class OpenXRManagerImpl; @@ -122,7 +106,7 @@ namespace MWVR void handleEvents(); void waitFrame(); void beginFrame(); - void endFrame(); + void endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack); void updateControls(); void realize(osg::GraphicsContext* gc); diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index c41b6e6b7..227f66109 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -254,60 +254,81 @@ namespace MWVR Log(Debug::Verbose) << ss.str(); } + XrFrameState OpenXRManagerImpl::frameState() + { + std::unique_lock lock(mFrameStateMutex); + return mFrameState; + } + void OpenXRManagerImpl::waitFrame() { + Log(Debug::Verbose) << "OpenXRSesssion::WaitFrame"; 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); + static std::mutex waitFrameMutex; - if (!mSessionRunning) - return; + if (!mSessionRunning) + return; - XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; - XrFrameState frameState{ XR_TYPE_FRAME_STATE }; + XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; + XrFrameState frameState{ XR_TYPE_FRAME_STATE }; - CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); - - - mTimeKeeper.progressToNextFrame(frameState); - - //}).detach(); + CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); + std::unique_lock lock(mFrameStateMutex); + mFrameState = frameState; + Log(Debug::Verbose) << "OpenXRSesssion::WaitFrame END"; } void OpenXRManagerImpl::beginFrame() { + Log(Debug::Verbose) << "OpenXRSesssion::BeginFrame"; Timer timer("beginFrame"); XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO }; CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo)); + Log(Debug::Verbose) << "OpenXRSesssion::BeginFrame END"; } void - OpenXRManagerImpl::endFrame() + OpenXRManagerImpl::endFrame(int64_t displayTime, OpenXRLayerStack* layerStack) { + Log(Debug::Verbose) << "OpenXRSesssion::EndFrame"; Timer timer("endFrame()"); if (!mSessionRunning) return; XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO }; - frameEndInfo.displayTime = mTimeKeeper.predictedDisplayTime(OpenXRFrameIndexer::instance().renderIndex()); + frameEndInfo.displayTime = displayTime; frameEndInfo.environmentBlendMode = mEnvironmentBlendMode; - frameEndInfo.layerCount = mLayerStack.layerCount(); - frameEndInfo.layers = mLayerStack.layerHeaders(); + if (layerStack) + { + frameEndInfo.layerCount = layerStack->layerCount(); + frameEndInfo.layers = layerStack->layerHeaders(); + } + else { + frameEndInfo.layerCount = 0; + frameEndInfo.layers = nullptr; + } + //Log(Debug::Verbose) << "LayerStack<" << frameEndInfo.layerCount << ", " << frameEndInfo.layers << ">"; + + static std::chrono::steady_clock::time_point last = std::chrono::steady_clock::now(); + + auto now = std::chrono::steady_clock::now(); + auto elapsed = now -last; + last = now; + + Log(Debug::Verbose) << "endFrame(): period: " << std::chrono::duration_cast(elapsed).count(); + CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo)); + Log(Debug::Verbose) << "OpenXRSesssion::EndFrame END"; } std::array OpenXRManagerImpl::getPredictedViews( - int64_t frameIndex, + int64_t predictedDisplayTime, TrackedSpace space) { @@ -317,7 +338,7 @@ namespace MWVR XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; viewLocateInfo.viewConfigurationType = mViewConfigType; - viewLocateInfo.displayTime = mTimeKeeper.predictedDisplayTime(frameIndex); + viewLocateInfo.displayTime = predictedDisplayTime; switch (space) { case TrackedSpace::STAGE: @@ -332,7 +353,7 @@ namespace MWVR return views; } - MWVR::Pose OpenXRManagerImpl::getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space) + MWVR::Pose OpenXRManagerImpl::getPredictedLimbPose(int64_t predictedDisplayTime, TrackedLimb limb, TrackedSpace space) { XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION }; XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY }; @@ -360,9 +381,9 @@ namespace MWVR referenceSpace = mReferenceSpaceView; break; } - 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"; + CHECK_XRCMD(xrLocateSpace(limbSpace, referenceSpace, predictedDisplayTime, &location)); + //if (!(velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT)) + // Log(Debug::Warning) << "Unable to acquire linear velocity"; return MWVR::Pose{ osg::fromXR(location.pose.position), osg::fromXR(location.pose.orientation), @@ -439,12 +460,6 @@ namespace MWVR } } - XrTime OpenXRManagerImpl::predictedDisplayTime(int64_t frameIndex) - { - - return mTimeKeeper.predictedDisplayTime(frameIndex); - } - const XrEventDataBaseHeader* OpenXRManagerImpl::nextEvent() { @@ -476,65 +491,65 @@ namespace MWVR return XrPosef{ osg::toXR(pose.orientation), osg::toXR(pose.position) }; } - XrTime OpenXRTimeKeeper::predictedDisplayTime(int64_t frameIndex) - { - std::unique_lock lock(mMutex); + //XrTime OpenXRTimeKeeper::predictedDisplayTime(int64_t frameIndex) + //{ + // std::unique_lock lock(mMutex); - //auto prediction = mPredictedFrameTime; - //auto predictedPeriod = mPredictedPeriod; + // //auto prediction = mPredictedFrameTime; + // //auto predictedPeriod = mPredictedPeriod; - //auto futureFrames = frameIndex - OpenXRFrameIndexer::instance().renderIndex(); + // //auto futureFrames = frameIndex - OpenXRFrameIndexer::instance().renderIndex(); - //prediction += ( 0 + futureFrames) * predictedPeriod; + // //prediction += ( 0 + futureFrames) * predictedPeriod; - //Log(Debug::Verbose) << "Predicted: displayTime[" << futureFrames << "]=" << prediction; + // //Log(Debug::Verbose) << "Predicted: displayTime[" << futureFrames << "]=" << prediction; - //return prediction; + // //return prediction; - //return mFrameState.predictedDisplayTime; + // //return mFrameState.predictedDisplayTime; - return mPredictedFrameTime; - } + // return mPredictedFrameTime; + //} - 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; + //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; + // auto now = clock::now(); + // auto nanoseconds_elapsed = std::chrono::duration_cast(now - mLastFrameTimePoint); + // XrDuration realPeriod = nanoseconds_elapsed.count(); + // mFrameState = frameState; - mPredictedFrameTime = mFrameState.predictedDisplayTime; - mPredictedPeriod = mFrameState.predictedDisplayPeriod; + // mPredictedFrameTime = mFrameState.predictedDisplayTime + mFrameState.predictedDisplayPeriod * 4; + // 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) / 2; - mPredictedPeriod = realPeriod; - } + // // 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) / 2; + // // 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; + // 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; - } + // 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 753d7d217..02d7f19fc 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.hpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.hpp @@ -44,31 +44,6 @@ 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 { OpenXRManagerImpl(void); @@ -81,14 +56,14 @@ namespace MWVR const XrEventDataBaseHeader* nextEvent(); void waitFrame(); void beginFrame(); - void endFrame(); - std::array getPredictedViews(int64_t frameIndex, TrackedSpace mSpace); - MWVR::Pose getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space); + void endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack); + std::array getPredictedViews(int64_t predictedDisplayTime, TrackedSpace mSpace); + MWVR::Pose getPredictedLimbPose(int64_t predictedDisplayTime, TrackedLimb limb, TrackedSpace space); int eyes(); void handleEvents(); void updateControls(); void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent); - XrTime predictedDisplayTime(int64_t frameIndex); + XrFrameState frameState(); bool initialized = false; long long mFrameIndex = 0; @@ -105,10 +80,10 @@ namespace MWVR XrSpace mReferenceSpaceView = XR_NULL_HANDLE; XrSpace mReferenceSpaceStage = XR_NULL_HANDLE; XrEventDataBuffer mEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER }; - OpenXRTimeKeeper mTimeKeeper{}; - OpenXRLayerStack mLayerStack{}; + XrFrameState mFrameState{}; XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN; bool mSessionRunning = false; + std::mutex mFrameStateMutex{}; std::mutex mEventMutex{}; }; } diff --git a/apps/openmw/mwvr/openxrmenu.cpp b/apps/openmw/mwvr/openxrmenu.cpp index 0c66bbd37..0ad6c49ee 100644 --- a/apps/openmw/mwvr/openxrmenu.cpp +++ b/apps/openmw/mwvr/openxrmenu.cpp @@ -5,27 +5,16 @@ 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, title) + OpenXRMenu::OpenXRMenu( + osg::ref_ptr XR, + OpenXRSwapchain::Config config, + osg::ref_ptr state, + const std::string& title, + osg::Vec2 extent_meters) + : OpenXRView(XR, title, config, state) , mTitle(title) + , mExtent(extent_meters) { - setWidth(width); - setHeight(height); - setSamples(1); - if (!realize(state)) - throw std::runtime_error(std::string("Failed to create swapchain for menu \"") + title + "\""); - mLayer.reset(new XrCompositionLayerQuad); - mLayer->type = XR_TYPE_COMPOSITION_LAYER_QUAD; - mLayer->next = nullptr; - mLayer->layerFlags = 0; - mLayer->space = XR->impl().mReferenceSpaceStage; - mLayer->eyeVisibility = XR_EYE_VISIBILITY_BOTH; - mLayer->subImage = swapchain().subImage(); - mLayer->size.width = extent_meters.x(); - mLayer->size.height = extent_meters.y(); - - // 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(); } @@ -38,6 +27,14 @@ namespace MWVR OpenXRMenu::layer() { updatePosition(); + //Log(Debug::Verbose) << "MenuPose: " << mPose; + + if (mLayer) + { + mLayer->pose.position = osg::toXR(mPose.position); + mLayer->pose.orientation = osg::toXR(mPose.orientation); + } + return reinterpret_cast(mLayer.get()); } @@ -47,22 +44,36 @@ 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().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); + mPose = predictedPose(); + mPose.position += mPose.orientation * osg::Vec3(0, 0, -1); + mPose.orientation = -mPose.orientation; + + Log(Debug::Verbose) << "Menu pose updated to: " << mPose; mPositionNeedsUpdate = false; - Log(Debug::Verbose) << "Menu pose updated to: " << pose; } - void OpenXRMenu::postrenderCallback(osg::RenderInfo& info) + void OpenXRMenu::swapBuffers( + osg::GraphicsContext* gc) { - OpenXRView::postrenderCallback(info); + OpenXRView::swapBuffers(gc); + + if (!mLayer) + { + mLayer.reset(new XrCompositionLayerQuad); + mLayer->type = XR_TYPE_COMPOSITION_LAYER_QUAD; + mLayer->next = nullptr; + mLayer->layerFlags = 0; + mLayer->space = mXR->impl().mReferenceSpaceStage; + mLayer->eyeVisibility = XR_EYE_VISIBILITY_BOTH; + mLayer->subImage = swapchain().subImage(); + mLayer->size.width = mExtent.x(); + mLayer->size.height = mExtent.y(); + + // 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; + } } } diff --git a/apps/openmw/mwvr/openxrmenu.hpp b/apps/openmw/mwvr/openxrmenu.hpp index 2e0b8d435..905e4d69c 100644 --- a/apps/openmw/mwvr/openxrmenu.hpp +++ b/apps/openmw/mwvr/openxrmenu.hpp @@ -11,17 +11,20 @@ namespace MWVR { public: - OpenXRMenu(osg::ref_ptr XR, osg::ref_ptr state, const std::string& title, int width, int height, osg::Vec2 extent_meters); + OpenXRMenu(osg::ref_ptr XR, OpenXRSwapchain::Config config, osg::ref_ptr state, const std::string& title, osg::Vec2 extent_meters); ~OpenXRMenu(); const XrCompositionLayerBaseHeader* layer() override; const std::string& title() const { return mTitle; } void updatePosition(); - void postrenderCallback(osg::RenderInfo& renderInfo) override; + + void swapBuffers(osg::GraphicsContext* gc) override; protected: bool mPositionNeedsUpdate{ true }; std::unique_ptr mLayer = nullptr; std::string mTitle; + Pose mPose{ {}, {0,0,0,1}, {} }; + osg::Vec2 mExtent; }; } diff --git a/apps/openmw/mwvr/openxrsession.cpp b/apps/openmw/mwvr/openxrsession.cpp new file mode 100644 index 000000000..43afb3d48 --- /dev/null +++ b/apps/openmw/mwvr/openxrsession.cpp @@ -0,0 +1,166 @@ +#include "openxrmanager.hpp" +#include "openxrmanagerimpl.hpp" +#include "openxrswapchain.hpp" +#include "../mwinput/inputmanagerimp.hpp" + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "openxrsession.hpp" +#include + +namespace MWVR +{ + OpenXRSession::OpenXRSession( + osg::ref_ptr XR) + : mXR(XR) + , mFrameEndTimePoint(clock::now()) + , mFrameBeginTimePoint(mFrameEndTimePoint) + { + mXRThread = std::thread([this] {run(); }); + } + + OpenXRSession::~OpenXRSession() + { + mShouldQuit = true; + } + + void OpenXRSession::setLayer( + OpenXRLayerStack::Layer layerType, + OpenXRLayer* layer) + { + mLayerStack.setLayer(layerType, layer); + } + + void OpenXRSession::swapBuffers(osg::GraphicsContext* gc) + { + Log(Debug::Verbose) << "OpenXRSession::swapBuffers"; + Timer timer("OpenXRSession::SwapBuffers"); + static int wat = 0; + if (!mXR->sessionRunning()) + return; + if (!mShouldRenderLayers) + { + return; + } + + //if (wat++ % 100 != 0) + // return; + + + //std::unique_lock renderLock(mRenderMutex); + //timer.checkpoint("Mutex"); + for (auto layer : mLayerStack.layerObjects()) + layer->swapBuffers(gc); + mFrameEndTimePoint = clock::now(); + auto nanoseconds_elapsed = std::chrono::duration_cast(mFrameEndTimePoint - mFrameBeginTimePoint); + auto milliseconds_elapsed = std::chrono::duration_cast(nanoseconds_elapsed); + mRenderTimes.push_back(nanoseconds_elapsed.count()); + Log(Debug::Verbose) << "Render time: " << milliseconds_elapsed.count() << "ms"; + //mShouldRenderLayers = true; + timer.checkpoint("Rendered"); + + mXR->endFrame(predictedDisplayTime(), &mLayerStack); + Log(Debug::Verbose) << "mFrameEndTimePoint: " << mFrameEndTimePoint.time_since_epoch().count() / 1000000; + Log(Debug::Verbose) << "mFrameBeginTimePoint: " << mFrameBeginTimePoint.time_since_epoch().count() / 1000000; + Log(Debug::Verbose) << "mPredictedDisplayTimeRealTime: " << mPredictedDisplayTimeRealTime.time_since_epoch().count() / 1000000; + Log(Debug::Verbose) << "mPredictedTime: " << predictedDisplayTime() / 1000000; + Log(Debug::Verbose) << "mXrDisplayTime: " << mXR->impl().frameState().predictedDisplayTime / 1000000; + //mShouldRenderLayers = false; + mFrameBeginTimePoint = clock::now(); + + //mRenderTimes.push_front(std::chrono::duration_cast(mFrameEndTimePoint - mFrameBeginTimePoint).count()); + //if(mRenderTimes.size() > 33) mRenderTimes.pop_back(); + + //if (mPredictedPeriod == 0) + //{ + // mPredictedPeriod = milliseconds_elapsed; + // mPredictedPeriods = 1; + //} + //else + //{ + // double periodDevianceMagnitude = static_cast(std::max(milliseconds_elapsed, mPredictedPeriod)) / static_cast(std::min(milliseconds_elapsed, mPredictedPeriod)); + // double periodStability = std::pow(periodDevianceMagnitude, -2.); + // mPredictedPeriod = (1. - periodStability) * mPredictedPeriod + (periodStability)*milliseconds_elapsed; + //} + + } + + void OpenXRSession::run() + { + } + + void OpenXRSession::waitFrame() + { + // For now it seems we must just accept crap performance from the rendering loop + // Since Oculus' implementation of waitFrame() does not even attempt to reflect real + // render time and just incurs a huge useless delay. + + Log(Debug::Verbose) << "OpenXRSesssion::beginframe"; + Timer timer("OpenXRSession::beginFrame"); + mXR->waitFrame(); + timer.checkpoint("waitFrame"); + predictNext(0); + mShouldRenderLayers = true; + + } + + void OpenXRSession::predictNext(int extraPeriods) + { + int64_t sum = 0; + while (mRenderTimes.size() > 10) + mRenderTimes.pop_front(); + for (unsigned i = 0; i < mRenderTimes.size(); i++) + { + sum += mRenderTimes[i] / mXR->impl().frameState().predictedDisplayPeriod; + } + int64_t average = sum / std::max((int64_t)mRenderTimes.size(), (int64_t)1); + + mPredictedPeriods = std::max(average, (int64_t)0) + extraPeriods; + + Log(Debug::Verbose) << "Periods: " << mPredictedPeriods; + + int64_t future = (mPredictedPeriods)*mXR->impl().frameState().predictedDisplayPeriod; + + mPredictedDisplayTime = mXR->impl().frameState().predictedDisplayTime + future; + mPredictedDisplayTimeRealTime = mFrameBeginTimePoint + nanoseconds(future + mXR->impl().frameState().predictedDisplayPeriod); + + // Update pose predictions + mPredictedPoses.head[(int)TrackedSpace::STAGE] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE); + mPredictedPoses.head[(int)TrackedSpace::VIEW] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW); + mPredictedPoses.hands[(int)Chirality::LEFT_HAND][(int)TrackedSpace::STAGE] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::LEFT_HAND, TrackedSpace::STAGE); + mPredictedPoses.hands[(int)Chirality::LEFT_HAND][(int)TrackedSpace::VIEW] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::LEFT_HAND, TrackedSpace::VIEW); + mPredictedPoses.hands[(int)Chirality::RIGHT_HAND][(int)TrackedSpace::STAGE] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::RIGHT_HAND, TrackedSpace::STAGE); + mPredictedPoses.hands[(int)Chirality::RIGHT_HAND][(int)TrackedSpace::VIEW] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::RIGHT_HAND, TrackedSpace::VIEW); + auto stageViews = mXR->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::STAGE); + auto hmdViews = mXR->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::VIEW); + mPredictedPoses.eye[(int)Chirality::LEFT_HAND][(int)TrackedSpace::STAGE] = fromXR(stageViews[(int)Chirality::LEFT_HAND].pose); + mPredictedPoses.eye[(int)Chirality::LEFT_HAND][(int)TrackedSpace::VIEW] = fromXR(hmdViews[(int)Chirality::LEFT_HAND].pose); + mPredictedPoses.eye[(int)Chirality::RIGHT_HAND][(int)TrackedSpace::STAGE] = fromXR(stageViews[(int)Chirality::RIGHT_HAND].pose); + mPredictedPoses.eye[(int)Chirality::RIGHT_HAND][(int)TrackedSpace::VIEW] = fromXR(hmdViews[(int)Chirality::RIGHT_HAND].pose); + + //std::unique_lock lock(mSyncMutex); + //mSync.notify_all(); + } +} + +std::ostream& operator <<( + std::ostream& os, + const MWVR::Pose& pose) +{ + os << "position=" << pose.position << " orientation=" << pose.orientation << " velocity=" << pose.velocity; + return os; +} + diff --git a/apps/openmw/mwvr/openxrsession.hpp b/apps/openmw/mwvr/openxrsession.hpp new file mode 100644 index 000000000..f6ded7aea --- /dev/null +++ b/apps/openmw/mwvr/openxrsession.hpp @@ -0,0 +1,79 @@ +#ifndef MWVR_OPENRXSESSION_H +#define MWVR_OPENRXSESSION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "openxrmanager.hpp" +#include "openxrlayer.hpp" + +namespace MWVR +{ + + using PoseSet = std::array; + + struct PoseSets + { + PoseSet eye[2]{}; + PoseSet hands[2]{}; + PoseSet head{}; + }; + + class OpenXRSession + { + using seconds = std::chrono::duration; + using nanoseconds = std::chrono::nanoseconds; + using clock = std::chrono::steady_clock; + using time_point = clock::time_point; + + public: + OpenXRSession(osg::ref_ptr XR); + ~OpenXRSession(); + + void setLayer(OpenXRLayerStack::Layer layerType, OpenXRLayer* layer); + void swapBuffers(osg::GraphicsContext* gc); + void run(); + + PoseSets& predictedPoses() { return mPredictedPoses; }; + + //! Call before rendering to update predictions + void waitFrame(); + + //! Most recent prediction for display time of next frame. + int64_t predictedDisplayTime() { return mPredictedDisplayTime; }; + + //! Update predictions + void predictNext(int extraPeriods); + + OpenXRLayerStack mLayerStack{}; + osg::ref_ptr mXR; + std::thread mXRThread; + bool mShouldQuit = false; + + PoseSets mPredictedPoses{}; + + int64_t mPredictedDisplayTime{ 0 }; + time_point mPredictedDisplayTimeRealTime{ nanoseconds(0) }; + std::deque mRenderTimes; + int64_t mPredictedPeriods{ 0 }; + double mFps{ 0. }; + time_point mFrameEndTimePoint; + time_point mFrameBeginTimePoint; + + bool mNeedSync = true; + std::condition_variable mSync{}; + std::mutex mSyncMutex{}; + + bool mShouldRenderLayers = false; + std::condition_variable mRenderSignal{}; + std::mutex mRenderMutex{}; + }; + +} + +#endif diff --git a/apps/openmw/mwvr/openxrswapchain.cpp b/apps/openmw/mwvr/openxrswapchain.cpp index 654404c0a..b8c79aad3 100644 --- a/apps/openmw/mwvr/openxrswapchain.cpp +++ b/apps/openmw/mwvr/openxrswapchain.cpp @@ -27,7 +27,7 @@ namespace MWVR { ~OpenXRSwapchainImpl(); void beginFrame(osg::GraphicsContext* gc); - void endFrame(osg::GraphicsContext* gc); + int endFrame(osg::GraphicsContext* gc); osg::ref_ptr mXR; XrSwapchain mSwapchain = XR_NULL_HANDLE; @@ -118,13 +118,16 @@ namespace MWVR { mRenderBuffer->beginFrame(gc); } - void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc) + int swapCount = 0; + + int OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc) { Timer timer("Swapchain::endFrame"); // Blit frame to swapchain if (!mXR->sessionRunning()) - return; + return -1; + XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; uint32_t swapchainImageIndex = 0; @@ -134,11 +137,23 @@ namespace MWVR { waitInfo.timeout = XR_INFINITE_DURATION; CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo)); + // Oculus bug: Either the swapchain image index is off by 1 or xrEndFrame() choses the wrong swapchain image. + //mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[0].image); + //mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[1].image); + //mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[2].image); + // mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[(swapchainImageIndex + 1) % 3].image); mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[swapchainImageIndex].image); + //Log(Debug::Verbose) << "swapchainImageIndex: " << swapchainImageIndex; + //Log(Debug::Verbose) << "swapchainImage: " << mSwapchainImageBuffers[swapchainImageIndex].image; + static int asdf = 0; + //Log(Debug::Verbose) << "OpenXRSwapchainImpl ENDFRAME[ " << (asdf++ / 3) << "]"; + + swapCount = asdf / 3; + XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)); - + return swapchainImageIndex; } OpenXRSwapchain::OpenXRSwapchain( @@ -156,9 +171,9 @@ namespace MWVR { impl().beginFrame(gc); } - void OpenXRSwapchain::endFrame(osg::GraphicsContext* gc) + int OpenXRSwapchain::endFrame(osg::GraphicsContext* gc) { - impl().endFrame(gc); + return impl().endFrame(gc); } const XrSwapchainSubImage& diff --git a/apps/openmw/mwvr/openxrswapchain.hpp b/apps/openmw/mwvr/openxrswapchain.hpp index 90e70e81b..496ca6049 100644 --- a/apps/openmw/mwvr/openxrswapchain.hpp +++ b/apps/openmw/mwvr/openxrswapchain.hpp @@ -10,12 +10,13 @@ namespace MWVR { class OpenXRSwapchainImpl; + extern int swapCount; + class OpenXRSwapchain { public: struct Config { - std::vector requestedFormats{}; int width = -1; int height = -1; int samples = -1; @@ -29,7 +30,7 @@ namespace MWVR //! Prepare for render (set FBO) void beginFrame(osg::GraphicsContext* gc); //! Finalize render - void endFrame(osg::GraphicsContext* gc); + int endFrame(osg::GraphicsContext* gc); //! Get the view surface const XrSwapchainSubImage& subImage(void) const; //! Width of the view surface diff --git a/apps/openmw/mwvr/openxrview.cpp b/apps/openmw/mwvr/openxrview.cpp index 165937274..28d3c1872 100644 --- a/apps/openmw/mwvr/openxrview.cpp +++ b/apps/openmw/mwvr/openxrview.cpp @@ -17,17 +17,15 @@ namespace MWVR { OpenXRView::OpenXRView( osg::ref_ptr XR, - std::string name) + std::string name, + OpenXRSwapchain::Config config, + osg::ref_ptr state) : mXR(XR) - , mSwapchain(nullptr) - , mSwapchainConfig{} + , mSwapchainConfig{ config } + , mSwapchain(new OpenXRSwapchain(mXR, state, mSwapchainConfig)) , mName(name) + , mTimer(mName.c_str()) { - mSwapchainConfig.requestedFormats = { - GL_RGBA8, - GL_RGBA8_SNORM, - }; - } OpenXRView::~OpenXRView() @@ -49,78 +47,35 @@ namespace MWVR { camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback()); - mPredraw = new OpenXRView::PredrawCallback(camera.get(), this); - - camera->setPreDrawCallback(mPredraw); - camera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(camera.get(), this)); - return camera.release(); } - void OpenXRView::setWidth(int width) - { - mSwapchainConfig.width = width; - } - - void OpenXRView::setHeight(int height) - { - mSwapchainConfig.height = height; - } - - void OpenXRView::setSamples(int samples) - { - mSwapchainConfig.samples = samples; - } - void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo) { - - if(mName == "LeftEye") - mXR->waitFrame(); - Log(Debug::Verbose) << mName << ": prerenderCallback"; if (mSwapchain) { mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext()); } + mTimer.checkpoint("Prerender"); } void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo) { - // 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()); + mTimer.checkpoint("Postrender"); } - bool OpenXRView::realize(osg::ref_ptr state) + void OpenXRView::swapBuffers(osg::GraphicsContext* gc) { - try { - mSwapchain.reset(new OpenXRSwapchain(mXR, state, mSwapchainConfig)); - } - catch (...) - { - } - - return !!mSwapchain; + swapchain().endFrame(gc); } - void OpenXRView::PredrawCallback::operator()(osg::RenderInfo& info) const - { - mView->prerenderCallback(info); - } - - void OpenXRView::PostdrawCallback::operator()(osg::RenderInfo& info) const - { - mView->postrenderCallback(info); - } + void OpenXRView::setPredictedPose(const Pose& pose) + { + mPredictedPose = pose; + //Log(Debug::Verbose) << mName << " predicted pose updated to " << pose; + }; } diff --git a/apps/openmw/mwvr/openxrview.hpp b/apps/openmw/mwvr/openxrview.hpp index 09a8e6b93..646ea7145 100644 --- a/apps/openmw/mwvr/openxrview.hpp +++ b/apps/openmw/mwvr/openxrview.hpp @@ -13,36 +13,6 @@ namespace MWVR class OpenXRView : public osg::Referenced { public: - class PredrawCallback : public osg::Camera::DrawCallback - { - public: - PredrawCallback(osg::Camera* camera, OpenXRView* view) - : mCamera(camera) - , mView(view) - {} - - void operator()(osg::RenderInfo& info) const override; - - private: - - osg::observer_ptr mCamera; - OpenXRView* mView; - }; - class PostdrawCallback : public osg::Camera::DrawCallback - { - public: - PostdrawCallback(osg::Camera* camera, OpenXRView* view) - : mCamera(camera) - , mView(view) - {} - - void operator()(osg::RenderInfo& info) const override; - - private: - - osg::observer_ptr mCamera; - OpenXRView* mView; - }; class InitialDrawCallback : public osg::Camera::DrawCallback { @@ -51,11 +21,8 @@ namespace MWVR }; protected: - OpenXRView(osg::ref_ptr XR, std::string name); + OpenXRView(osg::ref_ptr XR, std::string name, OpenXRSwapchain::Config config, osg::ref_ptr state); virtual ~OpenXRView(); - void setWidth(int width); - void setHeight(int height); - void setSamples(int samples); public: //! Prepare for render (set FBO) @@ -66,16 +33,21 @@ namespace MWVR osg::Camera* createCamera(int order, const osg::Vec4& clearColor, osg::GraphicsContext* gc); //! Get the view surface OpenXRSwapchain& swapchain(void) { return *mSwapchain; } - //! Create the view surface - bool realize(osg::ref_ptr state); - protected: + void swapBuffers(osg::GraphicsContext* gc); + + void setPredictedPose(const Pose& pose); + Pose& predictedPose() { return mPredictedPose; }; + + public: osg::ref_ptr mXR; - std::unique_ptr mSwapchain; OpenXRSwapchain::Config mSwapchainConfig; + std::unique_ptr mSwapchain; std::string mName{}; bool mRendering{ false }; - osg::ref_ptr mPredraw{ nullptr }; + Timer mTimer; + + Pose mPredictedPose{ {}, {0,0,0,1}, {} }; }; } diff --git a/apps/openmw/mwvr/openxrviewer.cpp b/apps/openmw/mwvr/openxrviewer.cpp index 1cfaaf70e..c3ad1486e 100644 --- a/apps/openmw/mwvr/openxrviewer.cpp +++ b/apps/openmw/mwvr/openxrviewer.cpp @@ -16,8 +16,13 @@ namespace MWVR , mMetersPerUnit(metersPerUnit) , mConfigured(false) , mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}) + , mXRSession(nullptr) + , mPreDraw(new PredrawCallback(this)) + , mPostDraw(new PostdrawCallback(this)) { mViewer->setRealizeOperation(mRealizeOperation); + mCompositionLayerProjectionViews[0].pose.orientation.w = 1; + mCompositionLayerProjectionViews[1].pose.orientation.w = 1; } OpenXRViewer::~OpenXRViewer(void) @@ -36,24 +41,18 @@ namespace MWVR const XrCompositionLayerBaseHeader* OpenXRViewer::layer() { - 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; - mCompositionLayerProjectionViews[1].fov = stageViews[1].fov; return reinterpret_cast(mLayer.get()); } void OpenXRViewer::traversals() { - Log(Debug::Verbose) << "Pre-Update"; + //Log(Debug::Verbose) << "Pre-Update"; mXR->handleEvents(); - OpenXRFrameIndexer::instance().advanceUpdateIndex(); mViewer->updateTraversal(); - Log(Debug::Verbose) << "Post-Update"; - Log(Debug::Verbose) << "Pre-Rendering"; + //Log(Debug::Verbose) << "Post-Update"; + //Log(Debug::Verbose) << "Pre-Rendering"; mViewer->renderingTraversals(); - Log(Debug::Verbose) << "Post-Rendering"; + //Log(Debug::Verbose) << "Post-Rendering"; } void OpenXRViewer::realize(osg::GraphicsContext* context) @@ -70,91 +69,102 @@ namespace MWVR return; } - mMainCamera = mViewer->getCamera(); - mMainCamera->setName("Main"); - mMainCamera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback()); + + + auto mainCamera = mCameras["MainCamera"] = mViewer->getCamera(); + mainCamera->setName("Main"); + mainCamera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback()); // 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); + mainCamera->setCullMask(MWRender::Mask_GUI); - osg::Vec4 clearColor = mMainCamera->getClearColor(); + osg::Vec4 clearColor = mainCamera->getClearColor(); 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); + OpenXRSwapchain::Config leftConfig; + leftConfig.width = mXR->impl().mConfigViews[(int)Chirality::LEFT_HAND].recommendedImageRectWidth; + leftConfig.height = mXR->impl().mConfigViews[(int)Chirality::LEFT_HAND].recommendedImageRectHeight; + leftConfig.samples = mXR->impl().mConfigViews[(int)Chirality::LEFT_HAND].recommendedSwapchainSampleCount; + OpenXRSwapchain::Config rightConfig; + rightConfig.width = mXR->impl().mConfigViews[(int)Chirality::RIGHT_HAND].recommendedImageRectWidth; + rightConfig.height = mXR->impl().mConfigViews[(int)Chirality::RIGHT_HAND].recommendedImageRectHeight; + rightConfig.samples = mXR->impl().mConfigViews[(int)Chirality::RIGHT_HAND].recommendedSwapchainSampleCount; + + auto leftView = new OpenXRWorldView(mXR, "LeftEye", context->getState(), leftConfig, mMetersPerUnit); + auto rightView = new OpenXRWorldView(mXR, "RightEye", context->getState(), rightConfig, mMetersPerUnit); + + mViews["LeftEye"] = leftView; + mViews["RightEye"] = rightView; + + auto leftCamera = mCameras["LeftEye"] = leftView->createCamera(0, clearColor, context); + auto rightCamera = mCameras["RightEye"] = rightView->createCamera(1, clearColor, context); + + leftCamera->setPreDrawCallback(mPreDraw); + rightCamera->setPreDrawCallback(mPreDraw); + + leftCamera->setPostDrawCallback(mPostDraw); + rightCamera->setPostDrawCallback(mPostDraw); - mLeftCamera = mViews[OpenXRWorldView::LEFT_VIEW]->createCamera(OpenXRWorldView::LEFT_VIEW, clearColor, context); - mRightCamera = mViews[OpenXRWorldView::RIGHT_VIEW]->createCamera(OpenXRWorldView::RIGHT_VIEW, clearColor, context); // Stereo cameras should only draw the scene (AR layers should later add minimap, health, etc.) + leftCamera->setCullMask(~MWRender::Mask_GUI); + rightCamera->setCullMask(~MWRender::Mask_GUI); - //mLeftCamera->setGraphicsContext(mLeftGW); - //mRightCamera->setGraphicsContext(mRightGW); + leftCamera->setName("LeftEye"); + rightCamera->setName("RightEye"); - mLeftCamera->setCullMask(~MWRender::Mask_GUI); - mRightCamera->setCullMask(~MWRender::Mask_GUI); + mViewer->addSlave(leftCamera, leftView->projectionMatrix(), leftView->viewMatrix(), true); + mViewer->addSlave(rightCamera, rightView->projectionMatrix(), rightView->viewMatrix(), true); - mLeftCamera->setName("LeftEye"); - mRightCamera->setName("RightEye"); - - 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 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); - // Rendering main camera is a waste of time with VR enabled - //camera->setGraphicsContext(nullptr); mXRInput.reset(new OpenXRInputManager(mXR)); + mCompositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage(); + mCompositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage(); - mLayer.reset(new XrCompositionLayerProjection); - mLayer->type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; - mLayer->space = mXR->impl().mReferenceSpaceStage; - mLayer->viewCount = 2; - mLayer->views = mCompositionLayerProjectionViews.data(); - mCompositionLayerProjectionViews[OpenXRWorldView::LEFT_VIEW].subImage = mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().subImage(); - mCompositionLayerProjectionViews[OpenXRWorldView::RIGHT_VIEW].subImage = mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().subImage(); - - mXR->impl().mLayerStack.setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this); OpenXRSwapchain::Config config; - config.requestedFormats = { - GL_RGBA8, - GL_RGBA8_SNORM, - }; - config.width = mMainCamera->getViewport()->width(); - config.height = mMainCamera->getViewport()->height(); + config.width = mainCamera->getViewport()->width(); + config.height = mainCamera->getViewport()->height(); config.samples = 1; // Mirror texture doesn't have to be an OpenXR swapchain. // It's just convenient. 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))); - 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()); + auto menuView = new OpenXRMenu(mXR, config, context->getState(), "MainMenu", osg::Vec2(1.f, 1.f)); + mViews["MenuView"] = menuView; - mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this)); - mMainCamera->setGraphicsContext(nullptr); + auto menuCamera = mCameras["MenuView"] = menuView->createCamera(2, clearColor, context); + menuCamera->setCullMask(MWRender::Mask_GUI); + menuCamera->setName("MenuView"); + menuCamera->setPreDrawCallback(mPreDraw); + menuCamera->setPostDrawCallback(mPostDraw); + + mViewer->addSlave(menuCamera, true); + + mXRSession.reset(new OpenXRSession(mXR)); + mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mXRSession.get(), leftView, context); + mViewer->getSlave(1)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mXRSession.get(), rightView, context); + + mainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this)); + mainCamera->setGraphicsContext(nullptr); + mXRSession->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this); + mXRSession->setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, dynamic_cast(mViews["MenuView"].get())); mConfigured = true; + } - void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) const + void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) { mMirrorTextureSwapchain->beginFrame(gc); - 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()); + mViews["LeftEye"]->swapchain().renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); + mViews["RightEye"]->swapchain().renderBuffer()->blit(gc, mMirrorTextureSwapchain->width() / 2, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); //mXRMenu->swapchain().current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); } @@ -163,34 +173,46 @@ namespace MWVR OpenXRViewer::SwapBuffersCallback::swapBuffersImplementation( osg::GraphicsContext* gc) { - mViewer->swapBuffers(gc); + mViewer->mXRSession->swapBuffers(gc); } 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); + ////// NEW SYSTEM + Timer timer("OpenXRViewer::SwapBuffers"); + mViews["LeftEye"]->swapBuffers(gc); + mViews["RightEye"]->swapBuffers(gc); + timer.checkpoint("Views"); - 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); - - //mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); + auto eyePoses = mXRSession->predictedPoses().eye; + auto leftEyePose = toXR(mViews["LeftEye"]->predictedPose()); + auto rightEyePose = toXR(mViews["RightEye"]->predictedPose()); + mCompositionLayerProjectionViews[0].pose = leftEyePose; + mCompositionLayerProjectionViews[1].pose = rightEyePose; + timer.checkpoint("Poses"); - //mMirrorTextureSwapchain->endFrame(gc); - gc->swapBuffersImplementation(); + // TODO: Keep track of these in the session too. + auto stageViews = mXR->impl().getPredictedViews(mXRSession->predictedDisplayTime(), TrackedSpace::STAGE); + mCompositionLayerProjectionViews[0].fov = stageViews[0].fov; + mCompositionLayerProjectionViews[1].fov = stageViews[1].fov; + timer.checkpoint("Fovs"); + + + if (!mLayer) + { + mLayer.reset(new XrCompositionLayerProjection); + mLayer->type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + mLayer->space = mXR->impl().mReferenceSpaceStage; + mLayer->viewCount = 2; + mLayer->views = mCompositionLayerProjectionViews.data(); + } } void @@ -206,4 +228,37 @@ namespace MWVR { return mViewer->realized(); } + + void OpenXRViewer::preDrawCallback(osg::RenderInfo& info) + { + auto* camera = info.getCurrentCamera(); + auto name = camera->getName(); + auto& view = mViews[name]; + + if (name == "LeftEye") + { + mXR->beginFrame(); + auto& poses = mXRSession->predictedPoses(); + auto menuPose = poses.head[(int)TrackedSpace::STAGE]; + mViews["MenuView"]->setPredictedPose(menuPose); + } + + view->prerenderCallback(info); + } + + void OpenXRViewer::postDrawCallback(osg::RenderInfo& info) + { + auto* camera = info.getCurrentCamera(); + auto name = camera->getName(); + auto& view = mViews[name]; + + view->postrenderCallback(info); + + // OSG will sometimes overwrite the predraw callback. + if (camera->getPreDrawCallback() != mPreDraw) + { + camera->setPreDrawCallback(mPreDraw); + Log(Debug::Warning) << ("osg overwrote predraw"); + } + } } diff --git a/apps/openmw/mwvr/openxrviewer.hpp b/apps/openmw/mwvr/openxrviewer.hpp index 4277155d6..e7c39f058 100644 --- a/apps/openmw/mwvr/openxrviewer.hpp +++ b/apps/openmw/mwvr/openxrviewer.hpp @@ -3,11 +3,13 @@ #include #include +#include #include #include #include #include "openxrmanager.hpp" +#include "openxrsession.hpp" #include "openxrlayer.hpp" #include "openxrworldview.hpp" #include "openxrmenu.hpp" @@ -41,6 +43,34 @@ namespace MWVR OpenXRViewer* mViewer; }; + class PredrawCallback : public osg::Camera::DrawCallback + { + public: + PredrawCallback(OpenXRViewer* viewer) + : mViewer(viewer) + {} + + void operator()(osg::RenderInfo& info) const override { mViewer->preDrawCallback(info); }; + + private: + + OpenXRViewer* mViewer; + }; + + class PostdrawCallback : public osg::Camera::DrawCallback + { + public: + PostdrawCallback(OpenXRViewer* viewer) + : mViewer(viewer) + {} + + void operator()(osg::RenderInfo& info) const override { mViewer->postDrawCallback(info); }; + + private: + + OpenXRViewer* mViewer; + }; + public: OpenXRViewer( osg::ref_ptr XR, @@ -54,31 +84,28 @@ namespace MWVR const XrCompositionLayerBaseHeader* layer() override; void traversals(); - void blitEyesToMirrorTexture(osg::GraphicsContext* gc) const; - void swapBuffers(osg::GraphicsContext* gc) ; + void preDrawCallback(osg::RenderInfo& info); + void postDrawCallback(osg::RenderInfo& info); + void blitEyesToMirrorTexture(osg::GraphicsContext* gc); + void swapBuffers(osg::GraphicsContext* gc) override; void realize(osg::GraphicsContext* gc); bool realized() { return mConfigured; } - protected: + public: std::unique_ptr mLayer = nullptr; std::vector mCompositionLayerProjectionViews; osg::observer_ptr mXR = nullptr; osg::ref_ptr mRealizeOperation = nullptr; osg::observer_ptr mViewer = nullptr; - std::unique_ptr mXRInput = nullptr; - std::unique_ptr mXRMenu = nullptr; - std::array, 2> mViews{}; + std::unique_ptr mXRInput = nullptr; + std::unique_ptr mXRSession = nullptr; + std::map > mViews{}; + std::map > mCameras{}; - //osg::ref_ptr mViewerGW; - //osg::ref_ptr mLeftGW; - //osg::ref_ptr mRightGW; - - osg::Camera* mMainCamera = nullptr; - osg::Camera* mMenuCamera = nullptr; - osg::Camera* mLeftCamera = nullptr; - osg::Camera* mRightCamera = nullptr; + PredrawCallback* mPreDraw{ nullptr }; + PostdrawCallback* mPostDraw{ nullptr }; std::unique_ptr mMirrorTextureSwapchain = nullptr; diff --git a/apps/openmw/mwvr/openxrworldview.cpp b/apps/openmw/mwvr/openxrworldview.cpp index 8397212f2..2ad8e26b7 100644 --- a/apps/openmw/mwvr/openxrworldview.cpp +++ b/apps/openmw/mwvr/openxrworldview.cpp @@ -78,57 +78,45 @@ namespace MWVR osg::Matrix OpenXRWorldView::projectionMatrix() { - auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW); + // TODO: Get this from the session predictions instead + auto hmdViews = mXR->impl().getPredictedViews(mXR->impl().frameState().predictedDisplayTime, TrackedSpace::VIEW); float near = Settings::Manager::getFloat("near clip", "Camera"); float far = Settings::Manager::getFloat("viewing distance", "Camera") * mMetersPerUnit; //return perspectiveFovMatrix() - return perspectiveFovMatrix(near, far, hmdViews[mView].fov); + if(mName == "LeftEye") + return perspectiveFovMatrix(near, far, hmdViews[0].fov); + return perspectiveFovMatrix(near, far, hmdViews[1].fov); } osg::Matrix OpenXRWorldView::viewMatrix() { - osg::Matrix viewMatrix; - auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW); - auto pose = hmdViews[mView].pose; - osg::Vec3 position = osg::fromXR(pose.position); + auto pose = predictedPose(); + osg::Vec3 position = pose.position; - auto stageViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::STAGE); - auto stagePose = stageViews[mView].pose; - - // Comfort shortcut. - // TODO: STAGE movement should affect in-game movement but not like this. - // This method should only be using HEAD view. - // But for comfort i'm keeping this until such movement has been implemented. #if 1 - position = -osg::fromXR(stagePose.position); - position.y() += 0.9144 * 2.; + // Comfort shortcut. + // TODO: Head pose should affect more than just the view ! + position.y() -= 0.9144 * 2.; #endif - // invert orientation (conjugate of Quaternion) and position to apply to the view matrix as offset - viewMatrix.setTrans(position * mMetersPerUnit); - viewMatrix.postMultRotate(osg::fromXR(stagePose.orientation).conj()); - - // Scale to world units - //viewMatrix.postMultScale(osg::Vec3d(1.f / mMetersPerUnit, 1.f / mMetersPerUnit, 1.f / mMetersPerUnit)); + // invert orientation (co jugate of Quaternion) and position to apply to the view matrix as offset + osg::Matrix viewMatrix; + viewMatrix.setTrans(-position * mMetersPerUnit); + viewMatrix.postMultRotate(pose.orientation.conj()); return viewMatrix; } OpenXRWorldView::OpenXRWorldView( - osg::ref_ptr XR, std::string name, osg::ref_ptr state, float metersPerUnit, SubView view) - : OpenXRView(XR, name) + osg::ref_ptr XR, + std::string name, + osg::ref_ptr state, + OpenXRSwapchain::Config config, + float metersPerUnit ) + : OpenXRView(XR, name, config, state) , mMetersPerUnit(metersPerUnit) - , mView(view) { - auto config = mXR->impl().mConfigViews[view]; - - setWidth(config.recommendedImageRectWidth); - setHeight(config.recommendedImageRectHeight); - setSamples(config.recommendedSwapchainSampleCount); - - realize(state); - // XR->setViewSubImage(view, mSwapchain->subImage()); } OpenXRWorldView::~OpenXRWorldView() @@ -154,13 +142,8 @@ namespace MWVR auto* camera = renderInfo.getCurrentCamera(); auto name = camera->getName(); - Log(Debug::Verbose) << "Updating camera " << name; + //Log(Debug::Verbose) << "Updating camera " << name; - auto viewMatrix = view->getCamera()->getViewMatrix() * this->viewMatrix(); - auto projectionMatrix = this->projectionMatrix(); - - camera->setViewMatrix(viewMatrix); - camera->setProjectionMatrix(projectionMatrix); } void @@ -168,21 +151,38 @@ namespace MWVR osg::View& view, osg::View::Slave& slave) { - mXR->handleEvents(); - if (!mXR->sessionRunning()) - return; - - + mView->mTimer.checkpoint("UpdateSlave"); auto* camera = slave._camera.get(); auto name = camera->getName(); - Log(Debug::Verbose) << "Updating slave " << name; + Log(Debug::Verbose) << name << ": slave update"; - //auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix(); - //auto projMatrix = mView->projectionMatrix(); + auto& poses = mSession->predictedPoses(); - //camera->setViewMatrix(viewMatrix); - //camera->setProjectionMatrix(projMatrix); + if (name == "LeftEye") + { + mXR->handleEvents(); + mSession->waitFrame(); + auto leftEyePose = poses.eye[(int)Chirality::LEFT_HAND][(int)TrackedSpace::STAGE]; + mView->setPredictedPose(leftEyePose); + } + else + { + auto rightEyePose = poses.eye[(int)Chirality::RIGHT_HAND][(int)TrackedSpace::STAGE]; + mView->setPredictedPose(rightEyePose); + } + if (!mXR->sessionRunning()) + return; + + // TODO: This is where controls should update + + + + auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix(); + auto projectionMatrix = mView->projectionMatrix(); + + camera->setViewMatrix(viewMatrix); + camera->setProjectionMatrix(projectionMatrix); slave.updateSlaveImplementation(view); } diff --git a/apps/openmw/mwvr/openxrworldview.hpp b/apps/openmw/mwvr/openxrworldview.hpp index f21155f7d..39070b20b 100644 --- a/apps/openmw/mwvr/openxrworldview.hpp +++ b/apps/openmw/mwvr/openxrworldview.hpp @@ -2,36 +2,31 @@ #define OPENXRWORLDVIEW_HPP #include "openxrview.hpp" +#include "openxrsession.hpp" namespace MWVR { class OpenXRWorldView : public OpenXRView { public: - enum SubView - { - LEFT_VIEW = 0, - RIGHT_VIEW = 1, - 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) + UpdateSlaveCallback(osg::ref_ptr XR, OpenXRSession* session, osg::ref_ptr view, osg::GraphicsContext* gc) + : mXR(XR), mSession(session), mView(view), mGC(gc) {} void updateSlave(osg::View& view, osg::View::Slave& slave) override; private: osg::ref_ptr mXR; + OpenXRSession* mSession; osg::ref_ptr mView; osg::ref_ptr mGC; }; public: - OpenXRWorldView(osg::ref_ptr XR, std::string name, osg::ref_ptr state, float metersPerUnit, SubView view); + OpenXRWorldView(osg::ref_ptr XR, std::string name, osg::ref_ptr state, OpenXRSwapchain::Config config, float metersPerUnit); ~OpenXRWorldView(); //! Prepare for render (update matrices) @@ -40,11 +35,8 @@ namespace MWVR osg::Matrix projectionMatrix(); //! View offset for this view osg::Matrix viewMatrix(); - //! Which view this is - SubView view() { return mView; } float mMetersPerUnit = 1.f; - SubView mView; }; }