From 3e3ed7ecee98c3384366dc98675f2b6843c83ade Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Thu, 21 May 2020 19:52:21 +0200 Subject: [PATCH] STATIC data variance and asynchronous xrwaitframe. --- apps/openmw/mwvr/openxrsession.cpp | 290 ++++++++++++++++------------- apps/openmw/mwvr/openxrsession.hpp | 3 + apps/openmw/mwvr/vranimation.cpp | 4 +- apps/openmw/mwvr/vrgui.cpp | 5 +- 4 files changed, 162 insertions(+), 140 deletions(-) diff --git a/apps/openmw/mwvr/openxrsession.cpp b/apps/openmw/mwvr/openxrsession.cpp index fad4a7f19..03de92b92 100644 --- a/apps/openmw/mwvr/openxrsession.cpp +++ b/apps/openmw/mwvr/openxrsession.cpp @@ -27,155 +27,179 @@ namespace MWVR { - OpenXRSession::OpenXRSession() +OpenXRSession::OpenXRSession() +{ +} + +OpenXRSession::~OpenXRSession() +{ +} + +void OpenXRSession::setLayer( + OpenXRLayerStack::Layer layerType, + OpenXRLayer* layer) +{ + mLayerStack.setLayer(layerType, layer); +} + +void OpenXRSession::swapBuffers(osg::GraphicsContext* gc) +{ + Timer timer("OpenXRSession::SwapBuffers"); + + auto* xr = Environment::get().getManager(); + + if (!mShouldRender) + return; + + if (mRenderedFrames != mRenderFrame - 1) { + Log(Debug::Warning) << "swapBuffers called out of order"; + waitFrame(); + return; } - OpenXRSession::~OpenXRSession() - { - } + for (auto layer : mLayerStack.layerObjects()) + if (layer) + layer->swapBuffers(gc); - void OpenXRSession::setLayer( - OpenXRLayerStack::Layer layerType, - OpenXRLayer* layer) - { - mLayerStack.setLayer(layerType, layer); - } + timer.checkpoint("Rendered"); + xr->endFrame(mDrawPredictedDisplayTime, &mLayerStack); + mRenderedFrames++; +} - void OpenXRSession::swapBuffers(osg::GraphicsContext* gc) +const PoseSets& OpenXRSession::predictedPoses(PredictionSlice slice) +{ + if (slice == PredictionSlice::Predraw) + waitFrame(); + return mPredictedPoses[(int)slice]; +} + +static std::chrono::steady_clock::time_point gLast{}; + +void OpenXRSession::waitFrame() +{ + if (mPredictedFrames < mPredictionFrame) { - Timer timer("OpenXRSession::SwapBuffers"); - + Timer timer("OpenXRSession::waitFrame"); auto* xr = Environment::get().getManager(); - - if (!mShouldRender) - return; - - if (mRenderedFrames != mRenderFrame - 1) - { - Log(Debug::Warning) << "swapBuffers called out of order"; - waitFrame(); - return; - } - - for (auto layer : mLayerStack.layerObjects()) - if(layer) - layer->swapBuffers(gc); - - timer.checkpoint("Rendered"); - xr->endFrame(xr->impl().frameState().predictedDisplayTime, &mLayerStack); - mRenderedFrames++; - } - - const PoseSets& OpenXRSession::predictedPoses(PredictionSlice slice) - { - if(slice == PredictionSlice::Predraw) - waitFrame(); - return mPredictedPoses[(int)slice]; - } - - void OpenXRSession::waitFrame() - { - if(mPredictedFrames < mPredictionFrame) - { - Timer timer("OpenXRSession::waitFrame"); - auto* xr = Environment::get().getManager(); - xr->handleEvents(); - mIsRunning = xr->sessionRunning(); - if (!mIsRunning) - return; - - xr->waitFrame(); - timer.checkpoint("waitFrame"); - predictNext(0); - - mPredictedFrames++; - } - } - - - // OSG doesn't provide API to extract euler angles from a quat, but i need it. - // Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4 - void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll) - { - // Now do the computation - osg::Matrixd m2(osg::Matrixd::rotate(quat)); - double* mat = (double*)m2.ptr(); - double angle_x = 0.0; - double angle_y = 0.0; - double angle_z = 0.0; - double D, C, tr_x, tr_y; - angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */ - C = cos(angle_y); - if (fabs(C) > 0.005) /* Test for Gimball lock? */ - { - tr_x = mat[10] / C; /* No, so get X-axis angle */ - tr_y = -mat[6] / C; - angle_x = atan2(tr_y, tr_x); - tr_x = mat[0] / C; /* Get Z-axis angle */ - tr_y = -mat[1] / C; - angle_z = atan2(tr_y, tr_x); - } - else /* Gimball lock has occurred */ - { - angle_x = 0; /* Set X-axis angle to zero - */ - tr_x = mat[5]; /* And calculate Z-axis angle - */ - tr_y = mat[4]; - angle_z = atan2(tr_y, tr_x); - } - - yaw = angle_z; - pitch = angle_x; - roll = angle_y; - } - - void OpenXRSession::movementAngles(float& yaw, float& pitch) - { - auto lhandquat = predictedPoses(PredictionSlice::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation; - float roll = 0.f; - getEulerAngles(lhandquat, yaw, pitch, roll); - - } - - void OpenXRSession::advanceFrame(void) - { - auto* xr = Environment::get().getManager(); - mShouldRender = mIsRunning; + xr->handleEvents(); + mIsRunning = xr->sessionRunning(); if (!mIsRunning) return; - if (mPredictedFrames != mPredictionFrame) + + // Until OpenXR allows us to get a prediction without waiting + // we make our own (bad) prediction and let openxr wait asynchronously. + auto frameState = xr->impl().frameState(); + + std::thread waitThread([]{ + Environment::get().getManager()->waitFrame(); + }); + + if (mPredrawPredictedDisplayTime == 0) { - Log(Debug::Warning) << "advanceFrame() called out of order"; - return; + // First time + waitThread.join(); + mPredrawPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime; + } + else + { + waitThread.detach(); + mPredrawPredictedDisplayTime = frameState.predictedDisplayTime + frameState.predictedDisplayPeriod; } - xr->beginFrame(); - mPredictionFrame++; - mRenderFrame++; - mPredictedPoses[(int)PredictionSlice::Draw] = mPredictedPoses[(int)PredictionSlice::Predraw]; + timer.checkpoint("waitFrame"); + predictNext(0); + mPredictedFrames++; } +} - void OpenXRSession::predictNext(int extraPeriods) + +// OSG doesn't provide API to extract euler angles from a quat, but i need it. +// Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4 +void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll) +{ + // Now do the computation + osg::Matrixd m2(osg::Matrixd::rotate(quat)); + double* mat = (double*)m2.ptr(); + double angle_x = 0.0; + double angle_y = 0.0; + double angle_z = 0.0; + double D, C, tr_x, tr_y; + angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */ + C = cos(angle_y); + if (fabs(C) > 0.005) /* Test for Gimball lock? */ { - auto* xr = Environment::get().getManager(); - auto* input = Environment::get().getInputManager(); - auto mPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime; - - PoseSets newPoses{}; - newPoses.head[(int)TrackedSpace::STAGE] = xr->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE); - newPoses.head[(int)TrackedSpace::VIEW] = xr->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW); - newPoses.hands[(int)TrackedSpace::STAGE] = input->getHandPoses(mPredictedDisplayTime, TrackedSpace::STAGE); - newPoses.hands[(int)TrackedSpace::VIEW] = input->getHandPoses(mPredictedDisplayTime, TrackedSpace::VIEW); - auto stageViews = xr->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::STAGE); - auto hmdViews = xr->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::VIEW); - newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose); - newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::LEFT_HAND] = fromXR(hmdViews[(int)Side::LEFT_HAND].pose); - newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose); - newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::RIGHT_HAND] = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose); - mPredictedPoses[(int)PredictionSlice::Predraw] = newPoses; + tr_x = mat[10] / C; /* No, so get X-axis angle */ + tr_y = -mat[6] / C; + angle_x = atan2(tr_y, tr_x); + tr_x = mat[0] / C; /* Get Z-axis angle */ + tr_y = -mat[1] / C; + angle_z = atan2(tr_y, tr_x); } + else /* Gimball lock has occurred */ + { + angle_x = 0; /* Set X-axis angle to zero + */ + tr_x = mat[5]; /* And calculate Z-axis angle + */ + tr_y = mat[4]; + angle_z = atan2(tr_y, tr_x); + } + + yaw = angle_z; + pitch = angle_x; + roll = angle_y; +} + +void OpenXRSession::movementAngles(float& yaw, float& pitch) +{ + auto lhandquat = predictedPoses(PredictionSlice::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation; + float roll = 0.f; + getEulerAngles(lhandquat, yaw, pitch, roll); + +} + +void OpenXRSession::advanceFrame(void) +{ + auto* xr = Environment::get().getManager(); + mShouldRender = mIsRunning; + if (!mIsRunning) + return; + if (mPredictedFrames != mPredictionFrame) + { + Log(Debug::Warning) << "advanceFrame() called out of order"; + return; + } + + // For the same reason we do waitFrame() asynchronously, + // we do beginFrame async and just blit to the swapchains later. + //std::thread([=] { + xr->beginFrame(); + //}).detach(); + mPredictionFrame++; + mRenderFrame++; + mPredictedPoses[(int)PredictionSlice::Draw] = mPredictedPoses[(int)PredictionSlice::Predraw]; + mDrawPredictedDisplayTime = mPredrawPredictedDisplayTime; +} + +void OpenXRSession::predictNext(int extraPeriods) +{ + auto* xr = Environment::get().getManager(); + auto* input = Environment::get().getInputManager(); + + PoseSets newPoses{}; + newPoses.head[(int)TrackedSpace::STAGE] = xr->impl().getPredictedLimbPose(mPredrawPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE); + newPoses.head[(int)TrackedSpace::VIEW] = xr->impl().getPredictedLimbPose(mPredrawPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW); + newPoses.hands[(int)TrackedSpace::STAGE] = input->getHandPoses(mPredrawPredictedDisplayTime, TrackedSpace::STAGE); + newPoses.hands[(int)TrackedSpace::VIEW] = input->getHandPoses(mPredrawPredictedDisplayTime, TrackedSpace::VIEW); + auto stageViews = xr->impl().getPredictedViews(mPredrawPredictedDisplayTime, TrackedSpace::STAGE); + auto hmdViews = xr->impl().getPredictedViews(mPredrawPredictedDisplayTime, TrackedSpace::VIEW); + newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose); + newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::LEFT_HAND] = fromXR(hmdViews[(int)Side::LEFT_HAND].pose); + newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose); + newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::RIGHT_HAND] = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose); + mPredictedPoses[(int)PredictionSlice::Predraw] = newPoses; +} } std::ostream& operator <<( diff --git a/apps/openmw/mwvr/openxrsession.hpp b/apps/openmw/mwvr/openxrsession.hpp index 3c4cef5c1..d3f83c08d 100644 --- a/apps/openmw/mwvr/openxrsession.hpp +++ b/apps/openmw/mwvr/openxrsession.hpp @@ -64,6 +64,9 @@ public: int mPredictionFrame{ 1 }; int mPredictedFrames{ 0 }; + long long mPredrawPredictedDisplayTime{ 0 }; + long long mDrawPredictedDisplayTime{ 0 }; + bool mIsRunning{ false }; bool mShouldRender{ false }; }; diff --git a/apps/openmw/mwvr/vranimation.cpp b/apps/openmw/mwvr/vranimation.cpp index 1581d2e69..c2cd64279 100644 --- a/apps/openmw/mwvr/vranimation.cpp +++ b/apps/openmw/mwvr/vranimation.cpp @@ -603,9 +603,7 @@ osg::ref_ptr VRAnimation::createPointerGeometry(void) geometry->setVertexArray(vertexArray); geometry->setColorArray(colorArray, osg::Array::BIND_PER_VERTEX); geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, numVertices)); - geometry->setDataVariance(osg::Object::DYNAMIC); - geometry->setSupportsDisplayList(false); - geometry->setCullingActive(false); + geometry->setDataVariance(osg::Object::STATIC); auto stateset = geometry->getOrCreateStateSet(); stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); diff --git a/apps/openmw/mwvr/vrgui.cpp b/apps/openmw/mwvr/vrgui.cpp index d2384c38d..17846c824 100644 --- a/apps/openmw/mwvr/vrgui.cpp +++ b/apps/openmw/mwvr/vrgui.cpp @@ -172,8 +172,7 @@ VRGUILayer::VRGUILayer( (*normals)[0].set(0.0f, -1.0f, 0.0f); mGeometry->setNormalArray(normals, osg::Array::BIND_OVERALL); mGeometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); - mGeometry->setDataVariance(osg::Object::DYNAMIC); - mGeometry->setSupportsDisplayList(false); + mGeometry->setDataVariance(osg::Object::STATIC); mGeometry->setName("VRGUILayer"); // Create the camera that will render the menu texture @@ -183,8 +182,6 @@ VRGUILayer::VRGUILayer( mGUICamera = new GUICamera(config.pixelResolution.x(), config.pixelResolution.y(), config.backgroundColor); osgMyGUI::RenderManager& renderManager = static_cast(MyGUI::RenderManager::getInstance()); mMyGUICamera = renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter); - //myGUICamera->setViewport(0, 0, 256, 256); - //mMyGUICamera->setProjectionMatrixAsOrtho2D(-1, 1, -1, 1); mGUICamera->setScene(mMyGUICamera); // Define state set that allows rendering with transparency