Commit 18c94525 authored by Kevin Qin's avatar Kevin Qin Committed by Commit Bot

Add OpenXR Gamepad Support

To try out this code with the Microsoft OpenXR Runtime in Chromium, perform the
following steps.
    - Install 'Mixed Reality OpenXR Developer Preview' from the Microsoft Store
    - If prompted also download the compatibility pack

Add the following to custom_vars in .gclient, located at the root of the
chromium clone:
    "checkout_openxr": True
Add the following to args.gn, located in the out folder:
    enable_openxr = true

gclient sync and build

Once you've built and installed Chromium go to about:flags and:
    - Enable WebVR or WebXR
    - Enable 'OpenXR Support'

Bug: 976432
Change-Id: Ie4aa927afc7389c0a88e818889bfbcee75e4feea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1693842Reviewed-by: default avatarBrandon Jones <bajones@chromium.org>
Reviewed-by: default avatarAlexander Cooper <alcooper@chromium.org>
Commit-Queue: Zheng Qin <zheqi@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#683282}
parent 7aeebf41
......@@ -37,7 +37,8 @@ enum GamepadSource {
GAMEPAD_SOURCE_WIN_XINPUT,
GAMEPAD_SOURCE_WIN_RAW,
GAMEPAD_SOURCE_WIN_MR,
kMaxValue = GAMEPAD_SOURCE_WIN_MR,
GAMEPAD_SOURCE_OPENXR,
kMaxValue = GAMEPAD_SOURCE_OPENXR,
};
struct PadState {
......
......@@ -231,8 +231,12 @@ if (enable_vr) {
sources += [
"openxr/openxr_api_wrapper.cc",
"openxr/openxr_api_wrapper.h",
"openxr/openxr_controller.cc",
"openxr/openxr_controller.h",
"openxr/openxr_device.cc",
"openxr/openxr_device.h",
"openxr/openxr_gamepad_helper.cc",
"openxr/openxr_gamepad_helper.h",
"openxr/openxr_render_loop.cc",
"openxr/openxr_render_loop.h",
"openxr/openxr_util.cc",
......
......@@ -30,6 +30,11 @@ bool IsValidDeviceId(device::mojom::XRDeviceId id) {
return true;
#endif
#if BUILDFLAG(ENABLE_OPENXR)
if (id == device::mojom::XRDeviceId::OPENXR_DEVICE_ID)
return true;
#endif
return false;
}
......@@ -51,6 +56,11 @@ GamepadSource GamepadSourceFromDeviceId(device::mojom::XRDeviceId id) {
return GAMEPAD_SOURCE_WIN_MR;
#endif
#if BUILDFLAG(ENABLE_OPENXR)
if (id == device::mojom::XRDeviceId::OPENXR_DEVICE_ID)
return GAMEPAD_SOURCE_OPENXR;
#endif
NOTREACHED();
return GAMEPAD_SOURCE_NONE;
}
......@@ -217,7 +227,9 @@ void IsolatedGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
// per runtime, we use the XRDeviceId now to associate the controller with
// a headset. This doesn't change behavior, but the device/display naming
// could be confusing here.
if (this->source() == GAMEPAD_SOURCE_OPENVR) {
if (this->source() == GAMEPAD_SOURCE_OPENXR) {
dest.SetID(base::UTF8ToUTF16("OpenXR Controller"));
} else if (this->source() == GAMEPAD_SOURCE_OPENVR) {
dest.SetID(base::UTF8ToUTF16("OpenVR Gamepad"));
} else if (this->source() == GAMEPAD_SOURCE_OCULUS) {
if (dest.hand == GamepadHand::kLeft) {
......
......@@ -10,6 +10,7 @@
#include <array>
#include "base/logging.h"
#include "device/vr/openxr/openxr_gamepad_helper.h"
#include "device/vr/openxr/openxr_util.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/quaternion.h"
......@@ -137,7 +138,7 @@ bool OpenXrApiWrapper::IsInitialized() const {
}
void OpenXrApiWrapper::Uninitialize() {
// Destroying an instance in OpenXR also destroys all child objects of that
// Destroying an instance in OpenXr also destroys all child objects of that
// instance (including the session, swapchain, and spaces objects),
// so they don't need to be manually destroyed.
if (HasInstance()) {
......@@ -198,7 +199,7 @@ XrResult OpenXrApiWrapper::InitializeSystem() {
RETURN_IF_XR_FAILED(xrEnumerateViewConfigurationViews(
instance_, system, kSupportedViewConfiguration, 0, &view_count, nullptr));
// It would be an error for an OpenXR runtime to return anything other than 2
// It would be an error for an OpenXr runtime to return anything other than 2
// views to an app that only requested
// XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO.
DCHECK(view_count == kNumViews);
......@@ -251,7 +252,8 @@ XrResult OpenXrApiWrapper::PickEnvironmentBlendMode(XrSystemId system) {
// this OpenXrApiWrapper object on failure to clean up any intermediate
// objects that may have been created before the failure.
XrResult OpenXrApiWrapper::StartSession(
const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device) {
const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device,
std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper) {
DCHECK(d3d_device.Get());
DCHECK(IsInitialized());
......@@ -262,6 +264,7 @@ XrResult OpenXrApiWrapper::StartSession(
RETURN_IF_XR_FAILED(
CreateSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, &local_space_));
RETURN_IF_XR_FAILED(CreateSpace(XR_REFERENCE_SPACE_TYPE_VIEW, &view_space_));
RETURN_IF_XR_FAILED(CreateGamepadHelper(gamepad_helper));
RETURN_IF_XR_FAILED(BeginSession());
// Since the objects in these arrays are used on every frame,
......@@ -275,6 +278,7 @@ XrResult OpenXrApiWrapper::StartSession(
DCHECK(HasColorSwapChain());
DCHECK(HasSpace(XR_REFERENCE_SPACE_TYPE_LOCAL));
DCHECK(HasSpace(XR_REFERENCE_SPACE_TYPE_VIEW));
DCHECK(gamepad_helper);
return xr_result;
}
......@@ -355,6 +359,17 @@ XrResult OpenXrApiWrapper::CreateSpace(XrReferenceSpaceType type,
return xrCreateReferenceSpace(session_, &space_create_info, space);
}
XrResult OpenXrApiWrapper::CreateGamepadHelper(
std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper) {
DCHECK(HasSession());
DCHECK(HasSpace(XR_REFERENCE_SPACE_TYPE_LOCAL));
XrResult xr_result = OpenXrGamepadHelper::GetOpenXrGamepadHelper(
instance_, session_, local_space_, gamepad_helper);
return xr_result;
}
XrResult OpenXrApiWrapper::BeginSession() {
DCHECK(HasSession());
......
......@@ -22,6 +22,8 @@ class Point3F;
namespace device {
class OpenXrGamepadHelper;
class OpenXrApiWrapper {
public:
OpenXrApiWrapper();
......@@ -32,7 +34,8 @@ class OpenXrApiWrapper {
static bool IsHardwareAvailable();
static bool IsApiAvailable();
XrResult StartSession(const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device);
XrResult StartSession(const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device,
std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper);
XrResult BeginFrame(Microsoft::WRL::ComPtr<ID3D11Texture2D>* texture);
XrResult EndFrame();
......@@ -59,6 +62,8 @@ class OpenXrApiWrapper {
XrResult CreateSwapchain();
XrResult CreateSpace(XrReferenceSpaceType type, XrSpace* space);
XrResult CreateViewSpace();
XrResult CreateGamepadHelper(
std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper);
XrResult BeginSession();
XrResult UpdateProjectionLayers();
......@@ -73,7 +78,7 @@ class OpenXrApiWrapper {
uint32_t GetRecommendedSwapchainSampleCount() const;
// OpenXR objects
// OpenXr objects
// These objects are valid on successful initialization.
XrInstance instance_;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openxr/openxr_controller.h"
#include <stdint.h>
#include "base/logging.h"
#include "device/vr/openxr/openxr_util.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
namespace device {
namespace {
constexpr char kMicrosoftInteractionProfileName[] =
"/interaction_profiles/microsoft/motion_controller";
const char* GetStringFromType(OpenXrControllerType type) {
switch (type) {
case OpenXrControllerType::kLeft:
return "left";
case OpenXrControllerType::kRight:
return "right";
default:
NOTREACHED();
return "";
}
}
} // namespace
OpenXrController::OpenXrController()
: type_(OpenXrControllerType::kCount), // COUNT refers to invalid.
action_set_(XR_NULL_HANDLE),
palm_pose_action_{XR_NULL_HANDLE},
palm_pose_space_(XR_NULL_HANDLE) {}
OpenXrController::~OpenXrController() {
// We don't need to destroy all of the actions because destroying an
// action set destroys all actions that are part of the set.
if (action_set_ != XR_NULL_HANDLE) {
xrDestroyActionSet(action_set_);
}
if (palm_pose_space_ != XR_NULL_HANDLE) {
xrDestroySpace(palm_pose_space_);
}
}
XrResult OpenXrController::Initialize(
OpenXrControllerType type,
XrInstance instance,
XrSession session,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings) {
type_ = type;
std::string type_string = GetStringFromType(type_);
std::string action_set_name = type_string + "_action_set";
XrActionSetCreateInfo action_set_create_info = {
XR_TYPE_ACTION_SET_CREATE_INFO};
errno_t error =
strcpy_s(action_set_create_info.actionSetName, action_set_name.c_str());
DCHECK(!error);
error = strcpy_s(action_set_create_info.localizedActionSetName,
action_set_name.c_str());
DCHECK(!error);
XrResult xr_result;
RETURN_IF_XR_FAILED(
xrCreateActionSet(session, &action_set_create_info, &action_set_));
RETURN_IF_XR_FAILED(InitializeMicrosoftMotionControllers(
instance, session, type_string, bindings));
XrActionSpaceCreateInfo action_space_create_info = {
XR_TYPE_ACTION_SPACE_CREATE_INFO};
action_space_create_info.poseInActionSpace = PoseIdentity();
RETURN_IF_XR_FAILED(xrCreateActionSpace(
palm_pose_action_, &action_space_create_info, &palm_pose_space_));
return xr_result;
}
XrResult OpenXrController::InitializeMicrosoftMotionControllers(
XrInstance instance,
XrSession session,
const std::string& type_string,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings) {
XrResult xr_result;
const std::string binding_prefix = "/user/hand/" + type_string + "/input/";
const std::string name_prefix = type_string + "_controller_";
RETURN_IF_XR_FAILED(CreateAction(
instance, XR_INPUT_ACTION_TYPE_BOOLEAN, kMicrosoftInteractionProfileName,
binding_prefix + "trackpad/click", name_prefix + "trackpad_button_press",
&(button_action_map_[OpenXrButtonType::kTrackpad].press_action),
bindings));
RETURN_IF_XR_FAILED(CreateAction(
instance, XR_INPUT_ACTION_TYPE_BOOLEAN, kMicrosoftInteractionProfileName,
binding_prefix + "trigger/value", name_prefix + "trigger_button_press",
&(button_action_map_[OpenXrButtonType::kTrigger].press_action),
bindings));
RETURN_IF_XR_FAILED(CreateAction(
instance, XR_INPUT_ACTION_TYPE_BOOLEAN, kMicrosoftInteractionProfileName,
binding_prefix + "grip/click", name_prefix + "grip_button_press",
&(button_action_map_[OpenXrButtonType::kGrip].press_action), bindings));
RETURN_IF_XR_FAILED(CreateAction(
instance, XR_INPUT_ACTION_TYPE_BOOLEAN, kMicrosoftInteractionProfileName,
binding_prefix + "menu/click", name_prefix + "menu_button_press",
&(button_action_map_[OpenXrButtonType::kMenu].press_action), bindings));
RETURN_IF_XR_FAILED(CreateAction(
instance, XR_INPUT_ACTION_TYPE_VECTOR2F, kMicrosoftInteractionProfileName,
binding_prefix + "trackpad", name_prefix + "trackpad_axis",
&(axis_action_map_[OpenXrAxisType::kTrackpad]), bindings));
RETURN_IF_XR_FAILED(CreateAction(
instance, XR_INPUT_ACTION_TYPE_VECTOR2F, kMicrosoftInteractionProfileName,
binding_prefix + "thumbstick", name_prefix + "thumbstick_axis",
&(axis_action_map_[OpenXrAxisType::kThumbstick]), bindings));
RETURN_IF_XR_FAILED(
CreateAction(instance, XR_INPUT_ACTION_TYPE_POSE,
kMicrosoftInteractionProfileName, binding_prefix + "palm",
name_prefix + "grip_pose", &palm_pose_action_, bindings));
return xr_result;
}
int OpenXrController::GetID() {
return static_cast<int>(type_);
}
device::mojom::XRHandedness OpenXrController::GetHandness() {
switch (type_) {
case OpenXrControllerType::kLeft:
return device::mojom::XRHandedness::LEFT;
case OpenXrControllerType::kRight:
return device::mojom::XRHandedness::RIGHT;
default:
// LEFT controller and RIGHT controller are currently the only supported
// controllers. In the future, other controllers such as sound (which
// does not have a handedness) will be added here.
NOTREACHED();
return device::mojom::XRHandedness::NONE;
}
}
XrResult OpenXrController::GetButton(OpenXrButtonType type,
mojom::XRGamepadButtonPtr* data) {
XrResult xr_result;
XrActionStateBoolean press_state_bool = {XR_TYPE_ACTION_STATE_BOOLEAN};
RETURN_IF_XR_FAILED(
QueryState(button_action_map_[type].press_action, &press_state_bool));
RETURN_IF_XR_STATE_IS_NOT_ACTIVE(press_state_bool);
mojom::XRGamepadButtonPtr mojo_button_ptr =
GetGamepadButton(press_state_bool);
*data = std::move(mojo_button_ptr);
return xr_result;
}
XrResult OpenXrController::GetWebVrButtons(
std::vector<mojom::XRGamepadButtonPtr>* buttons) {
DCHECK(buttons);
XrResult xr_result;
for (uint32_t i = 0; i <= static_cast<uint32_t>(OpenXrButtonType::kMaxValue);
i++) {
mojom::XRGamepadButtonPtr mojo_button_ptr;
RETURN_IF_XR_FAILED(
GetButton(static_cast<OpenXrButtonType>(i), &mojo_button_ptr));
if (xr_result == XR_STATE_UNAVAILABLE) {
return xr_result;
}
buttons->push_back(std::move(mojo_button_ptr));
}
return xr_result;
}
XrResult OpenXrController::GetAxes(OpenXrAxisType type,
std::array<double, 2>* axes) {
DCHECK(axes);
XrResult xr_result;
XrActionStateVector2f axis_state_v2f = {XR_TYPE_ACTION_STATE_VECTOR2F};
RETURN_IF_XR_FAILED(QueryState(axis_action_map_[type], &axis_state_v2f));
RETURN_IF_XR_STATE_IS_NOT_ACTIVE(axis_state_v2f);
(*axes)[0] = axis_state_v2f.currentState.x;
(*axes)[1] = axis_state_v2f.currentState.y;
return xr_result;
}
XrResult OpenXrController::GetAllWebVrAxes(std::vector<double>* axes) {
DCHECK(axes);
XrResult xr_result;
for (uint32_t i = 0; i <= static_cast<uint32_t>(OpenXrAxisType::kMaxValue);
i++) {
std::array<double, 2> axe;
RETURN_IF_XR_FAILED(GetAxes(static_cast<OpenXrAxisType>(i), &axe));
if (xr_result == XR_STATE_UNAVAILABLE) {
return xr_result;
}
axes->insert(axes->end(), axe.begin(), axe.end());
}
return xr_result;
}
XrResult OpenXrController::GetPose(XrTime predicted_display_time,
XrSpace local_space,
mojom::XRGamepad* gamepad_ptr) {
XrResult xr_result;
XrActionStatePose palm_state_pose = {XR_TYPE_ACTION_STATE_POSE};
RETURN_IF_XR_FAILED(QueryState(palm_pose_action_, &palm_state_pose));
RETURN_IF_XR_STATE_IS_NOT_ACTIVE(palm_state_pose);
XrSpaceRelation space_relation = {XR_TYPE_SPACE_RELATION};
RETURN_IF_XR_FAILED(xrLocateSpace(palm_pose_space_, local_space,
predicted_display_time, &space_relation));
gamepad_ptr->pose = mojom::VRPose::New();
if (space_relation.relationFlags & XR_SPACE_RELATION_POSITION_VALID_BIT) {
gamepad_ptr->can_provide_position = true;
gamepad_ptr->pose->position = gfx::Point3F(space_relation.pose.position.x,
space_relation.pose.position.y,
space_relation.pose.position.z);
}
if (space_relation.relationFlags & XR_SPACE_RELATION_ORIENTATION_VALID_BIT) {
gamepad_ptr->can_provide_orientation = true;
gamepad_ptr->pose->orientation = gfx::Quaternion(
space_relation.pose.orientation.x, space_relation.pose.orientation.y,
space_relation.pose.orientation.z, space_relation.pose.orientation.w);
}
if (space_relation.relationFlags &
XR_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT) {
gamepad_ptr->pose->linear_velocity = gfx::Vector3dF(
space_relation.linearVelocity.x, space_relation.linearVelocity.y,
space_relation.linearVelocity.z);
}
if (space_relation.relationFlags &
XR_SPACE_RELATION_LINEAR_ACCELERATION_VALID_BIT) {
gamepad_ptr->pose->linear_acceleration =
gfx::Vector3dF(space_relation.linearAcceleration.x,
space_relation.linearAcceleration.y,
space_relation.linearAcceleration.z);
}
if (space_relation.relationFlags &
XR_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT) {
gamepad_ptr->pose->angular_velocity = gfx::Vector3dF(
space_relation.angularVelocity.x, space_relation.angularVelocity.y,
space_relation.angularVelocity.z);
}
if (space_relation.relationFlags &
XR_SPACE_RELATION_ANGULAR_ACCELERATION_VALID_BIT) {
gamepad_ptr->pose->angular_acceleration =
gfx::Vector3dF(space_relation.angularAcceleration.x,
space_relation.angularAcceleration.y,
space_relation.angularAcceleration.z);
}
return xr_result;
}
mojom::XRGamepadButtonPtr OpenXrController::GetGamepadButton(
const XrActionStateBoolean& action_state) {
mojom::XRGamepadButtonPtr ret = mojom::XRGamepadButton::New();
bool button_pressed = action_state.currentState;
ret->touched = button_pressed;
ret->pressed = button_pressed;
ret->value = button_pressed ? 1.0 : 0.0;
return ret;
}
XrActionSet OpenXrController::GetActionSet() const {
return action_set_;
}
XrResult OpenXrController::CreateAction(
XrInstance instance,
XrActionType type,
const char* interaction_profile_name,
const std::string& binding_string,
const std::string& action_name,
XrAction* action,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings) {
XrResult xr_result;
XrActionCreateInfo action_create_info = {XR_TYPE_ACTION_CREATE_INFO};
action_create_info.actionType = type;
errno_t error = strcpy_s(action_create_info.actionName, action_name.data());
DCHECK(error == 0);
error = strcpy_s(action_create_info.localizedActionName, action_name.data());
DCHECK(error == 0);
RETURN_IF_XR_FAILED(xrCreateAction(action_set_, &action_create_info, action));
XrPath profile_path, action_path;
RETURN_IF_XR_FAILED(
xrStringToPath(instance, interaction_profile_name, &profile_path));
RETURN_IF_XR_FAILED(
xrStringToPath(instance, binding_string.c_str(), &action_path));
(*bindings)[profile_path].push_back({*action, action_path});
return xr_result;
}
} // namespace device
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_OPENXR_OPENXR_CONTROLLER_H_
#define DEVICE_VR_OPENXR_OPENXR_CONTROLLER_H_
#include <stdint.h>
#include <unordered_map>
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/util/gamepad_builder.h"
#include "third_party/openxr/include/openxr/openxr.h"
namespace device {
enum class OpenXrControllerType {
kLeft = 0,
kRight = 1,
kCount = 2,
};
enum class OpenXrButtonType {
kTrackpad = 0,
kTrigger = 1,
kGrip = 2,
kMenu = 3,
kMaxValue = 3,
};
enum class OpenXrAxisType {
kTrackpad = 0,
kThumbstick = 1,
kMaxValue = 1,
};
class OpenXrController {
public:
OpenXrController();
~OpenXrController();
XrActionSet GetActionSet() const;
XrResult Initialize(
OpenXrControllerType type,
XrInstance instance,
XrSession session,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings);
int GetID();
device::mojom::XRHandedness GetHandness();
XrResult GetButton(OpenXrButtonType type, mojom::XRGamepadButtonPtr* data);
XrResult GetWebVrButtons(std::vector<mojom::XRGamepadButtonPtr>* buttons);
XrResult GetAxes(OpenXrAxisType type, std::array<double, 2>* axes);
XrResult GetAllWebVrAxes(std::vector<double>* axes);
XrResult GetPose(XrTime predicted_display_time,
XrSpace local_space,
mojom::XRGamepad* gamepad_ptr);
private:
// ActionButton struct is used to store all XrAction that is related to the
// button. For example, we may need to query the analog value for button press
// which require a seperate XrAction than the current boolean XrAction.
struct ActionButton {
XrAction press_action;
ActionButton() : press_action(XR_NULL_HANDLE) {}
};
XrResult InitializeMicrosoftMotionControllers(
XrInstance instance,
XrSession session,
const std::string& type_string,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings);
XrResult CreateAction(
XrInstance instance,
XrActionType type,
const char* interaction_profile_name,
const std::string& binding_string,
const std::string& action_name,
XrAction* action,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings);
template <typename T>
XrResult QueryState(XrAction action, T* action_state) {
// this function should never be called because each valid XrActionState
// has its own template function defined below.
NOTREACHED();
return XR_ERROR_ACTION_TYPE_MISMATCH;
}
template <>
XrResult QueryState<XrActionStateBoolean>(
XrAction action,
XrActionStateBoolean* action_state) {
action_state->type = XR_TYPE_ACTION_STATE_BOOLEAN;
return xrGetActionStateBoolean(action, 0, nullptr, action_state);
}
template <>
XrResult QueryState<XrActionStateVector1f>(
XrAction action,
XrActionStateVector1f* action_state) {
action_state->type = XR_TYPE_ACTION_STATE_VECTOR1F;
return xrGetActionStateVector1f(action, 0, nullptr, action_state);
}
template <>
XrResult QueryState<XrActionStateVector2f>(
XrAction action,
XrActionStateVector2f* action_state) {
action_state->type = XR_TYPE_ACTION_STATE_VECTOR2F;
return xrGetActionStateVector2f(action, 0, nullptr, action_state);
}
template <>
XrResult QueryState<XrActionStatePose>(XrAction action,
XrActionStatePose* action_state) {
action_state->type = XR_TYPE_ACTION_STATE_POSE;
return xrGetActionStatePose(action, XR_NULL_PATH, action_state);
}
mojom::XRGamepadButtonPtr GetGamepadButton(
const XrActionStateBoolean& action_state);
OpenXrControllerType type_;
XrActionSet action_set_;
XrAction palm_pose_action_;
XrSpace palm_pose_space_;
std::unordered_map<OpenXrButtonType, ActionButton> button_action_map_;
std::unordered_map<OpenXrAxisType, XrAction> axis_action_map_;
DISALLOW_COPY_AND_ASSIGN(OpenXrController);
};
} // namespace device
#endif // DEVICE_VR_OPENXR_OPENXR_CONTROLLER_H_
......@@ -127,7 +127,7 @@ void OpenXrDevice::RequestSession(
base::BindOnce(&OpenXrDevice::OnRequestSessionResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
// OpenXR doesn't need to handle anything when presentation has ended, but
// OpenXr doesn't need to handle anything when presentation has ended, but
// the mojo interface to call to XRCompositorCommon::RequestSession requires
// a method and cannot take nullptr, so passing in base::DoNothing::Once()
// for on_presentation_ended
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openxr/openxr_gamepad_helper.h"
#include "device/vr/openxr/openxr_util.h"
namespace device {
XrResult OpenXrGamepadHelper::GetOpenXrGamepadHelper(
XrInstance instance,
XrSession session,
XrSpace local_space,
std::unique_ptr<OpenXrGamepadHelper>* helper) {
XrResult xr_result;
// This map is used to store bindings for different kinds of interaction
// profiles. This allows the runtime to choose a different input sources based
// on availability.
std::map<XrPath, std::vector<XrActionSuggestedBinding>> bindings;
std::unique_ptr<OpenXrGamepadHelper> new_helper =
std::make_unique<OpenXrGamepadHelper>(session, local_space);
for (size_t i = 0; i < new_helper->controllers_.size(); i++) {
xr_result = new_helper->controllers_[i].Initialize(
static_cast<OpenXrControllerType>(i), instance, session, &bindings);
RETURN_IF_XR_FAILED(xr_result);
}
for (auto it = bindings.begin(); it != bindings.end(); it++) {
XrInteractionProfileSuggestedBinding profile_suggested_bindings = {
XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
profile_suggested_bindings.interactionProfile = it->first;
profile_suggested_bindings.suggestedBindings = it->second.data();
profile_suggested_bindings.countSuggestedBindings = it->second.size();
xr_result = xrSetInteractionProfileSuggestedBindings(
session, &profile_suggested_bindings);
}
RETURN_IF_XR_FAILED(xr_result);
*helper = std::move(new_helper);
return xr_result;
}
OpenXrGamepadHelper::OpenXrGamepadHelper(XrSession session, XrSpace local_space)
: session_(session), local_space_(local_space) {}
OpenXrGamepadHelper::~OpenXrGamepadHelper() = default;
mojom::XRGamepadDataPtr OpenXrGamepadHelper::GetGamepadData(
XrTime predicted_display_time) {
XrResult xr_result;
std::array<XrActiveActionSet, 2> active_action_set_arr;
for (size_t i = 0; i < controllers_.size(); i++) {
active_action_set_arr[i] = {XR_TYPE_ACTIVE_ACTION_SET};
active_action_set_arr[i].actionSet = controllers_[i].GetActionSet();
active_action_set_arr[i].subactionPath = XR_NULL_PATH;
}
xr_result = xrSyncActionData(session_, controllers_.size(),
active_action_set_arr.data());
if (XR_FAILED(xr_result))
return nullptr;
mojom::XRGamepadDataPtr gamepad_data_ptr = mojom::XRGamepadData::New();
for (OpenXrController& controller : controllers_) {
mojom::XRGamepadPtr gamepad_ptr = mojom::XRGamepad::New();
gamepad_ptr->controller_id = controller.GetID();
gamepad_ptr->timestamp = base::TimeTicks::Now();
gamepad_ptr->hand = controller.GetHandness();
// GetButtons, GetAxes and GetPose may return success codes >= 0, including
// XR_SUCCESS and XR_STATE_UNAVAILABLE. We should continue to populate the
// data only on XR_SUCCESS and ignore other success codes.
if (!XR_UNQUALIFIED_SUCCESS(
controller.GetWebVrButtons(&(gamepad_ptr->buttons)))) {
continue;
}
if (!XR_UNQUALIFIED_SUCCESS(
controller.GetAllWebVrAxes(&(gamepad_ptr->axes)))) {
continue;
}
if (!XR_UNQUALIFIED_SUCCESS(controller.GetPose(
predicted_display_time, local_space_, gamepad_ptr.get()))) {
continue;
}
if (XR_UNQUALIFIED_SUCCESS(xr_result)) {
gamepad_data_ptr->gamepads.push_back(std::move(gamepad_ptr));
}
}
return gamepad_data_ptr;
}
} // namespace device
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_OPENXR_OPENXR_GAMEPAD_HELPER_H_
#define DEVICE_VR_OPENXR_OPENXR_GAMEPAD_HELPER_H_
#include "device/vr/openxr/openxr_controller.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
namespace device {
class OpenXrGamepadHelper {
public:
static XrResult GetOpenXrGamepadHelper(
XrInstance instance,
XrSession session,
XrSpace local_space,
std::unique_ptr<OpenXrGamepadHelper>* helper);
OpenXrGamepadHelper(XrSession session, XrSpace local_space);
~OpenXrGamepadHelper();
mojom::XRGamepadDataPtr GetGamepadData(XrTime predicted_display_time);
private:
XrSession session_;
XrSpace local_space_;
std::array<OpenXrController, static_cast<int>(OpenXrControllerType::kCount)>
controllers_;
DISALLOW_COPY_AND_ASSIGN(OpenXrGamepadHelper);
};
} // namespace device
#endif // DEVICE_VR_OPENXR_OPENXR_GAMEPAD_HELPER_H_
......@@ -5,6 +5,7 @@
#include "device/vr/openxr/openxr_render_loop.h"
#include "device/vr/openxr/openxr_api_wrapper.h"
#include "device/vr/openxr/openxr_gamepad_helper.h"
namespace device {
......@@ -45,11 +46,12 @@ mojom::XRFrameDataPtr OpenXrRenderLoop::GetNextFrameData() {
}
mojom::XRGamepadDataPtr OpenXrRenderLoop::GetNextGamepadData() {
return nullptr;
return gamepad_helper_->GetGamepadData(openxr_->GetPredictedDisplayTime());
}
bool OpenXrRenderLoop::StartRuntime() {
DCHECK(!openxr_);
DCHECK(!gamepad_helper_);
// The new wrapper object is stored in a temporary variable instead of
// openxr_ so that the local unique_ptr cleans up the object if starting
......@@ -64,7 +66,8 @@ bool OpenXrRenderLoop::StartRuntime() {
if (XR_FAILED(openxr->GetLuid(&luid)) ||
!texture_helper_.SetAdapterLUID(luid) ||
!texture_helper_.EnsureInitialized() ||
XR_FAILED(openxr->StartSession(texture_helper_.GetDevice()))) {
XR_FAILED(openxr->StartSession(texture_helper_.GetDevice(),
&gamepad_helper_))) {
texture_helper_.Reset();
return false;
}
......@@ -78,12 +81,15 @@ bool OpenXrRenderLoop::StartRuntime() {
texture_helper_.SetDefaultSize(gfx::Size(width, height));
DCHECK(openxr_);
DCHECK(gamepad_helper_);
return true;
}
void OpenXrRenderLoop::StopRuntime() {
openxr_ = nullptr;
texture_helper_.Reset();
gamepad_helper_.reset();
}
void OpenXrRenderLoop::OnSessionStart() {
......
......@@ -14,6 +14,7 @@
namespace device {
class OpenXrApiWrapper;
class OpenXrGamepadHelper;
class OpenXrRenderLoop : public XRCompositorCommon {
public:
......@@ -33,6 +34,7 @@ class OpenXrRenderLoop : public XRCompositorCommon {
bool SubmitCompositedFrame() override;
std::unique_ptr<OpenXrApiWrapper> openxr_;
std::unique_ptr<OpenXrGamepadHelper> gamepad_helper_;
DISALLOW_COPY_AND_ASSIGN(OpenXrRenderLoop);
};
......
......@@ -22,6 +22,14 @@ namespace device {
return xr_result; \
} while (false)
// If xrstate variable is not active, early return XR error code
// XR_STATE_UNAVAILABLE because its value won't be valid.
#define RETURN_IF_XR_STATE_IS_NOT_ACTIVE(xrstate) \
do { \
if (!xrstate.isActive) \
return XR_STATE_UNAVAILABLE; \
} while (false)
// Returns the identity pose, where the position is {0, 0, 0} and the
// orientation is {0, 0, 0, 1}.
XrPosef PoseIdentity();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment