first commit
This commit is contained in:
Vendored
+188
@@ -0,0 +1,188 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Action.h"
|
||||
#include "Path.h"
|
||||
#include "Session.h"
|
||||
#include "Space.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Action::Action(ActionSet *actionSet,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
XrActionType type) :
|
||||
_actionSet(actionSet),
|
||||
_createInfo{ XR_TYPE_ACTION_CREATE_INFO },
|
||||
_action(XR_NULL_HANDLE)
|
||||
{
|
||||
strncpy(_createInfo.actionName, name.c_str(),
|
||||
XR_MAX_ACTION_NAME_SIZE - 1);
|
||||
strncpy(_createInfo.localizedActionName, localizedName.c_str(),
|
||||
XR_MAX_LOCALIZED_ACTION_NAME_SIZE - 1);
|
||||
_createInfo.actionType = type;
|
||||
}
|
||||
|
||||
Action::~Action()
|
||||
{
|
||||
if (_action != XR_NULL_HANDLE)
|
||||
{
|
||||
check(xrDestroyAction(_action),
|
||||
"Failed to destroy OpenXR action");
|
||||
}
|
||||
}
|
||||
|
||||
void Action::addSubaction(const Path &path)
|
||||
{
|
||||
assert(path.getInstance() == getInstance());
|
||||
_subactionPaths.push_back(path.getXrPath());
|
||||
}
|
||||
|
||||
bool Action::init()
|
||||
{
|
||||
if (valid())
|
||||
return true;
|
||||
|
||||
if (!_subactionPaths.empty())
|
||||
{
|
||||
_createInfo.countSubactionPaths = _subactionPaths.size();
|
||||
_createInfo.subactionPaths = _subactionPaths.data();
|
||||
}
|
||||
return check(xrCreateAction(getXrActionSet(), &_createInfo, &_action),
|
||||
"Failed to create OpenXR action");
|
||||
}
|
||||
|
||||
ActionStateBase::ActionStateBase(Action *action, Session *session,
|
||||
Path subactionPath) :
|
||||
_action(action),
|
||||
_session(session),
|
||||
_subactionPath(subactionPath),
|
||||
_valid(false),
|
||||
_syncCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
ActionStateBase::~ActionStateBase()
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionStateBase::checkUpdate()
|
||||
{
|
||||
unsigned int sessionSyncCount = _session->getActionSyncCount();
|
||||
// If an xrSyncActions has taken place, the state is out of date
|
||||
bool needsUpdate = (_syncCount < sessionSyncCount);
|
||||
// Update the counter as caller is expected to update the state
|
||||
_syncCount = sessionSyncCount;
|
||||
return needsUpdate;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonBoolean::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_BOOLEAN };
|
||||
|
||||
_valid = check(xrGetActionStateBoolean(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get boolean OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonFloat::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_FLOAT };
|
||||
|
||||
_valid = check(xrGetActionStateFloat(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get float OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonVector2f::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_VECTOR2F };
|
||||
|
||||
_valid = check(xrGetActionStateVector2f(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get vector2f OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonPose::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_POSE };
|
||||
|
||||
_valid = check(xrGetActionStatePose(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get pose OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
ActionStatePose::ActionStatePose(ActionPose *action, Session *session,
|
||||
Path subactionPath) :
|
||||
Base(action, session, subactionPath),
|
||||
_space(new Space(session, action, subactionPath))
|
||||
{
|
||||
}
|
||||
|
||||
ActionStatePose::~ActionStatePose()
|
||||
{
|
||||
}
|
||||
|
||||
ActionStateVibration::ActionStateVibration(ActionVibration *action,
|
||||
Session *session,
|
||||
Path subactionPath) :
|
||||
_action(action),
|
||||
_session(session),
|
||||
_subactionPath(subactionPath)
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionStateVibration::applyHapticFeedback(int64_t duration_ns,
|
||||
float frequency,
|
||||
float amplitude) const
|
||||
{
|
||||
XrHapticActionInfo actionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
actionInfo.action = _action->getXrAction();
|
||||
actionInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
|
||||
vibration.duration = duration_ns;
|
||||
vibration.frequency = frequency;
|
||||
vibration.amplitude = amplitude;
|
||||
|
||||
return check(xrApplyHapticFeedback(_session->getXrSession(), &actionInfo,
|
||||
reinterpret_cast<XrHapticBaseHeader*>(&vibration)),
|
||||
"Failed to apply haptic feedback");
|
||||
}
|
||||
|
||||
bool ActionStateVibration::stopHapticFeedback() const
|
||||
{
|
||||
XrHapticActionInfo actionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
actionInfo.action = _action->getXrAction();
|
||||
actionInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
return check(xrStopHapticFeedback(_session->getXrSession(), &actionInfo),
|
||||
"Failed to stop haptic feedback");
|
||||
}
|
||||
Vendored
+371
@@ -0,0 +1,371 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_ACTION
|
||||
#define OSGXR_OPENXR_ACTION 1
|
||||
|
||||
#include "ActionSet.h"
|
||||
#include "Path.h"
|
||||
|
||||
#include <osg/Vec2f>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Path;
|
||||
class Space;
|
||||
|
||||
class Action : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
Action(ActionSet *actionSet,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
XrActionType type);
|
||||
virtual ~Action();
|
||||
|
||||
// Action initialisation
|
||||
|
||||
void addSubaction(const Path &path);
|
||||
|
||||
// Returns true on success
|
||||
bool init();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _action != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _actionSet->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<ActionSet> getActionSet() const
|
||||
{
|
||||
return _actionSet;
|
||||
}
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _actionSet->getInstance();
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _actionSet->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrActionSet getXrActionSet() const
|
||||
{
|
||||
return _actionSet->getXrActionSet();
|
||||
}
|
||||
|
||||
inline XrAction getXrAction() const
|
||||
{
|
||||
return _action;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Action data
|
||||
osg::ref_ptr<ActionSet> _actionSet;
|
||||
std::vector<XrPath> _subactionPaths;
|
||||
XrActionCreateInfo _createInfo;
|
||||
XrAction _action;
|
||||
};
|
||||
|
||||
/// Base action state for inputs.
|
||||
class ActionStateBase : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
ActionStateBase(Action *action, Session *session,
|
||||
Path subactionPath = Path());
|
||||
virtual ~ActionStateBase();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _valid;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _action->check(result, warnMsg);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Utilities for synchronisation
|
||||
|
||||
// Find whether the state needs update and update sync counter
|
||||
bool checkUpdate();
|
||||
|
||||
// Member data
|
||||
|
||||
osg::ref_ptr<Action> _action;
|
||||
osg::ref_ptr<Session> _session;
|
||||
Path _subactionPath;
|
||||
bool _valid;
|
||||
unsigned int _syncCount;
|
||||
};
|
||||
|
||||
/// All action states have an isActive field.
|
||||
template <typename T>
|
||||
class ActionStateCommon : public ActionStateBase
|
||||
{
|
||||
private:
|
||||
|
||||
typedef ActionStateBase Base;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
ActionStateCommon(Action *action, Session *session,
|
||||
Path subactionPath = Path()) :
|
||||
Base(action, session, subactionPath)
|
||||
{
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
bool isActive() const
|
||||
{
|
||||
assert(valid());
|
||||
return _state.isActive;
|
||||
}
|
||||
|
||||
// Operations
|
||||
|
||||
/// Update state if a sync has taken place
|
||||
bool update()
|
||||
{
|
||||
if (Base::checkUpdate())
|
||||
return updateState();
|
||||
return valid();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Protected operations
|
||||
|
||||
bool updateState();
|
||||
|
||||
// Data members
|
||||
|
||||
T _state;
|
||||
};
|
||||
|
||||
// These are the base action state classes
|
||||
typedef ActionStateCommon<XrActionStateBoolean> ActionStateCommonBoolean;
|
||||
typedef ActionStateCommon<XrActionStateFloat> ActionStateCommonFloat;
|
||||
typedef ActionStateCommon<XrActionStateVector2f> ActionStateCommonVector2f;
|
||||
typedef ActionStateCommon<XrActionStatePose> ActionStateCommonPose;
|
||||
|
||||
// Convert action values to app / OSG friendly formats
|
||||
template <typename T>
|
||||
struct ActionTypeInfo;
|
||||
|
||||
// XrBool32 -> bool
|
||||
template <>
|
||||
struct ActionTypeInfo<XrActionStateBoolean>
|
||||
{
|
||||
static bool convert(XrBool32 value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool defaultValue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// float -> float
|
||||
template <>
|
||||
struct ActionTypeInfo<XrActionStateFloat>
|
||||
{
|
||||
static float convert(float value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
static float defaultValue()
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
// XrVector2f -> osg::Vec2f
|
||||
template <>
|
||||
struct ActionTypeInfo<XrActionStateVector2f>
|
||||
{
|
||||
static osg::Vec2f convert(const XrVector2f &value)
|
||||
{
|
||||
return osg::Vec2f(value.x, value.y);
|
||||
}
|
||||
|
||||
static osg::Vec2f defaultValue()
|
||||
{
|
||||
return osg::Vec2f(0.0f, 0.0f);
|
||||
}
|
||||
};
|
||||
|
||||
/// Some action states have currentValue and related fields.
|
||||
template <typename T>
|
||||
class ActionStateSimple : public ActionStateCommon<T>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef ActionStateCommon<T> Base;
|
||||
|
||||
public:
|
||||
|
||||
typedef ActionTypeInfo<T> Info;
|
||||
|
||||
// Constructors
|
||||
|
||||
ActionStateSimple(Action *action, Session *session,
|
||||
Path subactionPath = Path()) :
|
||||
Base(action, session, subactionPath)
|
||||
{
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
auto getCurrentState() const
|
||||
{
|
||||
assert(this->valid());
|
||||
return Info::convert(Base::_state.currentState);
|
||||
}
|
||||
|
||||
bool hasChangedSinceLastSync() const
|
||||
{
|
||||
assert(this->valid());
|
||||
return Base::_state.changedSinceLastSync;
|
||||
}
|
||||
|
||||
XrTime getLastChangedTime() const
|
||||
{
|
||||
assert(this->valid());
|
||||
return Base::_state.lastChangedTime;
|
||||
}
|
||||
};
|
||||
|
||||
// These are the simple action state classes
|
||||
typedef ActionStateSimple<XrActionStateBoolean> ActionStateBoolean;
|
||||
typedef ActionStateSimple<XrActionStateFloat> ActionStateFloat;
|
||||
typedef ActionStateSimple<XrActionStateVector2f> ActionStateVector2f;
|
||||
|
||||
/// Specialise Action for a specific input type
|
||||
template <XrActionType type, typename T>
|
||||
class ActionTyped : public Action
|
||||
{
|
||||
public:
|
||||
|
||||
typedef T State;
|
||||
|
||||
ActionTyped(ActionSet *actionSet,
|
||||
const std::string &name,
|
||||
const std::string &localizedName) :
|
||||
Action(actionSet, name, localizedName, type)
|
||||
{
|
||||
}
|
||||
|
||||
osg::ref_ptr<State> createState(Session *session,
|
||||
Path subactionPath = Path())
|
||||
{
|
||||
return new State(this, session, subactionPath);
|
||||
}
|
||||
};
|
||||
|
||||
// So ActionStatePose etc can take ActionPose etc in constructor
|
||||
class ActionStatePose;
|
||||
class ActionStateVibration;
|
||||
|
||||
// These are the final typed action classes
|
||||
typedef ActionTyped<XR_ACTION_TYPE_BOOLEAN_INPUT, ActionStateBoolean> ActionBoolean;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_FLOAT_INPUT, ActionStateFloat> ActionFloat;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_VECTOR2F_INPUT, ActionStateVector2f> ActionVector2f;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_POSE_INPUT, ActionStatePose> ActionPose;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_VIBRATION_OUTPUT, ActionStateVibration> ActionVibration;
|
||||
|
||||
/// Pose actions have their own way to get the pose
|
||||
class ActionStatePose : public ActionStateCommon<XrActionStatePose>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef ActionStateCommon<XrActionStatePose> Base;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
ActionStatePose(ActionPose *action, Session *session,
|
||||
Path subactionPath = Path());
|
||||
~ActionStatePose();
|
||||
|
||||
// Accessors
|
||||
|
||||
Space *getSpace()
|
||||
{
|
||||
return _space.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<Space> _space;
|
||||
};
|
||||
|
||||
class ActionStateVibration : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
ActionStateVibration(ActionVibration *action, Session *session,
|
||||
Path subactionPath = Path());
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _action->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Haptic vibrations
|
||||
|
||||
bool applyHapticFeedback(int64_t duration_ns, float frequency,
|
||||
float amplitude) const;
|
||||
bool stopHapticFeedback() const;
|
||||
|
||||
protected:
|
||||
|
||||
// Member data
|
||||
|
||||
osg::ref_ptr<Action> _action;
|
||||
osg::ref_ptr<Session> _session;
|
||||
Path _subactionPath;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "ActionSet.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
ActionSet::ActionSet(Instance *instance,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
uint32_t priority) :
|
||||
_instance(instance),
|
||||
_actionSet(XR_NULL_HANDLE)
|
||||
{
|
||||
XrActionSetCreateInfo createInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||
strncpy(createInfo.actionSetName, name.c_str(),
|
||||
XR_MAX_ACTION_SET_NAME_SIZE - 1);
|
||||
strncpy(createInfo.localizedActionSetName, localizedName.c_str(),
|
||||
XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE - 1);
|
||||
createInfo.priority = priority;
|
||||
|
||||
check(xrCreateActionSet(getXrInstance(), &createInfo, &_actionSet),
|
||||
"Failed to create OpenXR action set");
|
||||
}
|
||||
|
||||
ActionSet::~ActionSet()
|
||||
{
|
||||
if (_actionSet != XR_NULL_HANDLE)
|
||||
{
|
||||
check(xrDestroyActionSet(_actionSet),
|
||||
"Failed to destroy OpenXR action set");
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_ACTION_SET
|
||||
#define OSGXR_OPENXR_ACTION_SET 1
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class ActionSet : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
ActionSet(Instance *instance,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
uint32_t priority);
|
||||
virtual ~ActionSet();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _actionSet != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _instance->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrActionSet getXrActionSet() const
|
||||
{
|
||||
return _actionSet;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Action set data
|
||||
osg::ref_ptr<Instance> _instance;
|
||||
XrActionSet _actionSet;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Compositor.h"
|
||||
#include "DepthInfo.h"
|
||||
#include "Space.h"
|
||||
#include "SwapchainGroupSubImage.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
// CompositionLayerProjection
|
||||
|
||||
void CompositionLayerProjection::addView(osg::ref_ptr<Session::Frame> frame, uint32_t viewIndex,
|
||||
const SwapchainGroup::SubImage &subImage,
|
||||
const DepthInfo *depthInfo)
|
||||
{
|
||||
assert(viewIndex < _projViews.size());
|
||||
|
||||
XrCompositionLayerProjectionView &projView = _projViews[viewIndex];
|
||||
projView = { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW };
|
||||
projView.pose = frame->getViewPose(viewIndex);
|
||||
projView.fov = frame->getViewFov(viewIndex);
|
||||
subImage.getXrSubImage(&projView.subImage);
|
||||
|
||||
if (depthInfo && subImage.depthValid())
|
||||
{
|
||||
// depth info
|
||||
XrCompositionLayerDepthInfoKHR &xrDepthInfo = _depthInfos[viewIndex];
|
||||
xrDepthInfo = { XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR };
|
||||
subImage.getDepthXrSubImage(&xrDepthInfo.subImage);
|
||||
xrDepthInfo.minDepth = depthInfo->getMinDepth();
|
||||
xrDepthInfo.maxDepth = depthInfo->getMaxDepth();
|
||||
xrDepthInfo.nearZ = depthInfo->getNearZ();
|
||||
xrDepthInfo.farZ = depthInfo->getFarZ();
|
||||
|
||||
// add depth info to projection view chain
|
||||
projView.next = &xrDepthInfo;
|
||||
}
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader *CompositionLayerProjection::getXr()
|
||||
{
|
||||
unsigned int validDepthInfos = 0;
|
||||
for (unsigned int i = 0; i < _projViews.size(); ++i)
|
||||
{
|
||||
auto &view = _projViews[i];
|
||||
auto &depthInfo = _depthInfos[i];
|
||||
if (view.type != XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW)
|
||||
{
|
||||
// Eek, some views have been omitted!
|
||||
OSG_WARN << "Partial projection views!" << std::endl;
|
||||
}
|
||||
|
||||
if (depthInfo.type == XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR)
|
||||
++validDepthInfos;
|
||||
}
|
||||
|
||||
// Sanity check that depth info is entirely missing or complete
|
||||
if (validDepthInfos > 0 && validDepthInfos < _projViews.size())
|
||||
{
|
||||
OSG_WARN << "Partial projection depth info, disabling depth information" << std::endl;
|
||||
for (auto &view: _projViews)
|
||||
view.next = nullptr;
|
||||
}
|
||||
|
||||
_layer.layerFlags = _layerFlags;
|
||||
_layer.space = _space->getXrSpace();
|
||||
_layer.viewCount = _projViews.size();
|
||||
_layer.views = _projViews.data();
|
||||
return reinterpret_cast<const XrCompositionLayerBaseHeader*>(&_layer);
|
||||
}
|
||||
|
||||
// CompositionLayerQuad
|
||||
|
||||
void CompositionLayerQuad::setSubImage(const SwapchainGroup::SubImage &subImage)
|
||||
{
|
||||
subImage.getXrSubImage(&_layer.subImage);
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader *CompositionLayerQuad::getXr()
|
||||
{
|
||||
_layer.layerFlags = _layerFlags;
|
||||
_layer.space = _space->getXrSpace();
|
||||
return reinterpret_cast<const XrCompositionLayerBaseHeader*>(&_layer);
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_COMPOSITOR
|
||||
#define OSGXR_OPENXR_COMPOSITOR 1
|
||||
|
||||
#include "Session.h"
|
||||
#include "Space.h"
|
||||
#include "SwapchainGroup.h"
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class DepthInfo;
|
||||
|
||||
class CompositionLayer : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
CompositionLayer() :
|
||||
_layerFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CompositionLayer()
|
||||
{
|
||||
}
|
||||
|
||||
inline XrCompositionLayerFlags getLayerFlags() const
|
||||
{
|
||||
return _layerFlags;
|
||||
}
|
||||
inline void setLayerFlags(XrCompositionLayerFlags layerFlags)
|
||||
{
|
||||
_layerFlags = layerFlags;
|
||||
}
|
||||
|
||||
inline Space *getSpace() const
|
||||
{
|
||||
return _space;
|
||||
}
|
||||
inline void setSpace(Space *space)
|
||||
{
|
||||
_space = space;
|
||||
}
|
||||
|
||||
virtual const XrCompositionLayerBaseHeader *getXr() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
XrCompositionLayerFlags _layerFlags;
|
||||
osg::ref_ptr<Space> _space;
|
||||
};
|
||||
|
||||
class CompositionLayerProjection : public CompositionLayer
|
||||
{
|
||||
public:
|
||||
|
||||
CompositionLayerProjection(unsigned int viewCount)
|
||||
{
|
||||
_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
||||
_layer.next = nullptr;
|
||||
_projViews.resize(viewCount);
|
||||
_depthInfos.resize(viewCount);
|
||||
}
|
||||
|
||||
virtual ~CompositionLayerProjection()
|
||||
{
|
||||
}
|
||||
|
||||
void addView(osg::ref_ptr<Session::Frame> frame, uint32_t viewIndex,
|
||||
const SwapchainGroup::SubImage &subImage,
|
||||
const DepthInfo *depthInfo = nullptr);
|
||||
|
||||
const XrCompositionLayerBaseHeader *getXr() override;
|
||||
|
||||
protected:
|
||||
|
||||
mutable XrCompositionLayerProjection _layer;
|
||||
std::vector<XrCompositionLayerProjectionView> _projViews;
|
||||
std::vector<XrCompositionLayerDepthInfoKHR> _depthInfos;
|
||||
};
|
||||
|
||||
class CompositionLayerQuad : public CompositionLayer
|
||||
{
|
||||
public:
|
||||
|
||||
CompositionLayerQuad() :
|
||||
_layer{ XR_TYPE_COMPOSITION_LAYER_QUAD }
|
||||
{
|
||||
_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
|
||||
_layer.subImage.swapchain = XR_NULL_HANDLE;
|
||||
_layer.pose.orientation = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
}
|
||||
|
||||
virtual ~CompositionLayerQuad()
|
||||
{
|
||||
}
|
||||
|
||||
inline XrEyeVisibility getEyeVisibility() const
|
||||
{
|
||||
return _layer.eyeVisibility;
|
||||
}
|
||||
inline void setEyeVisibility(XrEyeVisibility eyeVisibility)
|
||||
{
|
||||
_layer.eyeVisibility = eyeVisibility;
|
||||
}
|
||||
|
||||
void setSubImage(const SwapchainGroup::SubImage &subImage);
|
||||
|
||||
inline osg::Quat getOrientation() const
|
||||
{
|
||||
return osg::Quat(_layer.pose.orientation.x,
|
||||
_layer.pose.orientation.y,
|
||||
_layer.pose.orientation.z,
|
||||
_layer.pose.orientation.w);
|
||||
}
|
||||
inline void setOrientation(const osg::Quat &quat)
|
||||
{
|
||||
_layer.pose.orientation.x = quat.x();
|
||||
_layer.pose.orientation.y = quat.y();
|
||||
_layer.pose.orientation.z = quat.z();
|
||||
_layer.pose.orientation.w = quat.w();
|
||||
}
|
||||
|
||||
inline osg::Vec3f getPosition() const
|
||||
{
|
||||
return osg::Vec3f(_layer.pose.position.x,
|
||||
_layer.pose.position.y,
|
||||
_layer.pose.position.z);
|
||||
}
|
||||
inline void setPosition(const osg::Vec3f &pos)
|
||||
{
|
||||
_layer.pose.position.x = pos.x();
|
||||
_layer.pose.position.y = pos.y();
|
||||
_layer.pose.position.z = pos.z();
|
||||
}
|
||||
|
||||
inline osg::Vec2f getSize() const
|
||||
{
|
||||
return osg::Vec2f(_layer.size.width,
|
||||
_layer.size.height);
|
||||
}
|
||||
inline void setSize(const osg::Vec2f &size)
|
||||
{
|
||||
_layer.size.width = size.x();
|
||||
_layer.size.height = size.y();
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader *getXr() override;
|
||||
|
||||
protected:
|
||||
|
||||
mutable XrCompositionLayerQuad _layer;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_DEPTH_INFO
|
||||
#define OSGXR_OPENXR_DEPTH_INFO 1
|
||||
|
||||
#include <osg/Matrixd>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
// Represents depth information for a view
|
||||
class DepthInfo
|
||||
{
|
||||
public:
|
||||
|
||||
DepthInfo() :
|
||||
_minDepth(0),
|
||||
_maxDepth(1),
|
||||
_nearZ(1),
|
||||
_farZ(10)
|
||||
{
|
||||
}
|
||||
|
||||
// Mutators
|
||||
|
||||
void setDepthRange(float minDepth, float maxDepth)
|
||||
{
|
||||
_minDepth = minDepth;
|
||||
_maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
void setZRange(float nearZ, float farZ)
|
||||
{
|
||||
_nearZ = nearZ;
|
||||
_farZ = farZ;
|
||||
}
|
||||
|
||||
void setZRangeFromProjection(const osg::Matrixd &proj)
|
||||
{
|
||||
float left, right, bottom, top;
|
||||
proj.getFrustum(left, right, bottom, top, _nearZ, _farZ);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
float getMinDepth() const
|
||||
{
|
||||
return _minDepth;
|
||||
}
|
||||
|
||||
float getMaxDepth() const
|
||||
{
|
||||
return _maxDepth;
|
||||
}
|
||||
|
||||
float getNearZ() const
|
||||
{
|
||||
return _nearZ;
|
||||
}
|
||||
|
||||
float getFarZ() const
|
||||
{
|
||||
return _farZ;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
float _minDepth;
|
||||
float _maxDepth;
|
||||
float _nearZ;
|
||||
float _farZ;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "EventHandler.h"
|
||||
#include "Instance.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
void EventHandler::onEvent(Instance *instance,
|
||||
const XrEventDataBuffer *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case XR_TYPE_EVENT_DATA_EVENTS_LOST:
|
||||
onEventsLost(instance,
|
||||
reinterpret_cast<const XrEventDataEventsLost *>(event));
|
||||
break;
|
||||
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
|
||||
onInstanceLossPending(instance,
|
||||
reinterpret_cast<const XrEventDataInstanceLossPending *>(event));
|
||||
break;
|
||||
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
|
||||
{
|
||||
auto *profileEvent = reinterpret_cast<const XrEventDataInteractionProfileChanged *>(event);
|
||||
Session *session = instance->getSession(profileEvent->session);
|
||||
if (session)
|
||||
onInteractionProfileChanged(session, profileEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR interaction profile changed event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
|
||||
{
|
||||
auto *spaceEvent = reinterpret_cast<const XrEventDataReferenceSpaceChangePending *>(event);
|
||||
Session *session = instance->getSession(spaceEvent->session);
|
||||
if (session)
|
||||
onReferenceSpaceChangePending(session, spaceEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR reference space change pending event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR:
|
||||
{
|
||||
auto *maskEvent = reinterpret_cast<const XrEventDataVisibilityMaskChangedKHR *>(event);
|
||||
Session *session = instance->getSession(maskEvent->session);
|
||||
if (session)
|
||||
onVisibilityMaskChanged(session, maskEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR visibility mask change event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
||||
{
|
||||
auto *stateEvent = reinterpret_cast<const XrEventDataSessionStateChanged *>(event);
|
||||
Session *session = instance->getSession(stateEvent->session);
|
||||
if (session)
|
||||
onSessionStateChanged(session, stateEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR session state change event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
onUnhandledEvent(instance, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::onUnhandledEvent(Instance *instance,
|
||||
const XrEventDataBuffer *event)
|
||||
{
|
||||
OSG_WARN << "Unhandled OpenXR Event: " << event->type << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onEventsLost(Instance *instance,
|
||||
const XrEventDataEventsLost *event)
|
||||
{
|
||||
OSG_WARN << event->lostEventCount << " OpenXR events lost" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onInstanceLossPending(Instance *instance,
|
||||
const XrEventDataInstanceLossPending *event)
|
||||
{
|
||||
OSG_WARN << "OpenXR instance loss pending" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onInteractionProfileChanged(Session *session,
|
||||
const XrEventDataInteractionProfileChanged *event)
|
||||
{
|
||||
OSG_WARN << "OpenXR interaction profile changed" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onReferenceSpaceChangePending(Session *session,
|
||||
const XrEventDataReferenceSpaceChangePending *event)
|
||||
{
|
||||
OSG_WARN << "OpenXR reference space change penging" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onVisibilityMaskChanged(Session *session,
|
||||
const XrEventDataVisibilityMaskChangedKHR *event)
|
||||
{
|
||||
session->updateVisibilityMasks(event->viewConfigurationType,
|
||||
event->viewIndex);
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateChanged(Session *session,
|
||||
const XrEventDataSessionStateChanged *event)
|
||||
{
|
||||
XrSessionState oldState = session->getState();
|
||||
session->setState(event->state);
|
||||
switch (event->state)
|
||||
{
|
||||
case XR_SESSION_STATE_IDLE:
|
||||
// Either starting or soon to be stopping
|
||||
if (oldState == XR_SESSION_STATE_UNKNOWN)
|
||||
onSessionStateStart(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_READY:
|
||||
// Session ready to begin
|
||||
onSessionStateReady(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_SYNCHRONIZED:
|
||||
// Either session synchronised or no longer visible
|
||||
break;
|
||||
case XR_SESSION_STATE_VISIBLE:
|
||||
// Either session now visible or lost focus
|
||||
if (oldState == XR_SESSION_STATE_FOCUSED)
|
||||
onSessionStateUnfocus(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_FOCUSED:
|
||||
// Session visible and in focus
|
||||
onSessionStateFocus(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_STOPPING:
|
||||
// Session now stopping
|
||||
onSessionStateStopping(session, false);
|
||||
break;
|
||||
case XR_SESSION_STATE_LOSS_PENDING:
|
||||
// Session loss is pending, which can happen at any time
|
||||
if (oldState == XR_SESSION_STATE_FOCUSED)
|
||||
onSessionStateUnfocus(session);
|
||||
if (session->isRunning())
|
||||
onSessionStateStopping(session, true);
|
||||
// Attempt restart
|
||||
onSessionStateEnd(session, true);
|
||||
break;
|
||||
case XR_SESSION_STATE_EXITING:
|
||||
// Session is exiting and should be cleaned up
|
||||
onSessionStateEnd(session, false);
|
||||
break;
|
||||
default:
|
||||
OSG_WARN << "Unknown OpenXR session state: " << event->state << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateStart(Session *session)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateEnd(Session *session, bool retry)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateReady(Session *session)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateStopping(Session *session, bool loss)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateFocus(Session *session)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateUnfocus(Session *session)
|
||||
{
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_EVENT_HANDLER
|
||||
#define OSGXR_OPENXR_EVENT_HANDLER 1
|
||||
|
||||
#include <osg/Referenced>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Instance;
|
||||
class Session;
|
||||
|
||||
/// This class handles OpenXR events.
|
||||
class EventHandler : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// Instance events
|
||||
|
||||
/// Top level OpenXR event handler.
|
||||
void onEvent(Instance *instance, const XrEventDataBuffer *event);
|
||||
/// Handle an otherwise unhandled event.
|
||||
virtual void onUnhandledEvent(Instance *instance,
|
||||
const XrEventDataBuffer *event);
|
||||
|
||||
/// Handle an events lost event.
|
||||
virtual void onEventsLost(Instance *instance,
|
||||
const XrEventDataEventsLost *event);
|
||||
/// Handle an instance loss pending event.
|
||||
virtual void onInstanceLossPending(Instance *instance,
|
||||
const XrEventDataInstanceLossPending *event);
|
||||
|
||||
// Session events
|
||||
|
||||
/// Handle an interaction profile changed event.
|
||||
virtual void onInteractionProfileChanged(Session *session,
|
||||
const XrEventDataInteractionProfileChanged *event);
|
||||
/// Handle a reference space change pending event.
|
||||
virtual void onReferenceSpaceChangePending(Session *session,
|
||||
const XrEventDataReferenceSpaceChangePending *event);
|
||||
/// Handle a visibility mask change event.
|
||||
virtual void onVisibilityMaskChanged(Session *session,
|
||||
const XrEventDataVisibilityMaskChangedKHR *event);
|
||||
/// Handle a session state change event.
|
||||
virtual void onSessionStateChanged(Session *session,
|
||||
const XrEventDataSessionStateChanged *event);
|
||||
|
||||
// Session state events
|
||||
|
||||
/// Transition into initial idle state (idle, after init).
|
||||
virtual void onSessionStateStart(Session *session);
|
||||
/// Transition into ending state (exiting / loss pending, before cleanup).
|
||||
virtual void onSessionStateEnd(Session *session, bool retry);
|
||||
|
||||
/// Transition into a ready state.
|
||||
virtual void onSessionStateReady(Session *session);
|
||||
/// Transition out of running state (stopping, before end).
|
||||
virtual void onSessionStateStopping(Session *session, bool loss);
|
||||
|
||||
/// Transition into focused session state.
|
||||
virtual void onSessionStateFocus(Session *session);
|
||||
/// Transition out of focused session state.
|
||||
virtual void onSessionStateUnfocus(Session *session);
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "GraphicsBinding.h"
|
||||
#include "GraphicsBindingWin32.h"
|
||||
#include "GraphicsBindingX11.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class GraphicsBindingProxy : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
virtual ~GraphicsBindingProxy() {}
|
||||
|
||||
virtual GraphicsBinding *create(osgViewer::GraphicsWindow *window) = 0;
|
||||
};
|
||||
|
||||
template <typename GRAPHICS_BINDING>
|
||||
class GraphicsBindingProxyImpl : public GraphicsBindingProxy
|
||||
{
|
||||
protected:
|
||||
typedef GRAPHICS_BINDING Binding;
|
||||
typedef typename Binding::GraphicsWindow Window;
|
||||
|
||||
virtual ~GraphicsBindingProxyImpl() {}
|
||||
|
||||
public:
|
||||
GraphicsBinding *create(osgViewer::GraphicsWindow *window) override
|
||||
{
|
||||
Window *win = dynamic_cast<Window *>(window);
|
||||
if (!win)
|
||||
return nullptr;
|
||||
|
||||
return new Binding(win);
|
||||
}
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
typedef std::vector<osg::ref_ptr<GraphicsBindingProxy> > ProxyList;
|
||||
|
||||
static ProxyList proxies = {
|
||||
#ifdef OSGXR_USE_WIN32
|
||||
new GraphicsBindingProxyImpl<GraphicsBindingWin32>(),
|
||||
#endif
|
||||
#ifdef OSGXR_USE_X11
|
||||
new GraphicsBindingProxyImpl<GraphicsBindingX11>(),
|
||||
#endif
|
||||
};
|
||||
|
||||
osg::ref_ptr<GraphicsBinding> osgXR::OpenXR::createGraphicsBinding(osgViewer::GraphicsWindow *window)
|
||||
{
|
||||
GraphicsBinding *ret = nullptr;
|
||||
for (GraphicsBindingProxy *proxy: proxies)
|
||||
{
|
||||
ret = proxy->create(window);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_GRAPHICS_BINDING
|
||||
#define OSGXR_OPENXR_GRAPHICS_BINDING 1
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class GraphicsBinding : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
virtual ~GraphicsBinding() { }
|
||||
|
||||
virtual void *getXrGraphicsBinding() = 0;
|
||||
};
|
||||
|
||||
template <typename GRAPHICS_WINDOW, typename XR_BINDING>
|
||||
class GraphicsBindingImpl : public GraphicsBinding
|
||||
{
|
||||
public:
|
||||
typedef GRAPHICS_WINDOW GraphicsWindow;
|
||||
|
||||
GraphicsBindingImpl(GraphicsWindow *window);
|
||||
virtual ~GraphicsBindingImpl() {}
|
||||
|
||||
void *getXrGraphicsBinding() override
|
||||
{
|
||||
return &_binding;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
XR_BINDING _binding;
|
||||
};
|
||||
|
||||
osg::ref_ptr<GraphicsBinding> createGraphicsBinding(osgViewer::GraphicsWindow *window);
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "GraphicsBindingWin32.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
template <>
|
||||
GraphicsBindingWin32::GraphicsBindingImpl(osgViewer::GraphicsWindowWin32 *window) :
|
||||
_binding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR }
|
||||
{
|
||||
_binding.hDC = window->getHDC();
|
||||
_binding.hGLRC = window->getWGLContext();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_GRAPHICS_BINDING_WIN32
|
||||
#define OSGXR_OPENXR_GRAPHICS_BINDING_WIN32 1
|
||||
|
||||
#ifdef OSGXR_USE_WIN32
|
||||
|
||||
#include "GraphicsBinding.h"
|
||||
|
||||
#include <osgViewer/api/Win32/GraphicsWindowWin32>
|
||||
#include <unknwn.h>
|
||||
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#define XR_USE_PLATFORM_WIN32
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
typedef GraphicsBindingImpl<osgViewer::GraphicsWindowWin32, XrGraphicsBindingOpenGLWin32KHR> GraphicsBindingWin32;
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif // OSGXR_USE_WIN32
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "GraphicsBindingX11.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Class to spy on protected members of GraphicsWindowX11.
|
||||
class GraphicsWindowX11Spy : public osgViewer::GraphicsWindowX11
|
||||
{
|
||||
public:
|
||||
const XVisualInfo *getVisualInfo() const
|
||||
{
|
||||
return _visualInfo;
|
||||
}
|
||||
|
||||
const GLXFBConfig &getFBConfig() const
|
||||
{
|
||||
return _fbConfig;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
GraphicsBindingX11::GraphicsBindingImpl(osgViewer::GraphicsWindowX11 *window) :
|
||||
_binding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR }
|
||||
{
|
||||
// window isn't actually of type GraphicsWindowX11Spy, but this allows us to
|
||||
// spy on protected members that don't have public accessors.
|
||||
auto spyWindow = static_cast<GraphicsWindowX11Spy *>(window);
|
||||
|
||||
_binding.xDisplay = window->getDisplay();
|
||||
_binding.visualid = spyWindow->getVisualInfo()->visualid;
|
||||
_binding.glxFBConfig = spyWindow->getFBConfig();
|
||||
_binding.glxDrawable = window->getWindow();
|
||||
_binding.glxContext = window->getContext();
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_GRAPHICS_BINDING_X11
|
||||
#define OSGXR_OPENXR_GRAPHICS_BINDING_X11 1
|
||||
|
||||
#ifdef OSGXR_USE_X11
|
||||
|
||||
#include "GraphicsBinding.h"
|
||||
|
||||
#include <osgViewer/api/X11/GraphicsWindowX11>
|
||||
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#define XR_USE_PLATFORM_XLIB
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
typedef GraphicsBindingImpl<osgViewer::GraphicsWindowX11, XrGraphicsBindingOpenGLXlibKHR> GraphicsBindingX11;
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif // OSGXR_USE_X11
|
||||
|
||||
#endif
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "EventHandler.h"
|
||||
#include "Instance.h"
|
||||
#include "Session.h"
|
||||
#include "System.h"
|
||||
#include "generated/Version.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/Version>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#define ENGINE_NAME "osgXR"
|
||||
#define ENGINE_VERSION (OSGXR_MAJOR_VERSION << 16 | \
|
||||
OSGXR_MINOR_VERSION << 8 | \
|
||||
OSGXR_PATCH_VERSION)
|
||||
#define API_VERSION XR_MAKE_VERSION(1, 0, 0)
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
static std::vector<XrApiLayerProperties> layers;
|
||||
static std::vector<XrExtensionProperties> extensions;
|
||||
|
||||
static bool enumerateLayers(bool invalidate = false)
|
||||
{
|
||||
static bool layersEnumerated = false;
|
||||
if (invalidate)
|
||||
{
|
||||
layers.resize(0);
|
||||
layersEnumerated = false;
|
||||
return false;
|
||||
}
|
||||
if (layersEnumerated)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Count layers
|
||||
uint32_t layerCount = 0;
|
||||
XrResult res = xrEnumerateApiLayerProperties(0, &layerCount, nullptr);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to count OpenXR API layers: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (layerCount)
|
||||
{
|
||||
// Allocate memory
|
||||
layers.resize(layerCount);
|
||||
for (auto &layer: layers)
|
||||
{
|
||||
layer.type = XR_TYPE_API_LAYER_PROPERTIES;
|
||||
layer.next = nullptr;
|
||||
}
|
||||
|
||||
// Enumerate layers
|
||||
res = xrEnumerateApiLayerProperties(layers.size(), &layerCount, layers.data());
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to enumerate " << layerCount
|
||||
<< " OpenXR API layers: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Layers may change at any time
|
||||
layers.resize(layerCount);
|
||||
}
|
||||
|
||||
layersEnumerated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enumerateExtensions(bool invalidate = false)
|
||||
{
|
||||
static bool extensionsEnumerated = false;
|
||||
if (invalidate)
|
||||
{
|
||||
extensions.resize(0);
|
||||
extensionsEnumerated = false;
|
||||
return false;
|
||||
}
|
||||
if (extensionsEnumerated)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Count extensions
|
||||
uint32_t extensionCount;
|
||||
XrResult res = xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, nullptr);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to count OpenXR instance extensions: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extensionCount)
|
||||
{
|
||||
// Allocate memory
|
||||
extensions.resize(extensionCount);
|
||||
for (auto &extension: extensions)
|
||||
{
|
||||
extension.type = XR_TYPE_EXTENSION_PROPERTIES;
|
||||
extension.next = nullptr;
|
||||
}
|
||||
|
||||
// Enumerate extensions
|
||||
res = xrEnumerateInstanceExtensionProperties(nullptr, extensions.size(),
|
||||
&extensionCount, extensions.data());
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to enumerate " << extensionCount
|
||||
<< " OpenXR instance extensions: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extensions may change (?)
|
||||
extensions.resize(extensionCount);
|
||||
}
|
||||
|
||||
extensionsEnumerated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instance::invalidateLayers()
|
||||
{
|
||||
enumerateLayers(true);
|
||||
}
|
||||
|
||||
void Instance::invalidateExtensions()
|
||||
{
|
||||
enumerateExtensions(true);
|
||||
}
|
||||
|
||||
bool Instance::hasLayer(const char *name)
|
||||
{
|
||||
enumerateLayers();
|
||||
|
||||
for (auto &layer: layers)
|
||||
{
|
||||
if (!strncmp(name, layer.layerName, XR_MAX_API_LAYER_NAME_SIZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Instance::hasExtension(const char *name)
|
||||
{
|
||||
enumerateExtensions();
|
||||
|
||||
for (auto &extension: extensions)
|
||||
{
|
||||
if (!strncmp(name, extension.extensionName, XR_MAX_EXTENSION_NAME_SIZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Instance *Instance::instance()
|
||||
{
|
||||
static osg::ref_ptr<Instance> s_instance = new Instance();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
Instance::Instance():
|
||||
_layerValidation(false),
|
||||
_depthInfo(false),
|
||||
_visibilityMask(true),
|
||||
_instance(XR_NULL_HANDLE),
|
||||
_lost(false)
|
||||
{
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
{
|
||||
if (_instance != XR_NULL_HANDLE)
|
||||
{
|
||||
// Delete the systems
|
||||
for (System *system: _systems)
|
||||
{
|
||||
delete system;
|
||||
}
|
||||
|
||||
// Destroy the OpenXR instance
|
||||
XrResult res = xrDestroyInstance(_instance);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to destroy OpenXR instance" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instance::InitResult Instance::init(const char *appName, uint32_t appVersion)
|
||||
{
|
||||
if (_instance != XR_NULL_HANDLE)
|
||||
{
|
||||
return INIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<const char *> layerNames;
|
||||
std::vector<const char *> extensionNames;
|
||||
|
||||
// Enable validation layer if selected
|
||||
if (_layerValidation && hasLayer(XR_APILAYER_LUNARG_core_validation))
|
||||
{
|
||||
layerNames.push_back(XR_APILAYER_LUNARG_core_validation);
|
||||
}
|
||||
|
||||
// We need OpenGL support
|
||||
if (!hasExtension(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME))
|
||||
{
|
||||
OSG_WARN << "OpenXR runtime doesn't support XR_KHR_opengl_enable extension" << std::endl;
|
||||
return INIT_FAIL;
|
||||
}
|
||||
extensionNames.push_back(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME);
|
||||
|
||||
// Enable depth composition layer support if supported
|
||||
_supportsCompositionLayerDepth = hasExtension(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
|
||||
if (_depthInfo)
|
||||
{
|
||||
if (_supportsCompositionLayerDepth)
|
||||
extensionNames.push_back(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
|
||||
else
|
||||
_depthInfo = false;
|
||||
}
|
||||
|
||||
// Enable visibility mask support if supported
|
||||
_supportsVisibilityMask = hasExtension(XR_KHR_VISIBILITY_MASK_EXTENSION_NAME);
|
||||
if (_visibilityMask)
|
||||
{
|
||||
if (_supportsVisibilityMask)
|
||||
extensionNames.push_back(XR_KHR_VISIBILITY_MASK_EXTENSION_NAME);
|
||||
else
|
||||
_visibilityMask = false;
|
||||
}
|
||||
|
||||
// Create the instance
|
||||
XrInstanceCreateInfo info{ XR_TYPE_INSTANCE_CREATE_INFO };
|
||||
strncpy(info.applicationInfo.applicationName, appName,
|
||||
XR_MAX_APPLICATION_NAME_SIZE - 1);
|
||||
info.applicationInfo.applicationVersion = appVersion;
|
||||
strncpy(info.applicationInfo.engineName, ENGINE_NAME,
|
||||
XR_MAX_ENGINE_NAME_SIZE - 1);
|
||||
info.applicationInfo.engineVersion = ENGINE_VERSION;
|
||||
info.applicationInfo.apiVersion = API_VERSION;
|
||||
info.enabledApiLayerCount = layerNames.size();
|
||||
info.enabledApiLayerNames = layerNames.data();
|
||||
info.enabledExtensionCount = extensionNames.size();
|
||||
info.enabledExtensionNames = extensionNames.data();
|
||||
|
||||
XrResult res = xrCreateInstance(&info, &_instance);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to create OpenXR instance: " << res << std::endl;
|
||||
switch (res)
|
||||
{
|
||||
case XR_ERROR_RUNTIME_UNAVAILABLE:
|
||||
case XR_ERROR_RUNTIME_FAILURE: // Monado returns this when not running
|
||||
return INIT_LATER;
|
||||
|
||||
default:
|
||||
return INIT_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Log the runtime properties
|
||||
_properties.type = XR_TYPE_INSTANCE_PROPERTIES;
|
||||
_properties.next = nullptr;
|
||||
|
||||
if (XR_SUCCEEDED(xrGetInstanceProperties(_instance, &_properties)))
|
||||
{
|
||||
OSG_INFO << "OpenXR Runtime: \"" << _properties.runtimeName
|
||||
<< "\" version " << XR_VERSION_MAJOR(_properties.runtimeVersion)
|
||||
<< "." << XR_VERSION_MINOR(_properties.runtimeVersion)
|
||||
<< "." << XR_VERSION_PATCH(_properties.runtimeVersion) << std::endl;
|
||||
_quirks.probe(this);
|
||||
}
|
||||
|
||||
// Get extension functions
|
||||
_xrGetOpenGLGraphicsRequirementsKHR = (PFN_xrGetOpenGLGraphicsRequirementsKHR)getProcAddr("xrGetOpenGLGraphicsRequirementsKHR");
|
||||
if (_visibilityMask)
|
||||
_xrGetVisibilityMaskKHR = (PFN_xrGetVisibilityMaskKHR)getProcAddr("xrGetVisibilityMaskKHR");
|
||||
|
||||
return INIT_SUCCESS;
|
||||
}
|
||||
|
||||
bool Instance::check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
if (result == XR_ERROR_INSTANCE_LOST)
|
||||
_lost = true;
|
||||
|
||||
char resultName[XR_MAX_RESULT_STRING_SIZE];
|
||||
if (XR_FAILED(xrResultToString(_instance, result, resultName)))
|
||||
{
|
||||
OSG_WARN << warnMsg << ": " << result << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_WARN << warnMsg << ": " << resultName << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PFN_xrVoidFunction Instance::getProcAddr(const char *name) const
|
||||
{
|
||||
PFN_xrVoidFunction ret = nullptr;
|
||||
check(xrGetInstanceProcAddr(_instance, name, &ret),
|
||||
"Failed to get OpenXR procedure address");
|
||||
return ret;
|
||||
}
|
||||
|
||||
System *Instance::getSystem(XrFormFactor formFactor, bool *supported)
|
||||
{
|
||||
unsigned long ffId = formFactor - 1;
|
||||
if (ffId < _systems.size() && _systems[ffId])
|
||||
{
|
||||
if (supported)
|
||||
*supported = true;
|
||||
return _systems[ffId];
|
||||
}
|
||||
|
||||
XrSystemGetInfo getInfo{ XR_TYPE_SYSTEM_GET_INFO };
|
||||
getInfo.formFactor = formFactor;
|
||||
|
||||
XrSystemId systemId;
|
||||
XrResult res = xrGetSystem(_instance, &getInfo, &systemId);
|
||||
if (res == XR_ERROR_FORM_FACTOR_UNAVAILABLE)
|
||||
{
|
||||
// The system is only *TEMPORARILY* unavailable
|
||||
if (supported)
|
||||
*supported = true;
|
||||
return nullptr;
|
||||
}
|
||||
else if (check(res, "Failed to get OpenXR system"))
|
||||
{
|
||||
if (ffId >= _systems.size())
|
||||
_systems.resize(ffId+1, nullptr);
|
||||
|
||||
if (supported)
|
||||
*supported = true;
|
||||
return _systems[ffId] = new System(this, systemId);
|
||||
}
|
||||
|
||||
if (supported)
|
||||
*supported = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Instance::invalidateSystem(XrFormFactor formFactor)
|
||||
{
|
||||
unsigned long ffId = formFactor - 1;
|
||||
if (ffId < _systems.size())
|
||||
{
|
||||
delete _systems[ffId];
|
||||
_systems[ffId] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::registerSession(Session *session)
|
||||
{
|
||||
_sessions[session->getXrSession()] = session;
|
||||
}
|
||||
|
||||
void Instance::unregisterSession(Session *session)
|
||||
{
|
||||
_sessions.erase(session->getXrSession());
|
||||
}
|
||||
|
||||
Session *Instance::getSession(XrSession xrSession)
|
||||
{
|
||||
auto it = _sessions.find(xrSession);
|
||||
if (it == _sessions.end())
|
||||
return nullptr;
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
void Instance::pollEvents(EventHandler *handler)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
XrEventDataBuffer event;
|
||||
event.type = XR_TYPE_EVENT_DATA_BUFFER;
|
||||
event.next = nullptr;
|
||||
|
||||
XrResult res = xrPollEvent(_instance, &event);
|
||||
if (XR_FAILED(res))
|
||||
break;
|
||||
if (res == XR_EVENT_UNAVAILABLE)
|
||||
break;
|
||||
|
||||
handler->onEvent(this, &event);
|
||||
}
|
||||
}
|
||||
Vendored
+193
@@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_INSTANCE
|
||||
#define OSGXR_OPENXR_INSTANCE 1
|
||||
|
||||
#include "Quirks.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#define XR_APILAYER_LUNARG_core_validation "XR_APILAYER_LUNARG_core_validation"
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class EventHandler;
|
||||
class System;
|
||||
class Session;
|
||||
|
||||
class Instance : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
static Instance *instance();
|
||||
|
||||
Instance();
|
||||
virtual ~Instance();
|
||||
|
||||
// Layers and extensions
|
||||
|
||||
static void invalidateLayers();
|
||||
static void invalidateExtensions();
|
||||
static bool hasLayer(const char *name);
|
||||
static bool hasExtension(const char *name);
|
||||
|
||||
// Instance initialisation
|
||||
|
||||
void setValidationLayer(bool layerValidation)
|
||||
{
|
||||
_layerValidation = layerValidation;
|
||||
}
|
||||
|
||||
void setDepthInfo(bool depthInfo)
|
||||
{
|
||||
_depthInfo = depthInfo;
|
||||
}
|
||||
|
||||
void setVisibilityMask(bool visibilityMask)
|
||||
{
|
||||
_visibilityMask = visibilityMask;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
/// Instance creation successful.
|
||||
INIT_SUCCESS,
|
||||
/// Instance creation not possible at the moment, try again later.
|
||||
INIT_LATER,
|
||||
/// Instance creation failed.
|
||||
INIT_FAIL,
|
||||
} InitResult;
|
||||
InitResult init(const char *appName, uint32_t appVersion);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _instance != XR_NULL_SYSTEM_ID;
|
||||
}
|
||||
|
||||
inline bool lost() const
|
||||
{
|
||||
return _lost;
|
||||
}
|
||||
|
||||
bool check(XrResult result, const char *warnMsg) const;
|
||||
|
||||
// Conversions
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
// Instance properties
|
||||
inline const char *getRuntimeName() const
|
||||
{
|
||||
return _properties.runtimeName;
|
||||
}
|
||||
inline XrVersion getRuntimeVersion() const
|
||||
{
|
||||
return _properties.runtimeVersion;
|
||||
}
|
||||
|
||||
inline bool getQuirk(Quirk quirk) const
|
||||
{
|
||||
return _quirks[quirk];
|
||||
}
|
||||
|
||||
// Extensions
|
||||
|
||||
bool supportsCompositionLayerDepth() const
|
||||
{
|
||||
return _supportsCompositionLayerDepth;
|
||||
}
|
||||
|
||||
bool supportsVisibilityMask() const
|
||||
{
|
||||
return _supportsVisibilityMask;
|
||||
}
|
||||
|
||||
PFN_xrVoidFunction getProcAddr(const char *name) const;
|
||||
|
||||
XrResult getOpenGLGraphicsRequirements(XrSystemId systemId,
|
||||
XrGraphicsRequirementsOpenGLKHR* graphicsRequirements) const
|
||||
{
|
||||
if (!_xrGetOpenGLGraphicsRequirementsKHR)
|
||||
return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return _xrGetOpenGLGraphicsRequirementsKHR(_instance, systemId,
|
||||
graphicsRequirements);
|
||||
}
|
||||
|
||||
XrResult xrGetVisibilityMask(XrSession session,
|
||||
XrViewConfigurationType viewConfigurationType,
|
||||
uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
XrVisibilityMaskKHR *visibilityMask)
|
||||
{
|
||||
if (!_xrGetVisibilityMaskKHR)
|
||||
return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return _xrGetVisibilityMaskKHR(session, viewConfigurationType,
|
||||
viewIndex, visibilityMaskType,
|
||||
visibilityMask);
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
System *getSystem(XrFormFactor formFactor, bool *supported = nullptr);
|
||||
|
||||
// Up to caller to ensure no session
|
||||
void invalidateSystem(XrFormFactor formFactor);
|
||||
void registerSession(Session *session);
|
||||
void unregisterSession(Session *session);
|
||||
Session *getSession(XrSession xrSession);
|
||||
|
||||
// Events
|
||||
|
||||
void pollEvents(EventHandler *handler);
|
||||
|
||||
protected:
|
||||
|
||||
// Setup data
|
||||
bool _layerValidation;
|
||||
bool _depthInfo;
|
||||
bool _visibilityMask;
|
||||
|
||||
// Instance data
|
||||
XrInstance _instance;
|
||||
mutable bool _lost;
|
||||
|
||||
// Extension presence
|
||||
bool _supportsCompositionLayerDepth;
|
||||
bool _supportsVisibilityMask;
|
||||
// Extension functions
|
||||
mutable PFN_xrGetOpenGLGraphicsRequirementsKHR _xrGetOpenGLGraphicsRequirementsKHR;
|
||||
mutable PFN_xrGetVisibilityMaskKHR _xrGetVisibilityMaskKHR;
|
||||
|
||||
// Instance properties
|
||||
XrInstanceProperties _properties;
|
||||
|
||||
// Quirks
|
||||
Quirks _quirks;
|
||||
|
||||
// Systems
|
||||
mutable std::vector<System *> _systems;
|
||||
|
||||
// Sessions
|
||||
std::map<XrSession, Session *> _sessions;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "InteractionProfile.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
InteractionProfile::InteractionProfile(const Path &path) :
|
||||
_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
InteractionProfile::InteractionProfile(Instance *instance,
|
||||
const char *vendor, const char *type) :
|
||||
_path(instance, (std::string)"/interaction_profiles/" + vendor + "/" + type)
|
||||
{
|
||||
}
|
||||
|
||||
InteractionProfile::~InteractionProfile()
|
||||
{
|
||||
}
|
||||
|
||||
void InteractionProfile::addBinding(Action *action, const Path &binding)
|
||||
{
|
||||
assert(binding.getInstance() == getInstance());
|
||||
_bindings.insert(ActionBindingPair(action, binding.getXrPath()));
|
||||
}
|
||||
|
||||
bool InteractionProfile::suggestBindings()
|
||||
{
|
||||
// No bindings: nothing to do!
|
||||
if (_bindings.empty())
|
||||
return true;
|
||||
|
||||
// Construct binding vector from _bindings map
|
||||
std::vector<XrActionSuggestedBinding> bindings;
|
||||
bindings.reserve(_bindings.size());
|
||||
for (auto pair: _bindings)
|
||||
{
|
||||
if (pair.first->init())
|
||||
bindings.push_back({ pair.first->getXrAction(),
|
||||
pair.second });
|
||||
}
|
||||
|
||||
// Suggest the bindings
|
||||
XrInteractionProfileSuggestedBinding suggestedBinding{
|
||||
XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING
|
||||
};
|
||||
suggestedBinding.interactionProfile = _path.getXrPath();
|
||||
suggestedBinding.countSuggestedBindings = bindings.size();
|
||||
suggestedBinding.suggestedBindings = bindings.data();
|
||||
|
||||
return check(xrSuggestInteractionProfileBindings(getXrInstance(),
|
||||
&suggestedBinding),
|
||||
"Failed to suggest interaction profile bindings");
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_INTERACTION_PROFILE
|
||||
#define OSGXR_OPENXR_INTERACTION_PROFILE 1
|
||||
|
||||
#include "Action.h"
|
||||
#include "Path.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class InteractionProfile : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
InteractionProfile(const Path &path);
|
||||
InteractionProfile(Instance *instance,
|
||||
const char *vendor, const char *type);
|
||||
virtual ~InteractionProfile();
|
||||
|
||||
// Accessors
|
||||
|
||||
void addBinding(Action *action, const std::string &binding)
|
||||
{
|
||||
Path path(_path.getInstance(), binding);
|
||||
addBinding(action, path);
|
||||
}
|
||||
|
||||
void addBinding(Action *action, const Path &binding);
|
||||
|
||||
// returns true on success
|
||||
bool suggestBindings();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _path.check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _path.getInstance();
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _path.getXrInstance();
|
||||
}
|
||||
|
||||
inline const Path &getPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Interaction profile data
|
||||
Path _path;
|
||||
typedef std::pair<osg::ref_ptr<Action>, XrPath> ActionBindingPair;
|
||||
std::set<ActionBindingPair> _bindings;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Path.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Path::Path(Instance *instance,
|
||||
XrPath path) :
|
||||
_instance(instance),
|
||||
_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
Path::Path(Instance *instance,
|
||||
const std::string &path) :
|
||||
_instance(instance),
|
||||
_path(XR_NULL_PATH)
|
||||
{
|
||||
check(xrStringToPath(getXrInstance(), path.c_str(), &_path),
|
||||
"Failed to create OpenXR path from string");
|
||||
}
|
||||
|
||||
std::string Path::toString() const
|
||||
{
|
||||
if (!valid())
|
||||
return "";
|
||||
|
||||
uint32_t count;
|
||||
if (!check(xrPathToString(getXrInstance(), _path,
|
||||
0, &count, nullptr),
|
||||
"Failed to size OpenXR path string"))
|
||||
return "";
|
||||
std::vector<char> buffer(count);
|
||||
if (!check(xrPathToString(getXrInstance(), _path,
|
||||
buffer.size(), &count, buffer.data()),
|
||||
"Failed to get OpenXR path string"))
|
||||
return "";
|
||||
|
||||
return buffer.data();
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_PATH
|
||||
#define OSGXR_OPENXR_PATH 1
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
|
||||
Path(Instance *instance = nullptr, XrPath path = XR_NULL_PATH);
|
||||
Path(Instance *instance, const std::string &path);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _path != XR_NULL_PATH;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _instance->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrPath getXrPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
// Comparisons
|
||||
|
||||
bool operator == (const Path &other) const
|
||||
{
|
||||
return _path == other._path &&
|
||||
_instance == other._instance;
|
||||
}
|
||||
|
||||
bool operator != (const Path &other) const
|
||||
{
|
||||
return _path != other._path ||
|
||||
_instance != other._instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Path data
|
||||
osg::ref_ptr<Instance> _instance;
|
||||
XrPath _path;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Instance.h"
|
||||
#include "Quirks.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef OSGXR_USE_X11
|
||||
#define USING_X11 1
|
||||
#else
|
||||
#define USING_X11 0
|
||||
#endif
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
void Quirks::probe(Instance *instance)
|
||||
{
|
||||
static struct {
|
||||
Quirk quirk;
|
||||
const char *envName;
|
||||
|
||||
bool condition;
|
||||
const char *runtimeMatch;
|
||||
XrVersion runtimeVersionMin;
|
||||
XrVersion runtimeVersionMax;
|
||||
|
||||
const char *description;
|
||||
} quirkInfo[] = {
|
||||
#define QUIRK(NAME, COND, RUNTIME, VMIN, VMAX, LINK) \
|
||||
{ NAME, "OSGXR_"#NAME, COND, RUNTIME, VMIN, VMAX, \
|
||||
#NAME LINK }
|
||||
#define MIN_XR_VERSION XR_MAKE_VERSION(0, 0, 0)
|
||||
#define MAX_XR_VERSION XR_MAKE_VERSION(-1, -1, -1)
|
||||
#define MATCH_MONADO "Monado"
|
||||
#define MATCH_STEAMVR "SteamVR"
|
||||
|
||||
// As of 2021-12-16 Monado expects the GL context to be current.
|
||||
// See https://gitlab.freedesktop.org/monado/monado/-/issues/145
|
||||
// Fixed by https://gitlab.freedesktop.org/monado/monado/-/merge_requests/1216
|
||||
QUIRK(QUIRK_GL_CONTEXT_IGNORED,
|
||||
USING_X11,
|
||||
MATCH_MONADO, MIN_XR_VERSION, XR_MAKE_VERSION(21, 0, 0),
|
||||
" (https://gitlab.freedesktop.org/monado/monado/-/issues/145)"),
|
||||
|
||||
// Prior to 1.16.4 linux_v1.14 switched context but didn't restore.
|
||||
// The SteamVR runtimeVersion is unfortunately fairly useless here.
|
||||
QUIRK(QUIRK_GL_CONTEXT_CHANGED,
|
||||
USING_X11,
|
||||
MATCH_STEAMVR, MIN_XR_VERSION, XR_MAKE_VERSION(0, 1, 0),
|
||||
""),
|
||||
|
||||
// Since SteamVR 1.16.4 the GL context is cleared by various calls.
|
||||
// The SteamVR runtimeVersion is unfortunately fairly useless here.
|
||||
QUIRK(QUIRK_GL_CONTEXT_CLEARED,
|
||||
USING_X11,
|
||||
MATCH_STEAMVR, MIN_XR_VERSION, MAX_XR_VERSION,
|
||||
" (https://github.com/ValveSoftware/SteamVR-for-Linux/issues/421)"),
|
||||
|
||||
// Since SteamVR 1.15.x apps hang during xrDestroyInstance.
|
||||
QUIRK(QUIRK_AVOID_DESTROY_INSTANCE,
|
||||
USING_X11,
|
||||
MATCH_STEAMVR, MIN_XR_VERSION, MAX_XR_VERSION,
|
||||
" (https://github.com/ValveSoftware/SteamVR-for-Linux/issues/422)"),
|
||||
|
||||
#undef QUIRK
|
||||
};
|
||||
|
||||
const char *runtime = instance->getRuntimeName();
|
||||
XrVersion version = instance->getRuntimeVersion();
|
||||
|
||||
// Clear all quirks
|
||||
reset();
|
||||
|
||||
// Probe each quirk
|
||||
for (auto &quirk: quirkInfo)
|
||||
{
|
||||
const char *env = getenv(quirk.envName);
|
||||
if (env)
|
||||
{
|
||||
if (!strncmp(env, "0", 2))
|
||||
{
|
||||
set(quirk.quirk, false);
|
||||
}
|
||||
else if (!strncmp(env, "1", 2))
|
||||
{
|
||||
set(quirk.quirk, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
env = nullptr;
|
||||
OSG_WARN << "osgXR: Unknown value for env \""
|
||||
<< quirk.envName << "\", ignored" << std::endl;
|
||||
}
|
||||
}
|
||||
// Probe the runtime name and version
|
||||
if (!env &&
|
||||
quirk.condition &&
|
||||
!strncmp(runtime, quirk.runtimeMatch, strlen(quirk.runtimeMatch)) &&
|
||||
version >= quirk.runtimeVersionMin &&
|
||||
version <= quirk.runtimeVersionMax)
|
||||
{
|
||||
set(quirk.quirk, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Print to log any enabled quirks
|
||||
if (any())
|
||||
{
|
||||
OSG_WARN << "osgXR: OpenXR Runtime: \"" << runtime
|
||||
<< "\" version " << XR_VERSION_MAJOR(version)
|
||||
<< "." << XR_VERSION_MINOR(version)
|
||||
<< "." << XR_VERSION_PATCH(version) << std::endl;
|
||||
for (auto &quirk: quirkInfo)
|
||||
if (test(quirk.quirk))
|
||||
OSG_WARN << "osgXR: Enabling " << quirk.description << std::endl;
|
||||
}
|
||||
}
|
||||
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_QUIRKS
|
||||
#define OSGXR_OPENXR_QUIRKS 1
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Instance;
|
||||
|
||||
typedef enum Quirk {
|
||||
/**
|
||||
* This quirk indicates that the GLX context may be assumed to be current by
|
||||
* certain XR calls.
|
||||
* The affected calls are:
|
||||
* - xrCreateSession
|
||||
* - xrCreateSwapchain
|
||||
*/
|
||||
QUIRK_GL_CONTEXT_IGNORED = 0,
|
||||
|
||||
/**
|
||||
* This quirk indicates that the GLX context may be switched but not
|
||||
* restored by certain XR calls.
|
||||
* The affected calls are:
|
||||
* - xrCreateSwapchain
|
||||
*/
|
||||
QUIRK_GL_CONTEXT_CHANGED,
|
||||
|
||||
/**
|
||||
* This quirk indicates that the GLX context may be unconditionally cleared
|
||||
* by various XR calls.
|
||||
* The affected calls are:
|
||||
* - xrCreateSwapchain
|
||||
* - xrAcquireSwapchainImage
|
||||
* - xrWaitSwapchainImage
|
||||
* - xrReleaseSwapchainImage
|
||||
* - xrEndFrame
|
||||
*/
|
||||
QUIRK_GL_CONTEXT_CLEARED,
|
||||
|
||||
/**
|
||||
* This quirk indicates that the app should avoid destroying the XR instance
|
||||
* to avoid hangs.
|
||||
*/
|
||||
QUIRK_AVOID_DESTROY_INSTANCE,
|
||||
|
||||
QUIRK_MAX
|
||||
} Quirk;
|
||||
|
||||
/**
|
||||
* Represents a set of OpenXR runtime quirks which require workarounds.
|
||||
*/
|
||||
class Quirks : public std::bitset<QUIRK_MAX>
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Probe the OpenXR instance to see which quirks are required.
|
||||
*/
|
||||
void probe(Instance *instance);
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+527
@@ -0,0 +1,527 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#include "ActionSet.h"
|
||||
#include "Compositor.h"
|
||||
#include "Session.h"
|
||||
#include "GraphicsBinding.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#ifdef OSGXR_USE_X11
|
||||
#include <osgViewer/api/X11/GraphicsWindowX11>
|
||||
#include <GL/glx.h>
|
||||
#endif // OSGXR_USE_X11
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Session::Session(System *system,
|
||||
osgViewer::GraphicsWindow *window) :
|
||||
_window(window),
|
||||
_instance(system->getInstance()),
|
||||
_system(system),
|
||||
_session(XR_NULL_HANDLE),
|
||||
_viewConfiguration(nullptr),
|
||||
_actionSyncCount(0),
|
||||
_state(XR_SESSION_STATE_UNKNOWN),
|
||||
_running(false),
|
||||
_exiting(false),
|
||||
_readSwapchainFormats(false),
|
||||
_lastDisplayTime(0)
|
||||
{
|
||||
XrSessionCreateInfo createInfo = { XR_TYPE_SESSION_CREATE_INFO };
|
||||
createInfo.systemId = getXrSystemId();
|
||||
|
||||
// Get OpenGL graphics requirements
|
||||
XrGraphicsRequirementsOpenGLKHR req;
|
||||
req.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR;
|
||||
req.next = nullptr;
|
||||
check(_instance->getOpenGLGraphicsRequirements(getXrSystemId(), &req),
|
||||
"Failed to get OpenXR's OpenGL graphics requirements");
|
||||
// ... and pretty much ignore what it says
|
||||
|
||||
osg::ref_ptr<GraphicsBinding> graphicsBinding = createGraphicsBinding(window);
|
||||
if (graphicsBinding == nullptr)
|
||||
{
|
||||
OSG_WARN << "Failed to get OpenXR graphics binding" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
createInfo.next = graphicsBinding->getXrGraphicsBinding();
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
bool switchContext = shouldSwitchContext();
|
||||
if (switchContext)
|
||||
makeCurrent();
|
||||
if (check(xrCreateSession(getXrInstance(), &createInfo, &_session),
|
||||
"Failed to create OpenXR session"))
|
||||
{
|
||||
_instance->registerSession(this);
|
||||
}
|
||||
if (switchContext)
|
||||
releaseContext();
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
releaseGLObjects();
|
||||
}
|
||||
|
||||
void Session::releaseGLObjects(osg::State *state)
|
||||
{
|
||||
if (_session != XR_NULL_HANDLE)
|
||||
{
|
||||
_instance->unregisterSession(this);
|
||||
_localSpace = nullptr;
|
||||
// GL context must not be bound in another thread
|
||||
check(xrDestroySession(_session),
|
||||
"Failed to destroy OpenXR session");
|
||||
_session = XR_NULL_HANDLE;
|
||||
_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::addActionSet(ActionSet *actionSet)
|
||||
{
|
||||
assert(actionSet->getInstance() == getInstance());
|
||||
_actionSets.insert(actionSet);
|
||||
}
|
||||
|
||||
bool Session::attachActionSets()
|
||||
{
|
||||
assert(valid());
|
||||
if (_actionSets.empty())
|
||||
return false;
|
||||
|
||||
// Construct vector of XrActionSets
|
||||
std::vector<XrActionSet> actionSets;
|
||||
actionSets.reserve(_actionSets.size());
|
||||
for (auto actionSet: _actionSets)
|
||||
actionSets.push_back(actionSet->getXrActionSet());
|
||||
|
||||
XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
|
||||
attachInfo.countActionSets = actionSets.size();
|
||||
attachInfo.actionSets = actionSets.data();
|
||||
|
||||
return check(xrAttachSessionActionSets(_session, &attachInfo),
|
||||
"Failed to attach action sets to OpenXR session");
|
||||
}
|
||||
|
||||
Path Session::getCurrentInteractionProfile(const Path &subactionPath) const
|
||||
{
|
||||
XrInteractionProfileState interactionProfile{ XR_TYPE_INTERACTION_PROFILE_STATE };
|
||||
|
||||
if (check(xrGetCurrentInteractionProfile(_session, subactionPath.getXrPath(),
|
||||
&interactionProfile),
|
||||
"Failed to get OpenXR current interaction profile"))
|
||||
{
|
||||
return Path(getInstance(), interactionProfile.interactionProfile);
|
||||
}
|
||||
return Path();
|
||||
}
|
||||
|
||||
bool Session::getActionBoundSources(Action *action,
|
||||
std::vector<XrPath> &sourcePaths) const
|
||||
{
|
||||
if (!valid())
|
||||
return false;
|
||||
|
||||
// Count bound sources
|
||||
XrBoundSourcesForActionEnumerateInfo enumerateInfo{ XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO };
|
||||
enumerateInfo.action = action->getXrAction();
|
||||
uint32_t count;
|
||||
if (check(xrEnumerateBoundSourcesForAction(_session, &enumerateInfo,
|
||||
0, &count, nullptr),
|
||||
"Failed to count OpenXR action bound sources"))
|
||||
{
|
||||
// Resize output buffer
|
||||
sourcePaths.resize(count);
|
||||
if (!count)
|
||||
return true;
|
||||
|
||||
// Fill buffer
|
||||
if (check(xrEnumerateBoundSourcesForAction(_session, &enumerateInfo,
|
||||
sourcePaths.size(),
|
||||
&count,
|
||||
sourcePaths.data()),
|
||||
"Failed to enumerate OpenXR action bound sources"))
|
||||
{
|
||||
// Success!
|
||||
if (count < sourcePaths.size())
|
||||
sourcePaths.resize(count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Failure!
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Session::getInputSourceLocalizedName(XrPath sourcePath,
|
||||
XrInputSourceLocalizedNameFlags whichComponents) const
|
||||
{
|
||||
if (!valid())
|
||||
return "";
|
||||
|
||||
XrInputSourceLocalizedNameGetInfo getInfo{ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
|
||||
getInfo.sourcePath = sourcePath;
|
||||
getInfo.whichComponents = whichComponents;
|
||||
|
||||
uint32_t count;
|
||||
if (!check(xrGetInputSourceLocalizedName(_session, &getInfo,
|
||||
0, &count, nullptr),
|
||||
"Failed to size OpenXR input source localized name string"))
|
||||
return "";
|
||||
std::vector<char> buffer(count);
|
||||
if (!check(xrGetInputSourceLocalizedName(_session, &getInfo,
|
||||
buffer.size(), &count, buffer.data()),
|
||||
"Failed to get OpenXR input source localized name string"))
|
||||
return "";
|
||||
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
void Session::activateActionSet(ActionSet *actionSet, Path subactionPath)
|
||||
{
|
||||
assert(_actionSets.count(actionSet));
|
||||
_activeActionSets.insert(ActionSetSubactionPair(actionSet, subactionPath.getXrPath()));
|
||||
}
|
||||
|
||||
void Session::deactivateActionSet(ActionSet *actionSet, Path subactionPath)
|
||||
{
|
||||
_activeActionSets.erase(ActionSetSubactionPair(actionSet, subactionPath.getXrPath()));
|
||||
}
|
||||
|
||||
bool Session::syncActions()
|
||||
{
|
||||
if (!valid())
|
||||
return false;
|
||||
|
||||
XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO };
|
||||
std::vector<XrActiveActionSet> actionSets;
|
||||
if (!_activeActionSets.empty())
|
||||
{
|
||||
// Construct vector of XrActionSets
|
||||
actionSets.reserve(_activeActionSets.size());
|
||||
for (auto actionSet: _activeActionSets)
|
||||
{
|
||||
XrActiveActionSet activeActionSet;
|
||||
activeActionSet.actionSet = actionSet.first->getXrActionSet();
|
||||
activeActionSet.subactionPath = actionSet.second;
|
||||
actionSets.push_back(activeActionSet);
|
||||
}
|
||||
|
||||
syncInfo.countActiveActionSets = actionSets.size();
|
||||
syncInfo.activeActionSets = actionSets.data();
|
||||
|
||||
bool ret = check(xrSyncActions(_session, &syncInfo),
|
||||
"Failed to sync action sets to OpenXR session");
|
||||
if (ret)
|
||||
++_actionSyncCount;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const Session::SwapchainFormats &Session::getSwapchainFormats() const
|
||||
{
|
||||
if (!_readSwapchainFormats && valid())
|
||||
{
|
||||
uint32_t formatCount;
|
||||
if (check(xrEnumerateSwapchainFormats(_session, 0, &formatCount, nullptr),
|
||||
"Failed to count OpenXR swapchain formats"))
|
||||
{
|
||||
if (formatCount)
|
||||
{
|
||||
_swapchainFormats.resize(formatCount);
|
||||
if (!check(xrEnumerateSwapchainFormats(_session, formatCount,
|
||||
&formatCount, _swapchainFormats.data()),
|
||||
"Failed to enumerate OpenXR swapchain formats"))
|
||||
{
|
||||
_swapchainFormats.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readSwapchainFormats = true;
|
||||
}
|
||||
|
||||
return _swapchainFormats;
|
||||
}
|
||||
|
||||
Space *Session::getLocalSpace()
|
||||
{
|
||||
if (!_localSpace.valid())
|
||||
_localSpace = new Space(this, XR_REFERENCE_SPACE_TYPE_LOCAL);
|
||||
|
||||
return _localSpace;
|
||||
}
|
||||
|
||||
void Session::updateVisibilityMasks(XrViewConfigurationType viewConfigurationType,
|
||||
uint32_t viewIndex)
|
||||
{
|
||||
// Session must be started ...
|
||||
if (!_viewConfiguration)
|
||||
return;
|
||||
// ... and with a matching view configuration
|
||||
if (viewConfigurationType != _viewConfiguration->getType())
|
||||
return;
|
||||
|
||||
if (viewIndex >= _viewConfiguration->getViews().size())
|
||||
return;
|
||||
VisMaskGeometryView &visMaskView = _visMaskCache[viewIndex];
|
||||
|
||||
// Regenerate cached visibility mask geometries for this viewIndex
|
||||
for (uint32_t visMaskType = 0; visMaskType < visMaskView.size(); ++visMaskType)
|
||||
if (visMaskView[visMaskType].valid())
|
||||
getVisibilityMask(viewIndex, static_cast<XrVisibilityMaskTypeKHR>(1 + visMaskType), true);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> Session::getVisibilityMask(uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
bool force)
|
||||
{
|
||||
if (!_viewConfiguration)
|
||||
return nullptr;
|
||||
if (viewIndex >= _viewConfiguration->getViews().size())
|
||||
return nullptr;
|
||||
if (visibilityMaskType == 0 || visibilityMaskType > XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR)
|
||||
return nullptr;
|
||||
|
||||
// Size cache to match number of views...
|
||||
if (_visMaskCache.size() == 0)
|
||||
_visMaskCache.resize(_viewConfiguration->getViews().size());
|
||||
// ... and number of vis mask types
|
||||
VisMaskGeometryView &visMaskView = _visMaskCache[viewIndex];
|
||||
if (visMaskView.size() == 0)
|
||||
visMaskView.resize(XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR);
|
||||
// Cache hit?
|
||||
VisMaskGeometry &visMaskGeometry = visMaskView[visibilityMaskType - 1];
|
||||
if (!force && visMaskGeometry.valid())
|
||||
return visMaskGeometry;
|
||||
|
||||
// Get counts of visibility mask
|
||||
XrVisibilityMaskKHR visibilityMask{ XR_TYPE_VISIBILITY_MASK_KHR };
|
||||
XrResult res = xrGetVisibilityMask(*_viewConfiguration, viewIndex,
|
||||
visibilityMaskType, &visibilityMask);
|
||||
if (res != XR_ERROR_FUNCTION_UNSUPPORTED &&
|
||||
check(res, "Failed to size OpenXR visibility mask"))
|
||||
{
|
||||
osg::PrimitiveSet::Mode mode;
|
||||
switch (visibilityMaskType)
|
||||
{
|
||||
case XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR:
|
||||
// fall through
|
||||
case XR_VISIBILITY_MASK_TYPE_VISIBLE_TRIANGLE_MESH_KHR:
|
||||
mode = osg::PrimitiveSet::TRIANGLES;
|
||||
break;
|
||||
case XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR:
|
||||
mode = osg::PrimitiveSet::LINE_LOOP;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate space for data
|
||||
osg::ref_ptr<osg::Vec2Array> vertices = new osg::Vec2Array(visibilityMask.vertexCountOutput);
|
||||
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(mode, visibilityMask.indexCountOutput);
|
||||
|
||||
// Get the actual data
|
||||
static_assert(sizeof((*vertices)[0]) == sizeof(XrVector2f));
|
||||
static_assert(sizeof((*indices)[0]) == sizeof(uint32_t));
|
||||
visibilityMask.vertexCapacityInput = vertices->size();
|
||||
visibilityMask.vertices = reinterpret_cast<XrVector2f *>(&vertices->front());
|
||||
visibilityMask.indexCapacityInput = indices->size();
|
||||
visibilityMask.indices = reinterpret_cast<uint32_t *>(&indices->front());
|
||||
XrResult res = xrGetVisibilityMask(*_viewConfiguration, viewIndex,
|
||||
visibilityMaskType, &visibilityMask);
|
||||
if (check(res, "Failed to get OpenXR visibility mask"))
|
||||
{
|
||||
if (!visMaskGeometry.valid())
|
||||
{
|
||||
// Create a new geometry object
|
||||
osg::Geometry *geometry = new osg::Geometry();
|
||||
geometry->setVertexArray(vertices);
|
||||
geometry->addPrimitiveSet(indices);
|
||||
visMaskGeometry = geometry;
|
||||
return geometry;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the existing geometry object
|
||||
osg::Geometry *geometry = visMaskGeometry.get();
|
||||
geometry->setVertexArray(vertices);
|
||||
geometry->setPrimitiveSet(0, indices);
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Session::checkCurrent() const
|
||||
{
|
||||
#ifdef OSGXR_USE_X11
|
||||
// Ugly X11 specific hack
|
||||
const auto *window = dynamic_cast<const osgViewer::GraphicsWindowX11*>(_window.get());
|
||||
return glXGetCurrentContext() == window->getContext();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Session::makeCurrent() const
|
||||
{
|
||||
#ifdef OSGXR_USE_X11
|
||||
_window->makeCurrentImplementation();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Session::releaseContext() const
|
||||
{
|
||||
#ifdef OSGXR_USE_X11
|
||||
_window->releaseContextImplementation();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Session::begin(const System::ViewConfiguration &viewConfiguration)
|
||||
{
|
||||
_viewConfiguration = &viewConfiguration;
|
||||
|
||||
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
|
||||
beginInfo.primaryViewConfigurationType = viewConfiguration.getType();
|
||||
if (check(xrBeginSession(_session, &beginInfo),
|
||||
"Failed to begin OpenXR session"))
|
||||
{
|
||||
_running = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Session::end()
|
||||
{
|
||||
check(xrEndSession(_session),
|
||||
"Failed to end OpenXR session");
|
||||
_running = false;
|
||||
_viewConfiguration = nullptr;
|
||||
_visMaskCache.resize(0);
|
||||
}
|
||||
|
||||
void Session::requestExit()
|
||||
{
|
||||
_exiting = true;
|
||||
if (isRunning())
|
||||
check(xrRequestExitSession(_session),
|
||||
"Failed to request OpenXR exit");
|
||||
}
|
||||
|
||||
osg::ref_ptr<Session::Frame> Session::waitFrame()
|
||||
{
|
||||
if (_instance->lost())
|
||||
return nullptr;
|
||||
|
||||
osg::ref_ptr<Frame> frame;
|
||||
|
||||
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
|
||||
XrFrameState frameState;
|
||||
frameState.type = XR_TYPE_FRAME_STATE;
|
||||
frameState.next = nullptr;
|
||||
if (check(xrWaitFrame(_session, &frameWaitInfo, &frameState),
|
||||
"Failed to wait for OpenXR frame"))
|
||||
{
|
||||
frame = new Frame(this, &frameState);
|
||||
_lastDisplayTime = frameState.predictedDisplayTime;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
Session::Frame::Frame(osg::ref_ptr<Session> session, XrFrameState *frameState) :
|
||||
_session(session),
|
||||
_time(frameState->predictedDisplayTime),
|
||||
_period(frameState->predictedDisplayPeriod),
|
||||
_shouldRender(frameState->shouldRender),
|
||||
_osgFrameNumber(0),
|
||||
_locatedViews(false),
|
||||
_begun(false),
|
||||
_envBlendMode(XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM)
|
||||
{
|
||||
}
|
||||
|
||||
Session::Frame::~Frame()
|
||||
{
|
||||
}
|
||||
|
||||
void Session::Frame::locateViews()
|
||||
{
|
||||
// Get view locations
|
||||
XrViewLocateInfo locateInfo = { XR_TYPE_VIEW_LOCATE_INFO };
|
||||
locateInfo.viewConfigurationType = _session->getViewConfiguration()->getType();
|
||||
locateInfo.displayTime = _time;
|
||||
locateInfo.space = _session->getLocalSpace()->getXrSpace();
|
||||
|
||||
_viewState = { XR_TYPE_VIEW_STATE };
|
||||
|
||||
uint32_t viewCount;
|
||||
if (!check(xrLocateViews(_session->getXrSession(), &locateInfo, &_viewState, 0, &viewCount, nullptr),
|
||||
"Failed to count OpenXR views"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_views.resize(viewCount);
|
||||
for (auto &view: _views)
|
||||
view = { XR_TYPE_VIEW };
|
||||
if (!check(xrLocateViews(_session->getXrSession(), &locateInfo, &_viewState, _views.size(), &viewCount, _views.data()),
|
||||
"Failed to locate OpenXR views"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_locatedViews = true;
|
||||
}
|
||||
|
||||
void Session::Frame::addLayer(osg::ref_ptr<CompositionLayer> layer)
|
||||
{
|
||||
_layers.push_back(layer);
|
||||
}
|
||||
|
||||
bool Session::Frame::begin()
|
||||
{
|
||||
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
|
||||
return _begun = check(xrBeginFrame(_session->getXrSession(), &frameBeginInfo),
|
||||
"Failed to begin OpenXR frame");
|
||||
}
|
||||
|
||||
bool Session::Frame::end()
|
||||
{
|
||||
std::vector<const XrCompositionLayerBaseHeader *> layers;
|
||||
layers.reserve(_layers.size());
|
||||
for (auto &layer: _layers)
|
||||
layers.push_back(layer->getXr());
|
||||
|
||||
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
|
||||
frameEndInfo.displayTime = _time;
|
||||
frameEndInfo.environmentBlendMode = _envBlendMode;
|
||||
frameEndInfo.layerCount = layers.size();
|
||||
frameEndInfo.layers = layers.data();
|
||||
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
bool ret = check(xrEndFrame(_session->getXrSession(), &frameEndInfo),
|
||||
"Failed to end OpenXR frame");
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return ret;
|
||||
}
|
||||
Vendored
+471
@@ -0,0 +1,471 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SESSION
|
||||
#define OSGXR_OPENXR_SESSION 1
|
||||
|
||||
#include "Path.h"
|
||||
#include "System.h"
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
#include <OpenThreads/Mutex>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Action;
|
||||
class ActionSet;
|
||||
class CompositionLayer;
|
||||
class Space;
|
||||
|
||||
class Session : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
Session(System *system, osgViewer::GraphicsWindow *window);
|
||||
// GL context must not be bound in another thread
|
||||
virtual ~Session();
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
void releaseGLObjects(osg::State *state = nullptr);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _session != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _system->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Action set attachment
|
||||
|
||||
/// Add an action set to the list.
|
||||
void addActionSet(ActionSet *actionSet);
|
||||
/**
|
||||
* Attach the added action sets to the OpenXR session.
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool attachActionSets();
|
||||
|
||||
/// Get the current interaction profile for the given subaction path.
|
||||
Path getCurrentInteractionProfile(const Path &subactionPath) const;
|
||||
|
||||
/// Get a list of bound source paths for an action.
|
||||
bool getActionBoundSources(Action *action,
|
||||
std::vector<XrPath> &sourcePaths) const;
|
||||
|
||||
/**
|
||||
* Get a localized name for an input source.
|
||||
* @param sourcePath Input source path.
|
||||
* @param whichComponents Which components to include.
|
||||
* @return Localized name string
|
||||
*/
|
||||
std::string getInputSourceLocalizedName(XrPath sourcePath,
|
||||
XrInputSourceLocalizedNameFlags whichComponents) const;
|
||||
|
||||
// Action syncing
|
||||
|
||||
/// Activate a certain action set.
|
||||
void activateActionSet(ActionSet *actionSet,
|
||||
Path subactionPath = Path());
|
||||
/// Deactivate a certain action set.
|
||||
void deactivateActionSet(ActionSet *actionSet,
|
||||
Path subactionPath = Path());
|
||||
/// Sync active action sets.
|
||||
bool syncActions();
|
||||
|
||||
/// Get the number of action sync counts that have taken place.
|
||||
unsigned int getActionSyncCount() const
|
||||
{
|
||||
return _actionSyncCount;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
// Find whether the session is ready to begin
|
||||
inline bool isReady() const
|
||||
{
|
||||
return _state == XR_SESSION_STATE_READY;
|
||||
}
|
||||
|
||||
// Find whether the session is running
|
||||
inline bool isRunning() const
|
||||
{
|
||||
return _running;
|
||||
}
|
||||
|
||||
// Find whether the session is already in the process of exiting
|
||||
inline bool isExiting() const
|
||||
{
|
||||
return _exiting;
|
||||
}
|
||||
|
||||
inline osgViewer::GraphicsWindow *getWindow() const
|
||||
{
|
||||
return _window.get();
|
||||
}
|
||||
|
||||
// State management
|
||||
|
||||
inline XrSessionState getState() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
inline void setState(XrSessionState state)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline const System *getSystem() const
|
||||
{
|
||||
return _system;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _system->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrSystemId getXrSystemId() const
|
||||
{
|
||||
return _system->getXrSystemId();
|
||||
}
|
||||
|
||||
inline XrSession getXrSession()
|
||||
{
|
||||
return _session;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
typedef std::vector<int64_t> SwapchainFormats;
|
||||
const SwapchainFormats &getSwapchainFormats() const;
|
||||
|
||||
Space *getLocalSpace();
|
||||
XrTime getLastDisplayTime() const
|
||||
{
|
||||
return _lastDisplayTime;
|
||||
}
|
||||
|
||||
void updateVisibilityMasks(XrViewConfigurationType viewConfigurationType,
|
||||
uint32_t viewIndex);
|
||||
osg::ref_ptr<osg::Geometry> getVisibilityMask(uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
bool force = false);
|
||||
|
||||
// Operations
|
||||
|
||||
/**
|
||||
* Check whether the session's GLX context is current.
|
||||
* This is intended for internal use only by the functions below.
|
||||
* @return whether the session's GLX context is current.
|
||||
*/
|
||||
bool checkCurrent() const;
|
||||
|
||||
/**
|
||||
* Make the GLX context current.
|
||||
* This makes the GLX context current to workaround broken XR runtimes.
|
||||
* It should be used to ensure the context is current before XR calls
|
||||
* affected by QUIRK_GL_CONTEXT_IGNORED if shouldSwitchContext()
|
||||
* (because the XR runtime will fail to do so), and to restore the
|
||||
* context after XR calls affected by QUIRK_GL_CONTEXT_CLEARED if
|
||||
* shouldRestoreContext() or getRestoreAction() == CONTEXT_RESTORE
|
||||
* (because the XR runtime will have explicitly released the context).
|
||||
*/
|
||||
void makeCurrent() const;
|
||||
/**
|
||||
* Release the GLX context so that none is current.
|
||||
* This makes no GLX context current to workaround broken XR runtimes.
|
||||
* It should be used release the switched context after XR calls
|
||||
* affected by QUIRK_GL_CONTEXT_IGNORED if shouldSwitchContext()
|
||||
* (because we had to call makeCurrent() before the call), and after XR
|
||||
* calls affected by QUIRK_GL_CONTEXT_CHANGED if getRestoreAction() ==
|
||||
* CONTEXT_RELEASE (because the XR runtime failed to do so).
|
||||
*/
|
||||
void releaseContext() const;
|
||||
|
||||
/// Action to perform before or after XR call due to quirks.
|
||||
typedef enum ContextAction {
|
||||
/// No action is necessary.
|
||||
CONTEXT_IGNORE = 0,
|
||||
/// The GLX context should be restored.
|
||||
CONTEXT_RESTORE,
|
||||
/**
|
||||
* The GLX context should be released leaving no context current.
|
||||
* It is assumed that no other context will need restoring in its
|
||||
* place.
|
||||
*/
|
||||
CONTEXT_RELEASE
|
||||
} ContextAction;
|
||||
|
||||
/**
|
||||
* Find whether for an XR call affected by QUIRK_GL_CONTEXT_IGNORED it
|
||||
* is necessary to switch the context prior to the XR call with
|
||||
* makeCurrent(), and also release it afterwards with releaseContext.
|
||||
* @return true if the context should be switched and released,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool shouldSwitchContext() const
|
||||
{
|
||||
return _instance->getQuirk(QUIRK_GL_CONTEXT_IGNORED) &&
|
||||
!checkCurrent();
|
||||
}
|
||||
/**
|
||||
* Find whether for an XR call affected by QUIRK_GL_CONTEXT_CLEARED it
|
||||
* is necessary to restore the context after the XR call.
|
||||
* @return true if the context should be restored after,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool shouldRestoreContext() const
|
||||
{
|
||||
return _instance->getQuirk(QUIRK_GL_CONTEXT_CLEARED) &&
|
||||
checkCurrent();
|
||||
}
|
||||
/**
|
||||
* Find what GL context action to perform after an XR call affected by
|
||||
* QUIRK_GL_CONTEXT_CLEARED or QUIRK_GL_CONTEXT_CHANGED.
|
||||
* @return CONTEXT_RESTORE if the runtime may clear the context and it
|
||||
* will need restoring.
|
||||
* CONTEXT_RELEASE if the runtime may change the context and
|
||||
* fail to release it.
|
||||
* CONTEXT_IGNORE otherwise.
|
||||
*/
|
||||
ContextAction getRestoreAction() const
|
||||
{
|
||||
bool cleared = _instance->getQuirk(QUIRK_GL_CONTEXT_CLEARED);
|
||||
bool changed = _instance->getQuirk(QUIRK_GL_CONTEXT_CHANGED);
|
||||
if (cleared || changed)
|
||||
{
|
||||
bool current = checkCurrent();
|
||||
if (cleared && current)
|
||||
return CONTEXT_RESTORE;
|
||||
if (changed && !current)
|
||||
return CONTEXT_RELEASE;
|
||||
}
|
||||
return CONTEXT_IGNORE;
|
||||
}
|
||||
|
||||
|
||||
bool begin(const System::ViewConfiguration &viewConfiguration);
|
||||
void end();
|
||||
void requestExit();
|
||||
|
||||
const System::ViewConfiguration *getViewConfiguration() const
|
||||
{
|
||||
return _viewConfiguration;
|
||||
}
|
||||
|
||||
class Frame : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
Frame(osg::ref_ptr<Session> session, XrFrameState *frameState);
|
||||
|
||||
virtual ~Frame();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _session->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
inline Session *getSession()
|
||||
{
|
||||
return _session;
|
||||
}
|
||||
|
||||
inline bool shouldRender() const
|
||||
{
|
||||
return _shouldRender;
|
||||
}
|
||||
|
||||
inline bool hasBegun() const
|
||||
{
|
||||
return _begun;
|
||||
}
|
||||
|
||||
inline XrTime getTime() const
|
||||
{
|
||||
return _time;
|
||||
}
|
||||
|
||||
void locateViews();
|
||||
|
||||
void checkLocateViews()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_locateViewsMutex);
|
||||
if (!_locatedViews)
|
||||
locateViews();
|
||||
}
|
||||
|
||||
bool isOrientationValid()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT;
|
||||
}
|
||||
bool isPositionValid()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT;
|
||||
}
|
||||
bool isOrientationTracked()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_TRACKED_BIT;
|
||||
}
|
||||
bool isPositionTracked()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_POSITION_TRACKED_BIT;
|
||||
}
|
||||
uint32_t getNumViews()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _views.size();
|
||||
}
|
||||
const XrFovf &getViewFov(uint32_t index)
|
||||
{
|
||||
checkLocateViews();
|
||||
return _views[index].fov;
|
||||
}
|
||||
const XrPosef &getViewPose(uint32_t index)
|
||||
{
|
||||
checkLocateViews();
|
||||
return _views[index].pose;
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
|
||||
inline void setEnvBlendMode(XrEnvironmentBlendMode envBlendMode)
|
||||
{
|
||||
_envBlendMode = envBlendMode;
|
||||
}
|
||||
inline XrEnvironmentBlendMode getEnvBlendMode() const
|
||||
{
|
||||
return _envBlendMode;
|
||||
}
|
||||
|
||||
inline void setOsgFrameNumber(unsigned int osgFrameNumber)
|
||||
{
|
||||
_osgFrameNumber = osgFrameNumber;
|
||||
}
|
||||
inline unsigned int getOsgFrameNumber() const
|
||||
{
|
||||
return _osgFrameNumber;
|
||||
}
|
||||
|
||||
void addLayer(osg::ref_ptr<CompositionLayer> layer);
|
||||
|
||||
// Operations
|
||||
|
||||
bool begin();
|
||||
bool end();
|
||||
|
||||
protected:
|
||||
|
||||
// Frame info
|
||||
osg::ref_ptr<Session> _session;
|
||||
XrTime _time;
|
||||
XrDuration _period;
|
||||
bool _shouldRender;
|
||||
|
||||
// OpenSceneGraph frame
|
||||
unsigned int _osgFrameNumber;
|
||||
|
||||
// For access to _locatedViews etc
|
||||
OpenThreads::Mutex _locateViewsMutex;
|
||||
|
||||
// View locations (protected by _locateViewsMutex)
|
||||
bool _locatedViews;
|
||||
XrViewState _viewState;
|
||||
std::vector<XrView> _views;
|
||||
|
||||
// Frame end info
|
||||
bool _begun;
|
||||
XrEnvironmentBlendMode _envBlendMode;
|
||||
std::vector<osg::ref_ptr<CompositionLayer> > _layers;
|
||||
};
|
||||
|
||||
osg::ref_ptr<Frame> waitFrame();
|
||||
|
||||
// OpenXR extension wrappers
|
||||
XrResult xrGetVisibilityMask(const System::ViewConfiguration &viewConfiguration,
|
||||
uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
XrVisibilityMaskKHR *visibilityMask)
|
||||
{
|
||||
return _instance->xrGetVisibilityMask(_session,
|
||||
viewConfiguration.getType(),
|
||||
viewIndex, visibilityMaskType,
|
||||
visibilityMask);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Init data
|
||||
osg::observer_ptr<osgViewer::GraphicsWindow> _window;
|
||||
|
||||
// Session data
|
||||
osg::ref_ptr<Instance> _instance;
|
||||
const System *_system;
|
||||
XrSession _session;
|
||||
const System::ViewConfiguration *_viewConfiguration;
|
||||
|
||||
// Action sets
|
||||
std::set<osg::ref_ptr<ActionSet>> _actionSets;
|
||||
typedef std::pair<ActionSet *, XrPath> ActionSetSubactionPair;
|
||||
std::set<ActionSetSubactionPair> _activeActionSets;
|
||||
unsigned int _actionSyncCount;
|
||||
|
||||
// Session state
|
||||
XrSessionState _state;
|
||||
bool _running;
|
||||
bool _exiting;
|
||||
|
||||
// Swapchain formats
|
||||
mutable bool _readSwapchainFormats;
|
||||
mutable SwapchainFormats _swapchainFormats;
|
||||
|
||||
// Reference spaces
|
||||
osg::ref_ptr<Space> _localSpace;
|
||||
XrTime _lastDisplayTime;
|
||||
|
||||
/*
|
||||
* Visibility mask geometry cache.
|
||||
* We keep visibility mask geometries cached to avoid duplication and so
|
||||
* we can update them after a VisibilityMaskChangedKHR event.
|
||||
*/
|
||||
typedef osg::ref_ptr<osg::Geometry> VisMaskGeometry;
|
||||
typedef std::vector<VisMaskGeometry> VisMaskGeometryView;
|
||||
typedef std::vector<VisMaskGeometryView> VisMaskGeometries;
|
||||
VisMaskGeometries _visMaskCache;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Space.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
static XrPosef poseIdentity = { { 0.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f } };
|
||||
|
||||
Space::Space(Session *session, XrReferenceSpaceType type) :
|
||||
_session(session),
|
||||
_space(XR_NULL_HANDLE)
|
||||
{
|
||||
// Attempt to create a reference space
|
||||
XrReferenceSpaceCreateInfo createInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
|
||||
createInfo.referenceSpaceType = type;
|
||||
createInfo.poseInReferenceSpace = poseIdentity;
|
||||
|
||||
check(xrCreateReferenceSpace(session->getXrSession(), &createInfo, &_space),
|
||||
"Failed to create OpenXR reference space");
|
||||
}
|
||||
|
||||
Space::Space(Session *session, ActionPose *action,
|
||||
Path subactionPath) :
|
||||
_session(session),
|
||||
_space(XR_NULL_HANDLE)
|
||||
{
|
||||
// Attempt to create an action space for this pose action
|
||||
XrActionSpaceCreateInfo createInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
|
||||
createInfo.action = action->getXrAction();
|
||||
createInfo.subactionPath = subactionPath.getXrPath();
|
||||
createInfo.poseInActionSpace = poseIdentity;
|
||||
|
||||
check(xrCreateActionSpace(session->getXrSession(), &createInfo, &_space),
|
||||
"Failed to create OpenXR action space");
|
||||
}
|
||||
|
||||
Space::~Space()
|
||||
{
|
||||
if (_session.valid() && _session->valid() && valid())
|
||||
{
|
||||
check(xrDestroySpace(_space),
|
||||
"Failed to destroy OpenXR space");
|
||||
}
|
||||
}
|
||||
|
||||
Space::Location::Location() :
|
||||
_flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
Space::Location::Location(XrSpaceLocationFlags flags,
|
||||
const osg::Quat &orientation,
|
||||
const osg::Vec3f &position) :
|
||||
_flags(flags),
|
||||
_orientation(orientation),
|
||||
_position(position)
|
||||
{
|
||||
}
|
||||
|
||||
bool Space::locate(const Space *baseSpace, XrTime time,
|
||||
Space::Location &location)
|
||||
{
|
||||
if (!_session.valid() || !valid())
|
||||
return false;
|
||||
assert(_session == baseSpace->_session);
|
||||
|
||||
XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
|
||||
bool ret = check(xrLocateSpace(getXrSpace(),
|
||||
baseSpace->getXrSpace(),
|
||||
time,
|
||||
&spaceLocation),
|
||||
"Failed to locate OpenXR space");
|
||||
if (ret)
|
||||
{
|
||||
osg::Quat orientation(spaceLocation.pose.orientation.x,
|
||||
spaceLocation.pose.orientation.y,
|
||||
spaceLocation.pose.orientation.z,
|
||||
spaceLocation.pose.orientation.w);
|
||||
osg::Vec3f position(spaceLocation.pose.position.x,
|
||||
spaceLocation.pose.position.y,
|
||||
spaceLocation.pose.position.z);
|
||||
location = Location(spaceLocation.locationFlags,
|
||||
orientation,
|
||||
position);
|
||||
}
|
||||
else
|
||||
{
|
||||
location = Location();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SPACE
|
||||
#define OSGXR_OPENXR_SPACE 1
|
||||
|
||||
#include "Action.h"
|
||||
#include "Path.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include <osg/Quat>
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Space : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
/// Create a reference space
|
||||
Space(Session *session, XrReferenceSpaceType type);
|
||||
/// Create an action space
|
||||
Space(Session *session, ActionPose *action,
|
||||
Path subactionPath = Path());
|
||||
virtual ~Space();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _space != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _session->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline XrSpace getXrSpace() const
|
||||
{
|
||||
return _space;
|
||||
}
|
||||
|
||||
// Locating a space
|
||||
|
||||
class Location
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Location();
|
||||
Location(XrSpaceLocationFlags flags,
|
||||
const osg::Quat &orientation,
|
||||
const osg::Vec3f &position);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _flags != 0;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
bool isOrientationValid() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT;
|
||||
}
|
||||
|
||||
bool isPositionValid() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
}
|
||||
|
||||
bool isOrientationTracked() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT;
|
||||
}
|
||||
|
||||
bool isPositionTracked() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
|
||||
}
|
||||
|
||||
XrSpaceLocationFlags getFlags() const
|
||||
{
|
||||
return _flags;
|
||||
}
|
||||
|
||||
const osg::Quat &getOrientation() const
|
||||
{
|
||||
return _orientation;
|
||||
}
|
||||
|
||||
const osg::Vec3f &getPosition() const
|
||||
{
|
||||
return _position;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
XrSpaceLocationFlags _flags;
|
||||
osg::Quat _orientation;
|
||||
osg::Vec3f _position;
|
||||
};
|
||||
|
||||
bool locate(const Space *baseSpace, XrTime time,
|
||||
Location &location);
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<Session> _session;
|
||||
XrSpace _space;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "Swapchain.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Swapchain::Swapchain(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format) :
|
||||
_session(session),
|
||||
_swapchain(XR_NULL_HANDLE),
|
||||
_width(view.getRecommendedWidth()),
|
||||
_height(view.getRecommendedHeight()),
|
||||
_samples(view.getRecommendedSamples()),
|
||||
_format(format),
|
||||
_readImageTextures(false)
|
||||
{
|
||||
XrSwapchainCreateInfo createInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
|
||||
createInfo.usageFlags = usageFlags;
|
||||
createInfo.format = format;
|
||||
createInfo.sampleCount = _samples;
|
||||
createInfo.width = _width;
|
||||
createInfo.height = _height;
|
||||
createInfo.faceCount = 1;
|
||||
createInfo.arraySize = 1;
|
||||
createInfo.mipCount = 1;
|
||||
|
||||
bool switchContext = _session->shouldSwitchContext();
|
||||
auto restoreAction = _session->getRestoreAction();
|
||||
if (switchContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
check(xrCreateSwapchain(getXrSession(), &createInfo, &_swapchain),
|
||||
"Failed to create OpenXR swapchain");
|
||||
|
||||
if (restoreAction == Session::CONTEXT_RESTORE)
|
||||
_session->makeCurrent();
|
||||
else if (switchContext || restoreAction == Session::CONTEXT_RELEASE)
|
||||
_session->releaseContext();
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain()
|
||||
{
|
||||
if (_session->valid() && valid())
|
||||
{
|
||||
// GL context must not be bound in another thread
|
||||
check(xrDestroySwapchain(_swapchain),
|
||||
"Failed to destroy OpenXR swapchain");
|
||||
}
|
||||
}
|
||||
|
||||
const Swapchain::ImageTextures &Swapchain::getImageTextures() const
|
||||
{
|
||||
if (!_readImageTextures)
|
||||
{
|
||||
// Enumerate the images
|
||||
uint32_t imageCount;
|
||||
// GL context must not be bound in another thread
|
||||
if (check(xrEnumerateSwapchainImages(_swapchain, 0, &imageCount, nullptr),
|
||||
"Failed to count OpenXR swapchain images"))
|
||||
{
|
||||
if (imageCount)
|
||||
{
|
||||
std::vector<XrSwapchainImageOpenGLKHR> images(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
|
||||
if (check(xrEnumerateSwapchainImages(_swapchain, images.size(), &imageCount,
|
||||
(XrSwapchainImageBaseHeader *)images.data()),
|
||||
"Failed to enumerate OpenXR swapchain images"))
|
||||
{
|
||||
for (auto image: images)
|
||||
_imageTextures.push_back(image.image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readImageTextures = true;
|
||||
}
|
||||
|
||||
return _imageTextures;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> Swapchain::getImageOsgTexture(unsigned int index) const
|
||||
{
|
||||
if (_imageOsgTextures.empty())
|
||||
{
|
||||
getImageTextures();
|
||||
_imageOsgTextures.resize(_imageTextures.size());
|
||||
}
|
||||
|
||||
assert(index < _imageOsgTextures.size());
|
||||
if (!_imageOsgTextures[index].valid())
|
||||
{
|
||||
// Create an OSG texture out of it
|
||||
osg::Texture2D *texture = new osg::Texture2D;
|
||||
texture->setTextureSize(getWidth(),
|
||||
getHeight());
|
||||
texture->setInternalFormat(getFormat());
|
||||
unsigned int contextID = _session->getWindow()->getState()->getContextID();
|
||||
texture->setTextureObject(contextID, new osg::Texture::TextureObject(texture, _imageTextures[index], GL_TEXTURE_2D));
|
||||
|
||||
_imageOsgTextures[index] = texture;
|
||||
}
|
||||
|
||||
return _imageOsgTextures[index];
|
||||
}
|
||||
|
||||
int Swapchain::acquireImage() const
|
||||
{
|
||||
// Acquire a swapchain image
|
||||
uint32_t imageIndex;
|
||||
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
// GL context must not be bound in another thread
|
||||
if (check(xrAcquireSwapchainImage(_swapchain, nullptr, &imageIndex),
|
||||
"Failed to acquire swapchain image"))
|
||||
{
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return imageIndex;
|
||||
}
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Swapchain::waitImage(XrDuration timeoutNs) const
|
||||
{
|
||||
// Wait on the swapchain image
|
||||
XrSwapchainImageWaitInfo waitInfo = { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
|
||||
waitInfo.timeout = timeoutNs; // 100ms
|
||||
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
// GL context must not be bound in another thread
|
||||
bool ret = check(xrWaitSwapchainImage(_swapchain, &waitInfo),
|
||||
"Failed to wait for swapchain image");
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Swapchain::releaseImage() const
|
||||
{
|
||||
// Release the swapchain image
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
// GL context must not be bound in another thread
|
||||
check(xrReleaseSwapchainImage(_swapchain, nullptr),
|
||||
"Failed to release OpenXR swapchain image");
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SWAPCHAIN
|
||||
#define OSGXR_OPENXR_SWAPCHAIN 1
|
||||
|
||||
#include "Session.h"
|
||||
#include "System.h"
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Swapchain : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
Swapchain(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format);
|
||||
// GL context must not be bound in another thread
|
||||
virtual ~Swapchain();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _swapchain != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _session->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline XrSession getXrSession() const
|
||||
{
|
||||
return _session->getXrSession();
|
||||
}
|
||||
|
||||
inline XrSwapchain getXrSwapchain() const
|
||||
{
|
||||
return _swapchain;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
inline uint32_t getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
inline uint32_t getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
inline uint32_t getSamples() const
|
||||
{
|
||||
return _samples;
|
||||
}
|
||||
|
||||
inline int64_t getFormat() const
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
typedef std::vector<GLuint> ImageTextures;
|
||||
// GL context must not be bound in another thread
|
||||
const ImageTextures &getImageTextures() const;
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> getImageOsgTexture(unsigned int index) const;
|
||||
|
||||
// Operations
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
int acquireImage() const;
|
||||
// GL context must not be bound in another thread
|
||||
bool waitImage(XrDuration timeoutNs) const;
|
||||
// GL context must not be bound in another thread
|
||||
void releaseImage() const;
|
||||
|
||||
protected:
|
||||
|
||||
// Session data
|
||||
osg::ref_ptr<Session> _session;
|
||||
XrSwapchain _swapchain;
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
uint32_t _samples;
|
||||
int64_t _format;
|
||||
|
||||
// Image OpenGL textures
|
||||
mutable bool _readImageTextures;
|
||||
mutable ImageTextures _imageTextures;
|
||||
mutable std::vector<osg::ref_ptr<osg::Texture2D>> _imageOsgTextures;
|
||||
|
||||
// Current image
|
||||
mutable int _currentImage;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "SwapchainGroup.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
SwapchainGroup::SwapchainGroup(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format,
|
||||
XrSwapchainUsageFlags depthUsageFlags,
|
||||
int64_t depthFormat) :
|
||||
_swapchain(new Swapchain(session, view, usageFlags, format))
|
||||
{
|
||||
if (depthFormat)
|
||||
_depthSwapchain = new Swapchain(session, view, depthUsageFlags, depthFormat);
|
||||
}
|
||||
|
||||
SwapchainGroup::~SwapchainGroup()
|
||||
{
|
||||
}
|
||||
|
||||
int SwapchainGroup::acquireImages() const
|
||||
{
|
||||
int imageIndex = _swapchain->acquireImage();
|
||||
if (depthValid())
|
||||
{
|
||||
int depthImageIndex = _depthSwapchain->acquireImage();
|
||||
if (imageIndex != depthImageIndex)
|
||||
OSG_WARN << "Depth swapchain image mismatch, expected " << imageIndex
|
||||
<< ", got " << depthImageIndex << std::endl;
|
||||
}
|
||||
return imageIndex;
|
||||
}
|
||||
|
||||
bool SwapchainGroup::waitImages(XrDuration timeoutNs) const
|
||||
{
|
||||
bool ret = _swapchain->waitImage(timeoutNs);
|
||||
if (depthValid())
|
||||
{
|
||||
bool depthRet = _depthSwapchain->waitImage(timeoutNs);
|
||||
ret = ret && depthRet;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SwapchainGroup::releaseImages() const
|
||||
{
|
||||
_swapchain->releaseImage();
|
||||
if (depthValid())
|
||||
_depthSwapchain->releaseImage();
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SWAPCHAIN_GROUP
|
||||
#define OSGXR_OPENXR_SWAPCHAIN_GROUP 1
|
||||
|
||||
#include "Swapchain.h"
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class SwapchainGroupSubImage;
|
||||
|
||||
/// Groups colour and depth swapchains together
|
||||
class SwapchainGroup : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SwapchainGroupSubImage SubImage;
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
SwapchainGroup(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format,
|
||||
XrSwapchainUsageFlags depthUsageFlags = 0,
|
||||
int64_t depthFormat = 0);
|
||||
// GL context must not be bound in another thread
|
||||
virtual ~SwapchainGroup();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _swapchain->valid();
|
||||
}
|
||||
|
||||
inline bool depthValid() const
|
||||
{
|
||||
return _depthSwapchain.valid() && _depthSwapchain->valid();
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
inline osg::ref_ptr<Swapchain> getSwapchain() const
|
||||
{
|
||||
return _swapchain;
|
||||
}
|
||||
|
||||
inline osg::ref_ptr<Swapchain> getDepthSwapchain() const
|
||||
{
|
||||
return _depthSwapchain;
|
||||
}
|
||||
|
||||
inline XrSwapchain getXrSwapchain() const
|
||||
{
|
||||
return _swapchain->getXrSwapchain();
|
||||
}
|
||||
|
||||
inline XrSwapchain getDepthXrSwapchain() const
|
||||
{
|
||||
if (_depthSwapchain.valid())
|
||||
return _depthSwapchain->getXrSwapchain();
|
||||
else
|
||||
return XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline uint32_t getWidth() const
|
||||
{
|
||||
return _swapchain->getWidth();
|
||||
}
|
||||
|
||||
inline uint32_t getHeight() const
|
||||
{
|
||||
return _swapchain->getHeight();
|
||||
}
|
||||
|
||||
inline uint32_t getSamples() const
|
||||
{
|
||||
return _swapchain->getSamples();
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
typedef Swapchain::ImageTextures ImageTextures;
|
||||
const ImageTextures &getImageTextures() const
|
||||
{
|
||||
return _swapchain->getImageTextures();
|
||||
}
|
||||
const ImageTextures &getDepthImageTextures() const
|
||||
{
|
||||
return _depthSwapchain->getImageTextures();
|
||||
}
|
||||
|
||||
// Operations
|
||||
|
||||
int acquireImages() const;
|
||||
bool waitImages(XrDuration timeoutNs) const;
|
||||
void releaseImages() const;
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<Swapchain> _swapchain;
|
||||
osg::ref_ptr<Swapchain> _depthSwapchain;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SWAPCHAIN_GROUP_SUB_IMAGE
|
||||
#define OSGXR_OPENXR_SWAPCHAIN_GROUP_SUB_IMAGE 1
|
||||
|
||||
#include "SwapchainGroup.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class SwapchainGroupSubImage
|
||||
{
|
||||
public:
|
||||
SwapchainGroupSubImage(SwapchainGroup *group) :
|
||||
_group(group),
|
||||
_x(0),
|
||||
_y(0),
|
||||
_width(group->getWidth()),
|
||||
_height(group->getHeight()),
|
||||
_arrayIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
SwapchainGroupSubImage(SwapchainGroup *group,
|
||||
const System::ViewConfiguration::View::Viewport &vp) :
|
||||
_group(group),
|
||||
_x(vp.x),
|
||||
_y(vp.y),
|
||||
_width(vp.width),
|
||||
_height(vp.height),
|
||||
_arrayIndex(vp.arrayIndex)
|
||||
{
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return _group->valid();
|
||||
}
|
||||
|
||||
bool depthValid() const
|
||||
{
|
||||
return _group->depthValid();
|
||||
}
|
||||
|
||||
osg::ref_ptr<SwapchainGroup> getSwapchainGroup() const
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
uint32_t getX() const
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
uint32_t getY() const
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
uint32_t getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
uint32_t getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
uint32_t getArrayIndex() const
|
||||
{
|
||||
return _arrayIndex;
|
||||
}
|
||||
|
||||
void getXrSubImage(XrSwapchainSubImage *out) const
|
||||
{
|
||||
out->swapchain = _group->getXrSwapchain();
|
||||
out->imageRect.offset = { (int32_t)_x,
|
||||
(int32_t)_y };
|
||||
out->imageRect.extent = { (int32_t)_width,
|
||||
(int32_t)_height };
|
||||
out->imageArrayIndex = _arrayIndex;
|
||||
}
|
||||
|
||||
void getDepthXrSubImage(XrSwapchainSubImage *out) const
|
||||
{
|
||||
out->swapchain = _group->getDepthXrSwapchain();
|
||||
out->imageRect.offset = { (int32_t)_x,
|
||||
(int32_t)_y };
|
||||
out->imageRect.extent = { (int32_t)_width,
|
||||
(int32_t)_height };
|
||||
out->imageArrayIndex = _arrayIndex;
|
||||
}
|
||||
|
||||
protected:
|
||||
osg::ref_ptr<SwapchainGroup> _group;
|
||||
uint32_t _x;
|
||||
uint32_t _y;
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
uint32_t _arrayIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "System.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
void System::getProperties() const
|
||||
{
|
||||
XrSystemProperties properties;
|
||||
properties.type = XR_TYPE_SYSTEM_PROPERTIES;
|
||||
properties.next = nullptr;
|
||||
|
||||
if (check(xrGetSystemProperties(getXrInstance(), _systemId, &properties),
|
||||
"Failed to get OpenXR system properties"))
|
||||
{
|
||||
memcpy(_systemName, properties.systemName, sizeof(_systemName));
|
||||
_orientationTracking = properties.trackingProperties.orientationTracking;
|
||||
_positionTracking = properties.trackingProperties.positionTracking;
|
||||
}
|
||||
|
||||
_readProperties = true;
|
||||
}
|
||||
|
||||
const System::ViewConfiguration::Views &System::ViewConfiguration::getViews() const
|
||||
{
|
||||
if (!_readViews)
|
||||
{
|
||||
uint32_t viewCount = 0;
|
||||
if (check(xrEnumerateViewConfigurationViews(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
0, &viewCount, nullptr),
|
||||
"Failed to count OpenXR view configuration views"))
|
||||
{
|
||||
if (viewCount)
|
||||
{
|
||||
std::vector<XrViewConfigurationView> views(viewCount,
|
||||
{ XR_TYPE_VIEW_CONFIGURATION_VIEW });
|
||||
if (check(xrEnumerateViewConfigurationViews(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
views.size(), &viewCount,
|
||||
views.data()),
|
||||
"Failed to enumerate OpenXR view configuration views"))
|
||||
{
|
||||
for (auto &view: views)
|
||||
_views.push_back(View(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readViews = true;
|
||||
}
|
||||
|
||||
return _views;
|
||||
}
|
||||
|
||||
const System::ViewConfiguration::EnvBlendModes &System::ViewConfiguration::getEnvBlendModes() const
|
||||
{
|
||||
if (!_readEnvBlendModes)
|
||||
{
|
||||
uint32_t blendModeCount = 0;
|
||||
if (check(xrEnumerateEnvironmentBlendModes(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
0, &blendModeCount, nullptr),
|
||||
"Failed to count OpenXR environment blend modes"))
|
||||
{
|
||||
if (blendModeCount)
|
||||
{
|
||||
_envBlendModes.resize(blendModeCount);
|
||||
if (!check(xrEnumerateEnvironmentBlendModes(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
_envBlendModes.size(),
|
||||
&blendModeCount,
|
||||
_envBlendModes.data()),
|
||||
"Failed to enumerate OpenXR environment blend modes"))
|
||||
{
|
||||
_envBlendModes.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readEnvBlendModes = true;
|
||||
}
|
||||
|
||||
return _envBlendModes;
|
||||
}
|
||||
|
||||
const System::ViewConfigurations &System::getViewConfigurations() const
|
||||
{
|
||||
if (!_readViewConfigurations)
|
||||
{
|
||||
uint32_t viewConfigCount = 0;
|
||||
if (check(xrEnumerateViewConfigurations(getXrInstance(), getXrSystemId(),
|
||||
0, &viewConfigCount, nullptr),
|
||||
"Failed to count OpenXR view configuration types"))
|
||||
{
|
||||
if (viewConfigCount)
|
||||
{
|
||||
std::vector<XrViewConfigurationType> types(viewConfigCount);
|
||||
if (check(xrEnumerateViewConfigurations(getXrInstance(), getXrSystemId(),
|
||||
types.size(), &viewConfigCount,
|
||||
types.data()),
|
||||
"Failed to enumerate OpenXR view configuration types"))
|
||||
{
|
||||
_viewConfigurations.reserve(viewConfigCount);
|
||||
for (auto type: types)
|
||||
_viewConfigurations.push_back(ViewConfiguration(this, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readViewConfigurations = true;
|
||||
}
|
||||
|
||||
return _viewConfigurations;
|
||||
}
|
||||
Vendored
+221
@@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SYSTEM
|
||||
#define OSGXR_OPENXR_SYSTEM 1
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <osg/DisplaySettings>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class System
|
||||
{
|
||||
public:
|
||||
|
||||
System(Instance *instance, XrSystemId systemId) :
|
||||
_instance(instance),
|
||||
_systemId(systemId),
|
||||
_readProperties(false),
|
||||
_orientationTracking(false),
|
||||
_positionTracking(false),
|
||||
_readViewConfigurations(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _instance->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline Instance *getInstance()
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
inline const Instance *getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrSystemId getXrSystemId() const
|
||||
{
|
||||
return _systemId;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
void getProperties() const;
|
||||
|
||||
inline const char *getSystemName() const
|
||||
{
|
||||
if (!_readProperties)
|
||||
getProperties();
|
||||
return _systemName;
|
||||
}
|
||||
|
||||
inline bool getOrientationTracking() const
|
||||
{
|
||||
if (!_readProperties)
|
||||
getProperties();
|
||||
return _orientationTracking;
|
||||
}
|
||||
|
||||
inline bool getPositionTracking() const
|
||||
{
|
||||
if (!_readProperties)
|
||||
getProperties();
|
||||
return _positionTracking;
|
||||
}
|
||||
|
||||
class ViewConfiguration
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ViewConfiguration(const System *system, XrViewConfigurationType type) :
|
||||
_system(system),
|
||||
_type(type),
|
||||
_readViews(false),
|
||||
_readEnvBlendModes(false)
|
||||
{
|
||||
}
|
||||
|
||||
XrViewConfigurationType getType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
class View
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
struct Viewport
|
||||
{
|
||||
uint32_t x, y, width, height, arrayIndex;
|
||||
};
|
||||
|
||||
View(uint32_t recommendedWidth,
|
||||
uint32_t recommendedHeight,
|
||||
uint32_t recommendedSamples = 1) :
|
||||
_recommendedWidth(recommendedWidth),
|
||||
_recommendedHeight(recommendedHeight),
|
||||
_recommendedSamples(recommendedSamples)
|
||||
{
|
||||
}
|
||||
|
||||
View(const XrViewConfigurationView &view) :
|
||||
_recommendedWidth(view.recommendedImageRectWidth),
|
||||
_recommendedHeight(view.recommendedImageRectHeight),
|
||||
_recommendedSamples(view.recommendedSwapchainSampleCount)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t getRecommendedWidth() const
|
||||
{
|
||||
return _recommendedWidth;
|
||||
}
|
||||
|
||||
uint32_t getRecommendedHeight() const
|
||||
{
|
||||
return _recommendedHeight;
|
||||
}
|
||||
|
||||
|
||||
uint32_t getRecommendedSamples() const
|
||||
{
|
||||
return _recommendedSamples;
|
||||
}
|
||||
|
||||
/// Tile another view horizontally after this one
|
||||
struct Viewport tileHorizontally(const View &other)
|
||||
{
|
||||
struct Viewport vp;
|
||||
vp.x = _recommendedWidth;
|
||||
vp.y = 0;
|
||||
vp.width = other._recommendedWidth;
|
||||
vp.height = other._recommendedHeight;
|
||||
vp.arrayIndex = 0;
|
||||
|
||||
_recommendedWidth += other._recommendedWidth;
|
||||
_recommendedHeight = std::max(_recommendedHeight,
|
||||
other._recommendedHeight);
|
||||
return vp;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t _recommendedWidth;
|
||||
uint32_t _recommendedHeight;
|
||||
uint32_t _recommendedSamples;
|
||||
};
|
||||
|
||||
typedef std::vector<View> Views;
|
||||
const Views &getViews() const;
|
||||
|
||||
typedef std::vector<XrEnvironmentBlendMode> EnvBlendModes;
|
||||
const EnvBlendModes &getEnvBlendModes() const;
|
||||
|
||||
protected:
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _system->getInstance()->check(result, warnMsg);
|
||||
}
|
||||
|
||||
const System *_system;
|
||||
XrViewConfigurationType _type;
|
||||
|
||||
// Views
|
||||
mutable bool _readViews;
|
||||
mutable Views _views;
|
||||
|
||||
// Environment blend modes
|
||||
mutable bool _readEnvBlendModes;
|
||||
mutable EnvBlendModes _envBlendModes;
|
||||
};
|
||||
|
||||
typedef std::vector<ViewConfiguration> ViewConfigurations;
|
||||
const ViewConfigurations &getViewConfigurations() const;
|
||||
|
||||
protected:
|
||||
|
||||
// System data
|
||||
Instance *_instance;
|
||||
XrSystemId _systemId;
|
||||
|
||||
// Properties
|
||||
mutable char _systemName[XR_MAX_SYSTEM_NAME_SIZE];
|
||||
mutable bool _readProperties;
|
||||
mutable bool _orientationTracking;
|
||||
mutable bool _positionTracking;
|
||||
|
||||
// View configurations
|
||||
mutable bool _readViewConfigurations;
|
||||
mutable ViewConfigurations _viewConfigurations;
|
||||
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user