1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-15 16:09:40 +00:00

Simplified render timing by separating rendering from the openxr swapchain, and instead blitting and submitting separately

This commit is contained in:
Mads Buvik Sandvei 2020-01-26 20:06:47 +01:00
parent 51125d4f3e
commit 951879240c
17 changed files with 449 additions and 547 deletions

View file

@ -731,12 +731,12 @@ void OMW::Engine::go()
mXRViewer->addChild(root); mXRViewer->addChild(root);
mViewer->setSceneData(mXRViewer); mViewer->setSceneData(mXRViewer);
#ifndef _NDEBUG #ifndef _NDEBUG
mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::STAGE)); //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::STAGE));
mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::VIEW)); //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::HEAD, MWVR::TrackedSpace::VIEW));
mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::STAGE)); //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::STAGE));
mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::VIEW)); //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::LEFT_HAND, MWVR::TrackedSpace::VIEW));
mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::STAGE)); //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::STAGE));
mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::VIEW)); //mXR->addPoseUpdateCallback(new MWVR::PoseLogger(MWVR::TrackedLimb::RIGHT_HAND, MWVR::TrackedSpace::VIEW));
#endif #endif
#endif #endif

View file

@ -515,7 +515,7 @@ namespace MWVR
OpenXRInputManagerImpl::updateHandTracking() OpenXRInputManagerImpl::updateHandTracking()
{ {
for (auto hand : { LEFT_HAND, RIGHT_HAND }) { for (auto hand : { LEFT_HAND, RIGHT_HAND }) {
CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().mFrameState.predictedDisplayTime, &mHandSpaceLocation[hand])); CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().predictedDisplayTime(OpenXRFrameIndexer::instance().updateIndex()), &mHandSpaceLocation[hand]));
} }
} }

View file

@ -67,10 +67,10 @@ namespace MWVR
return impl().waitFrame(); return impl().waitFrame();
} }
void OpenXRManager::beginFrame(long long frameIndex) void OpenXRManager::beginFrame()
{ {
if (realized()) if (realized())
return impl().beginFrame(frameIndex); return impl().beginFrame();
} }
void OpenXRManager::endFrame() void OpenXRManager::endFrame()
@ -85,12 +85,6 @@ namespace MWVR
return impl().updateControls(); return impl().updateControls();
} }
void OpenXRManager::updatePoses()
{
if (realized())
return impl().updatePoses();
}
void void
OpenXRManager::realize( OpenXRManager::realize(
osg::GraphicsContext* gc) osg::GraphicsContext* gc)
@ -111,14 +105,6 @@ namespace MWVR
} }
} }
void OpenXRManager::addPoseUpdateCallback(
osg::ref_ptr<PoseUpdateCallback> cb)
{
if (realized())
return impl().addPoseUpdateCallback(cb);
}
int OpenXRManager::eyes() int OpenXRManager::eyes()
{ {
if (realized()) if (realized())
@ -146,50 +132,58 @@ namespace MWVR
} }
void OpenXRManager::viewerBarrier() //#ifndef _NDEBUG
// void PoseLogger::operator()(MWVR::Pose pose)
// {
// const char* limb = nullptr;
// const char* space = nullptr;
// switch (mLimb)
// {
// case TrackedLimb::HEAD:
// limb = "HEAD"; break;
// case TrackedLimb::LEFT_HAND:
// limb = "LEFT_HAND"; break;
// case TrackedLimb::RIGHT_HAND:
// limb = "RIGHT_HAND"; break;
// }
// switch (mSpace)
// {
// case TrackedSpace::STAGE:
// space = "STAGE"; break;
// case TrackedSpace::VIEW:
// space = "VIEW"; break;
// }
//
// //TODO: Use a different output to avoid spamming the debug log when enabled
// Log(Debug::Verbose) << space << "." << limb << ": " << pose;
// }
//#endif
static OpenXRFrameIndexer g_OpenXRFrameIndexer;
OpenXRFrameIndexer& OpenXRFrameIndexer::instance()
{ {
if (realized()) return g_OpenXRFrameIndexer;
return impl().viewerBarrier();
} }
void OpenXRManager::registerToBarrier() int64_t OpenXRFrameIndexer::advanceUpdateIndex()
{ {
if (realized()) std::unique_lock<std::mutex> lock(mMutex);
return impl().registerToBarrier(); mUpdateIndex++;
Log(Debug::Verbose) << "Advancing update index to " << mUpdateIndex;
assert(mUpdateIndex > mRenderIndex);
return mUpdateIndex;
} }
void OpenXRManager::unregisterFromBarrier() int64_t OpenXRFrameIndexer::advanceRenderIndex()
{ {
if (realized()) std::unique_lock<std::mutex> lock(mMutex);
return impl().unregisterFromBarrier(); mRenderIndex++;
Log(Debug::Verbose) << "Advancing frame index to " << mRenderIndex;
if (!(mUpdateIndex >= mRenderIndex))
mUpdateIndex = mRenderIndex - 1;
return mRenderIndex;
} }
#ifndef _NDEBUG
void PoseLogger::operator()(MWVR::Pose pose)
{
const char* limb = nullptr;
const char* space = nullptr;
switch (mLimb)
{
case TrackedLimb::HEAD:
limb = "HEAD"; break;
case TrackedLimb::LEFT_HAND:
limb = "LEFT_HAND"; break;
case TrackedLimb::RIGHT_HAND:
limb = "RIGHT_HAND"; break;
}
switch (mSpace)
{
case TrackedSpace::STAGE:
space = "STAGE"; break;
case TrackedSpace::VIEW:
space = "VIEW"; break;
}
//TODO: Use a different output to avoid spamming the debug log when enabled
Log(Debug::Verbose) << space << "." << limb << ": " << pose;
}
#endif
} }
std::ostream& operator <<( std::ostream& operator <<(

View file

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp> #include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <osg/Camera> #include <osg/Camera>
@ -17,6 +18,23 @@ struct XrSwapchainSubImage;
namespace MWVR namespace MWVR
{ {
struct Timer
{
Timer(std::string name) : mName(name)
{
mBegin = std::chrono::steady_clock::now();
}
~Timer()
{
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::duration<double>>(end - mBegin);
Log(Debug::Verbose) << mName << "Elapsed: " << elapsed.count() << "s";
}
std::chrono::steady_clock::time_point mBegin;
std::string mName;
};
//! Represents the pose of a limb in VR space. //! Represents the pose of a limb in VR space.
struct Pose struct Pose
{ {
@ -43,25 +61,32 @@ namespace MWVR
VIEW //!< Track limb in the VR view space. Meaning a space with the head as origin and orientation. VIEW //!< Track limb in the VR view space. Meaning a space with the head as origin and orientation.
}; };
struct OpenXRFrameIndexer
{
static OpenXRFrameIndexer& instance();
OpenXRFrameIndexer() = default;
~OpenXRFrameIndexer() = default;
int64_t advanceUpdateIndex();
int64_t renderIndex() { return mRenderIndex; }
int64_t advanceRenderIndex();
int64_t updateIndex() { return mUpdateIndex; }
std::mutex mMutex{};
int64_t mUpdateIndex{ -1 };
int64_t mRenderIndex{ -1 };
};
// Use the pimpl pattern to avoid cluttering the namespace with openxr dependencies. // Use the pimpl pattern to avoid cluttering the namespace with openxr dependencies.
class OpenXRManagerImpl; class OpenXRManagerImpl;
class OpenXRManager : public osg::Referenced class OpenXRManager : public osg::Referenced
{ {
public:
class PoseUpdateCallback: public osg::Referenced
{
public:
PoseUpdateCallback(TrackedLimb limb, TrackedSpace space)
: mLimb(limb), mSpace(space){}
virtual void operator()(MWVR::Pose pose) = 0;
TrackedLimb mLimb;
TrackedSpace mSpace;
};
public: public:
class RealizeOperation : public osg::GraphicsOperation class RealizeOperation : public osg::GraphicsOperation
{ {
@ -96,24 +121,14 @@ namespace MWVR
void handleEvents(); void handleEvents();
void waitFrame(); void waitFrame();
void beginFrame(long long frameIndex); void beginFrame();
void endFrame(); void endFrame();
void updateControls(); void updateControls();
void updatePoses();
void realize(osg::GraphicsContext* gc); void realize(osg::GraphicsContext* gc);
void addPoseUpdateCallback(osg::ref_ptr<PoseUpdateCallback> cb);
int eyes(); int eyes();
//! A barrier used internally to ensure all views have released their frames before endFrame can complete.
void viewerBarrier();
//! Increments the target viewer counter of the barrier
void registerToBarrier();
//! Decrements the target viewer counter of the barrier
void unregisterFromBarrier();
OpenXRManagerImpl& impl() { return *mPrivate; } OpenXRManagerImpl& impl() { return *mPrivate; }
private: private:
@ -121,16 +136,6 @@ namespace MWVR
std::mutex mMutex; std::mutex mMutex;
using lock_guard = std::lock_guard<std::mutex>; using lock_guard = std::lock_guard<std::mutex>;
}; };
#ifndef _NDEBUG
class PoseLogger : public OpenXRManager::PoseUpdateCallback
{
public:
PoseLogger(TrackedLimb limb, TrackedSpace space)
: OpenXRManager::PoseUpdateCallback(limb, space) {};
void operator()(MWVR::Pose pose) override;
};
#endif
} }
std::ostream& operator <<(std::ostream& os, const MWVR::Pose& pose); std::ostream& operator <<(std::ostream& os, const MWVR::Pose& pose);

View file

@ -99,7 +99,6 @@ namespace MWVR
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO }; XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &mGraphicsBinding; createInfo.next = &mGraphicsBinding;
createInfo.systemId = mSystemId; createInfo.systemId = mSystemId;
createInfo.createFlags;
CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession)); CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession));
assert(mSession); assert(mSession);
} }
@ -117,13 +116,6 @@ namespace MWVR
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage)); CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage));
} }
{ // Set up layers
//mLayer.space = mReferenceSpaceStage;
//mLayer.viewCount = (uint32_t)mProjectionLayerViews.size();
//mLayer.views = mProjectionLayerViews.data();
}
{ // Read and log graphics properties for the swapchain { // Read and log graphics properties for the swapchain
xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties); xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties);
@ -142,32 +134,32 @@ namespace MWVR
uint32_t viewCount = 0; uint32_t viewCount = 0;
CHECK_XRCMD(xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewConfigType, 2, &viewCount, mConfigViews.data())); CHECK_XRCMD(xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewConfigType, 2, &viewCount, mConfigViews.data()));
// OpenXR gives me crazy bananas high resolutions. Likely an oculus bug.
mConfigViews[0].recommendedImageRectHeight = 1200;
mConfigViews[1].recommendedImageRectHeight = 1200;
mConfigViews[0].recommendedImageRectWidth = 1080;
mConfigViews[1].recommendedImageRectWidth = 1080;
if (viewCount != 2) if (viewCount != 2)
{ {
std::stringstream ss; std::stringstream ss;
ss << "xrEnumerateViewConfigurationViews returned " << viewCount << " views"; ss << "xrEnumerateViewConfigurationViews returned " << viewCount << " views";
Log(Debug::Verbose) << ss.str(); Log(Debug::Verbose) << ss.str();
} }
// TODO: This, including the projection layer views, should be moved to openxrviewer
//for (unsigned i = 0; i < 2; i++)
//{
// mEyes[i].reset(new OpenXRView(this, mConfigViews[i]));
// mProjectionLayerViews[i].subImage.swapchain = mEyes[i]->mSwapchain;
// mProjectionLayerViews[i].subImage.imageRect.offset = { 0, 0 };
// mProjectionLayerViews[i].subImage.imageRect.extent = { mEyes[i]->mWidth, mEyes[i]->mHeight };
//}
} }
} }
inline XrResult CheckXrResult(XrResult res, const char* originator, const char* sourceLocation) { inline XrResult CheckXrResult(XrResult res, const char* originator, const char* sourceLocation) {
if (XR_FAILED(res)) { if (XR_FAILED(res)) {
Log(Debug::Error) << sourceLocation << ": OpenXR[" << to_string(res) << "]: " << originator; std::stringstream ss;
ss << sourceLocation << ": OpenXR[" << to_string(res) << "]: " << originator;
Log(Debug::Error) << ss.str();
throw std::runtime_error(ss.str().c_str());
} }
else else
{ {
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator; // Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator;
} }
return res; return res;
@ -265,123 +257,59 @@ namespace MWVR
void void
OpenXRManagerImpl::waitFrame() OpenXRManagerImpl::waitFrame()
{ {
if (!mSessionRunning) Timer timer("waitFrame()");
return;
// In some implementations xrWaitFrame might not return immediately when it should.
// So i let it wait in a separate thread. xrBeginFrame() should wait on xrWaitFrame()
// and xrWaitFrame() doesn't happen again until xrEndFrame() so synchronization is not necessary.
std::thread([this]() {
static std::mutex waitFrameMutex;
std::unique_lock<std::mutex> lock(waitFrameMutex);
if (!mSessionRunning)
return;
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
XrFrameState frameState{ XR_TYPE_FRAME_STATE };
CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState));
mTimeKeeper.progressToNextFrame(frameState);
}).detach();
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
XrFrameState frameState{ XR_TYPE_FRAME_STATE };
CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState));
mFrameState = frameState;
} }
void void
OpenXRManagerImpl::beginFrame(long long frameIndex) OpenXRManagerImpl::beginFrame()
{ {
Log(Debug::Verbose) << "frameIndex = " << frameIndex; Timer timer("beginFrame");
if (!mSessionRunning) XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
return; CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo));
std::unique_lock<std::mutex> lock(mFrameStatusMutex);
// We need to wait for the frame to become idle or ready
// (There is no guarantee osg won't get us here before endFrame() returns)
while (mFrameStatus == OPENXR_FRAME_STATUS_ENDING || mFrameIndex < frameIndex)
mFrameStatusSignal.wait(lock);
if (mFrameStatus == OPENXR_FRAME_STATUS_IDLE)
{
Log(Debug::Verbose) << "beginFrame()";
handleEvents();
waitFrame();
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo));
updateControls();
updatePoses();
mFrameStatus = OPENXR_FRAME_STATUS_READY;
}
assert(mFrameStatus == OPENXR_FRAME_STATUS_READY);
}
void OpenXRManagerImpl::viewerBarrier()
{
std::unique_lock<std::mutex> lock(mBarrierMutex);
mBarrier++;
Log(Debug::Verbose) << "mBarrier=" << mBarrier << ", tid=" << std::this_thread::get_id();
if (mBarrier == mNBarrier)
{
std::unique_lock<std::mutex> lock(mFrameStatusMutex);
mFrameStatus = OPENXR_FRAME_STATUS_ENDING;
mFrameStatusSignal.notify_all();
//mBarrierSignal.notify_all();
mBarrier = 0;
}
//else
//{
// mBarrierSignal.wait(lock, [this]() { return mBarrier == mNBarrier; });
//}
}
void OpenXRManagerImpl::registerToBarrier()
{
std::unique_lock<std::mutex> lock(mBarrierMutex);
mNBarrier++;
}
void OpenXRManagerImpl::unregisterFromBarrier()
{
std::unique_lock<std::mutex> lock(mBarrierMutex);
mNBarrier--;
assert(mNBarrier >= 0);
} }
void void
OpenXRManagerImpl::endFrame() OpenXRManagerImpl::endFrame()
{ {
Timer timer("endFrame()");
if (!mSessionRunning) if (!mSessionRunning)
return; return;
std::unique_lock<std::mutex> lock(mFrameStatusMutex);
while(mFrameStatus != OPENXR_FRAME_STATUS_ENDING)
mFrameStatusSignal.wait(lock);
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO }; XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
frameEndInfo.displayTime = mFrameState.predictedDisplayTime; frameEndInfo.displayTime = mTimeKeeper.predictedDisplayTime(OpenXRFrameIndexer::instance().renderIndex());
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode; frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
//frameEndInfo.layerCount = (uint32_t)1;
//frameEndInfo.layers = &mLayer_p;
frameEndInfo.layerCount = mLayerStack.layerCount(); frameEndInfo.layerCount = mLayerStack.layerCount();
frameEndInfo.layers = mLayerStack.layerHeaders(); frameEndInfo.layers = mLayerStack.layerHeaders();
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo)); CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
mFrameStatus = OPENXR_FRAME_STATUS_IDLE;
mFrameIndex++;
mFrameStatusSignal.notify_all();
} }
std::array<XrView, 2> OpenXRManagerImpl::getStageViews() std::array<XrView, 2>
OpenXRManagerImpl::getPredictedViews(
int64_t frameIndex,
TrackedSpace space)
{ {
// Get the pose of each eye in the "stage" reference space.
// TODO: I likely won't ever use this, since it is only useful if the game world and the
// stage space have matching orientation, which is extremely unlikely. Instead we have
// to keep track of the head pose separately and move our world position based on progressive
// changes.
// In more detail:
// When we apply yaw to the game world to allow free rotation of the character, the orientation
// of the stage space and our world space deviates which breaks free movement.
//
// If we align the orientations by yawing the head pose, that yaw will happen around the origin
// of the stage space rather than the character, which will not be comfortable (or make any sense) to the player.
//
// If we align the orientations by yawing the view poses, the yaw will happen around the character
// as intended, but physically walking will move the player in the wrong direction.
//
// The solution that solves both problems is to yaw the view pose *and* progressively track changes in head pose
// in the stage space and yaw that change before adding it to our separately tracked pose.
std::array<XrView, 2> views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} }; std::array<XrView, 2> views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} };
XrViewState viewState{ XR_TYPE_VIEW_STATE }; XrViewState viewState{ XR_TYPE_VIEW_STATE };
@ -389,34 +317,22 @@ namespace MWVR
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
viewLocateInfo.viewConfigurationType = mViewConfigType; viewLocateInfo.viewConfigurationType = mViewConfigType;
viewLocateInfo.displayTime = mFrameState.predictedDisplayTime; viewLocateInfo.displayTime = mTimeKeeper.predictedDisplayTime(frameIndex);
switch (space)
viewLocateInfo.space = mReferenceSpaceStage; {
case TrackedSpace::STAGE:
viewLocateInfo.space = mReferenceSpaceStage;
break;
case TrackedSpace::VIEW:
viewLocateInfo.space = mReferenceSpaceView;
break;
}
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data())); CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data()));
return views; return views;
} }
std::array<XrView, 2> OpenXRManagerImpl::getHmdViews() MWVR::Pose OpenXRManagerImpl::getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space)
{
// Eye poses relative to the HMD rarely change. But they might
// if the user reconfigures his or her hmd during runtime, so we
// re-read this every frame anyway.
std::array<XrView, 2> views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} };
XrViewState viewState{ XR_TYPE_VIEW_STATE };
uint32_t viewCount = 2;
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
viewLocateInfo.viewConfigurationType = mViewConfigType;
viewLocateInfo.displayTime = mFrameState.predictedDisplayTime;
viewLocateInfo.space = mReferenceSpaceView;
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data()));
return views;
}
MWVR::Pose OpenXRManagerImpl::getLimbPose(TrackedLimb limb, TrackedSpace space)
{ {
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION }; XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY }; XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
@ -444,7 +360,7 @@ namespace MWVR
referenceSpace = mReferenceSpaceView; referenceSpace = mReferenceSpaceView;
break; break;
} }
CHECK_XRCMD(xrLocateSpace(limbSpace, referenceSpace, mFrameState.predictedDisplayTime, &location)); CHECK_XRCMD(xrLocateSpace(limbSpace, referenceSpace, mTimeKeeper.predictedDisplayTime(frameIndex), &location));
if (!(velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT)) if (!(velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT))
Log(Debug::Warning) << "Unable to acquire linear velocity"; Log(Debug::Warning) << "Unable to acquire linear velocity";
return MWVR::Pose{ return MWVR::Pose{
@ -503,12 +419,13 @@ namespace MWVR
switch (newState) switch (newState)
{ {
case XR_SESSION_STATE_READY: case XR_SESSION_STATE_READY:
case XR_SESSION_STATE_IDLE: //case XR_SESSION_STATE_IDLE:
{ {
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO }; XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
beginInfo.primaryViewConfigurationType = mViewConfigType; beginInfo.primaryViewConfigurationType = mViewConfigType;
CHECK_XRCMD(xrBeginSession(mSession, &beginInfo)); CHECK_XRCMD(xrBeginSession(mSession, &beginInfo));
mSessionRunning = true; mSessionRunning = true;
waitFrame();
break; break;
} }
case XR_SESSION_STATE_STOPPING: case XR_SESSION_STATE_STOPPING:
@ -522,41 +439,12 @@ namespace MWVR
} }
} }
void OpenXRManagerImpl::updatePoses() XrTime OpenXRManagerImpl::predictedDisplayTime(int64_t frameIndex)
{ {
//auto oldHeadTrackedPose = mHeadTrackedPose;
//auto oldLefthandPose = mLeftHandTrackedPose;
//auto oldRightHandPose = mRightHandTrackedPose;
//mHeadTrackedPose = getLimbPose(TrackedLimb::HEAD); return mTimeKeeper.predictedDisplayTime(frameIndex);
//mLeftHandTrackedPose = getLimbPose(TrackedLimb::LEFT_HAND);
//mRightHandTrackedPose = getLimbPose(TrackedLimb::RIGHT_HAND);
//for (auto& cb : mPoseUpdateCallbacks)
//{
// switch (cb->mLimb)
// {
// case TrackedLimb::HEAD:
// (*cb)(mHeadTrackedPose); break;
// case TrackedLimb::LEFT_HAND:
// (*cb)(mLeftHandTrackedPose); break;
// case TrackedLimb::RIGHT_HAND:
// (*cb)(mRightHandTrackedPose); break;
// }
//}
for (auto& cb : mPoseUpdateCallbacks)
(*cb)(getLimbPose(cb->mLimb, cb->mSpace));
} }
void OpenXRManagerImpl::addPoseUpdateCallback(
osg::ref_ptr<PoseUpdateCallback> cb)
{
mPoseUpdateCallbacks.push_back(cb);
}
const XrEventDataBaseHeader* const XrEventDataBaseHeader*
OpenXRManagerImpl::nextEvent() OpenXRManagerImpl::nextEvent()
{ {
@ -587,6 +475,62 @@ namespace MWVR
{ {
return XrPosef{ osg::toXR(pose.orientation), osg::toXR(pose.position) }; return XrPosef{ osg::toXR(pose.orientation), osg::toXR(pose.position) };
} }
XrTime OpenXRTimeKeeper::predictedDisplayTime(int64_t frameIndex)
{
std::unique_lock<std::mutex> lock(mMutex);
auto prediction = mPredictedFrameTime;
auto predictedPeriod = mPredictedPeriod;
auto futureFrames = frameIndex - OpenXRFrameIndexer::instance().renderIndex();
prediction += ( 0 + futureFrames) * predictedPeriod;
Log(Debug::Verbose) << "Predicted: displayTime[" << futureFrames << "]=" << prediction;
return prediction;
}
void OpenXRTimeKeeper::progressToNextFrame(XrFrameState frameState)
{
std::unique_lock<std::mutex> lock(mMutex);
OpenXRFrameIndexer::instance().advanceRenderIndex();
//XrDuration realPeriod = frameState.predictedDisplayPeriod;
//if(mFrameState.predictedDisplayTime != 0)
// realPeriod = frameState.predictedDisplayTime - mFrameState.predictedDisplayTime;
auto now = clock::now();
auto nanoseconds_elapsed = std::chrono::duration_cast<nanoseconds>(now - mLastFrame);
XrDuration realPeriod = nanoseconds_elapsed.count();
mFrameState = frameState;
mPredictedFrameTime = mFrameState.predictedDisplayTime;
mPredictedPeriod = mFrameState.predictedDisplayPeriod;
// Real fps is lower than expected fps
// Adjust predictions
// (Really wish OpenXR would handle this!)
if (realPeriod > mFrameState.predictedDisplayPeriod)
{
// predictedDisplayTime refers to the midpoint of the display period
// The upjustment must therefore only be half the magnitude
// mPredictedFrameTime += (realPeriod - mFrameState.predictedDisplayPeriod);
// mPredictedPeriod = realPeriod;
}
seconds elapsed = std::chrono::duration_cast<seconds>(now - mLastFrame);
std::swap(now, mLastFrame);
double fps = 1. / elapsed.count();
mFps = mFps * 0.8 + 0.2 * fps;
Log(Debug::Verbose) << "Render progressed to next frame: elapsed=" << elapsed.count() << ", fps=" << fps << ", ImplementationPeriod=" << mFrameState.predictedDisplayPeriod << " realPeriod=" << realPeriod;
Log(Debug::Verbose) << "Render progressed to next frame: predictedDisplayTime=" << mFrameState.predictedDisplayTime << ", mPredictedFrameTime=" << mPredictedFrameTime << ", mFps=" << mFps;
}
} }
namespace osg namespace osg

View file

@ -22,6 +22,7 @@
#include <map> #include <map>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <chrono>
namespace osg { namespace osg {
Vec3 fromXR(XrVector3f); Vec3 fromXR(XrVector3f);
@ -43,10 +44,33 @@ namespace MWVR
MWVR::Pose fromXR(XrPosef pose); MWVR::Pose fromXR(XrPosef pose);
XrPosef toXR(MWVR::Pose pose); XrPosef toXR(MWVR::Pose pose);
struct OpenXRTimeKeeper
{
using seconds = std::chrono::duration<double>;
using nanoseconds = std::chrono::nanoseconds;
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
OpenXRTimeKeeper() = default;
~OpenXRTimeKeeper() = default;
XrTime predictedDisplayTime(int64_t frameIndex);
void progressToNextFrame(XrFrameState frameState);
private:
XrFrameState mFrameState{ XR_TYPE_FRAME_STATE };
std::mutex mMutex{};
double mFps{ 0. };
time_point mLastFrame = clock::now();
XrTime mPredictedFrameTime{ 0 };
XrDuration mPredictedPeriod{ 0 };
};
struct OpenXRManagerImpl struct OpenXRManagerImpl
{ {
using PoseUpdateCallback = OpenXRManager::PoseUpdateCallback;
OpenXRManagerImpl(void); OpenXRManagerImpl(void);
~OpenXRManagerImpl(void); ~OpenXRManagerImpl(void);
@ -56,17 +80,15 @@ namespace MWVR
const XrEventDataBaseHeader* nextEvent(); const XrEventDataBaseHeader* nextEvent();
void waitFrame(); void waitFrame();
void beginFrame(long long frameIndex); void beginFrame();
void endFrame(); void endFrame();
std::array<XrView, 2> getStageViews(); std::array<XrView, 2> getPredictedViews(int64_t frameIndex, TrackedSpace mSpace);
std::array<XrView, 2> getHmdViews(); MWVR::Pose getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space);
MWVR::Pose getLimbPose(TrackedLimb limb, TrackedSpace space);
int eyes(); int eyes();
void handleEvents(); void handleEvents();
void updateControls(); void updateControls();
void updatePoses();
void addPoseUpdateCallback(osg::ref_ptr<PoseUpdateCallback> cb);
void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent); void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent);
XrTime predictedDisplayTime(int64_t frameIndex);
bool initialized = false; bool initialized = false;
long long mFrameIndex = 0; long long mFrameIndex = 0;
@ -83,38 +105,11 @@ namespace MWVR
XrSpace mReferenceSpaceView = XR_NULL_HANDLE; XrSpace mReferenceSpaceView = XR_NULL_HANDLE;
XrSpace mReferenceSpaceStage = XR_NULL_HANDLE; XrSpace mReferenceSpaceStage = XR_NULL_HANDLE;
XrEventDataBuffer mEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER }; XrEventDataBuffer mEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER };
//XrCompositionLayerProjection mLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION }; OpenXRTimeKeeper mTimeKeeper{};
//XrCompositionLayerBaseHeader const* mLayer_p = reinterpret_cast<XrCompositionLayerBaseHeader*>(&mLayer);
//std::array<XrCompositionLayerProjectionView, 2> mProjectionLayerViews{ { {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW} } };
OpenXRLayerStack mLayerStack{}; OpenXRLayerStack mLayerStack{};
XrFrameState mFrameState{ XR_TYPE_FRAME_STATE };
XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN; XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN;
bool mSessionRunning = false; bool mSessionRunning = false;
//osg::Pose mHeadTrackedPose{};
//osg::Pose mLeftHandTrackedPose{};
//osg::Pose mRightHandTrackedPose{};
std::vector< osg::ref_ptr<PoseUpdateCallback> > mPoseUpdateCallbacks{};
std::mutex mEventMutex{}; std::mutex mEventMutex{};
enum {
OPENXR_FRAME_STATUS_IDLE, //!< Frame is ready for initialization
OPENXR_FRAME_STATUS_READY, //!< Frame has been initialized and swapchains may be acquired
OPENXR_FRAME_STATUS_ENDING //!< All swapchains have been releazed, frame ready for presentation
} mFrameStatus{ OPENXR_FRAME_STATUS_IDLE };
std::condition_variable mFrameStatusSignal{};
std::mutex mFrameStatusMutex;
int mBarrier{ 0 };
int mNBarrier{ 0 };
std::condition_variable mBarrierSignal{};
std::mutex mBarrierMutex;
void viewerBarrier();
void registerToBarrier();
void unregisterFromBarrier();
}; };
} }

View file

@ -6,7 +6,7 @@ namespace MWVR
{ {
OpenXRMenu::OpenXRMenu(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, const std::string& title, int width, int height, osg::Vec2 extent_meters) OpenXRMenu::OpenXRMenu(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, const std::string& title, int width, int height, osg::Vec2 extent_meters)
: OpenXRView(XR) : OpenXRView(XR, title)
, mTitle(title) , mTitle(title)
{ {
setWidth(width); setWidth(width);
@ -27,7 +27,7 @@ namespace MWVR
// Orientation needs a norm of 1 to be accepted by OpenXR, so we default it to 0,0,0,1 // Orientation needs a norm of 1 to be accepted by OpenXR, so we default it to 0,0,0,1
mLayer->pose.orientation.w = 1.f; mLayer->pose.orientation.w = 1.f;
updatePosition(); //updatePosition();
} }
OpenXRMenu::~OpenXRMenu() OpenXRMenu::~OpenXRMenu()
@ -47,10 +47,12 @@ namespace MWVR
return; return;
if (!mXR->sessionRunning()) if (!mXR->sessionRunning())
return; return;
if (OpenXRFrameIndexer::instance().updateIndex() == 0)
return;
// Menus are position one meter in front of the player, facing the player. // Menus are position one meter in front of the player, facing the player.
// Go via osg since OpenXR doesn't distribute a linear maths library // Go via osg since OpenXR doesn't distribute a linear maths library
auto pose = mXR->impl().getLimbPose(TrackedLimb::HEAD, TrackedSpace::STAGE); auto pose = mXR->impl().getPredictedLimbPose(OpenXRFrameIndexer::instance().updateIndex(), TrackedLimb::HEAD, TrackedSpace::STAGE);
pose.position += pose.orientation * osg::Vec3(0, 0, -1); pose.position += pose.orientation * osg::Vec3(0, 0, -1);
mLayer->pose.position = osg::toXR(pose.position); mLayer->pose.position = osg::toXR(pose.position);
mLayer->pose.orientation = osg::toXR(-pose.orientation); mLayer->pose.orientation = osg::toXR(-pose.orientation);

View file

@ -26,21 +26,20 @@ namespace MWVR {
OpenXRSwapchainImpl(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config); OpenXRSwapchainImpl(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
~OpenXRSwapchainImpl(); ~OpenXRSwapchainImpl();
osg::ref_ptr<OpenXRTextureBuffer> prepareNextSwapchainImage();
void releaseSwapchainImage();
void beginFrame(osg::GraphicsContext* gc); void beginFrame(osg::GraphicsContext* gc);
void endFrame(osg::GraphicsContext* gc); void endFrame(osg::GraphicsContext* gc);
osg::ref_ptr<OpenXRManager> mXR; osg::ref_ptr<OpenXRManager> mXR;
XrSwapchain mSwapchain = XR_NULL_HANDLE; XrSwapchain mSwapchain = XR_NULL_HANDLE;
std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{}; std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
std::vector<osg::ref_ptr<OpenXRTextureBuffer> > mTextureBuffers{}; //std::vector<osg::ref_ptr<OpenXRTextureBuffer> > mTextureBuffers{};
XrSwapchainSubImage mSubImage{}; XrSwapchainSubImage mSubImage{};
int32_t mWidth = -1; int32_t mWidth = -1;
int32_t mHeight = -1; int32_t mHeight = -1;
int32_t mSamples = -1; int32_t mSamples = -1;
int64_t mSwapchainColorFormat = -1; int64_t mSwapchainColorFormat = -1;
OpenXRTextureBuffer* mCurrentBuffer = nullptr; uint32_t mFBO = 0;
OpenXRTextureBuffer* mRenderBuffer = nullptr;
}; };
OpenXRSwapchainImpl::OpenXRSwapchainImpl( OpenXRSwapchainImpl::OpenXRSwapchainImpl(
@ -99,8 +98,9 @@ namespace MWVR {
mSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR }); mSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mSwapchainImageBuffers.data()))); CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mSwapchainImageBuffers.data())));
for (const auto& swapchainImage : mSwapchainImageBuffers) //for (const auto& swapchainImage : mSwapchainImageBuffers)
mTextureBuffers.push_back(new OpenXRTextureBuffer(state, swapchainImage.image, mWidth, mHeight, 0)); // mTextureBuffers.push_back(new OpenXRTextureBuffer(state, swapchainImage.image, mWidth, mHeight, 0));
mRenderBuffer = new OpenXRTextureBuffer(state, mWidth, mHeight, 0);
mSubImage.swapchain = mSwapchain; mSubImage.swapchain = mSwapchain;
mSubImage.imageRect.offset = { 0, 0 }; mSubImage.imageRect.offset = { 0, 0 };
@ -113,11 +113,18 @@ namespace MWVR {
CHECK_XRCMD(xrDestroySwapchain(mSwapchain)); CHECK_XRCMD(xrDestroySwapchain(mSwapchain));
} }
osg::ref_ptr<OpenXRTextureBuffer> void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc)
OpenXRSwapchainImpl::prepareNextSwapchainImage()
{ {
mRenderBuffer->beginFrame(gc);
}
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc)
{
Timer timer("Swapchain::endFrame");
// Blit frame to swapchain
if (!mXR->sessionRunning()) if (!mXR->sessionRunning())
return nullptr; return;
XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
uint32_t swapchainImageIndex = 0; uint32_t swapchainImageIndex = 0;
@ -127,32 +134,11 @@ namespace MWVR {
waitInfo.timeout = XR_INFINITE_DURATION; waitInfo.timeout = XR_INFINITE_DURATION;
CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo)); CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo));
return mTextureBuffers[swapchainImageIndex]; mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[swapchainImageIndex].image);
}
void
OpenXRSwapchainImpl::releaseSwapchainImage()
{
if (!mXR->sessionRunning())
return;
XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)); CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo));
//mCurrentBuffer = nullptr;
}
void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc)
{
mCurrentBuffer = prepareNextSwapchainImage();
if (mCurrentBuffer)
mCurrentBuffer->beginFrame(gc);
}
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc)
{
if (mCurrentBuffer)
mCurrentBuffer->endFrame(gc);
releaseSwapchainImage();
} }
OpenXRSwapchain::OpenXRSwapchain( OpenXRSwapchain::OpenXRSwapchain(
@ -165,16 +151,6 @@ namespace MWVR {
{ {
} }
osg::ref_ptr<OpenXRTextureBuffer> OpenXRSwapchain::prepareNextSwapchainImage()
{
return impl().prepareNextSwapchainImage();
}
void OpenXRSwapchain::releaseSwapchainImage()
{
impl().releaseSwapchainImage();
}
void OpenXRSwapchain::beginFrame(osg::GraphicsContext* gc) void OpenXRSwapchain::beginFrame(osg::GraphicsContext* gc)
{ {
impl().beginFrame(gc); impl().beginFrame(gc);
@ -206,8 +182,8 @@ namespace MWVR {
return impl().mSamples; return impl().mSamples;
} }
OpenXRTextureBuffer* OpenXRSwapchain::current() OpenXRTextureBuffer* OpenXRSwapchain::renderBuffer()
{ {
return impl().mCurrentBuffer; return impl().mRenderBuffer;
} }
} }

View file

@ -26,11 +26,6 @@ namespace MWVR
~OpenXRSwapchain(); ~OpenXRSwapchain();
public: public:
//! Get the next color buffer.
//! \return The GL texture ID of the now current swapchain image
osg::ref_ptr<OpenXRTextureBuffer> prepareNextSwapchainImage();
//! Release current color buffer. Do not forget to call this after rendering to the color buffer.
void releaseSwapchainImage();
//! Prepare for render (set FBO) //! Prepare for render (set FBO)
void beginFrame(osg::GraphicsContext* gc); void beginFrame(osg::GraphicsContext* gc);
//! Finalize render //! Finalize render
@ -44,7 +39,7 @@ namespace MWVR
//! Samples of the view surface //! Samples of the view surface
int samples(); int samples();
//! Get the current texture //! Get the current texture
OpenXRTextureBuffer* current(); OpenXRTextureBuffer* renderBuffer();
//! Get the private implementation //! Get the private implementation
OpenXRSwapchainImpl& impl() { return *mPrivate; } OpenXRSwapchainImpl& impl() { return *mPrivate; }
//! Get the private implementation //! Get the private implementation

View file

@ -15,85 +15,57 @@ namespace MWVR
{ {
OpenXRTextureBuffer::OpenXRTextureBuffer( OpenXRTextureBuffer::OpenXRTextureBuffer(
osg::ref_ptr<osg::State> state, osg::ref_ptr<osg::State> state,
uint32_t XRColorBuffer,
std::size_t width, std::size_t width,
std::size_t height, std::size_t height,
uint32_t msaaSamples) uint32_t msaaSamples)
: mState(state) : mState(state)
, mWidth(width) , mWidth(width)
, mHeight(height) , mHeight(height)
, mXRColorBuffer(XRColorBuffer) , mSamples(msaaSamples)
, mMSAASamples(msaaSamples)
{ {
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
glBindTexture(GL_TEXTURE_2D, XRColorBuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLint w = 0; gl->glGenFramebuffers(1, &mBlitFBO);
GLint h = 0; gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mBlitFBO);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WIDTH, &w);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_HEIGHT, &h);
gl->glGenFramebuffers(1, &mFBO); gl->glGenFramebuffers(1, &mFBO);
glGenTextures(1, &mDepthBuffer);
glGenTextures(1, &mColorBuffer);
if (mMSAASamples == 0) if (mSamples == 0)
{ mTextureTarget = GL_TEXTURE_2D;
glGenTextures(1, &mDepthBuffer);
glBindTexture(GL_TEXTURE_2D, mDepthBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, mDepthBuffer, 0);
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
throw std::runtime_error("Failed to create OpenXR framebuffer");
}
else else
{ mTextureTarget = GL_TEXTURE_2D_MULTISAMPLE;
gl->glGenFramebuffers(1, &mMSAAFBO);
// Create MSAA color buffer glBindTexture(mTextureTarget, mColorBuffer);
glGenTextures(1, &mMSAAColorBuffer); if (mSamples == 0)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer); glTexImage2D(mTextureTarget, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_INT, nullptr);
gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_RGBA, mWidth, mHeight, false); else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB); gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_RGBA, mWidth, mHeight, false);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB); glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
// Create MSAA depth buffer glBindTexture(mTextureTarget, mDepthBuffer);
glGenTextures(1, &mMSAADepthBuffer); if (mSamples == 0)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer); glTexImage2D(mTextureTarget, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false); else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mMSAAFBO);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer, 0);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer, 0);
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
throw std::runtime_error("Failed to create MSAA framebuffer");
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0); gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, mTextureTarget, mColorBuffer, 0);
gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, mTextureTarget, mDepthBuffer, 0);
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
throw std::runtime_error("Failed to create OpenXR framebuffer"); throw std::runtime_error("Failed to create OpenXR framebuffer");
}
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
@ -117,55 +89,37 @@ namespace MWVR
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
if (mFBO) if (mFBO)
gl->glDeleteFramebuffers(1, &mFBO); gl->glDeleteFramebuffers(1, &mFBO);
if (mMSAAFBO)
gl->glDeleteFramebuffers(1, &mMSAAFBO);
} }
else if(mFBO || mMSAAFBO) else if(mFBO)
// Without access to opengl methods, i'll just let the FBOs leak. // Without access to opengl methods, i'll just let the FBOs leak.
Log(Debug::Warning) << "destroy() called without a State. Leaking FBOs"; Log(Debug::Warning) << "destroy() called without a State. Leaking FBO";
if (mDepthBuffer) if (mDepthBuffer)
glDeleteTextures(1, &mDepthBuffer); glDeleteTextures(1, &mDepthBuffer);
if (mMSAAColorBuffer) if (mColorBuffer)
glDeleteTextures(1, &mMSAAColorBuffer); glDeleteTextures(1, &mColorBuffer);
if (mMSAADepthBuffer)
glDeleteTextures(1, &mMSAADepthBuffer);
mFBO = mMSAAFBO = mDepthBuffer = mMSAAColorBuffer = mMSAADepthBuffer = 0; mFBO = mDepthBuffer = mColorBuffer;
} }
void OpenXRTextureBuffer::beginFrame(osg::GraphicsContext* gc) void OpenXRTextureBuffer::beginFrame(osg::GraphicsContext* gc)
{ {
auto state = gc->getState(); auto state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
if (mMSAASamples == 0)
{
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
}
else
{
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mMSAAFBO);
}
} }
void OpenXRTextureBuffer::endFrame(osg::GraphicsContext* gc) void OpenXRTextureBuffer::endFrame(osg::GraphicsContext* gc, uint32_t blitTarget)
{ {
auto* state = gc->getState(); auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
if (mMSAASamples == 0) gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mBlitFBO);
{ gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mFBO);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, blitTarget, 0);
} gl->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
else gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
{ gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mMSAAFBO); gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mFBO);
gl->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
}
} }
void OpenXRTextureBuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h) void OpenXRTextureBuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h)

View file

@ -13,22 +13,23 @@ namespace MWVR
class OpenXRTextureBuffer : public osg::Referenced class OpenXRTextureBuffer : public osg::Referenced
{ {
public: public:
OpenXRTextureBuffer(osg::ref_ptr<osg::State> state, uint32_t XRColorBuffer, std::size_t width, std::size_t height, uint32_t msaaSamples); OpenXRTextureBuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples);
~OpenXRTextureBuffer(); ~OpenXRTextureBuffer();
void destroy(osg::State* state); void destroy(osg::State* state);
auto width() const { return mWidth; } auto width() const { return mWidth; }
auto height() const { return mHeight; } auto height() const { return mHeight; }
auto msaaSamples() const { return mMSAASamples; } auto msaaSamples() const { return mSamples; }
void beginFrame(osg::GraphicsContext* gc); void beginFrame(osg::GraphicsContext* gc);
void endFrame(osg::GraphicsContext* gc); void endFrame(osg::GraphicsContext* gc, uint32_t blitTarget);
void writeToJpg(osg::State& state, std::string filename); void writeToJpg(osg::State& state, std::string filename);
uint32_t fbo(void) const { return mFBO; } uint32_t fbo(void) const { return mFBO; }
//! Blit to region in currently bound draw fbo
void blit(osg::GraphicsContext* gc, int x, int y, int w, int h); void blit(osg::GraphicsContext* gc, int x, int y, int w, int h);
private: private:
@ -39,18 +40,13 @@ namespace MWVR
std::size_t mWidth = 0; std::size_t mWidth = 0;
std::size_t mHeight = 0; std::size_t mHeight = 0;
// Swapchain buffer // Render Target
uint32_t mXRColorBuffer = 0;
// FBO target for swapchain buffer
uint32_t mDepthBuffer = 0;
uint32_t mFBO = 0; uint32_t mFBO = 0;
uint32_t mBlitFBO = 0;
// Render targets for MSAA uint32_t mDepthBuffer = 0;
uint32_t mMSAASamples = 0; uint32_t mColorBuffer = 0;
uint32_t mMSAAFBO = 0; uint32_t mSamples = 0;
uint32_t mMSAAColorBuffer = 0; uint32_t mTextureTarget = 0;
uint32_t mMSAADepthBuffer = 0;
}; };
} }

View file

@ -16,22 +16,22 @@
namespace MWVR { namespace MWVR {
OpenXRView::OpenXRView( OpenXRView::OpenXRView(
osg::ref_ptr<OpenXRManager> XR) osg::ref_ptr<OpenXRManager> XR,
std::string name)
: mXR(XR) : mXR(XR)
, mSwapchain(nullptr) , mSwapchain(nullptr)
, mSwapchainConfig{} , mSwapchainConfig{}
, mName(name)
{ {
mSwapchainConfig.requestedFormats = { mSwapchainConfig.requestedFormats = {
GL_RGBA8, GL_RGBA8,
GL_RGBA8_SNORM, GL_RGBA8_SNORM,
}; };
mXR->registerToBarrier();
} }
OpenXRView::~OpenXRView() OpenXRView::~OpenXRView()
{ {
mXR->unregisterFromBarrier();
} }
osg::Camera* OpenXRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc) osg::Camera* OpenXRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
@ -48,7 +48,10 @@ namespace MWVR {
camera->setGraphicsContext(gc); camera->setGraphicsContext(gc);
camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback()); camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback());
camera->setPreDrawCallback(new OpenXRView::PredrawCallback(camera.get(), this));
mPredraw = new OpenXRView::PredrawCallback(camera.get(), this);
camera->setPreDrawCallback(mPredraw);
camera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(camera.get(), this)); camera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(camera.get(), this));
return camera.release(); return camera.release();
@ -71,18 +74,28 @@ namespace MWVR {
void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo) void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo)
{ {
Log(Debug::Verbose) << "prerenderCallback"; Log(Debug::Verbose) << mName << ": prerenderCallback";
mXR->beginFrame(mFrameIndex); if (mSwapchain)
if(mSwapchain) {
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext()); mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
}
} }
void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo) void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo)
{ {
if (mSwapchain) // osg will sometimes call this without a corresponding prerender.
mSwapchain->endFrame(renderInfo.getState()->getGraphicsContext()); Log(Debug::Verbose) << mName << ": postrenderCallback";
mXR->viewerBarrier(); Log(Debug::Verbose) << renderInfo.getCurrentCamera()->getName() << ": " << renderInfo.getCurrentCamera()->getPreDrawCallback();
mFrameIndex++; if (renderInfo.getCurrentCamera()->getPreDrawCallback() != mPredraw)
{
// It seems OSG will sometimes overwrite the predraw callback.
// Undocumented behaviour?
renderInfo.getCurrentCamera()->setPreDrawCallback(mPredraw);
Log(Debug::Warning) << ("osg overwrote predraw");
}
//if (mSwapchain)
// mSwapchain->endFrame(renderInfo.getState()->getGraphicsContext());
} }
bool OpenXRView::realize(osg::ref_ptr<osg::State> state) bool OpenXRView::realize(osg::ref_ptr<osg::State> state)

View file

@ -1,6 +1,7 @@
#ifndef OPENXR_VIEW_HPP #ifndef OPENXR_VIEW_HPP
#define OPENXR_VIEW_HPP #define OPENXR_VIEW_HPP
#include <cassert>
#include "openxrmanager.hpp" #include "openxrmanager.hpp"
#include "openxrswapchain.hpp" #include "openxrswapchain.hpp"
@ -8,6 +9,7 @@ struct XrSwapchainSubImage;
namespace MWVR namespace MWVR
{ {
class OpenXRView : public osg::Referenced class OpenXRView : public osg::Referenced
{ {
public: public:
@ -49,7 +51,7 @@ namespace MWVR
}; };
protected: protected:
OpenXRView(osg::ref_ptr<OpenXRManager> XR); OpenXRView(osg::ref_ptr<OpenXRManager> XR, std::string name);
virtual ~OpenXRView(); virtual ~OpenXRView();
void setWidth(int width); void setWidth(int width);
void setHeight(int height); void setHeight(int height);
@ -66,14 +68,14 @@ namespace MWVR
OpenXRSwapchain& swapchain(void) { return *mSwapchain; } OpenXRSwapchain& swapchain(void) { return *mSwapchain; }
//! Create the view surface //! Create the view surface
bool realize(osg::ref_ptr<osg::State> state); bool realize(osg::ref_ptr<osg::State> state);
//! Current frame being rendered
long long frameIndex() { return mFrameIndex; };
protected: protected:
osg::ref_ptr<OpenXRManager> mXR; osg::ref_ptr<OpenXRManager> mXR;
std::unique_ptr<OpenXRSwapchain> mSwapchain; std::unique_ptr<OpenXRSwapchain> mSwapchain;
OpenXRSwapchain::Config mSwapchainConfig; OpenXRSwapchain::Config mSwapchainConfig;
long long mFrameIndex{ 0 }; std::string mName{};
bool mRendering{ false };
osg::ref_ptr<OpenXRView::PredrawCallback> mPredraw{ nullptr };
}; };
} }

View file

@ -18,20 +18,6 @@ namespace MWVR
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}) , mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
{ {
mViewer->setRealizeOperation(mRealizeOperation); mViewer->setRealizeOperation(mRealizeOperation);
//auto* mainContext = mViewer->getCamera()->getGraphicsContext();
//assert(mainContext);
//auto* mainTraits = mainContext->getTraits();
//osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(*mainTraits);
//traits->sharedContext = mainContext;
//mViewerGW = new SDLUtil::GraphicsWindowSDL2(traits);
//if (!mViewerGW->valid()) throw std::runtime_error("Failed to create GraphicsContext");
//mLeftGW = new SDLUtil::GraphicsWindowSDL2(traits);
//if (!mLeftGW->valid()) throw std::runtime_error("Failed to create GraphicsContext");
//mRightGW = new SDLUtil::GraphicsWindowSDL2(traits);
//if (!mRightGW->valid()) throw std::runtime_error("Failed to create GraphicsContext");
} }
OpenXRViewer::~OpenXRViewer(void) OpenXRViewer::~OpenXRViewer(void)
@ -50,7 +36,7 @@ namespace MWVR
const XrCompositionLayerBaseHeader* const XrCompositionLayerBaseHeader*
OpenXRViewer::layer() OpenXRViewer::layer()
{ {
auto stageViews = mXR->impl().getStageViews(); auto stageViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().mRenderIndex, TrackedSpace::STAGE);
mCompositionLayerProjectionViews[0].pose = stageViews[0].pose; mCompositionLayerProjectionViews[0].pose = stageViews[0].pose;
mCompositionLayerProjectionViews[1].pose = stageViews[1].pose; mCompositionLayerProjectionViews[1].pose = stageViews[1].pose;
mCompositionLayerProjectionViews[0].fov = stageViews[0].fov; mCompositionLayerProjectionViews[0].fov = stageViews[0].fov;
@ -61,6 +47,8 @@ namespace MWVR
void OpenXRViewer::traversals() void OpenXRViewer::traversals()
{ {
Log(Debug::Verbose) << "Pre-Update"; Log(Debug::Verbose) << "Pre-Update";
mXR->handleEvents();
OpenXRFrameIndexer::instance().advanceUpdateIndex();
mViewer->updateTraversal(); mViewer->updateTraversal();
Log(Debug::Verbose) << "Post-Update"; Log(Debug::Verbose) << "Post-Update";
Log(Debug::Verbose) << "Pre-Rendering"; Log(Debug::Verbose) << "Pre-Rendering";
@ -89,12 +77,14 @@ namespace MWVR
// Use the main camera to render any GUI to the OpenXR GUI quad's swapchain. // Use the main camera to render any GUI to the OpenXR GUI quad's swapchain.
// (When swapping the window buffer we'll blit the mirror texture to it instead.) // (When swapping the window buffer we'll blit the mirror texture to it instead.)
mMainCamera->setCullMask(MWRender::Mask_GUI); mMainCamera->setCullMask(MWRender::Mask_GUI);
mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
osg::Vec4 clearColor = mMainCamera->getClearColor(); osg::Vec4 clearColor = mMainCamera->getClearColor();
mViews[OpenXRWorldView::LEFT_VIEW] = new OpenXRWorldView(mXR, context->getState(), mMetersPerUnit, OpenXRWorldView::LEFT_VIEW); if (!mXR->realized())
mViews[OpenXRWorldView::RIGHT_VIEW] = new OpenXRWorldView(mXR, context->getState(), mMetersPerUnit, OpenXRWorldView::RIGHT_VIEW); mXR->realize(context);
mViews[OpenXRWorldView::LEFT_VIEW] = new OpenXRWorldView(mXR, "LeftEye", context->getState(), mMetersPerUnit, OpenXRWorldView::LEFT_VIEW);
mViews[OpenXRWorldView::RIGHT_VIEW] = new OpenXRWorldView(mXR, "RightEye", context->getState(), mMetersPerUnit, OpenXRWorldView::RIGHT_VIEW);
mLeftCamera = mViews[OpenXRWorldView::LEFT_VIEW]->createCamera(OpenXRWorldView::LEFT_VIEW, clearColor, context); mLeftCamera = mViews[OpenXRWorldView::LEFT_VIEW]->createCamera(OpenXRWorldView::LEFT_VIEW, clearColor, context);
mRightCamera = mViews[OpenXRWorldView::RIGHT_VIEW]->createCamera(OpenXRWorldView::RIGHT_VIEW, clearColor, context); mRightCamera = mViews[OpenXRWorldView::RIGHT_VIEW]->createCamera(OpenXRWorldView::RIGHT_VIEW, clearColor, context);
@ -112,8 +102,8 @@ namespace MWVR
mViewer->addSlave(mLeftCamera, mViews[OpenXRWorldView::LEFT_VIEW]->projectionMatrix(), mViews[OpenXRWorldView::LEFT_VIEW]->viewMatrix(), true); mViewer->addSlave(mLeftCamera, mViews[OpenXRWorldView::LEFT_VIEW]->projectionMatrix(), mViews[OpenXRWorldView::LEFT_VIEW]->viewMatrix(), true);
mViewer->addSlave(mRightCamera, mViews[OpenXRWorldView::RIGHT_VIEW]->projectionMatrix(), mViews[OpenXRWorldView::RIGHT_VIEW]->viewMatrix(), true); mViewer->addSlave(mRightCamera, mViews[OpenXRWorldView::RIGHT_VIEW]->projectionMatrix(), mViews[OpenXRWorldView::RIGHT_VIEW]->viewMatrix(), true);
mViewer->getSlave(OpenXRWorldView::LEFT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::LEFT_VIEW], context); mViewer->getSlave(OpenXRWorldView::LEFT_VIEW)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::LEFT_VIEW], context);
mViewer->getSlave(OpenXRWorldView::RIGHT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::RIGHT_VIEW], context); mViewer->getSlave(OpenXRWorldView::RIGHT_VIEW)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::RIGHT_VIEW], context);
mViewer->setLightingMode(osg::View::SKY_LIGHT); mViewer->setLightingMode(osg::View::SKY_LIGHT);
mViewer->setReleaseContextAtEndOfFrameHint(false); mViewer->setReleaseContextAtEndOfFrameHint(false);
@ -147,18 +137,24 @@ namespace MWVR
mMirrorTextureSwapchain.reset(new OpenXRSwapchain(mXR, context->getState(), config)); mMirrorTextureSwapchain.reset(new OpenXRSwapchain(mXR, context->getState(), config));
mXRMenu.reset(new OpenXRMenu(mXR, context->getState(), "MainMenu", config.width, config.height, osg::Vec2(1.f, 1.f))); mXRMenu.reset(new OpenXRMenu(mXR, context->getState(), "MainMenu", config.width, config.height, osg::Vec2(1.f, 1.f)));
mMainCamera->setPreDrawCallback(new OpenXRView::PredrawCallback(mMainCamera, mXRMenu.get())); mMenuCamera = mXRMenu->createCamera(2, clearColor, context);
mMainCamera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(mMainCamera, mXRMenu.get())); mMenuCamera->setCullMask(MWRender::Mask_GUI);
mMenuCamera->setName("MenuCamera");
mViewer->addSlave(mMenuCamera, true);
//mMenuCamera->setPreDrawCallback(new OpenXRView::PredrawCallback(mMenuCamera, mXRMenu.get()));
//mMenuCamera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(mMenuCamera, mXRMenu.get()));
mXR->impl().mLayerStack.setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, mXRMenu.get()); mXR->impl().mLayerStack.setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, mXRMenu.get());
mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
mMainCamera->setGraphicsContext(nullptr);
mConfigured = true; mConfigured = true;
} }
void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) const void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) const
{ {
mMirrorTextureSwapchain->beginFrame(gc); mMirrorTextureSwapchain->beginFrame(gc);
mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height());
mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().current()->blit(gc, mMirrorTextureSwapchain->width() / 2, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().renderBuffer()->blit(gc, mMirrorTextureSwapchain->width() / 2, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height());
//mXRMenu->swapchain().current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height()); //mXRMenu->swapchain().current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, mMirrorTextureSwapchain->height());
} }
@ -172,45 +168,30 @@ namespace MWVR
void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc) void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc)
{ {
Timer timer("swapBuffers");
Log(Debug::Verbose) << "SwapBuffers()";
std::unique_lock<std::mutex> lock(mMutex); std::unique_lock<std::mutex> lock(mMutex);
if (!mConfigured) if (!mConfigured)
return; return;
auto* state = gc->getState(); auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
blitEyesToMirrorTexture(gc);
mXR->beginFrame();
//blitEyesToMirrorTexture(gc);
mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().endFrame(gc);
mViews[OpenXRWorldView::RIGHT_VIEW]->swapchain().endFrame(gc);
mXRMenu->swapchain().endFrame(gc);
mXR->endFrame(); mXR->endFrame();
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); mXR->waitFrame();
//gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
mMirrorTextureSwapchain->current()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); //mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height());
mMirrorTextureSwapchain->releaseSwapchainImage(); //mMirrorTextureSwapchain->endFrame(gc);
gc->swapBuffersImplementation(); gc->swapBuffersImplementation();
}
void
OpenXRViewer::UpdateSlaveCallback::updateSlave(
osg::View& view,
osg::View::Slave& slave)
{
mXR->handleEvents();
if (!mXR->sessionRunning())
return;
auto* camera = slave._camera.get();
auto name = camera->getName();
Log(Debug::Verbose) << "Updating camera " << name;
mXR->beginFrame(mView->frameIndex());
auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix();
auto projMatrix = mView->projectionMatrix();
camera->setViewMatrix(viewMatrix);
camera->setProjectionMatrix(projMatrix);
} }
void void

View file

@ -31,20 +31,6 @@ namespace MWVR
osg::ref_ptr<OpenXRViewer> mViewer; osg::ref_ptr<OpenXRViewer> mViewer;
}; };
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
{
public:
UpdateSlaveCallback(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<OpenXRWorldView> view, osg::GraphicsContext* gc)
: mXR(XR), mView(view), mGC(gc)
{}
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
private:
osg::ref_ptr<OpenXRManager> mXR;
osg::ref_ptr<OpenXRWorldView> mView;
osg::ref_ptr<osg::GraphicsContext> mGC;
};
class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback
{ {
public: public:
@ -90,6 +76,7 @@ namespace MWVR
//osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> mRightGW; //osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> mRightGW;
osg::Camera* mMainCamera = nullptr; osg::Camera* mMainCamera = nullptr;
osg::Camera* mMenuCamera = nullptr;
osg::Camera* mLeftCamera = nullptr; osg::Camera* mLeftCamera = nullptr;
osg::Camera* mRightCamera = nullptr; osg::Camera* mRightCamera = nullptr;

View file

@ -75,9 +75,10 @@ namespace MWVR
return osg::Matrix(matrix); return osg::Matrix(matrix);
} }
osg::Matrix OpenXRWorldView::projectionMatrix() osg::Matrix OpenXRWorldView::projectionMatrix()
{ {
auto hmdViews = mXR->impl().getHmdViews(); auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW);
float near = Settings::Manager::getFloat("near clip", "Camera"); float near = Settings::Manager::getFloat("near clip", "Camera");
float far = Settings::Manager::getFloat("viewing distance", "Camera") * mMetersPerUnit; float far = Settings::Manager::getFloat("viewing distance", "Camera") * mMetersPerUnit;
@ -88,11 +89,11 @@ namespace MWVR
osg::Matrix OpenXRWorldView::viewMatrix() osg::Matrix OpenXRWorldView::viewMatrix()
{ {
osg::Matrix viewMatrix; osg::Matrix viewMatrix;
auto hmdViews = mXR->impl().getHmdViews(); auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW);
auto pose = hmdViews[mView].pose; auto pose = hmdViews[mView].pose;
osg::Vec3 position = osg::fromXR(pose.position); osg::Vec3 position = osg::fromXR(pose.position);
auto stageViews = mXR->impl().getStageViews(); auto stageViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::STAGE);
auto stagePose = stageViews[mView].pose; auto stagePose = stageViews[mView].pose;
// Comfort shortcut. // Comfort shortcut.
@ -115,8 +116,8 @@ namespace MWVR
} }
OpenXRWorldView::OpenXRWorldView( OpenXRWorldView::OpenXRWorldView(
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view) osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view)
: OpenXRView(XR) : OpenXRView(XR, name)
, mMetersPerUnit(metersPerUnit) , mMetersPerUnit(metersPerUnit)
, mView(view) , mView(view)
{ {
@ -145,4 +146,44 @@ namespace MWVR
renderer->setCameraRequiresSetUp(false); renderer->setCameraRequiresSetUp(false);
} }
} }
void OpenXRWorldView::prerenderCallback(osg::RenderInfo& renderInfo)
{
OpenXRView::prerenderCallback(renderInfo);
auto* view = renderInfo.getView();
auto* camera = renderInfo.getCurrentCamera();
auto name = camera->getName();
Log(Debug::Verbose) << "Updating camera " << name;
auto viewMatrix = view->getCamera()->getViewMatrix() * this->viewMatrix();
auto projectionMatrix = this->projectionMatrix();
camera->setViewMatrix(viewMatrix);
camera->setProjectionMatrix(projectionMatrix);
}
void
OpenXRWorldView::UpdateSlaveCallback::updateSlave(
osg::View& view,
osg::View::Slave& slave)
{
mXR->handleEvents();
if (!mXR->sessionRunning())
return;
auto* camera = slave._camera.get();
auto name = camera->getName();
Log(Debug::Verbose) << "Updating slave " << name;
//auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix();
//auto projMatrix = mView->projectionMatrix();
//camera->setViewMatrix(viewMatrix);
//camera->setProjectionMatrix(projMatrix);
slave.updateSlaveImplementation(view);
}
} }

View file

@ -15,10 +15,27 @@ namespace MWVR
SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input. SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input.
}; };
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
{
public:
UpdateSlaveCallback(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<OpenXRWorldView> view, osg::GraphicsContext* gc)
: mXR(XR), mView(view), mGC(gc)
{}
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
private:
osg::ref_ptr<OpenXRManager> mXR;
osg::ref_ptr<OpenXRWorldView> mView;
osg::ref_ptr<osg::GraphicsContext> mGC;
};
public: public:
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view); OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view);
~OpenXRWorldView(); ~OpenXRWorldView();
//! Prepare for render (update matrices)
void prerenderCallback(osg::RenderInfo& renderInfo) override;
//! Projection offset for this view //! Projection offset for this view
osg::Matrix projectionMatrix(); osg::Matrix projectionMatrix();
//! View offset for this view //! View offset for this view