1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-20 09:23:52 +00:00

More refactoring of VRSession. Moved call of BeginFrame and WaitFrame to the start of cull to keep shadow technique within openxr specs. There is still some jitter unless i change to single-threaded, and now i have no idea why.

This commit is contained in:
Mads Buvik Sandvei 2020-06-03 19:46:20 +02:00
parent 31336aba79
commit f25c3af9cb
10 changed files with 199 additions and 153 deletions

View file

@ -564,10 +564,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create sound system // Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
#ifdef USE_OPENXR
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
#endif
if (!mSkipMenu) if (!mSkipMenu)
{ {
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo"); const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
@ -735,6 +731,7 @@ void OMW::Engine::go()
#ifdef USE_OPENXR #ifdef USE_OPENXR
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer)); mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
//mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
#endif #endif
// Start the game // Start the game

View file

@ -1008,12 +1008,15 @@ private:
auto* vrGuiManager = Environment::get().getGUIManager(); auto* vrGuiManager = Environment::get().getGUIManager();
if (vrGuiManager)
{
bool vrHasFocus = vrGuiManager->updateFocus(); bool vrHasFocus = vrGuiManager->updateFocus();
auto guiCursor = vrGuiManager->guiCursor(); auto guiCursor = vrGuiManager->guiCursor();
if (vrHasFocus) if (vrHasFocus)
{ {
mMouseManager->setMousePosition(guiCursor.x(), guiCursor.y()); mMouseManager->setMousePosition(guiCursor.x(), guiCursor.y());
} }
}
while (auto* action = mXRInput->nextAction()) while (auto* action = mXRInput->nextAction())
{ {
@ -1237,7 +1240,7 @@ private:
void OpenXRInputManager::updateHead() void OpenXRInputManager::updateHead()
{ {
auto* session = Environment::get().getSession(); 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(); currentHeadPose.position *= Environment::get().unitsPerMeter();
osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position; osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position;
mHeadPose = currentHeadPose; mHeadPose = currentHeadPose;

View file

@ -277,44 +277,58 @@ namespace MWVR
void void
OpenXRManagerImpl::waitFrame() OpenXRManagerImpl::waitFrame()
{ {
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
auto XRDC = mGraphicsBinding.hDC;
auto XRGLRC = mGraphicsBinding.hGLRC;
wglMakeCurrent(XRDC, XRGLRC);
Timer timer("waitFrame()"); Timer timer("waitFrame()");
static std::mutex waitFrameMutex;
if (!mSessionRunning)
return;
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO }; XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
XrFrameState frameState{ XR_TYPE_FRAME_STATE }; XrFrameState frameState{ XR_TYPE_FRAME_STATE };
CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState)); CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState));
std::unique_lock<std::mutex> lock(mFrameStateMutex);
mFrameState = frameState; mFrameState = frameState;
wglMakeCurrent(DC, GLRC);
} }
void void
OpenXRManagerImpl::beginFrame() OpenXRManagerImpl::beginFrame()
{ {
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
auto XRDC = mGraphicsBinding.hDC;
auto XRGLRC = mGraphicsBinding.hGLRC;
wglMakeCurrent(XRDC, XRGLRC);
Timer timer("beginFrame"); Timer timer("beginFrame");
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO }; XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo)); CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo));
wglMakeCurrent(DC, GLRC);
} }
void void
OpenXRManagerImpl::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack) OpenXRManagerImpl::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack)
{ {
Timer timer("endFrame()"); auto DC = wglGetCurrentDC();
if (!mSessionRunning) auto GLRC = wglGetCurrentContext();
return; auto XRDC = mGraphicsBinding.hDC;
auto XRGLRC = mGraphicsBinding.hGLRC;
wglMakeCurrent(XRDC, XRGLRC);
Timer timer("endFrame()");
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO }; XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
frameEndInfo.displayTime = displayTime; frameEndInfo.displayTime = displayTime;
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode; frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
frameEndInfo.layerCount = layerCount; frameEndInfo.layerCount = layerCount;
frameEndInfo.layers = layerStack; frameEndInfo.layers = layerStack;
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo)); CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
wglMakeCurrent(DC, GLRC);
} }
std::array<XrView, 2> std::array<XrView, 2>

View file

@ -103,7 +103,7 @@ void StateMachine::update(float dt, bool enabled)
{ {
auto* session = Environment::get().getSession(); auto* session = Environment::get().getSession();
auto* world = MWBase::Environment::get().getWorld(); 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& handPose = predictedPoses.hands[(int)MWVR::Side::RIGHT_HAND];
auto weaponType = world->getActiveWeaponType(); auto weaponType = world->getActiveWeaponType();

View file

@ -114,8 +114,8 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
MWBase::Environment::get().getWorld()->getRenderingManager().getCamera()->updateCamera(); MWBase::Environment::get().getWorld()->getRenderingManager().getCamera()->updateCamera();
} }
MWVR::Pose handStage = session->predictedPoses(VRSession::FramePhase::Predraw).hands[side]; MWVR::Pose handStage = session->predictedPoses(VRSession::FramePhase::Update).hands[side];
MWVR::Pose headStage = session->predictedPoses(VRSession::FramePhase::Predraw).head; MWVR::Pose headStage = session->predictedPoses(VRSession::FramePhase::Update).head;
auto orientation = handStage.orientation; auto orientation = handStage.orientation;

View file

@ -24,6 +24,7 @@
#include "vrsession.hpp" #include "vrsession.hpp"
#include "vrgui.hpp" #include "vrgui.hpp"
#include <time.h> #include <time.h>
#include <thread>
namespace MWVR namespace MWVR
{ {
@ -38,7 +39,7 @@ VRSession::~VRSession()
osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side) osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side)
{ {
assert(((int)side) < 2); 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 near_ = Settings::Manager::getFloat("near clip", "Camera");
float far_ = Settings::Manager::getFloat("viewing distance", "Camera"); float far_ = Settings::Manager::getFloat("viewing distance", "Camera");
return fov.perspectiveMatrix(near_, far_); 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) 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::Vec3 position = pose.position * Environment::get().unitsPerMeter();
osg::Quat orientation = pose.orientation; osg::Quat orientation = pose.orientation;
@ -77,26 +78,15 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
Timer timer("VRSession::SwapBuffers"); Timer timer("VRSession::SwapBuffers");
auto* xr = Environment::get().getManager(); auto* xr = Environment::get().getManager();
beginPhase(FramePhase::Swap);
if (getFrame(FramePhase::Swap)->mShouldRender)
{ {
std::unique_lock<std::mutex> lock(mMutex);
xr->handleEvents();
mPostdrawFrame = std::move(mDrawFrame);
assert(mPostdrawFrame);
}
// TODO: Should blit mirror texture regardless
if (!isRunning())
return;
xr->waitFrame();
xr->beginFrame();
viewer.swapBuffers(gc);
auto leftView = viewer.mViews["LeftEye"]; auto leftView = viewer.mViews["LeftEye"];
auto rightView = viewer.mViews["RightEye"]; auto rightView = viewer.mViews["RightEye"];
viewer.blitEyesToMirrorTexture(gc);
gc->swapBuffersImplementation();
leftView->swapBuffers(gc); leftView->swapBuffers(gc);
rightView->swapBuffers(gc); rightView->swapBuffers(gc);
@ -105,10 +95,10 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
compositionLayerProjectionViews[1].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; compositionLayerProjectionViews[1].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
compositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage(); compositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage();
compositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage(); compositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage();
compositionLayerProjectionViews[0].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::LEFT_HAND]); compositionLayerProjectionViews[0].pose = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.eye[(int)Side::LEFT_HAND]);
compositionLayerProjectionViews[1].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::RIGHT_HAND]); compositionLayerProjectionViews[1].pose = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.eye[(int)Side::RIGHT_HAND]);
compositionLayerProjectionViews[0].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::LEFT_HAND].fov); compositionLayerProjectionViews[0].fov = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.view[(int)Side::LEFT_HAND].fov);
compositionLayerProjectionViews[1].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::RIGHT_HAND].fov); compositionLayerProjectionViews[1].fov = toXR(getFrame(FramePhase::Swap)->mPredictedPoses.view[(int)Side::RIGHT_HAND].fov);
XrCompositionLayerProjection layer{}; XrCompositionLayerProjection layer{};
layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
layer.space = xr->impl().getReferenceSpace(TrackedSpace::STAGE); layer.space = xr->impl().getReferenceSpace(TrackedSpace::STAGE);
@ -116,33 +106,92 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
layer.views = compositionLayerProjectionViews.data(); layer.views = compositionLayerProjectionViews.data();
auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer); auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
xr->endFrame(mPostdrawFrame->mPredictedDisplayTime, 1, &layerStack); Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": EndFrame";
mLastRenderedFrame = mPostdrawFrame->mFrameNo; xr->endFrame(getFrame(FramePhase::Swap)->mPredictedDisplayTime, 1, &layerStack);
}
std::unique_lock<std::mutex> 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(); auto now = std::chrono::steady_clock::now();
mLastFrameInterval = std::chrono::duration_cast<std::chrono::nanoseconds>(now - mLastRenderedFrameTimestamp); mLastFrameInterval = std::chrono::duration_cast<std::chrono::nanoseconds>(now - mLastRenderedFrameTimestamp);
mLastRenderedFrameTimestamp = now; 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<std::chrono::duration<double>>(now - mStart).count();
static int sBaseFrames = 0;
if (seconds > 10.f)
{
Log(Debug::Verbose) << "Fps: " << (static_cast<double>(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<std::mutex> lock(mMutex);
Timer timer("VRSession::advanceFrame"); Timer timer("VRSession::advanceFrame");
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ")";
assert(!mDrawFrame); if (phase != FramePhase::Update)
mDrawFrame = std::move(mPredrawFrame); {
std::unique_lock<std::mutex> lock(mMutex);
FramePhase previousPhase = static_cast<FramePhase>((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<std::mutex> 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::VRFrame>& 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<std::mutex> lock(mMutex); std::unique_lock<std::mutex> lock(mMutex);
assert(!mPredrawFrame); assert(!mPredrawFrame);
Timer timer("VRSession::startFrame"); Timer timer("VRSession::startFrame");
auto* xr = Environment::get().getManager(); auto* xr = Environment::get().getManager();
auto* input = Environment::get().getInputManager(); xr->handleEvents();
// Until OpenXR allows us to get a prediction without waiting // 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(); auto frameState = xr->impl().frameState();
long long predictedDisplayTime = 0; long long predictedDisplayTime = 0;
mFrames++; mFrames++;
@ -154,17 +203,19 @@ void VRSession::startFrame()
else if (mFrames > mLastRenderedFrame) else if (mFrames > mLastRenderedFrame)
{ {
//predictedDisplayTime = mLastPredictedDisplayTime + mLastFrameInterval.count() * (mFrames - mLastRenderedFrame); //predictedDisplayTime = mLastPredictedDisplayTime + mLastFrameInterval.count() * (mFrames - mLastRenderedFrame);
float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(frameState.predictedDisplayPeriod); float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(mLastPredictedDisplayPeriod);
int intervals = (int)std::roundf(intervalsf); #ifdef max
predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * frameState.predictedDisplayPeriod; #undef max
#endif
int intervals = std::max((int)std::roundf(intervalsf), 1);
predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * mLastPredictedDisplayPeriod;
} }
PoseSet predictedPoses{}; PoseSet predictedPoses{};
if (isRunning()) if (isRunning())
{ {
xr->impl().enablePredictions();
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale; 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); 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::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; 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); 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::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
predictedPoses.eye[(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale; predictedPoses.eye[(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
}
else auto* input = Environment::get().getInputManager();
if (input)
{ {
// If session is not running, copy poses from the last frame predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale;
if (mPostdrawFrame) predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * mPlayerScale;
predictedPoses = mPostdrawFrame->mPredictedPoses; }
xr->impl().disablePredictions();
} }
auto& frame = getFrame(FramePhase::Update);
mPredrawFrame.reset(new VRFrame); frame.reset(new VRFrame);
mPredrawFrame->mPredictedDisplayTime = predictedDisplayTime; frame->mPredictedDisplayTime = predictedDisplayTime;
mPredrawFrame->mFrameNo = mFrames; frame->mFrameNo = mFrames;
mPredrawFrame->mPredictedPoses = predictedPoses; frame->mPredictedPoses = predictedPoses;
frame->mShouldRender = isRunning();
} }
const PoseSet& VRSession::predictedPoses(FramePhase phase) const PoseSet& VRSession::predictedPoses(FramePhase phase)
{ {
auto& frame = getFrame(phase);
switch (phase) if (phase == FramePhase::Update && !frame)
{ beginPhase(FramePhase::Update);
case FramePhase::Predraw:
if (!mPredrawFrame)
startFrame();
assert(mPredrawFrame);
return mPredrawFrame->mPredictedPoses;
case FramePhase::Draw: if (!frame)
assert(mDrawFrame); throw std::logic_error("Attempted to get poses from a phase with no current pose");
return mDrawFrame->mPredictedPoses; return frame->mPredictedPoses;
case FramePhase::Postdraw:
assert(mPostdrawFrame);
return mPostdrawFrame->mPredictedPoses;
}
assert(0);
return mPredrawFrame->mPredictedPoses;
} }
// OSG doesn't provide API to extract euler angles from a quat, but i need it. // OSG doesn't provide API to extract euler angles from a quat, but i need it.
@ -253,7 +295,10 @@ void VRSession::movementAngles(float& yaw, float& pitch)
{ {
assert(mPredrawFrame); assert(mPredrawFrame);
auto* input = Environment::get().getInputManager(); auto* input = Environment::get().getInputManager();
auto lhandquat = input->getHandPose(mPredrawFrame->mPredictedDisplayTime, TrackedSpace::VIEW, 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; //predictedPoses(FramePhase::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation;
float roll = 0.f; float roll = 0.f;
getEulerAngles(lhandquat, yaw, pitch, roll); getEulerAngles(lhandquat, yaw, pitch, roll);

View file

@ -27,9 +27,11 @@ public:
enum class FramePhase enum class FramePhase
{ {
Predraw = 0, //!< Get poses predicted for the next frame to be drawn Update = 0, //!< The frame currently in update traversals
Draw = 1, //!< Get poses predicted for the current rendering frame Cull, //!< The frame currently in cull
Postdraw = 2, //!< Get poses predicted for the last rendered frame Draw, //!< The frame currently in draw
Swap, //!< The frame being swapped
NumPhases
}; };
struct VRFrame struct VRFrame
@ -37,6 +39,8 @@ public:
long long mFrameNo{ 0 }; long long mFrameNo{ 0 };
long long mPredictedDisplayTime{ 0 }; long long mPredictedDisplayTime{ 0 };
PoseSet mPredictedPoses{}; PoseSet mPredictedPoses{};
bool mShouldRender{ false };
bool mHasWaited{ false };
}; };
public: public:
@ -48,12 +52,13 @@ public:
const PoseSet& predictedPoses(FramePhase phase); const PoseSet& predictedPoses(FramePhase phase);
//! Starts a new frame //! Starts a new frame
void startFrame(); void prepareFrame();
//! Angles to be used for overriding movement direction //! Angles to be used for overriding movement direction
void movementAngles(float& yaw, float& pitch); void movementAngles(float& yaw, float& pitch);
void advanceFramePhase(void); void beginPhase(FramePhase phase);
std::unique_ptr<VRFrame>& getFrame(FramePhase phase);
bool isRunning() const; bool isRunning() const;
@ -63,9 +68,7 @@ public:
osg::Matrix viewMatrix(FramePhase phase, Side side); osg::Matrix viewMatrix(FramePhase phase, Side side);
osg::Matrix projectionMatrix(FramePhase phase, Side side); osg::Matrix projectionMatrix(FramePhase phase, Side side);
std::unique_ptr<VRFrame> mPredrawFrame{ nullptr }; std::array<std::unique_ptr<VRFrame>, (int)FramePhase::NumPhases> mFrame{ nullptr };
std::unique_ptr<VRFrame> mDrawFrame{ nullptr };
std::unique_ptr<VRFrame> mPostdrawFrame{ nullptr };
std::mutex mMutex{}; std::mutex mMutex{};
std::condition_variable mCondition{}; std::condition_variable mCondition{};
@ -74,6 +77,7 @@ public:
long long mLastRenderedFrame{ 0 }; long long mLastRenderedFrame{ 0 };
long long mLastPredictedDisplayTime{ 0 }; long long mLastPredictedDisplayTime{ 0 };
long long mLastPredictedDisplayPeriod{ 0 }; long long mLastPredictedDisplayPeriod{ 0 };
std::chrono::steady_clock::time_point mStart{ std::chrono::steady_clock::now() };
std::chrono::nanoseconds mLastFrameInterval{}; std::chrono::nanoseconds mLastFrameInterval{};
std::chrono::steady_clock::time_point mLastRenderedFrameTimestamp{std::chrono::steady_clock::now()}; std::chrono::steady_clock::time_point mLastRenderedFrameTimestamp{std::chrono::steady_clock::now()};

View file

@ -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::Camera* VRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
{ {
osg::ref_ptr<osg::Camera> camera = new osg::Camera(); osg::ref_ptr<osg::Camera> camera = new osg::Camera();
@ -53,6 +64,7 @@ namespace MWVR {
camera->setGraphicsContext(gc); camera->setGraphicsContext(gc);
camera->setInitialDrawCallback(new VRView::InitialDrawCallback()); camera->setInitialDrawCallback(new VRView::InitialDrawCallback());
camera->setCullCallback(new CullCallback);
return camera.release(); return camera.release();
} }
@ -69,6 +81,10 @@ namespace MWVR {
void VRView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const 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(); osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation); osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
if (renderer != nullptr) if (renderer != nullptr)
@ -76,7 +92,6 @@ namespace MWVR {
// Disable normal OSG FBO camera setup // Disable normal OSG FBO camera setup
renderer->setCameraRequiresSetUp(false); renderer->setCameraRequiresSetUp(false);
} }
auto name = renderInfo.getCurrentCamera()->getName();
} }
void VRView::UpdateSlaveCallback::updateSlave( void VRView::UpdateSlaveCallback::updateSlave(
osg::View& view, osg::View& view,
@ -93,8 +108,9 @@ namespace MWVR {
auto* session = Environment::get().getSession(); auto* session = Environment::get().getSession();
auto viewMatrix = view.getCamera()->getViewMatrix(); 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->setViewMatrix(modifiedViewMatrix);
camera->setProjectionMatrix(projectionMatrix); camera->setProjectionMatrix(projectionMatrix);
@ -109,6 +125,6 @@ namespace MWVR {
void VRView::swapBuffers(osg::GraphicsContext* gc) void VRView::swapBuffers(osg::GraphicsContext* gc)
{ {
swapchain().endFrame(gc); mSwapchain->endFrame(gc);
} }
} }

View file

@ -151,22 +151,16 @@ namespace MWVR
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
{ {
//includeMenu = false;
mMirrorTextureSwapchain->beginFrame(gc);
int mirror_width = mMirrorTextureSwapchain->width() / 2; int mirror_width = mMirrorTextureSwapchain->width() / 2;
mMirrorTextureSwapchain->beginFrame(gc);
mViews["RightEye"]->swapchain().renderBuffer()->blit(gc, 0, 0, mirror_width, mMirrorTextureSwapchain->height()); 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()); 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); mMirrorTextureSwapchain->endFrame(gc);
auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height());
@ -178,24 +172,6 @@ namespace MWVR
{ {
auto* session = Environment::get().getSession(); auto* session = Environment::get().getSession();
session->swapBuffers(gc, *mViewer); 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 void
@ -217,12 +193,7 @@ namespace MWVR
{ {
auto* camera = info.getCurrentCamera(); auto* camera = info.getCurrentCamera();
auto name = camera->getName(); auto name = camera->getName();
auto& view = mViews[name]; mViews[name]->prerenderCallback(info);
if (name == "LeftEye")
Environment::get().getSession()->advanceFramePhase();
view->prerenderCallback(info);
} }
void VRViewer::postDrawCallback(osg::RenderInfo& info) void VRViewer::postDrawCallback(osg::RenderInfo& info)
@ -239,8 +210,5 @@ namespace MWVR
camera->setPreDrawCallback(mPreDraw); camera->setPreDrawCallback(mPreDraw);
Log(Debug::Warning) << ("osg overwrote predraw"); Log(Debug::Warning) << ("osg overwrote predraw");
} }
//if(mDelay && name == "RightEye")
// std::this_thread::sleep_for(std::chrono::milliseconds(25));
} }
} }

View file

@ -77,7 +77,6 @@ namespace MWVR
void preDrawCallback(osg::RenderInfo& info); void preDrawCallback(osg::RenderInfo& info);
void postDrawCallback(osg::RenderInfo& info); void postDrawCallback(osg::RenderInfo& info);
void blitEyesToMirrorTexture(osg::GraphicsContext* gc); void blitEyesToMirrorTexture(osg::GraphicsContext* gc);
void swapBuffers(osg::GraphicsContext* gc);
void realize(osg::GraphicsContext* gc); void realize(osg::GraphicsContext* gc);
bool realized() { return mConfigured; } bool realized() { return mConfigured; }