diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 07c421d9e..797ab6197 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -564,10 +564,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); -#ifdef USE_OPENXR - mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer)); -#endif - if (!mSkipMenu) { const std::string& logo = Fallback::Map::getString("Movies_Company_Logo"); @@ -735,6 +731,7 @@ void OMW::Engine::go() #ifdef USE_OPENXR mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer)); + //mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded); #endif // Start the game diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index 521220b71..9852ba8c4 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -1008,11 +1008,14 @@ private: auto* vrGuiManager = Environment::get().getGUIManager(); - bool vrHasFocus = vrGuiManager->updateFocus(); - auto guiCursor = vrGuiManager->guiCursor(); - if (vrHasFocus) + if (vrGuiManager) { - mMouseManager->setMousePosition(guiCursor.x(), guiCursor.y()); + bool vrHasFocus = vrGuiManager->updateFocus(); + auto guiCursor = vrGuiManager->guiCursor(); + if (vrHasFocus) + { + mMouseManager->setMousePosition(guiCursor.x(), guiCursor.y()); + } } while (auto* action = mXRInput->nextAction()) @@ -1237,7 +1240,7 @@ private: void OpenXRInputManager::updateHead() { auto* session = Environment::get().getSession(); - auto currentHeadPose = session->predictedPoses(VRSession::FramePhase::Predraw).head; + auto currentHeadPose = session->predictedPoses(VRSession::FramePhase::Update).head; currentHeadPose.position *= Environment::get().unitsPerMeter(); osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position; mHeadPose = currentHeadPose; diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index eae8626a1..898a57782 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -277,44 +277,58 @@ namespace MWVR void OpenXRManagerImpl::waitFrame() { + + auto DC = wglGetCurrentDC(); + auto GLRC = wglGetCurrentContext(); + auto XRDC = mGraphicsBinding.hDC; + auto XRGLRC = mGraphicsBinding.hGLRC; + wglMakeCurrent(XRDC, XRGLRC); + Timer timer("waitFrame()"); - static std::mutex waitFrameMutex; - - if (!mSessionRunning) - return; - XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; XrFrameState frameState{ XR_TYPE_FRAME_STATE }; CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); - - std::unique_lock lock(mFrameStateMutex); mFrameState = frameState; + + wglMakeCurrent(DC, GLRC); } void OpenXRManagerImpl::beginFrame() { + auto DC = wglGetCurrentDC(); + auto GLRC = wglGetCurrentContext(); + auto XRDC = mGraphicsBinding.hDC; + auto XRGLRC = mGraphicsBinding.hGLRC; + wglMakeCurrent(XRDC, XRGLRC); + Timer timer("beginFrame"); XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO }; CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo)); + + wglMakeCurrent(DC, GLRC); } void OpenXRManagerImpl::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack) { - Timer timer("endFrame()"); - if (!mSessionRunning) - return; + auto DC = wglGetCurrentDC(); + auto GLRC = wglGetCurrentContext(); + auto XRDC = mGraphicsBinding.hDC; + auto XRGLRC = mGraphicsBinding.hGLRC; + wglMakeCurrent(XRDC, XRGLRC); + Timer timer("endFrame()"); XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO }; frameEndInfo.displayTime = displayTime; frameEndInfo.environmentBlendMode = mEnvironmentBlendMode; frameEndInfo.layerCount = layerCount; frameEndInfo.layers = layerStack; - CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo)); + + wglMakeCurrent(DC, GLRC); } std::array diff --git a/apps/openmw/mwvr/realisticcombat.cpp b/apps/openmw/mwvr/realisticcombat.cpp index 5a621b244..3ff32ab85 100644 --- a/apps/openmw/mwvr/realisticcombat.cpp +++ b/apps/openmw/mwvr/realisticcombat.cpp @@ -103,7 +103,7 @@ void StateMachine::update(float dt, bool enabled) { auto* session = Environment::get().getSession(); auto* world = MWBase::Environment::get().getWorld(); - auto& predictedPoses = session->predictedPoses(VRSession::FramePhase::Predraw); + auto& predictedPoses = session->predictedPoses(VRSession::FramePhase::Update); auto& handPose = predictedPoses.hands[(int)MWVR::Side::RIGHT_HAND]; auto weaponType = world->getActiveWeaponType(); diff --git a/apps/openmw/mwvr/vranimation.cpp b/apps/openmw/mwvr/vranimation.cpp index cf5a6e281..6cab594e9 100644 --- a/apps/openmw/mwvr/vranimation.cpp +++ b/apps/openmw/mwvr/vranimation.cpp @@ -114,8 +114,8 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv) MWBase::Environment::get().getWorld()->getRenderingManager().getCamera()->updateCamera(); } - MWVR::Pose handStage = session->predictedPoses(VRSession::FramePhase::Predraw).hands[side]; - MWVR::Pose headStage = session->predictedPoses(VRSession::FramePhase::Predraw).head; + MWVR::Pose handStage = session->predictedPoses(VRSession::FramePhase::Update).hands[side]; + MWVR::Pose headStage = session->predictedPoses(VRSession::FramePhase::Update).head; auto orientation = handStage.orientation; diff --git a/apps/openmw/mwvr/vrsession.cpp b/apps/openmw/mwvr/vrsession.cpp index 996b96450..3e5f0c0ee 100644 --- a/apps/openmw/mwvr/vrsession.cpp +++ b/apps/openmw/mwvr/vrsession.cpp @@ -24,6 +24,7 @@ #include "vrsession.hpp" #include "vrgui.hpp" #include +#include namespace MWVR { @@ -38,7 +39,7 @@ VRSession::~VRSession() osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side) { assert(((int)side) < 2); - auto fov = predictedPoses(VRSession::FramePhase::Predraw).view[(int)side].fov; + auto fov = predictedPoses(VRSession::FramePhase::Update).view[(int)side].fov; float near_ = Settings::Manager::getFloat("near clip", "Camera"); float far_ = Settings::Manager::getFloat("viewing distance", "Camera"); return fov.perspectiveMatrix(near_, far_); @@ -46,7 +47,7 @@ osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side) osg::Matrix VRSession::viewMatrix(FramePhase phase, Side side) { - MWVR::Pose pose = predictedPoses(VRSession::FramePhase::Predraw).view[(int)side].pose; + MWVR::Pose pose = predictedPoses(phase).view[(int)side].pose; osg::Vec3 position = pose.position * Environment::get().unitsPerMeter(); osg::Quat orientation = pose.orientation; @@ -77,72 +78,120 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer) Timer timer("VRSession::SwapBuffers"); auto* xr = Environment::get().getManager(); + beginPhase(FramePhase::Swap); + + if (getFrame(FramePhase::Swap)->mShouldRender) { - std::unique_lock lock(mMutex); + auto leftView = viewer.mViews["LeftEye"]; + auto rightView = viewer.mViews["RightEye"]; - xr->handleEvents(); + viewer.blitEyesToMirrorTexture(gc); + gc->swapBuffersImplementation(); + leftView->swapBuffers(gc); + rightView->swapBuffers(gc); - mPostdrawFrame = std::move(mDrawFrame); - assert(mPostdrawFrame); + std::array compositionLayerProjectionViews{}; + compositionLayerProjectionViews[0].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + compositionLayerProjectionViews[1].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + compositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage(); + compositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage(); + compositionLayerProjectionViews[0].pose = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.eye[(int)Side::LEFT_HAND]); + compositionLayerProjectionViews[1].pose = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.eye[(int)Side::RIGHT_HAND]); + compositionLayerProjectionViews[0].fov = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.view[(int)Side::LEFT_HAND].fov); + compositionLayerProjectionViews[1].fov = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.view[(int)Side::RIGHT_HAND].fov); + XrCompositionLayerProjection layer{}; + layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + layer.space = xr->impl().getReferenceSpace(TrackedSpace::STAGE); + layer.viewCount = 2; + layer.views = compositionLayerProjectionViews.data(); + auto* layerStack = reinterpret_cast(&layer); + + Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": EndFrame"; + xr->endFrame(getFrame(FramePhase::Swap)->mPredictedDisplayTime, 1, &layerStack); } - // TODO: Should blit mirror texture regardless - if (!isRunning()) - return; - - xr->waitFrame(); - xr->beginFrame(); - viewer.swapBuffers(gc); - - auto leftView = viewer.mViews["LeftEye"]; - auto rightView = viewer.mViews["RightEye"]; - - leftView->swapBuffers(gc); - rightView->swapBuffers(gc); - - std::array compositionLayerProjectionViews{}; - compositionLayerProjectionViews[0].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; - compositionLayerProjectionViews[1].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; - compositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage(); - compositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage(); - compositionLayerProjectionViews[0].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::LEFT_HAND]); - compositionLayerProjectionViews[1].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::RIGHT_HAND]); - compositionLayerProjectionViews[0].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::LEFT_HAND].fov); - compositionLayerProjectionViews[1].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::RIGHT_HAND].fov); - XrCompositionLayerProjection layer{}; - layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; - layer.space = xr->impl().getReferenceSpace(TrackedSpace::STAGE); - layer.viewCount = 2; - layer.views = compositionLayerProjectionViews.data(); - auto* layerStack = reinterpret_cast(&layer); - - xr->endFrame(mPostdrawFrame->mPredictedDisplayTime, 1, &layerStack); - mLastRenderedFrame = mPostdrawFrame->mFrameNo; + std::unique_lock lock(mMutex); + auto xrPredictionChange = xr->impl().frameState().predictedDisplayTime - mLastPredictedDisplayTime; + mLastPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime; + mLastPredictedDisplayPeriod = xr->impl().frameState().predictedDisplayPeriod; auto now = std::chrono::steady_clock::now(); mLastFrameInterval = std::chrono::duration_cast(now - mLastRenderedFrameTimestamp); mLastRenderedFrameTimestamp = now; + mLastRenderedFrame = getFrame(FramePhase::Swap)->mFrameNo; + + //Log(Debug::Verbose) << getFrame(FramePhase::Swap)->mFrameNo << ": xrPrediction=" << xr->impl().frameState().predictedDisplayTime << ", ourPrediction=" << getFrame(FramePhase::Swap)->mPredictedDisplayTime << ", miss=" << miss << "ms"; + Log(Debug::Debug) << "xrPredictionChange=" << (xrPredictionChange / 1000000) << "ms"; + auto seconds = std::chrono::duration_cast>(now - mStart).count(); + static int sBaseFrames = 0; + if (seconds > 10.f) + { + Log(Debug::Verbose) << "Fps: " << (static_cast(mLastRenderedFrame - sBaseFrames) / seconds); + mStart = now; + sBaseFrames = mLastRenderedFrame; + } + + getFrame(FramePhase::Swap) = nullptr; + mCondition.notify_one(); } -void VRSession::advanceFramePhase(void) +void VRSession::beginPhase(FramePhase phase) { - std::unique_lock lock(mMutex); Timer timer("VRSession::advanceFrame"); + Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ")"; - assert(!mDrawFrame); - mDrawFrame = std::move(mPredrawFrame); + if (phase != FramePhase::Update) + { + std::unique_lock lock(mMutex); + FramePhase previousPhase = static_cast((int)phase - 1); + + if (getFrame(phase)) + throw std::logic_error("advanceFramePhase called with a frame alreay in the target phase"); + if (!getFrame(previousPhase)) + throw std::logic_error("advanceFramePhase called without a frame in the predraw phase"); + getFrame(phase) = std::move(getFrame(previousPhase)); + } + else + prepareFrame(); + + if (phase == FramePhase::Cull && getFrame(phase)->mShouldRender) + { + auto* xr = Environment::get().getManager(); + // Since i am forced to do xr->beginFrame() before cull instead of draw + // i have to explicitly wait for xr->endFrame() to finish to avoid an + // out-of-order error from openxr. + // If i don't wait, we might hit beginFrame() before the previous frame + // reaches endFrame() in which case openxr will cancel the previous frame + // and we will get an out-of-order error due to two back-to-back calls to endFrame() + while (getFrame(phase)->mFrameNo != (mLastRenderedFrame + 1)) + { + std::unique_lock lock(mMutex); + mCondition.wait(lock); + } + Log(Debug::Debug) << getFrame(phase)->mFrameNo << ": WaitFrame"; + xr->waitFrame(); + Log(Debug::Debug) << getFrame(phase)->mFrameNo << ": BeginFrame"; + xr->beginFrame(); + } } -void VRSession::startFrame() +std::unique_ptr& VRSession::getFrame(FramePhase phase) +{ + if ((unsigned int)phase >= mFrame.size()) + throw std::logic_error("Invalid frame phase"); + return mFrame[(int)phase]; +} + +void VRSession::prepareFrame() { std::unique_lock lock(mMutex); assert(!mPredrawFrame); Timer timer("VRSession::startFrame"); auto* xr = Environment::get().getManager(); - auto* input = Environment::get().getInputManager(); + xr->handleEvents(); // Until OpenXR allows us to get a prediction without waiting - // we make our own (bad) prediction and let openxr wait + // we make our own (bad) prediction and call xrWaitFrame when it is more convenient auto frameState = xr->impl().frameState(); long long predictedDisplayTime = 0; mFrames++; @@ -154,17 +203,19 @@ void VRSession::startFrame() else if (mFrames > mLastRenderedFrame) { //predictedDisplayTime = mLastPredictedDisplayTime + mLastFrameInterval.count() * (mFrames - mLastRenderedFrame); - float intervalsf = static_cast(mLastFrameInterval.count()) / static_cast(frameState.predictedDisplayPeriod); - int intervals = (int)std::roundf(intervalsf); - predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * frameState.predictedDisplayPeriod; + float intervalsf = static_cast(mLastFrameInterval.count()) / static_cast(mLastPredictedDisplayPeriod); +#ifdef max +#undef max +#endif + int intervals = std::max((int)std::roundf(intervalsf), 1); + predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * mLastPredictedDisplayPeriod; } PoseSet predictedPoses{}; if (isRunning()) { + xr->impl().enablePredictions(); predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale; - predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale; - predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * mPlayerScale; auto hmdViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::VIEW); predictedPoses.view[(int)Side::LEFT_HAND].pose = fromXR(hmdViews[(int)Side::LEFT_HAND].pose) * mPlayerScale; predictedPoses.view[(int)Side::RIGHT_HAND].pose = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale; @@ -173,43 +224,34 @@ void VRSession::startFrame() auto stageViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::STAGE); predictedPoses.eye[(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose) * mPlayerScale; predictedPoses.eye[(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale; - } - else - { - // If session is not running, copy poses from the last frame - if (mPostdrawFrame) - predictedPoses = mPostdrawFrame->mPredictedPoses; + + auto* input = Environment::get().getInputManager(); + if (input) + { + predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale; + predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * mPlayerScale; + } + xr->impl().disablePredictions(); } - - mPredrawFrame.reset(new VRFrame); - mPredrawFrame->mPredictedDisplayTime = predictedDisplayTime; - mPredrawFrame->mFrameNo = mFrames; - mPredrawFrame->mPredictedPoses = predictedPoses; + auto& frame = getFrame(FramePhase::Update); + frame.reset(new VRFrame); + frame->mPredictedDisplayTime = predictedDisplayTime; + frame->mFrameNo = mFrames; + frame->mPredictedPoses = predictedPoses; + frame->mShouldRender = isRunning(); } const PoseSet& VRSession::predictedPoses(FramePhase phase) { - - switch (phase) - { - case FramePhase::Predraw: - if (!mPredrawFrame) - startFrame(); - assert(mPredrawFrame); - return mPredrawFrame->mPredictedPoses; + auto& frame = getFrame(phase); - case FramePhase::Draw: - assert(mDrawFrame); - return mDrawFrame->mPredictedPoses; + if (phase == FramePhase::Update && !frame) + beginPhase(FramePhase::Update); - case FramePhase::Postdraw: - assert(mPostdrawFrame); - return mPostdrawFrame->mPredictedPoses; - } - - assert(0); - return mPredrawFrame->mPredictedPoses; + if (!frame) + throw std::logic_error("Attempted to get poses from a phase with no current pose"); + return frame->mPredictedPoses; } // OSG doesn't provide API to extract euler angles from a quat, but i need it. @@ -253,8 +295,11 @@ void VRSession::movementAngles(float& yaw, float& pitch) { assert(mPredrawFrame); auto* input = Environment::get().getInputManager(); - auto lhandquat = input->getHandPose(mPredrawFrame->mPredictedDisplayTime, TrackedSpace::VIEW, Side::LEFT_HAND).orientation; - //predictedPoses(FramePhase::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation; + // TODO: This strictly speaking violates the rule of not making predictions outside of prepareFrame() + // I should either add VIEW hands to the predicted pose set, or compute this using STAGE poses + // It would likely suffice to compute euler angles for STAGE head and hand and return the difference? + auto lhandquat = input->getHandPose(getFrame(FramePhase::Update)->mPredictedDisplayTime, TrackedSpace::VIEW, Side::LEFT_HAND).orientation; + //predictedPoses(FramePhase::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation; float roll = 0.f; getEulerAngles(lhandquat, yaw, pitch, roll); diff --git a/apps/openmw/mwvr/vrsession.hpp b/apps/openmw/mwvr/vrsession.hpp index a0446a988..d8217717a 100644 --- a/apps/openmw/mwvr/vrsession.hpp +++ b/apps/openmw/mwvr/vrsession.hpp @@ -27,9 +27,11 @@ public: enum class FramePhase { - Predraw = 0, //!< Get poses predicted for the next frame to be drawn - Draw = 1, //!< Get poses predicted for the current rendering frame - Postdraw = 2, //!< Get poses predicted for the last rendered frame + Update = 0, //!< The frame currently in update traversals + Cull, //!< The frame currently in cull + Draw, //!< The frame currently in draw + Swap, //!< The frame being swapped + NumPhases }; struct VRFrame @@ -37,6 +39,8 @@ public: long long mFrameNo{ 0 }; long long mPredictedDisplayTime{ 0 }; PoseSet mPredictedPoses{}; + bool mShouldRender{ false }; + bool mHasWaited{ false }; }; public: @@ -48,12 +52,13 @@ public: const PoseSet& predictedPoses(FramePhase phase); //! Starts a new frame - void startFrame(); + void prepareFrame(); //! Angles to be used for overriding movement direction void movementAngles(float& yaw, float& pitch); - void advanceFramePhase(void); + void beginPhase(FramePhase phase); + std::unique_ptr& getFrame(FramePhase phase); bool isRunning() const; @@ -63,9 +68,7 @@ public: osg::Matrix viewMatrix(FramePhase phase, Side side); osg::Matrix projectionMatrix(FramePhase phase, Side side); - std::unique_ptr mPredrawFrame{ nullptr }; - std::unique_ptr mDrawFrame{ nullptr }; - std::unique_ptr mPostdrawFrame{ nullptr }; + std::array, (int)FramePhase::NumPhases> mFrame{ nullptr }; std::mutex mMutex{}; std::condition_variable mCondition{}; @@ -74,6 +77,7 @@ public: long long mLastRenderedFrame{ 0 }; long long mLastPredictedDisplayTime{ 0 }; long long mLastPredictedDisplayPeriod{ 0 }; + std::chrono::steady_clock::time_point mStart{ std::chrono::steady_clock::now() }; std::chrono::nanoseconds mLastFrameInterval{}; std::chrono::steady_clock::time_point mLastRenderedFrameTimestamp{std::chrono::steady_clock::now()}; diff --git a/apps/openmw/mwvr/vrview.cpp b/apps/openmw/mwvr/vrview.cpp index 863dbed38..add50f8f0 100644 --- a/apps/openmw/mwvr/vrview.cpp +++ b/apps/openmw/mwvr/vrview.cpp @@ -39,6 +39,17 @@ namespace MWVR { { } + class CullCallback : public osg::NodeCallback + { + void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + const auto& name = node->getName(); + if (name == "LeftEye") + Environment::get().getSession()->beginPhase(VRSession::FramePhase::Cull); + traverse(node, nv); + } + }; + osg::Camera* VRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc) { osg::ref_ptr camera = new osg::Camera(); @@ -53,6 +64,7 @@ namespace MWVR { camera->setGraphicsContext(gc); camera->setInitialDrawCallback(new VRView::InitialDrawCallback()); + camera->setCullCallback(new CullCallback); return camera.release(); } @@ -69,6 +81,10 @@ namespace MWVR { void VRView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const { + const auto& name = renderInfo.getCurrentCamera()->getName(); + if (name == "LeftEye") + Environment::get().getSession()->beginPhase(VRSession::FramePhase::Draw); + osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer(); osgViewer::Renderer* renderer = dynamic_cast(graphicsOperation); if (renderer != nullptr) @@ -76,7 +92,6 @@ namespace MWVR { // Disable normal OSG FBO camera setup renderer->setCameraRequiresSetUp(false); } - auto name = renderInfo.getCurrentCamera()->getName(); } void VRView::UpdateSlaveCallback::updateSlave( osg::View& view, @@ -93,8 +108,9 @@ namespace MWVR { auto* session = Environment::get().getSession(); auto viewMatrix = view.getCamera()->getViewMatrix(); - auto modifiedViewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Predraw, side); - auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Predraw, side); + + auto modifiedViewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Update, side); + auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Update, side); camera->setViewMatrix(modifiedViewMatrix); camera->setProjectionMatrix(projectionMatrix); @@ -109,6 +125,6 @@ namespace MWVR { void VRView::swapBuffers(osg::GraphicsContext* gc) { - swapchain().endFrame(gc); + mSwapchain->endFrame(gc); } } diff --git a/apps/openmw/mwvr/vrviewer.cpp b/apps/openmw/mwvr/vrviewer.cpp index 006f95456..16601b8a3 100644 --- a/apps/openmw/mwvr/vrviewer.cpp +++ b/apps/openmw/mwvr/vrviewer.cpp @@ -151,22 +151,16 @@ namespace MWVR void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) { - //includeMenu = false; - mMirrorTextureSwapchain->beginFrame(gc); - int mirror_width = mMirrorTextureSwapchain->width() / 2; - + mMirrorTextureSwapchain->beginFrame(gc); mViews["RightEye"]->swapchain().renderBuffer()->blit(gc, 0, 0, mirror_width, mMirrorTextureSwapchain->height()); mViews["LeftEye"]->swapchain().renderBuffer()->blit(gc, mirror_width, 0, 2 * mirror_width, mMirrorTextureSwapchain->height()); - auto* state = gc->getState(); - auto* gl = osg::GLExtensions::Get(state->getContextID(), false); - mMirrorTextureSwapchain->endFrame(gc); - - + auto* state = gc->getState(); + auto* gl = osg::GLExtensions::Get(state->getContextID(), false); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); @@ -178,24 +172,6 @@ namespace MWVR { auto* session = Environment::get().getSession(); session->swapBuffers(gc, *mViewer); - gc->swapBuffersImplementation(); - } - - void VRViewer::swapBuffers(osg::GraphicsContext* gc) - { - if (!mConfigured) - return; - - Timer timer("VRViewer::SwapBuffers"); - - for (auto& view : mViews) - view.second->swapBuffers(gc); - - timer.checkpoint("Views"); - - blitEyesToMirrorTexture(gc); - - gc->swapBuffersImplementation(); } void @@ -217,12 +193,7 @@ namespace MWVR { auto* camera = info.getCurrentCamera(); auto name = camera->getName(); - auto& view = mViews[name]; - - if (name == "LeftEye") - Environment::get().getSession()->advanceFramePhase(); - - view->prerenderCallback(info); + mViews[name]->prerenderCallback(info); } void VRViewer::postDrawCallback(osg::RenderInfo& info) @@ -239,8 +210,5 @@ namespace MWVR camera->setPreDrawCallback(mPreDraw); Log(Debug::Warning) << ("osg overwrote predraw"); } - - //if(mDelay && name == "RightEye") - // std::this_thread::sleep_for(std::chrono::milliseconds(25)); } } diff --git a/apps/openmw/mwvr/vrviewer.hpp b/apps/openmw/mwvr/vrviewer.hpp index fab101fb4..25cf53304 100644 --- a/apps/openmw/mwvr/vrviewer.hpp +++ b/apps/openmw/mwvr/vrviewer.hpp @@ -77,7 +77,6 @@ namespace MWVR void preDrawCallback(osg::RenderInfo& info); void postDrawCallback(osg::RenderInfo& info); void blitEyesToMirrorTexture(osg::GraphicsContext* gc); - void swapBuffers(osg::GraphicsContext* gc); void realize(osg::GraphicsContext* gc); bool realized() { return mConfigured; }