Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion runtime/windows-openvr/REV_CAPI_Audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,17 @@ ovrResult AudioEndPointToGuid(char* deviceStrBuffer, int deviceStrSize, GUID* de
}

PROPVARIANT pv;
PropVariantInit(&pv);
hr = pPropertyStore->GetValue(PKEY_AudioEndpoint_GUID, &pv);
if (FAILED(hr))
{
return ovrError_AccessDenied;
}

hr = IIDFromString(pv.pwszVal, deviceGuid);
hr = pv.vt == VT_LPWSTR && pv.pwszVal
? IIDFromString(pv.pwszVal, deviceGuid)
: E_INVALIDARG;
PropVariantClear(&pv);
if (FAILED(hr))
return ovrError_RuntimeException;
return ovrSuccess;
Expand Down Expand Up @@ -83,6 +87,7 @@ ovrResult GetDefaultAudioEndpoint(EDataFlow endpoint, WCHAR deviceStrBuffer[OVR_
if (FAILED(hr))
return ovrError_AudioComError;
wcscpy_s(deviceStrBuffer, OVR_AUDIO_MAX_DEVICE_STR_SIZE, pGuid);
CoTaskMemFree(pGuid);
return ovrSuccess;
}

Expand Down
4 changes: 2 additions & 2 deletions runtime/windows-openvr/REV_CAPI_Vk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ ovr_GetInstanceExtensionsVk(
uint32_t* inoutExtensionNamesSize)
{
if (!inoutExtensionNamesSize)
ovrError_InvalidParameter;
return ovrError_InvalidParameter;

std::stringstream extensions;
uint32_t required = vr::VRCompositor()->GetVulkanInstanceExtensionsRequired(nullptr, 0);
Expand Down Expand Up @@ -79,7 +79,7 @@ ovr_GetDeviceExtensionsVk(
uint32_t* inoutExtensionNamesSize)
{
if (!inoutExtensionNamesSize)
ovrError_InvalidParameter;
return ovrError_InvalidParameter;

std::stringstream extensions;
// xrizer cannot answer this query until ovr_GetSessionPhysicalDeviceVk has
Expand Down
120 changes: 95 additions & 25 deletions runtime/windows-openxr/InputManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,46 @@ InputManager::InputManager(XrInstance instance)
{
device->GetActiveSets(m_ActionSets);

std::vector<XrActionSuggestedBinding> bindings;
XrPath profile = device->GetSuggestedBindings(bindings);
if (!profile)
// Suggest every profile a device supports and let the runtime pick the
// one matching the connected controllers. A runtime that can't remap a
// single profile to other hardware leaves those controllers without any
// binding, so they never report a pose or a button.
std::vector<ProfileBindings> profiles;
device->GetSuggestedBindings(profiles);
for (const ProfileBindings& profile : profiles)
{
XrInteractionProfileSuggestedBinding suggestedBinding = XR_TYPE(INTERACTION_PROFILE_SUGGESTED_BINDING);
suggestedBinding.countSuggestedBindings = (uint32_t)profile.Bindings.size();
suggestedBinding.suggestedBindings = profile.Bindings.data();
suggestedBinding.interactionProfile = profile.Profile;
XrResult rs = xrSuggestInteractionProfileBindings(instance, &suggestedBinding);
// A rejected profile must not cost the others their bindings.
TraceXrResult("xrSuggestInteractionProfileBindings", rs);
}
}
}

void InputManager::UpdateInteractionProfiles(XrSession session)
{
std::lock_guard<std::mutex> actionLock(m_ActionMutex);

const XrPath touch = GetXrPath("/interaction_profiles/oculus/touch_controller");
const XrPath index = GetXrPath("/interaction_profiles/valve/index_controller");
const XrPath wmr = GetXrPath("/interaction_profiles/microsoft/motion_controller");
for (int i = 0; i < ovrHand_Count; i++)
{
XrInteractionProfileState state = XR_TYPE(INTERACTION_PROFILE_STATE);
if (XR_FAILED(xrGetCurrentInteractionProfile(session, s_SubActionPaths[i], &state)))
continue;

XrInteractionProfileSuggestedBinding suggestedBinding = XR_TYPE(INTERACTION_PROFILE_SUGGESTED_BINDING);
suggestedBinding.countSuggestedBindings = (uint32_t)bindings.size();
suggestedBinding.suggestedBindings = bindings.data();
suggestedBinding.interactionProfile = profile;
XrResult rs = xrSuggestInteractionProfileBindings(instance, &suggestedBinding);
assert(XR_SUCCEEDED(rs));
for (InputDevice* device : m_InputDevices)
device->SetInteractionProfile((ovrHandType)i, state.interactionProfile);

// 0 = no binding (the controller won't track), 1 = Touch, 2 = Index,
// 3 = WMR, 4 = a profile RiftLift doesn't suggest.
const XrPath profile = state.interactionProfile;
TraceOculusValue(i == ovrHand_Left ? "interactionProfile.left" : "interactionProfile.right",
profile == XR_NULL_PATH ? 0 : profile == touch ? 1 : profile == index ? 2 : profile == wmr ? 3 : 4);
}
}

Expand Down Expand Up @@ -428,8 +457,19 @@ InputManager::OculusTouch::OculusTouch(XrInstance instance)
, m_Pose(this, XR_ACTION_TYPE_POSE_INPUT, "pose", "Controller pose", true)
, m_Vibration(this, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibration", "Vibration", true)
{
if (Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE))
m_Trackpad_Buttons = Action(this, XR_ACTION_TYPE_FLOAT_INPUT, "trackpad-buttons", "Trackpad Buttons", true);
// The WMR profile is suggested on every runtime, so its trackpad split is too.
m_Trackpad_Buttons = Action(this, XR_ACTION_TYPE_FLOAT_INPUT, "trackpad-buttons", "Trackpad Buttons", true);
}

void InputManager::OculusTouch::SetInteractionProfile(ovrHandType hand, XrPath profile)
{
m_WmrBound[hand] = profile != XR_NULL_PATH &&
profile == GetXrPath("/interaction_profiles/microsoft/motion_controller");
}

bool InputManager::OculusTouch::UsesTrackpadButtons(ovrHandType hand) const
{
return Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE) || m_WmrBound[hand];
}

InputManager::OculusTouch::~OculusTouch()
Expand All @@ -456,20 +496,40 @@ void InputManager::OculusTouch::UpdateHaptics(XrSession session, XrDuration disp
}
}

XrPath InputManager::OculusTouch::GetSuggestedBindings(std::vector<XrActionSuggestedBinding>& outBindings) const
void InputManager::OculusTouch::GetSuggestedBindings(std::vector<ProfileBindings>& outProfiles) const
{
if (Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE))
{
outProfiles.push_back(GetLayoutBindings(Layout_WMR));
return;
}

// Always offer Touch: nearly every OpenXR game suggests it, so newer
// controllers ship bindings for it even where the runtime can't convert
// the Index profile to them. Monado doesn't convert Touch to WMR
// controllers, so offer WMR's own profile as well.
outProfiles.push_back(GetLayoutBindings(Layout_Touch));
if (Runtime::Get().UseHack(Runtime::HACK_VALVE_INDEX_PROFILE))
outProfiles.push_back(GetLayoutBindings(Layout_Index));
outProfiles.push_back(GetLayoutBindings(Layout_WMR));
}

InputManager::ProfileBindings InputManager::OculusTouch::GetLayoutBindings(Layout layout) const
{
std::string prefixes[ovrHand_Count] = { "/user/hand/left", "/user/hand/right" };
ProfileBindings result;
std::vector<XrActionSuggestedBinding>& outBindings = result.Bindings;

#define ADD_BINDING(action, path) outBindings.push_back(XrActionSuggestedBinding{ action, GetXrPath(path) })

if (Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE))
if (layout == Layout_WMR)
{
ADD_BINDING(m_Button_Enter, "/user/hand/left/input/menu/click");
ADD_BINDING(m_Button_Home, "/user/hand/right/input/menu/click");
}
else
{
if (Runtime::Get().UseHack(Runtime::HACK_VALVE_INDEX_PROFILE))
if (layout == Layout_Index)
{
ADD_BINDING(m_Button_Enter, "/user/hand/left/input/trackpad/force");
ADD_BINDING(m_Button_AX, "/user/hand/left/input/a/click");
Expand All @@ -495,7 +555,7 @@ XrPath InputManager::OculusTouch::GetSuggestedBindings(std::vector<XrActionSugge

for (int i = 0; i < ovrHand_Count; i++)
{
if (Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE))
if (layout == Layout_WMR)
{
ADD_BINDING(m_Trackpad_Buttons, prefixes[i] + "/input/trackpad/y");
ADD_BINDING(m_Button_AX, prefixes[i] + "/input/trackpad/click");
Expand All @@ -514,7 +574,7 @@ XrPath InputManager::OculusTouch::GetSuggestedBindings(std::vector<XrActionSugge
ADD_BINDING(m_Thumbstick, prefixes[i] + "/input/thumbstick");
ADD_BINDING(m_Button_Thumb, prefixes[i] + "/input/thumbstick/click");
ADD_BINDING(m_Touch_Thumb, prefixes[i] + "/input/thumbstick/touch");
if (Runtime::Get().UseHack(Runtime::HACK_VALVE_INDEX_PROFILE))
if (layout == Layout_Index)
ADD_BINDING(m_Touch_ThumbRest, prefixes[i] + "/input/trackpad/touch");
else
ADD_BINDING(m_Touch_ThumbRest, prefixes[i] + "/input/thumbrest/touch");
Expand All @@ -529,12 +589,13 @@ XrPath InputManager::OculusTouch::GetSuggestedBindings(std::vector<XrActionSugge

#undef ADD_BINDING

if (Runtime::Get().UseHack(Runtime::HACK_VALVE_INDEX_PROFILE))
return GetXrPath("/interaction_profiles/valve/index_controller");
else if (Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE))
return GetXrPath("/interaction_profiles/microsoft/motion_controller");
if (layout == Layout_Index)
result.Profile = GetXrPath("/interaction_profiles/valve/index_controller");
else if (layout == Layout_WMR)
result.Profile = GetXrPath("/interaction_profiles/microsoft/motion_controller");
else
return GetXrPath("/interaction_profiles/oculus/touch_controller");
result.Profile = GetXrPath("/interaction_profiles/oculus/touch_controller");
return result;
}

void InputManager::OculusTouch::GetActionSpaces(XrSession session, std::vector<XrSpace>& outSpaces) const
Expand Down Expand Up @@ -574,7 +635,7 @@ void InputManager::OculusTouch::GetInputState(XrSession session, ovrControllerTy
ovrHandType hand = (ovrHandType)i;
unsigned int buttons = 0, touches = 0;

if (Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE))
if (UsesTrackpadButtons(hand))
{
if (m_Button_AX.GetDigital(session, hand) || m_Button_BY.GetDigital(session, hand))
{
Expand Down Expand Up @@ -632,7 +693,7 @@ void InputManager::OculusTouch::GetInputState(XrSession session, ovrControllerTy
inputState->HandTriggerNoDeadzone[i] = m_HandTrigger.GetAnalog(session, hand);

// Derive gestures from touch flags
if (!Runtime::Get().UseHack(Runtime::HACK_WMR_PROFILE) || inputState->HandTriggerNoDeadzone[i] > 0.5f)
if (!UsesTrackpadButtons(hand) || inputState->HandTriggerNoDeadzone[i] > 0.5f)
{
if (!(touches & ovrTouch_RIndexTrigger))
touches |= ovrTouch_RIndexPointing;
Expand Down Expand Up @@ -784,8 +845,11 @@ InputManager::XboxGamepad::~XboxGamepad()
{
}

XrPath InputManager::XboxGamepad::GetSuggestedBindings(std::vector<XrActionSuggestedBinding>& outBindings) const
void InputManager::XboxGamepad::GetSuggestedBindings(std::vector<ProfileBindings>& outProfiles) const
{
ProfileBindings result;
std::vector<XrActionSuggestedBinding>& outBindings = result.Bindings;

#define ADD_BINDING(action, path) outBindings.push_back(XrActionSuggestedBinding{ action, GetXrPath(std::string("/user/gamepad") + path) })

ADD_BINDING(m_Button_A, "/input/a/click");
Expand All @@ -811,7 +875,8 @@ XrPath InputManager::XboxGamepad::GetSuggestedBindings(std::vector<XrActionSugge

#undef ADD_BINDING

return GetXrPath("/interaction_profiles/microsoft/xbox_controller");
result.Profile = GetXrPath("/interaction_profiles/microsoft/xbox_controller");
outProfiles.push_back(result);
}

void InputManager::XboxGamepad::GetInputState(XrSession session, ovrControllerType controllerType, ovrInputState* inputState)
Expand Down Expand Up @@ -905,6 +970,11 @@ ovrResult InputManager::AttachSession(XrSession session)
{
std::lock_guard<std::mutex> actionLock(m_ActionMutex);

// A new session reports its bound profiles again through events.
for (InputDevice* device : m_InputDevices)
for (int i = 0; i < ovrHand_Count; i++)
device->SetInteractionProfile((ovrHandType)i, XR_NULL_PATH);

for (XrSpace space : m_ActionSpaces)
CHK_XR(xrDestroySpace(space));
m_ActionSpaces.clear();
Expand Down
24 changes: 20 additions & 4 deletions runtime/windows-openxr/InputManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ class Runtime;
class InputManager
{
public:
struct ProfileBindings
{
XrPath Profile;
std::vector<XrActionSuggestedBinding> Bindings;
};

class InputDevice
{
public:
Expand All @@ -25,7 +31,8 @@ class InputManager
virtual void GetInputState(XrSession session, ovrControllerType controllerType, ovrInputState* inputState) = 0;

// Bindings
virtual XrPath GetSuggestedBindings(std::vector<XrActionSuggestedBinding>& outBindings) const { return XR_NULL_PATH; }
virtual void GetSuggestedBindings(std::vector<ProfileBindings>& outProfiles) const { }
virtual void SetInteractionProfile(ovrHandType hand, XrPath profile) { }
virtual void GetActionSpaces(XrSession session, std::vector<XrSpace>& outSpaces) const { }
virtual void GetActiveSets(std::vector<XrActiveActionSet>& outSets) const { }

Expand Down Expand Up @@ -73,7 +80,8 @@ class InputManager
virtual ovrControllerType GetType() const override;
virtual bool IsConnected() const override;
virtual void GetInputState(XrSession session, ovrControllerType controllerType, ovrInputState* inputState) override;
virtual XrPath GetSuggestedBindings(std::vector<XrActionSuggestedBinding>& outBindings) const override;
virtual void GetSuggestedBindings(std::vector<ProfileBindings>& outProfiles) const override;
virtual void SetInteractionProfile(ovrHandType hand, XrPath profile) override;
virtual void GetActionSpaces(XrSession session, std::vector<XrSpace>& outSpaces) const override;
virtual void GetActiveSets(std::vector<XrActiveActionSet>& outSets) const override;

Expand All @@ -83,6 +91,10 @@ class InputManager
virtual void UpdateHaptics(XrSession session, XrDuration displayPeriod) override;

private:
enum Layout { Layout_Touch, Layout_Index, Layout_WMR };
ProfileBindings GetLayoutBindings(Layout layout) const;
bool UsesTrackpadButtons(ovrHandType hand) const;

Action m_Button_AX;
Action m_Button_BY;
Action m_Button_Thumb;
Expand All @@ -99,8 +111,11 @@ class InputManager
Action m_HandTrigger;
Action m_Thumbstick;

// For WMR profile hack
// Splits WMR's shared trackpad click into A/X and B/Y
Action m_Trackpad_Buttons;
// The runtime bound the WMR profile to this hand. Guarded by the
// InputManager's action mutex, like every other input read.
bool m_WmrBound[ovrHand_Count] = {};

Action m_Pose;
Action m_Vibration;
Expand Down Expand Up @@ -141,7 +156,7 @@ class InputManager
virtual ovrControllerType GetType() const override { return ovrControllerType_XBox; }
virtual bool IsConnected() const override { return true; }
virtual void GetInputState(XrSession session, ovrControllerType controllerType, ovrInputState* inputState) override;
virtual XrPath GetSuggestedBindings(std::vector<XrActionSuggestedBinding>& outBindings) const override;
virtual void GetSuggestedBindings(std::vector<ProfileBindings>& outProfiles) const override;
virtual void GetActiveSets(std::vector<XrActiveActionSet>& outSets) const override;
virtual ovrResult SetVibration(XrSession session, ovrControllerType controllerType, float frequency, float amplitude) override;

Expand Down Expand Up @@ -172,6 +187,7 @@ class InputManager
~InputManager();

ovrResult AttachSession(XrSession session);
void UpdateInteractionProfiles(XrSession session);
ovrResult SyncInputState(XrSession session, XrDuration displayPeriod);

ovrResult SetControllerVibration(ovrSession session, ovrControllerType controllerType, float frequency, float amplitude);
Expand Down
6 changes: 6 additions & 0 deletions runtime/windows-openxr/REV_CAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,12 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetSessionStatus(ovrSession session, ovrSessi
session->SessionStatus = status;
break;
}
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
// The runtime picked (or dropped) a controller profile: the input
// translation follows it, and "none" means the hands won't track.
if (session->Input)
session->Input->UpdateInteractionProfiles(session->Session);
break;
case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR:
{
const XrEventDataVisibilityMaskChangedKHR& maskChange =
Expand Down
6 changes: 3 additions & 3 deletions runtime/windows-openxr/REV_CAPI_Vk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ovr_GetInstanceExtensionsVk(
uint32_t* inoutExtensionNamesSize)
{
if (!inoutExtensionNamesSize)
ovrError_InvalidParameter;
return ovrError_InvalidParameter;

if (Runtime::Get().Supports(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME))
{
Expand Down Expand Up @@ -64,7 +64,7 @@ ovr_GetDeviceExtensionsVk(
uint32_t* inoutExtensionNamesSize)
{
if (!inoutExtensionNamesSize)
ovrError_InvalidParameter;
return ovrError_InvalidParameter;

if (Runtime::Get().Supports(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME))
{
Expand Down Expand Up @@ -99,7 +99,7 @@ ovr_GetSessionPhysicalDeviceVk(
XR_FUNCTION(session->Instance, GetVulkanGraphicsDeviceKHR);

if (!out_physicalDevice)
ovrError_InvalidParameter;
return ovrError_InvalidParameter;

VulkanLibrary = LoadLibraryW(L"vulkan-1.dll");
if (!VulkanLibrary)
Expand Down
8 changes: 6 additions & 2 deletions runtime/windows-openxr/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ ovrResult ovrHmdStruct::InitSession(XrInstance instance)
SystemProperties = XR_TYPE(SYSTEM_PROPERTIES);
SystemColorSpace = XR_TYPE(SYSTEM_COLOR_SPACE_PROPERTIES_FB);

// Initialize view structures
// Initialize view structures. Chain the EPIC fov struct only when its
// extension is enabled: a struct from a disabled extension is invalid,
// and SteamVR fails xrEnumerateViewConfigurationViews over it. The view
// fallback below derives the fov without it.
const bool epicFov = Runtime::Get().Supports(XR_EPIC_VIEW_CONFIGURATION_FOV_EXTENSION_NAME);
for (int i = 0; i < ovrEye_Count; i++)
{
ViewConfigs[i] = XR_TYPE(VIEW_CONFIGURATION_VIEW);
ViewFov[i] = XR_TYPE(VIEW_CONFIGURATION_VIEW_FOV_EPIC);
ViewPoses[i] = XR_TYPE(VIEW);
ViewConfigs[i].next = &ViewFov[i];
ViewConfigs[i].next = epicFov ? &ViewFov[i] : nullptr;
}

XrSystemGetInfo systemInfo = XR_TYPE(SYSTEM_GET_INFO);
Expand Down
Loading