mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-15 16:39:40 +00:00
Simplified render timing by separating rendering from the openxr swapchain, and instead blitting and submitting separately
This commit is contained in:
parent
51125d4f3e
commit
951879240c
17 changed files with 449 additions and 547 deletions
|
@ -731,12 +731,12 @@ void OMW::Engine::go()
|
|||
mXRViewer->addChild(root);
|
||||
mViewer->setSceneData(mXRViewer);
|
||||
#ifndef _NDEBUG
|
||||
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::LEFT_HAND, MWVR::TrackedSpace::STAGE));
|
||||
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::VIEW));
|
||||
//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::LEFT_HAND, MWVR::TrackedSpace::STAGE));
|
||||
//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::VIEW));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -515,7 +515,7 @@ namespace MWVR
|
|||
OpenXRInputManagerImpl::updateHandTracking()
|
||||
{
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,10 +67,10 @@ namespace MWVR
|
|||
return impl().waitFrame();
|
||||
}
|
||||
|
||||
void OpenXRManager::beginFrame(long long frameIndex)
|
||||
void OpenXRManager::beginFrame()
|
||||
{
|
||||
if (realized())
|
||||
return impl().beginFrame(frameIndex);
|
||||
return impl().beginFrame();
|
||||
}
|
||||
|
||||
void OpenXRManager::endFrame()
|
||||
|
@ -85,12 +85,6 @@ namespace MWVR
|
|||
return impl().updateControls();
|
||||
}
|
||||
|
||||
void OpenXRManager::updatePoses()
|
||||
{
|
||||
if (realized())
|
||||
return impl().updatePoses();
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManager::realize(
|
||||
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()
|
||||
{
|
||||
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 impl().viewerBarrier();
|
||||
return g_OpenXRFrameIndexer;
|
||||
}
|
||||
|
||||
void OpenXRManager::registerToBarrier()
|
||||
int64_t OpenXRFrameIndexer::advanceUpdateIndex()
|
||||
{
|
||||
if (realized())
|
||||
return impl().registerToBarrier();
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mUpdateIndex++;
|
||||
Log(Debug::Verbose) << "Advancing update index to " << mUpdateIndex;
|
||||
assert(mUpdateIndex > mRenderIndex);
|
||||
return mUpdateIndex;
|
||||
}
|
||||
|
||||
void OpenXRManager::unregisterFromBarrier()
|
||||
int64_t OpenXRFrameIndexer::advanceRenderIndex()
|
||||
{
|
||||
if (realized())
|
||||
return impl().unregisterFromBarrier();
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
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 <<(
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <osg/Camera>
|
||||
|
@ -17,6 +18,23 @@ struct XrSwapchainSubImage;
|
|||
|
||||
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.
|
||||
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.
|
||||
};
|
||||
|
||||
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.
|
||||
class OpenXRManagerImpl;
|
||||
|
||||
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:
|
||||
class RealizeOperation : public osg::GraphicsOperation
|
||||
{
|
||||
|
@ -96,24 +121,14 @@ namespace MWVR
|
|||
|
||||
void handleEvents();
|
||||
void waitFrame();
|
||||
void beginFrame(long long frameIndex);
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
void updateControls();
|
||||
void updatePoses();
|
||||
|
||||
void realize(osg::GraphicsContext* gc);
|
||||
|
||||
void addPoseUpdateCallback(osg::ref_ptr<PoseUpdateCallback> cb);
|
||||
|
||||
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; }
|
||||
|
||||
private:
|
||||
|
@ -121,16 +136,6 @@ namespace MWVR
|
|||
std::mutex mMutex;
|
||||
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);
|
||||
|
|
|
@ -99,7 +99,6 @@ namespace MWVR
|
|||
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
|
||||
createInfo.next = &mGraphicsBinding;
|
||||
createInfo.systemId = mSystemId;
|
||||
createInfo.createFlags;
|
||||
CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession));
|
||||
assert(mSession);
|
||||
}
|
||||
|
@ -117,13 +116,6 @@ namespace MWVR
|
|||
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
|
||||
xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties);
|
||||
|
||||
|
@ -142,32 +134,32 @@ namespace MWVR
|
|||
|
||||
uint32_t viewCount = 0;
|
||||
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)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "xrEnumerateViewConfigurationViews returned " << viewCount << " views";
|
||||
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) {
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
@ -265,123 +257,59 @@ namespace MWVR
|
|||
void
|
||||
OpenXRManagerImpl::waitFrame()
|
||||
{
|
||||
Timer timer("waitFrame()");
|
||||
|
||||
// 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));
|
||||
mFrameState = frameState;
|
||||
|
||||
|
||||
mTimeKeeper.progressToNextFrame(frameState);
|
||||
|
||||
}).detach();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::beginFrame(long long frameIndex)
|
||||
OpenXRManagerImpl::beginFrame()
|
||||
{
|
||||
Log(Debug::Verbose) << "frameIndex = " << frameIndex;
|
||||
if (!mSessionRunning)
|
||||
return;
|
||||
|
||||
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();
|
||||
|
||||
Timer timer("beginFrame");
|
||||
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
|
||||
OpenXRManagerImpl::endFrame()
|
||||
{
|
||||
Timer timer("endFrame()");
|
||||
if (!mSessionRunning)
|
||||
return;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mFrameStatusMutex);
|
||||
while(mFrameStatus != OPENXR_FRAME_STATUS_ENDING)
|
||||
mFrameStatusSignal.wait(lock);
|
||||
|
||||
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
|
||||
frameEndInfo.displayTime = mFrameState.predictedDisplayTime;
|
||||
frameEndInfo.displayTime = mTimeKeeper.predictedDisplayTime(OpenXRFrameIndexer::instance().renderIndex());
|
||||
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
|
||||
//frameEndInfo.layerCount = (uint32_t)1;
|
||||
//frameEndInfo.layers = &mLayer_p;
|
||||
frameEndInfo.layerCount = mLayerStack.layerCount();
|
||||
frameEndInfo.layers = mLayerStack.layerHeaders();
|
||||
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}} };
|
||||
XrViewState viewState{ XR_TYPE_VIEW_STATE };
|
||||
|
@ -389,34 +317,22 @@ namespace MWVR
|
|||
|
||||
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
|
||||
viewLocateInfo.viewConfigurationType = mViewConfigType;
|
||||
viewLocateInfo.displayTime = mFrameState.predictedDisplayTime;
|
||||
|
||||
viewLocateInfo.displayTime = mTimeKeeper.predictedDisplayTime(frameIndex);
|
||||
switch (space)
|
||||
{
|
||||
case TrackedSpace::STAGE:
|
||||
viewLocateInfo.space = mReferenceSpaceStage;
|
||||
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data()));
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
std::array<XrView, 2> OpenXRManagerImpl::getHmdViews()
|
||||
{
|
||||
// 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;
|
||||
|
||||
break;
|
||||
case TrackedSpace::VIEW:
|
||||
viewLocateInfo.space = mReferenceSpaceView;
|
||||
break;
|
||||
}
|
||||
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data()));
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
MWVR::Pose OpenXRManagerImpl::getLimbPose(TrackedLimb limb, TrackedSpace space)
|
||||
MWVR::Pose OpenXRManagerImpl::getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space)
|
||||
{
|
||||
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
|
||||
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
|
||||
|
@ -444,7 +360,7 @@ namespace MWVR
|
|||
referenceSpace = mReferenceSpaceView;
|
||||
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))
|
||||
Log(Debug::Warning) << "Unable to acquire linear velocity";
|
||||
return MWVR::Pose{
|
||||
|
@ -503,12 +419,13 @@ namespace MWVR
|
|||
switch (newState)
|
||||
{
|
||||
case XR_SESSION_STATE_READY:
|
||||
case XR_SESSION_STATE_IDLE:
|
||||
//case XR_SESSION_STATE_IDLE:
|
||||
{
|
||||
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
|
||||
beginInfo.primaryViewConfigurationType = mViewConfigType;
|
||||
CHECK_XRCMD(xrBeginSession(mSession, &beginInfo));
|
||||
mSessionRunning = true;
|
||||
waitFrame();
|
||||
break;
|
||||
}
|
||||
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);
|
||||
//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));
|
||||
return mTimeKeeper.predictedDisplayTime(frameIndex);
|
||||
}
|
||||
|
||||
void OpenXRManagerImpl::addPoseUpdateCallback(
|
||||
osg::ref_ptr<PoseUpdateCallback> cb)
|
||||
{
|
||||
mPoseUpdateCallbacks.push_back(cb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const XrEventDataBaseHeader*
|
||||
OpenXRManagerImpl::nextEvent()
|
||||
{
|
||||
|
@ -587,6 +475,62 @@ namespace MWVR
|
|||
{
|
||||
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
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <map>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
namespace osg {
|
||||
Vec3 fromXR(XrVector3f);
|
||||
|
@ -43,10 +44,33 @@ namespace MWVR
|
|||
MWVR::Pose fromXR(XrPosef 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
|
||||
{
|
||||
using PoseUpdateCallback = OpenXRManager::PoseUpdateCallback;
|
||||
|
||||
OpenXRManagerImpl(void);
|
||||
~OpenXRManagerImpl(void);
|
||||
|
||||
|
@ -56,17 +80,15 @@ namespace MWVR
|
|||
|
||||
const XrEventDataBaseHeader* nextEvent();
|
||||
void waitFrame();
|
||||
void beginFrame(long long frameIndex);
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
std::array<XrView, 2> getStageViews();
|
||||
std::array<XrView, 2> getHmdViews();
|
||||
MWVR::Pose getLimbPose(TrackedLimb limb, TrackedSpace space);
|
||||
std::array<XrView, 2> getPredictedViews(int64_t frameIndex, TrackedSpace mSpace);
|
||||
MWVR::Pose getPredictedLimbPose(int64_t frameIndex, TrackedLimb limb, TrackedSpace space);
|
||||
int eyes();
|
||||
void handleEvents();
|
||||
void updateControls();
|
||||
void updatePoses();
|
||||
void addPoseUpdateCallback(osg::ref_ptr<PoseUpdateCallback> cb);
|
||||
void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent);
|
||||
XrTime predictedDisplayTime(int64_t frameIndex);
|
||||
|
||||
bool initialized = false;
|
||||
long long mFrameIndex = 0;
|
||||
|
@ -83,38 +105,11 @@ namespace MWVR
|
|||
XrSpace mReferenceSpaceView = XR_NULL_HANDLE;
|
||||
XrSpace mReferenceSpaceStage = XR_NULL_HANDLE;
|
||||
XrEventDataBuffer mEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER };
|
||||
//XrCompositionLayerProjection mLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };
|
||||
//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} } };
|
||||
OpenXRTimeKeeper mTimeKeeper{};
|
||||
OpenXRLayerStack mLayerStack{};
|
||||
XrFrameState mFrameState{ XR_TYPE_FRAME_STATE };
|
||||
XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN;
|
||||
bool mSessionRunning = false;
|
||||
|
||||
//osg::Pose mHeadTrackedPose{};
|
||||
//osg::Pose mLeftHandTrackedPose{};
|
||||
//osg::Pose mRightHandTrackedPose{};
|
||||
|
||||
std::vector< osg::ref_ptr<PoseUpdateCallback> > mPoseUpdateCallbacks{};
|
||||
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
: OpenXRView(XR)
|
||||
: OpenXRView(XR, title)
|
||||
, mTitle(title)
|
||||
{
|
||||
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
|
||||
mLayer->pose.orientation.w = 1.f;
|
||||
|
||||
updatePosition();
|
||||
//updatePosition();
|
||||
}
|
||||
|
||||
OpenXRMenu::~OpenXRMenu()
|
||||
|
@ -47,10 +47,12 @@ namespace MWVR
|
|||
return;
|
||||
if (!mXR->sessionRunning())
|
||||
return;
|
||||
if (OpenXRFrameIndexer::instance().updateIndex() == 0)
|
||||
return;
|
||||
|
||||
// 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
|
||||
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);
|
||||
mLayer->pose.position = osg::toXR(pose.position);
|
||||
mLayer->pose.orientation = osg::toXR(-pose.orientation);
|
||||
|
|
|
@ -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<OpenXRTextureBuffer> prepareNextSwapchainImage();
|
||||
void releaseSwapchainImage();
|
||||
void beginFrame(osg::GraphicsContext* gc);
|
||||
void endFrame(osg::GraphicsContext* gc);
|
||||
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
XrSwapchain mSwapchain = XR_NULL_HANDLE;
|
||||
std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
|
||||
std::vector<osg::ref_ptr<OpenXRTextureBuffer> > mTextureBuffers{};
|
||||
//std::vector<osg::ref_ptr<OpenXRTextureBuffer> > mTextureBuffers{};
|
||||
XrSwapchainSubImage mSubImage{};
|
||||
int32_t mWidth = -1;
|
||||
int32_t mHeight = -1;
|
||||
int32_t mSamples = -1;
|
||||
int64_t mSwapchainColorFormat = -1;
|
||||
OpenXRTextureBuffer* mCurrentBuffer = nullptr;
|
||||
uint32_t mFBO = 0;
|
||||
OpenXRTextureBuffer* mRenderBuffer = nullptr;
|
||||
};
|
||||
|
||||
OpenXRSwapchainImpl::OpenXRSwapchainImpl(
|
||||
|
@ -99,8 +98,9 @@ namespace MWVR {
|
|||
|
||||
mSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
|
||||
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mSwapchainImageBuffers.data())));
|
||||
for (const auto& swapchainImage : mSwapchainImageBuffers)
|
||||
mTextureBuffers.push_back(new OpenXRTextureBuffer(state, swapchainImage.image, mWidth, mHeight, 0));
|
||||
//for (const auto& swapchainImage : mSwapchainImageBuffers)
|
||||
// mTextureBuffers.push_back(new OpenXRTextureBuffer(state, swapchainImage.image, mWidth, mHeight, 0));
|
||||
mRenderBuffer = new OpenXRTextureBuffer(state, mWidth, mHeight, 0);
|
||||
|
||||
mSubImage.swapchain = mSwapchain;
|
||||
mSubImage.imageRect.offset = { 0, 0 };
|
||||
|
@ -113,11 +113,18 @@ namespace MWVR {
|
|||
CHECK_XRCMD(xrDestroySwapchain(mSwapchain));
|
||||
}
|
||||
|
||||
osg::ref_ptr<OpenXRTextureBuffer>
|
||||
OpenXRSwapchainImpl::prepareNextSwapchainImage()
|
||||
void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc)
|
||||
{
|
||||
mRenderBuffer->beginFrame(gc);
|
||||
}
|
||||
|
||||
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc)
|
||||
{
|
||||
Timer timer("Swapchain::endFrame");
|
||||
// Blit frame to swapchain
|
||||
|
||||
if (!mXR->sessionRunning())
|
||||
return nullptr;
|
||||
return;
|
||||
|
||||
XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
|
||||
uint32_t swapchainImageIndex = 0;
|
||||
|
@ -127,32 +134,11 @@ namespace MWVR {
|
|||
waitInfo.timeout = XR_INFINITE_DURATION;
|
||||
CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo));
|
||||
|
||||
return mTextureBuffers[swapchainImageIndex];
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRSwapchainImpl::releaseSwapchainImage()
|
||||
{
|
||||
if (!mXR->sessionRunning())
|
||||
return;
|
||||
mRenderBuffer->endFrame(gc, mSwapchainImageBuffers[swapchainImageIndex].image);
|
||||
|
||||
XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
||||
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(
|
||||
|
@ -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)
|
||||
{
|
||||
impl().beginFrame(gc);
|
||||
|
@ -206,8 +182,8 @@ namespace MWVR {
|
|||
return impl().mSamples;
|
||||
}
|
||||
|
||||
OpenXRTextureBuffer* OpenXRSwapchain::current()
|
||||
OpenXRTextureBuffer* OpenXRSwapchain::renderBuffer()
|
||||
{
|
||||
return impl().mCurrentBuffer;
|
||||
return impl().mRenderBuffer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,6 @@ namespace MWVR
|
|||
~OpenXRSwapchain();
|
||||
|
||||
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)
|
||||
void beginFrame(osg::GraphicsContext* gc);
|
||||
//! Finalize render
|
||||
|
@ -44,7 +39,7 @@ namespace MWVR
|
|||
//! Samples of the view surface
|
||||
int samples();
|
||||
//! Get the current texture
|
||||
OpenXRTextureBuffer* current();
|
||||
OpenXRTextureBuffer* renderBuffer();
|
||||
//! Get the private implementation
|
||||
OpenXRSwapchainImpl& impl() { return *mPrivate; }
|
||||
//! Get the private implementation
|
||||
|
|
|
@ -15,85 +15,57 @@ namespace MWVR
|
|||
{
|
||||
OpenXRTextureBuffer::OpenXRTextureBuffer(
|
||||
osg::ref_ptr<osg::State> state,
|
||||
uint32_t XRColorBuffer,
|
||||
std::size_t width,
|
||||
std::size_t height,
|
||||
uint32_t msaaSamples)
|
||||
: mState(state)
|
||||
, mWidth(width)
|
||||
, mHeight(height)
|
||||
, mXRColorBuffer(XRColorBuffer)
|
||||
, mMSAASamples(msaaSamples)
|
||||
, mSamples(msaaSamples)
|
||||
{
|
||||
|
||||
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;
|
||||
GLint h = 0;
|
||||
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WIDTH, &w);
|
||||
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_HEIGHT, &h);
|
||||
gl->glGenFramebuffers(1, &mBlitFBO);
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mBlitFBO);
|
||||
|
||||
gl->glGenFramebuffers(1, &mFBO);
|
||||
|
||||
if (mMSAASamples == 0)
|
||||
{
|
||||
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);
|
||||
glGenTextures(1, &mColorBuffer);
|
||||
|
||||
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");
|
||||
}
|
||||
if (mSamples == 0)
|
||||
mTextureTarget = GL_TEXTURE_2D;
|
||||
else
|
||||
{
|
||||
gl->glGenFramebuffers(1, &mMSAAFBO);
|
||||
mTextureTarget = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
|
||||
// Create MSAA color buffer
|
||||
glGenTextures(1, &mMSAAColorBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer);
|
||||
gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_RGBA, mWidth, mHeight, false);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glBindTexture(mTextureTarget, mColorBuffer);
|
||||
if (mSamples == 0)
|
||||
glTexImage2D(mTextureTarget, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_INT, nullptr);
|
||||
else
|
||||
gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_RGBA, mWidth, mHeight, false);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
// Create MSAA depth buffer
|
||||
glGenTextures(1, &mMSAADepthBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer);
|
||||
gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glBindTexture(mTextureTarget, mDepthBuffer);
|
||||
if (mSamples == 0)
|
||||
glTexImage2D(mTextureTarget, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
else
|
||||
gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
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->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0);
|
||||
gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
|
||||
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, mTextureTarget, mColorBuffer, 0);
|
||||
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, mTextureTarget, mDepthBuffer, 0);
|
||||
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
throw std::runtime_error("Failed to create OpenXR framebuffer");
|
||||
}
|
||||
|
||||
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
|
@ -117,56 +89,38 @@ namespace MWVR
|
|||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
if (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.
|
||||
Log(Debug::Warning) << "destroy() called without a State. Leaking FBOs";
|
||||
Log(Debug::Warning) << "destroy() called without a State. Leaking FBO";
|
||||
|
||||
if (mDepthBuffer)
|
||||
glDeleteTextures(1, &mDepthBuffer);
|
||||
if (mMSAAColorBuffer)
|
||||
glDeleteTextures(1, &mMSAAColorBuffer);
|
||||
if (mMSAADepthBuffer)
|
||||
glDeleteTextures(1, &mMSAADepthBuffer);
|
||||
if (mColorBuffer)
|
||||
glDeleteTextures(1, &mColorBuffer);
|
||||
|
||||
mFBO = mMSAAFBO = mDepthBuffer = mMSAAColorBuffer = mMSAADepthBuffer = 0;
|
||||
mFBO = mDepthBuffer = mColorBuffer;
|
||||
}
|
||||
|
||||
void OpenXRTextureBuffer::beginFrame(osg::GraphicsContext* gc)
|
||||
{
|
||||
auto state = gc->getState();
|
||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
|
||||
|
||||
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* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
if (mMSAASamples == 0)
|
||||
{
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mMSAAFBO);
|
||||
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mFBO);
|
||||
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mBlitFBO);
|
||||
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mFBO);
|
||||
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);
|
||||
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, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRTextureBuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h)
|
||||
{
|
||||
|
|
|
@ -13,22 +13,23 @@ namespace MWVR
|
|||
class OpenXRTextureBuffer : public osg::Referenced
|
||||
{
|
||||
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();
|
||||
|
||||
void destroy(osg::State* state);
|
||||
|
||||
auto width() const { return mWidth; }
|
||||
auto height() const { return mHeight; }
|
||||
auto msaaSamples() const { return mMSAASamples; }
|
||||
auto msaaSamples() const { return mSamples; }
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -39,18 +40,13 @@ namespace MWVR
|
|||
std::size_t mWidth = 0;
|
||||
std::size_t mHeight = 0;
|
||||
|
||||
// Swapchain buffer
|
||||
uint32_t mXRColorBuffer = 0;
|
||||
|
||||
// FBO target for swapchain buffer
|
||||
uint32_t mDepthBuffer = 0;
|
||||
// Render Target
|
||||
uint32_t mFBO = 0;
|
||||
|
||||
// Render targets for MSAA
|
||||
uint32_t mMSAASamples = 0;
|
||||
uint32_t mMSAAFBO = 0;
|
||||
uint32_t mMSAAColorBuffer = 0;
|
||||
uint32_t mMSAADepthBuffer = 0;
|
||||
uint32_t mBlitFBO = 0;
|
||||
uint32_t mDepthBuffer = 0;
|
||||
uint32_t mColorBuffer = 0;
|
||||
uint32_t mSamples = 0;
|
||||
uint32_t mTextureTarget = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,22 +16,22 @@
|
|||
namespace MWVR {
|
||||
|
||||
OpenXRView::OpenXRView(
|
||||
osg::ref_ptr<OpenXRManager> XR)
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
std::string name)
|
||||
: mXR(XR)
|
||||
, mSwapchain(nullptr)
|
||||
, mSwapchainConfig{}
|
||||
, mName(name)
|
||||
{
|
||||
mSwapchainConfig.requestedFormats = {
|
||||
GL_RGBA8,
|
||||
GL_RGBA8_SNORM,
|
||||
};
|
||||
|
||||
mXR->registerToBarrier();
|
||||
}
|
||||
|
||||
OpenXRView::~OpenXRView()
|
||||
{
|
||||
mXR->unregisterFromBarrier();
|
||||
}
|
||||
|
||||
osg::Camera* OpenXRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
|
||||
|
@ -48,7 +48,10 @@ namespace MWVR {
|
|||
camera->setGraphicsContext(gc);
|
||||
|
||||
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));
|
||||
|
||||
return camera.release();
|
||||
|
@ -71,18 +74,28 @@ namespace MWVR {
|
|||
|
||||
void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
Log(Debug::Verbose) << "prerenderCallback";
|
||||
mXR->beginFrame(mFrameIndex);
|
||||
Log(Debug::Verbose) << mName << ": prerenderCallback";
|
||||
if (mSwapchain)
|
||||
{
|
||||
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
if (mSwapchain)
|
||||
mSwapchain->endFrame(renderInfo.getState()->getGraphicsContext());
|
||||
mXR->viewerBarrier();
|
||||
mFrameIndex++;
|
||||
// osg will sometimes call this without a corresponding prerender.
|
||||
Log(Debug::Verbose) << mName << ": postrenderCallback";
|
||||
Log(Debug::Verbose) << renderInfo.getCurrentCamera()->getName() << ": " << renderInfo.getCurrentCamera()->getPreDrawCallback();
|
||||
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)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef OPENXR_VIEW_HPP
|
||||
#define OPENXR_VIEW_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrswapchain.hpp"
|
||||
|
||||
|
@ -8,6 +9,7 @@ struct XrSwapchainSubImage;
|
|||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
class OpenXRView : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
@ -49,7 +51,7 @@ namespace MWVR
|
|||
};
|
||||
|
||||
protected:
|
||||
OpenXRView(osg::ref_ptr<OpenXRManager> XR);
|
||||
OpenXRView(osg::ref_ptr<OpenXRManager> XR, std::string name);
|
||||
virtual ~OpenXRView();
|
||||
void setWidth(int width);
|
||||
void setHeight(int height);
|
||||
|
@ -66,14 +68,14 @@ namespace MWVR
|
|||
OpenXRSwapchain& swapchain(void) { return *mSwapchain; }
|
||||
//! Create the view surface
|
||||
bool realize(osg::ref_ptr<osg::State> state);
|
||||
//! Current frame being rendered
|
||||
long long frameIndex() { return mFrameIndex; };
|
||||
|
||||
protected:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
std::unique_ptr<OpenXRSwapchain> mSwapchain;
|
||||
OpenXRSwapchain::Config mSwapchainConfig;
|
||||
long long mFrameIndex{ 0 };
|
||||
std::string mName{};
|
||||
bool mRendering{ false };
|
||||
osg::ref_ptr<OpenXRView::PredrawCallback> mPredraw{ nullptr };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,20 +18,6 @@ namespace MWVR
|
|||
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
|
||||
{
|
||||
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)
|
||||
|
@ -50,7 +36,7 @@ namespace MWVR
|
|||
const XrCompositionLayerBaseHeader*
|
||||
OpenXRViewer::layer()
|
||||
{
|
||||
auto stageViews = mXR->impl().getStageViews();
|
||||
auto stageViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().mRenderIndex, TrackedSpace::STAGE);
|
||||
mCompositionLayerProjectionViews[0].pose = stageViews[0].pose;
|
||||
mCompositionLayerProjectionViews[1].pose = stageViews[1].pose;
|
||||
mCompositionLayerProjectionViews[0].fov = stageViews[0].fov;
|
||||
|
@ -61,6 +47,8 @@ namespace MWVR
|
|||
void OpenXRViewer::traversals()
|
||||
{
|
||||
Log(Debug::Verbose) << "Pre-Update";
|
||||
mXR->handleEvents();
|
||||
OpenXRFrameIndexer::instance().advanceUpdateIndex();
|
||||
mViewer->updateTraversal();
|
||||
Log(Debug::Verbose) << "Post-Update";
|
||||
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.
|
||||
// (When swapping the window buffer we'll blit the mirror texture to it instead.)
|
||||
mMainCamera->setCullMask(MWRender::Mask_GUI);
|
||||
mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
|
||||
|
||||
osg::Vec4 clearColor = mMainCamera->getClearColor();
|
||||
|
||||
mViews[OpenXRWorldView::LEFT_VIEW] = new OpenXRWorldView(mXR, context->getState(), mMetersPerUnit, OpenXRWorldView::LEFT_VIEW);
|
||||
mViews[OpenXRWorldView::RIGHT_VIEW] = new OpenXRWorldView(mXR, context->getState(), mMetersPerUnit, OpenXRWorldView::RIGHT_VIEW);
|
||||
if (!mXR->realized())
|
||||
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);
|
||||
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(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::RIGHT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::RIGHT_VIEW], context);
|
||||
mViewer->getSlave(OpenXRWorldView::LEFT_VIEW)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::LEFT_VIEW], context);
|
||||
mViewer->getSlave(OpenXRWorldView::RIGHT_VIEW)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mViews[OpenXRWorldView::RIGHT_VIEW], context);
|
||||
|
||||
mViewer->setLightingMode(osg::View::SKY_LIGHT);
|
||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||
|
@ -147,18 +137,24 @@ namespace MWVR
|
|||
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)));
|
||||
mMainCamera->setPreDrawCallback(new OpenXRView::PredrawCallback(mMainCamera, mXRMenu.get()));
|
||||
mMainCamera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(mMainCamera, mXRMenu.get()));
|
||||
mMenuCamera = mXRMenu->createCamera(2, clearColor, context);
|
||||
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());
|
||||
|
||||
mMainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
|
||||
mMainCamera->setGraphicsContext(nullptr);
|
||||
mConfigured = true;
|
||||
}
|
||||
|
||||
void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) const
|
||||
{
|
||||
mMirrorTextureSwapchain->beginFrame(gc);
|
||||
mViews[OpenXRWorldView::LEFT_VIEW]->swapchain().current()->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::LEFT_VIEW]->swapchain().renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width() / 2, 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());
|
||||
}
|
||||
|
@ -172,45 +168,30 @@ namespace MWVR
|
|||
|
||||
void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc)
|
||||
{
|
||||
Timer timer("swapBuffers");
|
||||
|
||||
Log(Debug::Verbose) << "SwapBuffers()";
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (!mConfigured)
|
||||
return;
|
||||
|
||||
auto* state = gc->getState();
|
||||
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();
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -31,20 +31,6 @@ namespace MWVR
|
|||
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
|
||||
{
|
||||
public:
|
||||
|
@ -90,6 +76,7 @@ namespace MWVR
|
|||
//osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> mRightGW;
|
||||
|
||||
osg::Camera* mMainCamera = nullptr;
|
||||
osg::Camera* mMenuCamera = nullptr;
|
||||
osg::Camera* mLeftCamera = nullptr;
|
||||
osg::Camera* mRightCamera = nullptr;
|
||||
|
||||
|
|
|
@ -75,9 +75,10 @@ namespace MWVR
|
|||
return osg::Matrix(matrix);
|
||||
}
|
||||
|
||||
|
||||
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 far = Settings::Manager::getFloat("viewing distance", "Camera") * mMetersPerUnit;
|
||||
|
@ -88,11 +89,11 @@ namespace MWVR
|
|||
osg::Matrix OpenXRWorldView::viewMatrix()
|
||||
{
|
||||
osg::Matrix viewMatrix;
|
||||
auto hmdViews = mXR->impl().getHmdViews();
|
||||
auto hmdViews = mXR->impl().getPredictedViews(OpenXRFrameIndexer::instance().updateIndex(), TrackedSpace::VIEW);
|
||||
auto pose = hmdViews[mView].pose;
|
||||
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;
|
||||
|
||||
// Comfort shortcut.
|
||||
|
@ -115,8 +116,8 @@ namespace MWVR
|
|||
}
|
||||
|
||||
OpenXRWorldView::OpenXRWorldView(
|
||||
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view)
|
||||
: OpenXRView(XR)
|
||||
osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view)
|
||||
: OpenXRView(XR, name)
|
||||
, mMetersPerUnit(metersPerUnit)
|
||||
, mView(view)
|
||||
{
|
||||
|
@ -145,4 +146,44 @@ namespace MWVR
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,27 @@ namespace MWVR
|
|||
SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input.
|
||||
};
|
||||
|
||||
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||
{
|
||||
public:
|
||||
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view);
|
||||
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:
|
||||
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, float metersPerUnit, SubView view);
|
||||
~OpenXRWorldView();
|
||||
|
||||
//! Prepare for render (update matrices)
|
||||
void prerenderCallback(osg::RenderInfo& renderInfo) override;
|
||||
//! Projection offset for this view
|
||||
osg::Matrix projectionMatrix();
|
||||
//! View offset for this view
|
||||
|
|
Loading…
Reference in a new issue