Commit 875db739 authored by Kevin Qin's avatar Kevin Qin Committed by Commit Bot

Handle All OpenXR Events

There are 3 more OpenXR events that require actions:
1. XrEventDataInstanceLossPending: Destroy instance and exit
presentation

2. XrEventDataInteractionProfileChanged: change WebXR input profiles

3. XrEventDataReferenceSpaceChangePending: Update Local to Stage Space
transform


Fixed: 1020354
Change-Id: I03ac3ef5fe4fcd70d4f1943c389456ca72ef1a75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1842615
Commit-Queue: Zheng Qin <zheqi@microsoft.com>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarAlexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711620}
parent 2cc1c99e
...@@ -141,10 +141,18 @@ void MockXRDeviceHookBase::WaitGetControllerData( ...@@ -141,10 +141,18 @@ void MockXRDeviceHookBase::WaitGetControllerData(
std::move(callback).Run(DeviceToMojoControllerFrameData(data)); std::move(callback).Run(DeviceToMojoControllerFrameData(data));
} }
void MockXRDeviceHookBase::WaitGetSessionStateStopping( void MockXRDeviceHookBase::WaitGetEventData(
device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback device_test::mojom::XRTestHook::WaitGetEventDataCallback callback) {
callback) { if (event_data_queue_.empty()) {
std::move(callback).Run(false); device_test::mojom::EventDataPtr ret = device_test::mojom::EventData::New();
ret->type = device_test::mojom::EventType::kNoEvent;
std::move(callback).Run(std::move(ret));
return;
}
device_test::mojom::EventDataPtr ret =
device_test::mojom::EventData::New(event_data_queue_.front());
std::move(callback).Run(std::move(ret));
event_data_queue_.pop();
} }
unsigned int MockXRDeviceHookBase::ConnectController( unsigned int MockXRDeviceHookBase::ConnectController(
...@@ -205,3 +213,7 @@ device::ControllerFrameData MockXRDeviceHookBase::CreateValidController( ...@@ -205,3 +213,7 @@ device::ControllerFrameData MockXRDeviceHookBase::CreateValidController(
ret.pose_data.device_to_origin[15] = 1; ret.pose_data.device_to_origin[15] = 1;
return ret; return ret;
} }
void MockXRDeviceHookBase::PopulateEvent(device_test::mojom::EventData data) {
event_data_queue_.push(data);
}
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_VR_TEST_MOCK_XR_DEVICE_HOOK_BASE_H_ #ifndef CHROME_BROWSER_VR_TEST_MOCK_XR_DEVICE_HOOK_BASE_H_
#define CHROME_BROWSER_VR_TEST_MOCK_XR_DEVICE_HOOK_BASE_H_ #define CHROME_BROWSER_VR_TEST_MOCK_XR_DEVICE_HOOK_BASE_H_
#include <queue>
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "device/vr/public/mojom/browser_test_interfaces.mojom.h" #include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
#include "device/vr/test/test_hook.h" #include "device/vr/test/test_hook.h"
...@@ -41,9 +43,8 @@ class MockXRDeviceHookBase : public device_test::mojom::XRTestHook { ...@@ -41,9 +43,8 @@ class MockXRDeviceHookBase : public device_test::mojom::XRTestHook {
unsigned int index, unsigned int index,
device_test::mojom::XRTestHook::WaitGetControllerDataCallback callback) device_test::mojom::XRTestHook::WaitGetControllerDataCallback callback)
override; override;
void WaitGetSessionStateStopping( void WaitGetEventData(device_test::mojom::XRTestHook::WaitGetEventDataCallback
device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback callback) override;
callback) override;
// MockXRDeviceHookBase // MockXRDeviceHookBase
void TerminateDeviceServiceProcessForTesting(); void TerminateDeviceServiceProcessForTesting();
...@@ -54,6 +55,7 @@ class MockXRDeviceHookBase : public device_test::mojom::XRTestHook { ...@@ -54,6 +55,7 @@ class MockXRDeviceHookBase : public device_test::mojom::XRTestHook {
void DisconnectController(unsigned int index); void DisconnectController(unsigned int index);
device::ControllerFrameData CreateValidController( device::ControllerFrameData CreateValidController(
device::ControllerRole role); device::ControllerRole role);
void PopulateEvent(device_test::mojom::EventData data);
void StopHooking(); void StopHooking();
protected: protected:
...@@ -61,6 +63,7 @@ class MockXRDeviceHookBase : public device_test::mojom::XRTestHook { ...@@ -61,6 +63,7 @@ class MockXRDeviceHookBase : public device_test::mojom::XRTestHook {
tracked_classes_[device::kMaxTrackedDevices]; tracked_classes_[device::kMaxTrackedDevices];
base::flat_map<unsigned int, device::ControllerFrameData> base::flat_map<unsigned int, device::ControllerFrameData>
controller_data_map_; controller_data_map_;
std::queue<device_test::mojom::EventData> event_data_queue_;
private: private:
mojo::Receiver<device_test::mojom::XRTestHook> receiver_{this}; mojo::Receiver<device_test::mojom::XRTestHook> receiver_{this};
......
...@@ -15,22 +15,6 @@ ...@@ -15,22 +15,6 @@
namespace vr { namespace vr {
class TransitionXRMock : public MockXRDeviceHookBase {
public:
void WaitGetSessionStateStopping(
device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback
callback) final;
bool session_state_stopping_ = false;
};
void TransitionXRMock::WaitGetSessionStateStopping(
device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback
callback) {
std::move(callback).Run(session_state_stopping_);
session_state_stopping_ = false;
}
// Tests that WebXR is not exposed if the flag is not on and the page does // Tests that WebXR is not exposed if the flag is not on and the page does
// not have an origin trial token. // not have an origin trial token.
void TestApiDisabledWithoutFlagSetImpl(WebXrVrBrowserTestBase* t, void TestApiDisabledWithoutFlagSetImpl(WebXrVrBrowserTestBase* t,
...@@ -124,24 +108,35 @@ WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestNonImmersiveStopsDuringImmersive) { ...@@ -124,24 +108,35 @@ WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestNonImmersiveStopsDuringImmersive) {
} }
#if BUILDFLAG(ENABLE_OPENXR) #if BUILDFLAG(ENABLE_OPENXR)
IN_PROC_BROWSER_TEST_F(WebXrVrOpenXrBrowserTest, TestSessionEnded) { // Tests that WebXR session ends when certain events are received.
TransitionXRMock transition_mock; void TestWebXRSessionEndWhenEventTriggered(
WebXrVrBrowserTestBase* t,
// Load the test page, and enter presentation. device_test::mojom::EventType event_type) {
this->LoadUrlAndAwaitInitialization( MockXRDeviceHookBase transition_mock;
this->GetFileUrlForHtmlTestFile("test_webxr_presentation_ended")); t->LoadUrlAndAwaitInitialization(
this->EnterSessionWithUserGestureOrFail(); t->GetFileUrlForHtmlTestFile("test_webxr_presentation_ended"));
t->EnterSessionWithUserGestureOrFail();
// Wait for JavaScript to submit at least one frame. // Wait for JavaScript to submit at least one frame.
ASSERT_TRUE(this->PollJavaScriptBoolean("hasPresentedFrame", ASSERT_TRUE(
this->kPollTimeoutMedium)) t->PollJavaScriptBoolean("hasPresentedFrame", t->kPollTimeoutMedium))
<< "No frame submitted"; << "No frame submitted";
// Trigger the OpenXr Runtime to send the stop event and wait until we see the device_test::mojom::EventData data = {};
// session get terminated. data.type = event_type;
transition_mock.session_state_stopping_ = true; transition_mock.PopulateEvent(data);
// Tell JavaScript that it is done with the test. // Tell JavaScript that it is done with the test.
this->WaitOnJavaScriptStep(); t->WaitOnJavaScriptStep();
this->EndTest(); t->EndTest();
}
IN_PROC_BROWSER_TEST_F(WebXrVrOpenXrBrowserTest, TestSessionEnded) {
TestWebXRSessionEndWhenEventTriggered(
this, device_test::mojom::EventType::kSessionLost);
}
IN_PROC_BROWSER_TEST_F(WebXrVrOpenXrBrowserTest, TestInsanceLost) {
TestWebXRSessionEndWhenEventTriggered(
this, device_test::mojom::EventType::kInstanceLost);
} }
#endif // BUILDFLAG(ENABLE_OPENXR) #endif // BUILDFLAG(ENABLE_OPENXR)
......
...@@ -166,13 +166,16 @@ ControllerFrameData XRTestHookWrapper::WaitGetControllerData( ...@@ -166,13 +166,16 @@ ControllerFrameData XRTestHookWrapper::WaitGetControllerData(
return {}; return {};
} }
bool XRTestHookWrapper::WaitGetSessionStateStopping() { device_test::mojom::EventData XRTestHookWrapper::WaitGetEventData() {
device_test::mojom::EventData ret = {};
if (hook_) { if (hook_) {
bool stopping = false; device_test::mojom::EventDataPtr data;
hook_->WaitGetSessionStateStopping(&stopping); hook_->WaitGetEventData(&data);
return stopping; if (data) {
ret = *data;
}
} }
return false; return ret;
} }
void XRTestHookWrapper::AttachCurrentThread() { void XRTestHookWrapper::AttachCurrentThread() {
......
...@@ -33,7 +33,7 @@ class XRTestHookWrapper : public VRTestHook { ...@@ -33,7 +33,7 @@ class XRTestHookWrapper : public VRTestHook {
unsigned int index) override; unsigned int index) override;
TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) override; TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) override;
ControllerFrameData WaitGetControllerData(unsigned int index) override; ControllerFrameData WaitGetControllerData(unsigned int index) override;
bool WaitGetSessionStateStopping() override; device_test::mojom::EventData WaitGetEventData() override;
void AttachCurrentThread() override; void AttachCurrentThread() override;
void DetachCurrentThread() override; void DetachCurrentThread() override;
......
...@@ -49,13 +49,13 @@ OpenXrApiWrapper::~OpenXrApiWrapper() { ...@@ -49,13 +49,13 @@ OpenXrApiWrapper::~OpenXrApiWrapper() {
} }
void OpenXrApiWrapper::Reset() { void OpenXrApiWrapper::Reset() {
session_ended_ = false;
local_space_ = XR_NULL_HANDLE; local_space_ = XR_NULL_HANDLE;
stage_space_ = XR_NULL_HANDLE; stage_space_ = XR_NULL_HANDLE;
view_space_ = XR_NULL_HANDLE; view_space_ = XR_NULL_HANDLE;
color_swapchain_ = XR_NULL_HANDLE; color_swapchain_ = XR_NULL_HANDLE;
session_ = XR_NULL_HANDLE; session_ = XR_NULL_HANDLE;
blend_mode_ = XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM; blend_mode_ = XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM;
stage_bounds_ = {};
system_ = kInvalidSystem; system_ = kInvalidSystem;
instance_ = XR_NULL_HANDLE; instance_ = XR_NULL_HANDLE;
...@@ -69,7 +69,7 @@ void OpenXrApiWrapper::Reset() { ...@@ -69,7 +69,7 @@ void OpenXrApiWrapper::Reset() {
bool OpenXrApiWrapper::Initialize() { bool OpenXrApiWrapper::Initialize() {
Reset(); Reset();
session_ended_ = false;
if (XR_FAILED(CreateInstance(&instance_))) { if (XR_FAILED(CreateInstance(&instance_))) {
return false; return false;
} }
...@@ -114,6 +114,7 @@ void OpenXrApiWrapper::Uninitialize() { ...@@ -114,6 +114,7 @@ void OpenXrApiWrapper::Uninitialize() {
test_hook_->DetachCurrentThread(); test_hook_->DetachCurrentThread();
Reset(); Reset();
session_ended_ = true;
} }
bool OpenXrApiWrapper::HasInstance() const { bool OpenXrApiWrapper::HasInstance() const {
...@@ -595,6 +596,29 @@ XrResult OpenXrApiWrapper::ProcessEvents() { ...@@ -595,6 +596,29 @@ XrResult OpenXrApiWrapper::ProcessEvents() {
default: default:
break; break;
} }
} else if (event_data.type == XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING) {
DCHECK(session_ != XR_NULL_HANDLE);
Uninitialize();
} else if (event_data.type ==
XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) {
XrEventDataReferenceSpaceChangePending* reference_space_change_pending =
reinterpret_cast<XrEventDataReferenceSpaceChangePending*>(
&event_data);
DCHECK(reference_space_change_pending->session == session_);
// TODO(crbug.com/1015049)
// Currently WMR only throw reference space change event for stage.
// Other runtimes may decide to do it differently.
if (reference_space_change_pending->referenceSpaceType ==
XR_REFERENCE_SPACE_TYPE_STAGE) {
UpdateStageBounds();
}
} else if (event_data.type ==
XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) {
XrEventDataInteractionProfileChanged* interaction_profile_changed =
reinterpret_cast<XrEventDataInteractionProfileChanged*>(&event_data);
DCHECK(interaction_profile_changed->session == session_);
interaction_profile_changed_callback_.Run(&xr_result);
RETURN_IF_XR_FAILED(xr_result);
} }
event_data.type = XR_TYPE_EVENT_DATA_BUFFER; event_data.type = XR_TYPE_EVENT_DATA_BUFFER;
xr_result = xrPollEvent(instance_, &event_data); xr_result = xrPollEvent(instance_, &event_data);
...@@ -698,6 +722,13 @@ bool OpenXrApiWrapper::GetStageParameters(XrExtent2Df* stage_bounds, ...@@ -698,6 +722,13 @@ bool OpenXrApiWrapper::GetStageParameters(XrExtent2Df* stage_bounds,
return true; return true;
} }
void OpenXrApiWrapper::RegisterInteractionProfileChangeCallback(
const base::RepeatingCallback<void(XrResult*)>&
interaction_profile_callback) {
interaction_profile_changed_callback_ =
std::move(interaction_profile_callback);
}
VRTestHook* OpenXrApiWrapper::test_hook_ = nullptr; VRTestHook* OpenXrApiWrapper::test_hook_ = nullptr;
ServiceTestHook* OpenXrApiWrapper::service_test_hook_ = nullptr; ServiceTestHook* OpenXrApiWrapper::service_test_hook_ = nullptr;
void OpenXrApiWrapper::SetTestHook(VRTestHook* hook) { void OpenXrApiWrapper::SetTestHook(VRTestHook* hook) {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "base/callback.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "device/vr/vr_export.h" #include "device/vr/vr_export.h"
...@@ -59,6 +60,9 @@ class OpenXrApiWrapper { ...@@ -59,6 +60,9 @@ class OpenXrApiWrapper {
std::string GetRuntimeName() const; std::string GetRuntimeName() const;
bool GetStageParameters(XrExtent2Df* stage_bounds, bool GetStageParameters(XrExtent2Df* stage_bounds,
gfx::Transform* local_from_stage); gfx::Transform* local_from_stage);
void RegisterInteractionProfileChangeCallback(
const base::RepeatingCallback<void(XrResult*)>&
interaction_profile_callback);
static void DEVICE_VR_EXPORT SetTestHook(VRTestHook* hook); static void DEVICE_VR_EXPORT SetTestHook(VRTestHook* hook);
...@@ -96,6 +100,9 @@ class OpenXrApiWrapper { ...@@ -96,6 +100,9 @@ class OpenXrApiWrapper {
bool session_ended_; bool session_ended_;
base::RepeatingCallback<void(XrResult*)>
interaction_profile_changed_callback_;
// Testing objects // Testing objects
static VRTestHook* test_hook_; static VRTestHook* test_hook_;
static ServiceTestHook* service_test_hook_; static ServiceTestHook* service_test_hook_;
......
This diff is collapsed.
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <vector>
#include "base/optional.h" #include "base/optional.h"
#include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/public/mojom/vr_service.mojom.h"
...@@ -53,7 +54,7 @@ class OpenXrController { ...@@ -53,7 +54,7 @@ class OpenXrController {
uint32_t GetId() const; uint32_t GetId() const;
device::mojom::XRHandedness GetHandness() const; device::mojom::XRHandedness GetHandness() const;
device::mojom::XRInputSourceDescriptionPtr GetDescription( mojom::XRInputSourceDescriptionPtr GetDescription(
XrTime predicted_display_time); XrTime predicted_display_time);
base::Optional<GamepadButton> GetButton(OpenXrButtonType type) const; base::Optional<GamepadButton> GetButton(OpenXrButtonType type) const;
...@@ -63,6 +64,8 @@ class OpenXrController { ...@@ -63,6 +64,8 @@ class OpenXrController {
XrTime predicted_display_time, XrTime predicted_display_time,
XrSpace local_space) const; XrSpace local_space) const;
XrResult UpdateInteractionProfile();
private: private:
// ActionButton struct is used to store all XrAction that is related to the // 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 // button. For example, we may need to query the analog value for button press
...@@ -78,14 +81,12 @@ class OpenXrController { ...@@ -78,14 +81,12 @@ class OpenXrController {
}; };
XrResult InitializeMicrosoftMotionControllerActions( XrResult InitializeMicrosoftMotionControllerActions(
XrInstance instance,
const std::string& type_string, const std::string& type_string,
std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings); std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings);
XrResult InitializeMicrosoftMotionControllerSpaces(); XrResult InitializeMicrosoftMotionControllerSpaces();
XrResult CreateAction( XrResult CreateAction(
XrInstance instance,
XrActionType type, XrActionType type,
const char* interaction_profile_name, const char* interaction_profile_name,
const std::string& binding_string, const std::string& binding_string,
...@@ -103,6 +104,8 @@ class OpenXrController { ...@@ -103,6 +104,8 @@ class OpenXrController {
XrSpace target, XrSpace target,
XrSpace origin) const; XrSpace origin) const;
std::vector<std::string> GetProfiles() const;
template <typename T> template <typename T>
XrResult QueryState(XrAction action, T* action_state) const { XrResult QueryState(XrAction action, T* action_state) const {
// this function should never be called because each valid XrActionState // this function should never be called because each valid XrActionState
...@@ -154,6 +157,7 @@ class OpenXrController { ...@@ -154,6 +157,7 @@ class OpenXrController {
device::mojom::XRInputSourceDescriptionPtr description_; device::mojom::XRInputSourceDescriptionPtr description_;
OpenXrHandednessType type_; OpenXrHandednessType type_;
XrInstance instance_;
XrSession session_; XrSession session_;
XrActionSet action_set_; XrActionSet action_set_;
XrAction grip_pose_action_; XrAction grip_pose_action_;
...@@ -161,6 +165,9 @@ class OpenXrController { ...@@ -161,6 +165,9 @@ class OpenXrController {
XrAction pointer_pose_action_; XrAction pointer_pose_action_;
XrSpace pointer_pose_space_; XrSpace pointer_pose_space_;
std::string interaction_profile_;
std::string top_level_user_path_string_;
std::unordered_map<OpenXrButtonType, ActionButton> button_action_map_; std::unordered_map<OpenXrButtonType, ActionButton> button_action_map_;
std::unordered_map<OpenXrAxisType, XrAction> axis_action_map_; std::unordered_map<OpenXrAxisType, XrAction> axis_action_map_;
......
...@@ -120,8 +120,10 @@ std::vector<mojom::XRInputSourceStatePtr> OpenXRInputHelper::GetInputState( ...@@ -120,8 +120,10 @@ std::vector<mojom::XRInputSourceStatePtr> OpenXRInputHelper::GetInputState(
// To ensure that we don't have any collisions with other ids, increment // To ensure that we don't have any collisions with other ids, increment
// all of the ids by one. // all of the ids by one.
state->source_id = i + 1; state->source_id = i + 1;
state->description = controller->GetDescription(predicted_display_time); state->description = controller->GetDescription(predicted_display_time);
if (!state->description) {
continue;
}
state->mojo_from_input = controller->GetMojoFromGripTransform( state->mojo_from_input = controller->GetMojoFromGripTransform(
predicted_display_time, local_space_); predicted_display_time, local_space_);
...@@ -138,6 +140,19 @@ std::vector<mojom::XRInputSourceStatePtr> OpenXRInputHelper::GetInputState( ...@@ -138,6 +140,19 @@ std::vector<mojom::XRInputSourceStatePtr> OpenXRInputHelper::GetInputState(
return input_states; return input_states;
} }
void OpenXRInputHelper::OnInteractionProfileChanged(XrResult* xr_result) {
for (OpenXrControllerState& controller_state : controller_states_) {
*xr_result = controller_state.controller.UpdateInteractionProfile();
if (XR_FAILED(*xr_result)) {
return;
}
}
}
base::WeakPtr<OpenXRInputHelper> OpenXRInputHelper::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
base::Optional<Gamepad> OpenXRInputHelper::GetWebXRGamepad( base::Optional<Gamepad> OpenXRInputHelper::GetWebXRGamepad(
const OpenXrController& controller) const { const OpenXrController& controller) const {
XRStandardGamepadBuilder builder(controller.GetHandness()); XRStandardGamepadBuilder builder(controller.GetHandness());
......
...@@ -30,6 +30,10 @@ class OpenXRInputHelper { ...@@ -30,6 +30,10 @@ class OpenXRInputHelper {
std::vector<mojom::XRInputSourceStatePtr> GetInputState( std::vector<mojom::XRInputSourceStatePtr> GetInputState(
XrTime predicted_display_time); XrTime predicted_display_time);
void OnInteractionProfileChanged(XrResult* xr_result);
base::WeakPtr<OpenXRInputHelper> GetWeakPtr();
private: private:
base::Optional<Gamepad> GetWebXRGamepad( base::Optional<Gamepad> GetWebXRGamepad(
const OpenXrController& controller) const; const OpenXrController& controller) const;
...@@ -47,6 +51,9 @@ class OpenXRInputHelper { ...@@ -47,6 +51,9 @@ class OpenXRInputHelper {
static_cast<size_t>(OpenXrHandednessType::kCount)> static_cast<size_t>(OpenXrHandednessType::kCount)>
controller_states_; controller_states_;
// This must be the last member
base::WeakPtrFactory<OpenXRInputHelper> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(OpenXRInputHelper); DISALLOW_COPY_AND_ASSIGN(OpenXRInputHelper);
}; };
......
...@@ -104,6 +104,10 @@ bool OpenXrRenderLoop::StartRuntime() { ...@@ -104,6 +104,10 @@ bool OpenXrRenderLoop::StartRuntime() {
openxr_ = std::move(openxr); openxr_ = std::move(openxr);
texture_helper_.SetDefaultSize(openxr_->GetViewSize()); texture_helper_.SetDefaultSize(openxr_->GetViewSize());
openxr_->RegisterInteractionProfileChangeCallback(
base::BindRepeating(&OpenXRInputHelper::OnInteractionProfileChanged,
input_helper_->GetWeakPtr()));
DCHECK(openxr_); DCHECK(openxr_);
DCHECK(input_helper_); DCHECK(input_helper_);
InitializeDisplayInfo(); InitializeDisplayInfo();
......
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include "base/callback.h"
#include "base/macros.h" #include "base/macros.h"
#include "device/vr/windows/compositor_base.h" #include "device/vr/windows/compositor_base.h"
#include "third_party/openxr/src/include/openxr/openxr.h"
struct XrView; struct XrView;
......
...@@ -558,6 +558,24 @@ XrResult xrGetInstanceProperties(XrInstance instance, ...@@ -558,6 +558,24 @@ XrResult xrGetInstanceProperties(XrInstance instance,
return XR_SUCCESS; return XR_SUCCESS;
} }
XrResult xrGetCurrentInteractionProfile(
XrSession session,
XrPath topLevelUserPath,
XrInteractionProfileState* interactionProfile) {
DVLOG(1) << __FUNCTION__;
XrResult xr_result;
RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
RETURN_IF(interactionProfile->type != XR_TYPE_INTERACTION_PROFILE_STATE,
XR_ERROR_VALIDATION_FAILURE,
"xrGetCurrentInteractionProfile type is not "
"XR_TYPE_INTERACTION_PROFILE_STATE");
RETURN_IF_XR_FAILED(g_test_helper.ValidatePath(topLevelUserPath));
interactionProfile->interactionProfile =
g_test_helper.GetCurrentInteractionProfile();
return XR_SUCCESS;
}
XrResult xrGetReferenceSpaceBoundsRect(XrSession session, XrResult xrGetReferenceSpaceBoundsRect(XrSession session,
XrReferenceSpaceType referenceSpaceType, XrReferenceSpaceType referenceSpaceType,
XrExtent2Df* bounds) { XrExtent2Df* bounds) {
...@@ -655,17 +673,8 @@ XrResult xrPollEvent(XrInstance instance, XrEventDataBuffer* event_data) { ...@@ -655,17 +673,8 @@ XrResult xrPollEvent(XrInstance instance, XrEventDataBuffer* event_data) {
RETURN_IF_FALSE(event_data->type == XR_TYPE_EVENT_DATA_BUFFER, RETURN_IF_FALSE(event_data->type == XR_TYPE_EVENT_DATA_BUFFER,
XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VALIDATION_FAILURE,
"xrPollEvent event_data type invalid"); "xrPollEvent event_data type invalid");
RETURN_IF_FALSE(g_test_helper.UpdateSessionStateEventQueue(),
XR_ERROR_VALIDATION_FAILURE,
"Update SessionStateEventQueue failed.");
if (g_test_helper.HasPendingSessionStateEvent()) {
XrEventDataSessionStateChanged* event_data_ptr =
reinterpret_cast<XrEventDataSessionStateChanged*>(event_data);
*event_data_ptr = g_test_helper.GetNextSessionStateEvent();
return XR_SUCCESS;
}
return XR_EVENT_UNAVAILABLE; return g_test_helper.PollEvent(event_data);
} }
XrResult xrReleaseSwapchainImage( XrResult xrReleaseSwapchainImage(
...@@ -725,6 +734,34 @@ XrResult xrStringToPath(XrInstance instance, ...@@ -725,6 +734,34 @@ XrResult xrStringToPath(XrInstance instance,
return XR_SUCCESS; return XR_SUCCESS;
} }
XrResult xrPathToString(XrInstance instance,
XrPath path,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer) {
DVLOG(1) << __FUNCTION__;
XrResult xr_result;
RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
RETURN_IF_XR_FAILED(g_test_helper.ValidatePath(path));
std::string path_string = g_test_helper.PathToString(path);
if (bufferCapacityInput == 0) {
// OpenXR spec counts terminating '\0'
*bufferCountOutput = path_string.size() + 1;
return XR_SUCCESS;
}
RETURN_IF(
*bufferCountOutput <= path_string.size(), XR_ERROR_SIZE_INSUFFICIENT,
"xrPathToString inputsize is not large enough to hold the output string");
errno_t error = strcpy_s(buffer, *bufferCountOutput, path_string.data());
DCHECK(error == 0);
return XR_SUCCESS;
}
XrResult xrSyncActions(XrSession session, const XrActionsSyncInfo* sync_info) { XrResult xrSyncActions(XrSession session, const XrActionsSyncInfo* sync_info) {
DVLOG(2) << __FUNCTION__; DVLOG(2) << __FUNCTION__;
XrResult xr_result; XrResult xr_result;
......
...@@ -78,6 +78,9 @@ XrResult XRAPI_PTR GetInstanceProcAddress(XrInstance instance, ...@@ -78,6 +78,9 @@ XrResult XRAPI_PTR GetInstanceProcAddress(XrInstance instance,
*function = reinterpret_cast<PFN_xrVoidFunction>(xrGetActionStatePose); *function = reinterpret_cast<PFN_xrVoidFunction>(xrGetActionStatePose);
} else if (strcmp(name, "xrGetInstanceProperties") == 0) { } else if (strcmp(name, "xrGetInstanceProperties") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(xrGetInstanceProperties); *function = reinterpret_cast<PFN_xrVoidFunction>(xrGetInstanceProperties);
} else if (strcmp(name, "xrGetCurrentInteractionProfile") == 0) {
*function =
reinterpret_cast<PFN_xrVoidFunction>(xrGetCurrentInteractionProfile);
} else if (strcmp(name, "xrGetReferenceSpaceBoundsRect") == 0) { } else if (strcmp(name, "xrGetReferenceSpaceBoundsRect") == 0) {
*function = *function =
reinterpret_cast<PFN_xrVoidFunction>(xrGetReferenceSpaceBoundsRect); reinterpret_cast<PFN_xrVoidFunction>(xrGetReferenceSpaceBoundsRect);
...@@ -98,6 +101,8 @@ XrResult XRAPI_PTR GetInstanceProcAddress(XrInstance instance, ...@@ -98,6 +101,8 @@ XrResult XRAPI_PTR GetInstanceProcAddress(XrInstance instance,
xrSuggestInteractionProfileBindings); xrSuggestInteractionProfileBindings);
} else if (strcmp(name, "xrStringToPath") == 0) { } else if (strcmp(name, "xrStringToPath") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(xrStringToPath); *function = reinterpret_cast<PFN_xrVoidFunction>(xrStringToPath);
} else if (strcmp(name, "xrPathToString") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(xrPathToString);
} else if (strcmp(name, "xrSyncActions") == 0) { } else if (strcmp(name, "xrSyncActions") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(xrSyncActions); *function = reinterpret_cast<PFN_xrVoidFunction>(xrSyncActions);
} else if (strcmp(name, "xrWaitFrame") == 0) { } else if (strcmp(name, "xrWaitFrame") == 0) {
......
...@@ -311,6 +311,10 @@ XrPath OpenXrTestHelper::GetPath(const char* path_string) { ...@@ -311,6 +311,10 @@ XrPath OpenXrTestHelper::GetPath(const char* path_string) {
return paths_.size() - 1; return paths_.size() - 1;
} }
XrPath OpenXrTestHelper::GetCurrentInteractionProfile() {
return GetPath("/interaction_profiles/microsoft/motion_controller");
}
XrResult OpenXrTestHelper::BeginSession() { XrResult OpenXrTestHelper::BeginSession() {
RETURN_IF(session_state_ != XR_SESSION_STATE_READY, RETURN_IF(session_state_ != XR_SESSION_STATE_READY,
XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VALIDATION_FAILURE,
...@@ -468,19 +472,27 @@ XrResult OpenXrTestHelper::UpdateAction(XrAction action) { ...@@ -468,19 +472,27 @@ XrResult OpenXrTestHelper::UpdateAction(XrAction action) {
void OpenXrTestHelper::SetSessionState(XrSessionState state) { void OpenXrTestHelper::SetSessionState(XrSessionState state) {
session_state_ = state; session_state_ = state;
XrEventDataSessionStateChanged event = { XrEventDataBuffer event_data;
XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED}; XrEventDataSessionStateChanged* event_data_ptr =
event.session = session_; reinterpret_cast<XrEventDataSessionStateChanged*>(&event_data);
event.state = session_state_;
event.time = next_predicted_display_time_; event_data_ptr->type = XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED;
session_state_event_queue_.push(event); event_data_ptr->session = session_;
event_data_ptr->state = session_state_;
event_data_ptr->time = next_predicted_display_time_;
event_queue_.push(event_data);
} }
XrEventDataSessionStateChanged OpenXrTestHelper::GetNextSessionStateEvent() { XrResult OpenXrTestHelper::PollEvent(XrEventDataBuffer* event_data) {
DCHECK(HasPendingSessionStateEvent()); UpdateEventQueue();
XrEventDataSessionStateChanged front = session_state_event_queue_.front(); if (!event_queue_.empty()) {
session_state_event_queue_.pop(); *event_data = event_queue_.front();
return front; event_queue_.pop();
return XR_SUCCESS;
}
return XR_EVENT_UNAVAILABLE;
} }
const std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>>& const std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>>&
...@@ -498,20 +510,24 @@ XrTime OpenXrTestHelper::NextPredictedDisplayTime() { ...@@ -498,20 +510,24 @@ XrTime OpenXrTestHelper::NextPredictedDisplayTime() {
return ++next_predicted_display_time_; return ++next_predicted_display_time_;
} }
bool OpenXrTestHelper::UpdateSessionStateEventQueue() { void OpenXrTestHelper::UpdateEventQueue() {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
if (test_hook_) { if (test_hook_) {
if (test_hook_->WaitGetSessionStateStopping()) { device_test::mojom::EventData data = {};
SetSessionState(XR_SESSION_STATE_STOPPING); do {
} data = test_hook_->WaitGetEventData();
return true; if (data.type == device_test::mojom::EventType::kSessionLost) {
SetSessionState(XR_SESSION_STATE_STOPPING);
} else if (data.type == device_test::mojom::EventType::kInstanceLost) {
XrEventDataBuffer event_data = {
XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING};
event_queue_.push(event_data);
} else if (data.type != device_test::mojom::EventType::kNoEvent) {
NOTREACHED() << "Event changed tests other than session lost and "
"instance lost is not implemented";
}
} while (data.type != device_test::mojom::EventType::kNoEvent);
} }
return false;
}
bool OpenXrTestHelper::HasPendingSessionStateEvent() {
return !session_state_event_queue_.empty();
} }
base::Optional<gfx::Transform> OpenXrTestHelper::GetPose() { base::Optional<gfx::Transform> OpenXrTestHelper::GetPose() {
......
...@@ -56,6 +56,7 @@ class OpenXrTestHelper : public device::ServiceTestHook { ...@@ -56,6 +56,7 @@ class OpenXrTestHelper : public device::ServiceTestHook {
XrActionSet CreateActionSet(const XrActionSetCreateInfo& createInfo); XrActionSet CreateActionSet(const XrActionSetCreateInfo& createInfo);
XrSpace CreateActionSpace(XrAction); XrSpace CreateActionSpace(XrAction);
XrPath GetPath(const char* path_string); XrPath GetPath(const char* path_string);
XrPath GetCurrentInteractionProfile();
XrResult GetSession(XrSession* session); XrResult GetSession(XrSession* session);
XrResult BeginSession(); XrResult BeginSession();
...@@ -75,9 +76,8 @@ class OpenXrTestHelper : public device::ServiceTestHook { ...@@ -75,9 +76,8 @@ class OpenXrTestHelper : public device::ServiceTestHook {
uint32_t NextSwapchainImageIndex(); uint32_t NextSwapchainImageIndex();
XrTime NextPredictedDisplayTime(); XrTime NextPredictedDisplayTime();
bool UpdateSessionStateEventQueue(); void UpdateEventQueue();
bool HasPendingSessionStateEvent(); XrResult PollEvent(XrEventDataBuffer* event_data);
XrEventDataSessionStateChanged GetNextSessionStateEvent();
// Methods that validate the parameter with the current state of the runtime. // Methods that validate the parameter with the current state of the runtime.
XrResult ValidateAction(XrAction action) const; XrResult ValidateAction(XrAction action) const;
...@@ -169,9 +169,7 @@ class OpenXrTestHelper : public device::ServiceTestHook { ...@@ -169,9 +169,7 @@ class OpenXrTestHelper : public device::ServiceTestHook {
std::array<device::ControllerFrameData, device::kMaxTrackedDevices> data_arr_; std::array<device::ControllerFrameData, device::kMaxTrackedDevices> data_arr_;
// session_state_event_queue_ is used to store XrEventDataSessionStateChanged std::queue<XrEventDataBuffer> event_queue_;
// event whenever session state changes.
std::queue<XrEventDataSessionStateChanged> session_state_event_queue_;
device::VRTestHook* test_hook_ GUARDED_BY(lock_) = nullptr; device::VRTestHook* test_hook_ GUARDED_BY(lock_) = nullptr;
base::Lock lock_; base::Lock lock_;
......
...@@ -80,6 +80,18 @@ struct ControllerFrameData { ...@@ -80,6 +80,18 @@ struct ControllerFrameData {
bool is_valid; bool is_valid;
}; };
// Event type is used by test to simulate runtime events.
enum EventType {
kSessionLost,
kInstanceLost,
kNoEvent
};
// EventData is used by test to pass all event related data.
struct EventData {
EventType type = kNoEvent;
};
// Tests may implement this, and register it to control behavior of devices for // Tests may implement this, and register it to control behavior of devices for
// tests. The test interface lives in the browser process, and may be consumed // tests. The test interface lives in the browser process, and may be consumed
// by the device utility process. // by the device utility process.
...@@ -118,10 +130,8 @@ interface XRTestHook { ...@@ -118,10 +130,8 @@ interface XRTestHook {
// given index, e.g. its current position and pressed buttons. // given index, e.g. its current position and pressed buttons.
[Sync] WaitGetControllerData(uint32 index) => (ControllerFrameData data); [Sync] WaitGetControllerData(uint32 index) => (ControllerFrameData data);
// Called by the OpenXR test. Test can inform runtime when it should stop // Called by the OpenXR test to simulate runtime events.
// session by calling this function and test if such events are handled [Sync] WaitGetEventData() => (EventData data);
// correctly.
[Sync] WaitGetSessionStateStopping() => (bool stopping);
}; };
// Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs // Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define DEVICE_VR_TEST_TEST_HOOK_H_ #define DEVICE_VR_TEST_TEST_HOOK_H_
#include "base/logging.h" #include "base/logging.h"
#include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
#include "ui/gfx/transform.h" #include "ui/gfx/transform.h"
#include <cstdint> #include <cstdint>
...@@ -145,7 +146,7 @@ class VRTestHook { ...@@ -145,7 +146,7 @@ class VRTestHook {
unsigned int index) = 0; unsigned int index) = 0;
virtual TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) = 0; virtual TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) = 0;
virtual ControllerFrameData WaitGetControllerData(unsigned int index) = 0; virtual ControllerFrameData WaitGetControllerData(unsigned int index) = 0;
virtual bool WaitGetSessionStateStopping() = 0; virtual device_test::mojom::EventData WaitGetEventData() = 0;
virtual void AttachCurrentThread() = 0; virtual void AttachCurrentThread() = 0;
virtual void DetachCurrentThread() = 0; virtual void DetachCurrentThread() = 0;
......
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