mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-01 08:45:32 +00:00
Some bugfixes, more experimenting with timing of frame sync calls.
This commit is contained in:
parent
f25c3af9cb
commit
9bd676f5be
11 changed files with 214 additions and 121 deletions
|
@ -564,6 +564,12 @@ 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));
|
||||
//mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
|
||||
#endif
|
||||
|
||||
if (!mSkipMenu)
|
||||
{
|
||||
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
|
||||
|
@ -729,11 +735,6 @@ void OMW::Engine::go()
|
|||
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
|
||||
mViewer->addEventHandler(resourceshandler);
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
|
||||
//mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
|
||||
#endif
|
||||
|
||||
// Start the game
|
||||
if (!mSaveGameFile.empty())
|
||||
{
|
||||
|
|
|
@ -288,13 +288,13 @@ namespace MWGui
|
|||
MyGUI::PointerManager::getInstance().setVisible(false);
|
||||
|
||||
mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>("ImageBox", 0,0,1,1,
|
||||
MyGUI::Align::Default, "InputBlocker");
|
||||
MyGUI::Align::Default, "VideoPlayer");
|
||||
mVideoBackground->setImageTexture("black");
|
||||
mVideoBackground->setVisible(false);
|
||||
mVideoBackground->setNeedMouseFocus(true);
|
||||
mVideoBackground->setNeedKeyFocus(true);
|
||||
|
||||
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
|
||||
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default, "VideoPlayer");
|
||||
mVideoWidget->setNeedMouseFocus(true);
|
||||
mVideoWidget->setNeedKeyFocus(true);
|
||||
mVideoWidget->setVFS(resourceSystem->getVFS());
|
||||
|
@ -1969,6 +1969,7 @@ namespace MWGui
|
|||
|
||||
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
|
||||
{
|
||||
auto* vrGuiManager = MWVR::Environment::get().getGUIManager();
|
||||
mVideoEnabled = true;
|
||||
mVideoWidget->playVideo("video\\" + name);
|
||||
|
||||
|
@ -1990,6 +1991,9 @@ namespace MWGui
|
|||
|
||||
mVideoBackground->setVisible(true);
|
||||
|
||||
vrGuiManager->updateTracking(mViewer->getCamera());
|
||||
vrGuiManager->insertLayer(mVideoBackground->getLayer()->getName());
|
||||
|
||||
bool cursorWasVisible = mCursorVisible;
|
||||
setCursorVisible(false);
|
||||
|
||||
|
@ -2019,6 +2023,7 @@ namespace MWGui
|
|||
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
//vrGuiManager->updateTracking(mViewer->getCamera());
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
// at the time this function is called we are in the middle of a frame,
|
||||
|
@ -2039,6 +2044,7 @@ namespace MWGui
|
|||
// Restore normal rendering
|
||||
updateVisible();
|
||||
|
||||
vrGuiManager->removeLayer(mVideoBackground->getLayer()->getName());
|
||||
mVideoBackground->setVisible(false);
|
||||
mVideoEnabled = false;
|
||||
}
|
||||
|
|
|
@ -907,10 +907,10 @@ namespace MWRender
|
|||
|
||||
const bool isPlayer = (mPtr == MWMechanics::getPlayer());
|
||||
|
||||
//if (isPlayer)
|
||||
//{
|
||||
// Log(Debug::Verbose) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
|
||||
//}
|
||||
if (isPlayer)
|
||||
{
|
||||
Log(Debug::Debug) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
|
||||
}
|
||||
|
||||
AnimStateMap::iterator stateiter = mStates.begin();
|
||||
while(stateiter != mStates.end())
|
||||
|
|
|
@ -57,9 +57,9 @@
|
|||
namespace MWVR
|
||||
{
|
||||
|
||||
// TODO: Make part of settings (is there already a setting like this?)
|
||||
//! Delay before a long-press action is activated
|
||||
static std::chrono::milliseconds gActionTime{ 1000 };
|
||||
//! Delay before a long-press action is activated (and regular press is discarded)
|
||||
//! TODO: Make this configurable?
|
||||
static std::chrono::milliseconds gActionTime{ 666 };
|
||||
//! Magnitude above which an axis action is considered active
|
||||
static float gAxisEpsilon{ 0.01f };
|
||||
|
||||
|
@ -568,7 +568,7 @@ OpenXRInput::OpenXRInput()
|
|||
, mMoveLeftRight(std::move(createMWAction<AxisAction>(MWInput::A_MoveLeftRight, "move_left_right", "Move Left Right", { })))
|
||||
, mJournal(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_Journal, "journal_book", "Journal Book", { })))
|
||||
, mQuickSave(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_QuickSave, "quick_save", "Quick Save", { })))
|
||||
, mRest(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_Rest, "rest", "Rest", { })))
|
||||
, mRest(std::move(createMWAction<ButtonPressAction>(MWInput::A_Rest, "rest", "Rest", { })))
|
||||
, mActivateTouch(std::move(createMWAction<AxisAction>(A_ActivateTouch, "activate_touched", "Activate Touch", { RIGHT_HAND })))
|
||||
, mAlwaysRun(std::move(createMWAction<ButtonPressAction>(MWInput::A_AlwaysRun, "always_run", "Always Run", { })))
|
||||
, mAutoMove(std::move(createMWAction<ButtonPressAction>(MWInput::A_AutoMove, "auto_move", "Auto Move", { })))
|
||||
|
@ -1027,9 +1027,16 @@ private:
|
|||
|
||||
MWInput::InputManager::update(dt, disableControls, disableEvents);
|
||||
|
||||
// Start next frame phase
|
||||
auto* session = Environment::get().getSession();
|
||||
if (session)
|
||||
session->beginPhase(VRSession::FramePhase::Update);
|
||||
|
||||
// The rest of this code assumes the game is running
|
||||
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
|
||||
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
|
||||
|
|
|
@ -274,10 +274,11 @@ void HandController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
osg::Quat rotate{ 0,0,0,1 };
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
auto windowManager = MWBase::Environment::get().getWindowManager();
|
||||
auto animation = MWVR::Environment::get().getPlayerAnimation();
|
||||
auto weaponType = world->getActiveWeaponType();
|
||||
// Morrowind models do not hold most weapons at a natural angle, so i rotate the hand
|
||||
// to more natural angles on weapons to allow more comfortable combat.
|
||||
if (!windowManager->isGuiMode())
|
||||
if (!windowManager->isGuiMode() && !animation->isPointingForward())
|
||||
{
|
||||
|
||||
switch (weaponType)
|
||||
|
@ -407,10 +408,8 @@ void WeaponPointerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
}
|
||||
else
|
||||
{
|
||||
// Hide the pointer
|
||||
matrixTransform->setMatrix(
|
||||
osg::Matrix::scale(1.f, 64.f, 1.f)
|
||||
//osg::Matrix::scale(0.f, 0.f, 0.f)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwgui/windowbase.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
||||
#include <MyGUI_Widget.h>
|
||||
#include <MyGUI_ILayer.h>
|
||||
|
@ -408,20 +409,11 @@ VRGUIManager::VRGUIManager(
|
|||
osg::ref_ptr<osgViewer::Viewer> viewer)
|
||||
: mOsgViewer(viewer)
|
||||
{
|
||||
mGUIGeometriesRoot->setName("XR GUI Geometry Root");
|
||||
mGUICamerasRoot->setName("XR GUI Cameras Root");
|
||||
mGUIGeometriesRoot->setName("VR GUI Geometry Root");
|
||||
mGUICamerasRoot->setName("VR GUI Cameras Root");
|
||||
auto* root = viewer->getSceneData();
|
||||
|
||||
SceneUtil::FindByNameVisitor findSceneVisitor("Scene Root");
|
||||
root->accept(findSceneVisitor);
|
||||
if(!findSceneVisitor.mFoundNode)
|
||||
{
|
||||
Log(Debug::Error) << "Scene Root doesn't exist";
|
||||
return;
|
||||
}
|
||||
|
||||
findSceneVisitor.mFoundNode->addChild(mGUIGeometriesRoot);
|
||||
root->asGroup()->addChild(mGUICamerasRoot);
|
||||
root->asGroup()->addChild(mGUIGeometriesRoot);
|
||||
|
||||
}
|
||||
|
||||
|
@ -447,10 +439,12 @@ static const LayerConfig createDefaultConfig(int priority, bool background = tru
|
|||
};
|
||||
}
|
||||
LayerConfig gDefaultConfig = createDefaultConfig(1);
|
||||
LayerConfig gVideoPlayerConfig = createDefaultConfig(1, true, SizingMode::Fixed);
|
||||
LayerConfig gLoadingScreenConfig = createDefaultConfig(1, true, SizingMode::Fixed);
|
||||
LayerConfig gJournalBooksConfig = createDefaultConfig(2, false, SizingMode::Fixed);
|
||||
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3, true);
|
||||
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
|
||||
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);;
|
||||
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);
|
||||
|
||||
static const float sSideBySideRadius = 1.f;
|
||||
static const float sSideBySideAzimuthInterval = -osg::PI_4;
|
||||
|
@ -527,6 +521,8 @@ static std::map<std::string, LayerConfig&> gLayerConfigs =
|
|||
{"MessageBox", gMessageBoxConfig},
|
||||
{"Windows", gDefaultWindowsConfig},
|
||||
{"Notification", gNotificationConfig},
|
||||
{"VideoPlayer", gVideoPlayerConfig},
|
||||
{"LoadingScreen", gLoadingScreenConfig},
|
||||
};
|
||||
|
||||
static std::set<std::string> layerBlacklist =
|
||||
|
@ -673,21 +669,41 @@ void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible)
|
|||
|
||||
void VRGUIManager::updateTracking(void)
|
||||
{
|
||||
// Get head pose by reading the camera view matrix to place the GUI in the world.
|
||||
osg::Vec3 eye{};
|
||||
osg::Vec3 center{};
|
||||
osg::Vec3 up{};
|
||||
Pose headPose{};
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
if (!world)
|
||||
return;
|
||||
auto* camera = world->getRenderingManager().getCamera()->getOsgCamera();
|
||||
if (!camera)
|
||||
return;
|
||||
camera->getViewMatrixAsLookAt(eye, center, up);
|
||||
headPose.position = eye;
|
||||
headPose.orientation = camera->getViewMatrix().getRotate();
|
||||
headPose.orientation = headPose.orientation.inverse();
|
||||
updateTracking(camera);
|
||||
}
|
||||
|
||||
void VRGUIManager::updateTracking(osg::Camera* camera)
|
||||
{
|
||||
// Get head pose by reading the camera view matrix to place the GUI in the world.
|
||||
osg::Vec3 eye{};
|
||||
osg::Vec3 center{};
|
||||
osg::Vec3 up{};
|
||||
Pose headPose{};
|
||||
|
||||
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
// If App is not running, tracking will not be propagated to camera.
|
||||
// So we adopt stage space.
|
||||
auto pose = MWVR::Environment::get().getSession()->predictedPoses(MWVR::VRSession::FramePhase::Update).head;
|
||||
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||
osg::Quat orientation = pose.orientation;
|
||||
headPose.position = position;
|
||||
headPose.orientation = orientation;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto viewMatrix = camera->getViewMatrix();
|
||||
viewMatrix.getLookAt(eye, center, up);
|
||||
headPose.position = eye;
|
||||
headPose.orientation = viewMatrix.getRotate();
|
||||
headPose.orientation = headPose.orientation.inverse();
|
||||
}
|
||||
|
||||
mHeadPose = headPose;
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ namespace MWVR
|
|||
void removeWidget(MWGui::Layout* widget);
|
||||
|
||||
void updateTracking(void);
|
||||
void updateTracking(osg::Camera* camera);
|
||||
|
||||
bool updateFocus();
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "openxrswapchain.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
@ -26,6 +27,14 @@
|
|||
#include <time.h>
|
||||
#include <thread>
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
VRSession::VRSession()
|
||||
|
@ -47,7 +56,23 @@ osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side)
|
|||
|
||||
osg::Matrix VRSession::viewMatrix(FramePhase phase, Side side)
|
||||
{
|
||||
MWVR::Pose pose = predictedPoses(phase).view[(int)side].pose;
|
||||
MWVR::Pose pose{};
|
||||
pose = predictedPoses(phase).view[(int)side].pose;
|
||||
|
||||
|
||||
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
pose = predictedPoses(phase).eye[(int)side];
|
||||
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||
osg::Quat orientation = pose.orientation;
|
||||
osg::Vec3d forward = orientation * osg::Vec3d(0, 1, 0);
|
||||
osg::Vec3d up = orientation * osg::Vec3d(0, 0, 1);
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.makeLookAt(position, position + forward, up);
|
||||
|
||||
return viewMatrix;
|
||||
}
|
||||
|
||||
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||
osg::Quat orientation = pose.orientation;
|
||||
|
||||
|
@ -106,72 +131,92 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
|
|||
layer.views = compositionLayerProjectionViews.data();
|
||||
auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
|
||||
|
||||
Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": EndFrame";
|
||||
Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": EndFrame " <<std::this_thread::get_id();
|
||||
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();
|
||||
mLastFrameInterval = std::chrono::duration_cast<std::chrono::nanoseconds>(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<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;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
getFrame(FramePhase::Swap) = nullptr;
|
||||
// Some of these values are useless until the prediction time bug is resolved by oculus.
|
||||
//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<std::chrono::nanoseconds>(now - mLastRenderedFrameTimestamp);
|
||||
mLastRenderedFrameTimestamp = now;
|
||||
mLastRenderedFrame = getFrame(FramePhase::Swap)->mFrameNo;
|
||||
|
||||
//Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": xrPrediction=" << xr->impl().frameState().predictedDisplayTime << ", ourPrediction=" << getFrame(FramePhase::Swap)->mPredictedDisplayTime << ", miss=" << miss << "ms";
|
||||
//Log(Debug::Debug) << "xrPredictionChange=" << (xrPredictionChange / 1000000) << "ms";
|
||||
//Log(Debug::Debug) << "xrPredictionPeriod=" << (mLastPredictedDisplayPeriod / 1000000) << "ms";
|
||||
// Just a quick averaging fps over some time rather than just the instantaneous.
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(now - mStart).count();
|
||||
static int sBaseFrames = 0;
|
||||
if (seconds > 10.f)
|
||||
{
|
||||
Log(Debug::Debug) << "Fps: " << (static_cast<double>(mLastRenderedFrame - sBaseFrames) / seconds);
|
||||
mStart = now;
|
||||
sBaseFrames = mLastRenderedFrame;
|
||||
}
|
||||
|
||||
getFrame(FramePhase::Swap) = nullptr;
|
||||
mFramesInFlight--;
|
||||
}
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
void VRSession::beginPhase(FramePhase phase)
|
||||
{
|
||||
Timer timer("VRSession::advanceFrame");
|
||||
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ")";
|
||||
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ") " << std::this_thread::get_id();
|
||||
|
||||
if (phase != FramePhase::Update)
|
||||
if (getFrame(phase))
|
||||
{
|
||||
Log(Debug::Warning) << "advanceFramePhase called with a frame alreay in the target phase";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (phase == FramePhase::Update)
|
||||
{
|
||||
prepareFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
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");
|
||||
throw std::logic_error("beginPhase called without a frame");
|
||||
getFrame(phase) = std::move(getFrame(previousPhase));
|
||||
}
|
||||
else
|
||||
prepareFrame();
|
||||
|
||||
if (phase == FramePhase::Cull && getFrame(phase)->mShouldRender)
|
||||
|
||||
// TODO: Invokation should depend on earliest render rather than necessarily phase.
|
||||
// Specifically. Without shadows this is fine because nothing is being rendered
|
||||
// during cull or earlier.
|
||||
// Thought: Add an Shadowmapping phase and invoke it from the shadow code
|
||||
// But with shadows rendering occurs during cull and we must do frame sync before those calls.
|
||||
// If you want to pay the FPS toll and play with shadows, change FramePhase::Draw to FramePhase::Cull or enjoy your eyes getting torn apart by jitters.
|
||||
if (phase == FramePhase::Draw && getFrame(phase)->mShouldRender)
|
||||
doFrameSync();
|
||||
}
|
||||
|
||||
void VRSession::doFrameSync()
|
||||
{
|
||||
{
|
||||
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);
|
||||
while (mLastRenderedFrame != mFrames - 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();
|
||||
}
|
||||
|
||||
auto* xr = Environment::get().getManager();
|
||||
Log(Debug::Debug) << mFrames << ": WaitFrame " << std::this_thread::get_id();
|
||||
xr->waitFrame();
|
||||
Log(Debug::Debug) << mFrames << ": BeginFrame " << std::this_thread::get_id();
|
||||
xr->beginFrame();
|
||||
}
|
||||
|
||||
std::unique_ptr<VRSession::VRFrame>& VRSession::getFrame(FramePhase phase)
|
||||
|
@ -183,56 +228,65 @@ std::unique_ptr<VRSession::VRFrame>& VRSession::getFrame(FramePhase phase)
|
|||
|
||||
void VRSession::prepareFrame()
|
||||
{
|
||||
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mFrames++;
|
||||
assert(!mPredrawFrame);
|
||||
|
||||
Timer timer("VRSession::startFrame");
|
||||
auto* xr = Environment::get().getManager();
|
||||
xr->handleEvents();
|
||||
|
||||
|
||||
// Until OpenXR allows us to get a prediction without waiting
|
||||
// we make our own (bad) prediction and call xrWaitFrame when it is more convenient
|
||||
auto frameState = xr->impl().frameState();
|
||||
long long predictedDisplayTime = 0;
|
||||
mFrames++;
|
||||
if (mLastPredictedDisplayTime == 0)
|
||||
// auto predictedDisplayTime = frameState.predictedDisplayTime;
|
||||
// if (predictedDisplayTime == 0)
|
||||
// {
|
||||
// // First time, need to invent a frame time since openxr won't help us without calling waitframe.
|
||||
// predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Predict display time based on real framerate
|
||||
// float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(mLastPredictedDisplayPeriod);
|
||||
// int intervals = std::max((int)std::roundf(intervalsf), 1);
|
||||
// predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * mLastPredictedDisplayPeriod;
|
||||
// }
|
||||
// TODO:
|
||||
//////////////////////// OCULUS BUG
|
||||
//////////////////// Oculus will suddenly start monotonically increasing their predicted display time by precisely 1 second
|
||||
//////////////////// regardless of real time passed, causing predictions to go crazy due to the time difference.
|
||||
//////////////////// Therefore, for the time being, i ignore oculus' predicted display time altogether.
|
||||
long long predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
if (mFrames > 1)
|
||||
{
|
||||
// First time, need to invent a frame time since openxr won't help us without calling waitframe.
|
||||
predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
else if (mFrames > mLastRenderedFrame)
|
||||
{
|
||||
//predictedDisplayTime = mLastPredictedDisplayTime + mLastFrameInterval.count() * (mFrames - mLastRenderedFrame);
|
||||
float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(mLastPredictedDisplayPeriod);
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
int intervals = std::max((int)std::roundf(intervalsf), 1);
|
||||
predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * mLastPredictedDisplayPeriod;
|
||||
float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(frameState.predictedDisplayPeriod);
|
||||
int intervals = std::max((int)std::roundf(intervalsf), 1);
|
||||
predictedDisplayTime = predictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * frameState.predictedDisplayPeriod;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PoseSet predictedPoses{};
|
||||
if (isRunning())
|
||||
{
|
||||
xr->impl().enablePredictions();
|
||||
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * 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;
|
||||
predictedPoses.view[(int)Side::LEFT_HAND].fov = fromXR(hmdViews[(int)Side::LEFT_HAND].fov);
|
||||
predictedPoses.view[(int)Side::RIGHT_HAND].fov = fromXR(hmdViews[(int)Side::RIGHT_HAND].fov);
|
||||
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;
|
||||
|
||||
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();
|
||||
xr->impl().enablePredictions();
|
||||
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * 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;
|
||||
predictedPoses.view[(int)Side::LEFT_HAND].fov = fromXR(hmdViews[(int)Side::LEFT_HAND].fov);
|
||||
predictedPoses.view[(int)Side::RIGHT_HAND].fov = fromXR(hmdViews[(int)Side::RIGHT_HAND].fov);
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
auto& frame = getFrame(FramePhase::Update);
|
||||
frame.reset(new VRFrame);
|
||||
|
@ -240,6 +294,7 @@ void VRSession::prepareFrame()
|
|||
frame->mFrameNo = mFrames;
|
||||
frame->mPredictedPoses = predictedPoses;
|
||||
frame->mShouldRender = isRunning();
|
||||
mFramesInFlight++;
|
||||
}
|
||||
|
||||
const PoseSet& VRSession::predictedPoses(FramePhase phase)
|
||||
|
@ -298,8 +353,9 @@ void VRSession::movementAngles(float& yaw, float& pitch)
|
|||
// 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?
|
||||
if (!getFrame(FramePhase::Update))
|
||||
beginPhase(FramePhase::Update);
|
||||
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);
|
||||
|
||||
|
|
|
@ -54,6 +54,9 @@ public:
|
|||
//! Starts a new frame
|
||||
void prepareFrame();
|
||||
|
||||
//! Synchronize with openxr
|
||||
void doFrameSync();
|
||||
|
||||
//! Angles to be used for overriding movement direction
|
||||
void movementAngles(float& yaw, float& pitch);
|
||||
|
||||
|
@ -68,6 +71,7 @@ public:
|
|||
osg::Matrix viewMatrix(FramePhase phase, Side side);
|
||||
osg::Matrix projectionMatrix(FramePhase phase, Side side);
|
||||
|
||||
int mFramesInFlight{ 0 };
|
||||
std::array<std::unique_ptr<VRFrame>, (int)FramePhase::NumPhases> mFrame{ nullptr };
|
||||
|
||||
std::mutex mMutex{};
|
||||
|
|
|
@ -607,6 +607,7 @@ namespace MWWorld
|
|||
|
||||
void World::useDeathCamera()
|
||||
{
|
||||
#ifndef USE_OPENXR
|
||||
if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )
|
||||
{
|
||||
mRendering->getCamera()->togglePreviewMode(false);
|
||||
|
@ -614,6 +615,7 @@ namespace MWWorld
|
|||
}
|
||||
if(mRendering->getCamera()->isFirstPerson())
|
||||
mRendering->getCamera()->toggleViewMode(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
MWWorld::Player& World::getPlayer()
|
||||
|
|
|
@ -25,5 +25,6 @@
|
|||
<Layer name="LoadingScreen" overlapped="false" pick="true"/>
|
||||
<Layer name="MessageBox" overlapped="false" pick="true"/>
|
||||
<Layer name="InputBlocker" overlapped="false" pick="true"/>
|
||||
<Layer name="VideoPlayer" overlapped="false" pick="true"/>
|
||||
<Layer name="Pointer" overlapped="false" pick="false"/>
|
||||
</MyGUI>
|
Loading…
Reference in a new issue