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:
parent
51125d4f3e
commit
951879240c
17 changed files with 449 additions and 547 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <<(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue