Commit 52ef60ac authored by bsheedy's avatar bsheedy Committed by Commit Bot

Add controller support to VR browser tests

Adds support for fake controller input when using OpenVR
in the VR browser tests. This is done by adding several
new functions and structs to the existing test hook and
Mojo interface so that controller data set by a test
is retrievable by the fake OpenVR implementation.

Bug: 863487
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:linux_vr;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: Ib9da6756df51093126d641e69fbf96ec05f2270b
Reviewed-on: https://chromium-review.googlesource.com/c/1220806
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarBill Orr <billorr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597258}
parent 93aa458f
...@@ -4,3 +4,10 @@ include_rules = [ ...@@ -4,3 +4,10 @@ include_rules = [
"+cc/test", "+cc/test",
"+cc/trees", "+cc/trees",
] ]
specific_include_rules = {
".*test.*\.cc": [
"+device/vr/openvr/test/test_hook.h",
"+third_party/openvr/src/headers/openvr.h",
],
}
\ No newline at end of file
...@@ -4,10 +4,55 @@ ...@@ -4,10 +4,55 @@
#include "chrome/browser/vr/test/mock_openvr_device_hook_base.h" #include "chrome/browser/vr/test/mock_openvr_device_hook_base.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/common/service_manager_connection.h"
#include "device/vr/openvr/test/test_hook.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h" #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/connector.h"
MockOpenVRDeviceHookBase::MockOpenVRDeviceHookBase() : binding_(this) { // TODO(https://crbug.com/891832): Remove these conversion functions as part of
// the switch to only mojom types.
device_test::mojom::ControllerRole DeviceToMojoControllerRole(
device::ControllerRole role) {
switch (role) {
case device::kControllerRoleInvalid:
return device_test::mojom::ControllerRole::kControllerRoleInvalid;
case device::kControllerRoleRight:
return device_test::mojom::ControllerRole::kControllerRoleRight;
case device::kControllerRoleLeft:
return device_test::mojom::ControllerRole::kControllerRoleLeft;
}
}
device_test::mojom::ControllerFrameDataPtr DeviceToMojoControllerFrameData(
const device::ControllerFrameData& data) {
device_test::mojom::ControllerFrameDataPtr ret =
device_test::mojom::ControllerFrameData::New();
ret->packet_number = data.packet_number;
ret->buttons_pressed = data.buttons_pressed;
ret->buttons_touched = data.buttons_touched;
ret->supported_buttons = data.supported_buttons;
for (unsigned int i = 0; i < device::kMaxNumAxes; ++i) {
ret->axis_data.emplace_back(device_test::mojom::ControllerAxisData::New());
ret->axis_data[i]->x = data.axis_data[i].x;
ret->axis_data[i]->y = data.axis_data[i].y;
ret->axis_data[i]->axis_type = data.axis_data[i].axis_type;
}
ret->role = DeviceToMojoControllerRole(data.role);
ret->is_valid = data.is_valid;
ret->pose_data = device_test::mojom::PoseFrameData::New();
ret->pose_data->device_to_origin = gfx::Transform();
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
ret->pose_data->device_to_origin->matrix().set(
row, col, data.pose_data.device_to_origin[row * 4 + col]);
}
}
return ret;
}
MockOpenVRDeviceHookBase::MockOpenVRDeviceHookBase()
: tracked_classes_{device_test::mojom::TrackedDeviceClass::
kTrackedDeviceInvalid},
binding_(this) {
content::ServiceManagerConnection* connection = content::ServiceManagerConnection* connection =
content::ServiceManagerConnection::GetForProcess(); content::ServiceManagerConnection::GetForProcess();
connection->GetConnector()->BindInterface( connection->GetConnector()->BindInterface(
...@@ -18,6 +63,9 @@ MockOpenVRDeviceHookBase::MockOpenVRDeviceHookBase() : binding_(this) { ...@@ -18,6 +63,9 @@ MockOpenVRDeviceHookBase::MockOpenVRDeviceHookBase() : binding_(this) {
binding_.Bind(mojo::MakeRequest(&client)); binding_.Bind(mojo::MakeRequest(&client));
mojo::ScopedAllowSyncCallForTesting scoped_allow_sync; mojo::ScopedAllowSyncCallForTesting scoped_allow_sync;
// For now, always have the HMD connected.
tracked_classes_[0] =
device_test::mojom::TrackedDeviceClass::kTrackedDeviceHmd;
test_hook_registration_->SetTestHook(std::move(client)); test_hook_registration_->SetTestHook(std::move(client));
} }
...@@ -62,3 +110,94 @@ void MockOpenVRDeviceHookBase::WaitGetMagicWindowPose( ...@@ -62,3 +110,94 @@ void MockOpenVRDeviceHookBase::WaitGetMagicWindowPose(
pose->device_to_origin = gfx::Transform(); pose->device_to_origin = gfx::Transform();
std::move(callback).Run(std::move(pose)); std::move(callback).Run(std::move(pose));
} }
void MockOpenVRDeviceHookBase::WaitGetControllerRoleForTrackedDeviceIndex(
unsigned int index,
device_test::mojom::XRTestHook::
WaitGetControllerRoleForTrackedDeviceIndexCallback callback) {
auto iter = controller_data_map_.find(index);
auto role = iter == controller_data_map_.end()
? device_test::mojom::ControllerRole::kControllerRoleInvalid
: DeviceToMojoControllerRole(iter->second.role);
std::move(callback).Run(role);
}
void MockOpenVRDeviceHookBase::WaitGetTrackedDeviceClass(
unsigned int index,
device_test::mojom::XRTestHook::WaitGetTrackedDeviceClassCallback
callback) {
DCHECK(index < device::kMaxTrackedDevices);
std::move(callback).Run(tracked_classes_[index]);
}
void MockOpenVRDeviceHookBase::WaitGetControllerData(
unsigned int index,
device_test::mojom::XRTestHook::WaitGetControllerDataCallback callback) {
if (tracked_classes_[index] ==
device_test::mojom::TrackedDeviceClass::kTrackedDeviceController) {
auto iter = controller_data_map_.find(index);
DCHECK(iter != controller_data_map_.end());
std::move(callback).Run(DeviceToMojoControllerFrameData(iter->second));
return;
}
// Default to not being valid so that controllers aren't connected unless
// a test specifically enables it.
auto data =
CreateValidController(device::ControllerRole::kControllerRoleInvalid);
data.is_valid = false;
std::move(callback).Run(DeviceToMojoControllerFrameData(data));
}
unsigned int MockOpenVRDeviceHookBase::ConnectController(
const device::ControllerFrameData& initial_data) {
// Find the first open tracked device slot and fill that.
for (unsigned int i = 0; i < device::kMaxTrackedDevices; ++i) {
if (tracked_classes_[i] ==
device_test::mojom::TrackedDeviceClass::kTrackedDeviceInvalid) {
tracked_classes_[i] =
device_test::mojom::TrackedDeviceClass::kTrackedDeviceController;
controller_data_map_.insert(std::make_pair(i, initial_data));
return i;
}
}
// We shouldn't be running out of slots during a test.
NOTREACHED();
// NOTREACHED should make it unnecessary to return here (as it does elsewhere
// in the code), but compilation fails if this is not present.
return device::kMaxTrackedDevices;
}
void MockOpenVRDeviceHookBase::UpdateController(
unsigned int index,
const device::ControllerFrameData& updated_data) {
auto iter = controller_data_map_.find(index);
DCHECK(iter != controller_data_map_.end());
iter->second = updated_data;
}
void MockOpenVRDeviceHookBase::DisconnectController(unsigned int index) {
DCHECK(tracked_classes_[index] ==
device_test::mojom::TrackedDeviceClass::kTrackedDeviceController);
auto iter = controller_data_map_.find(index);
DCHECK(iter != controller_data_map_.end());
controller_data_map_.erase(iter);
tracked_classes_[index] =
device_test::mojom::TrackedDeviceClass::kTrackedDeviceInvalid;
}
device::ControllerFrameData MockOpenVRDeviceHookBase::CreateValidController(
device::ControllerRole role) {
device::ControllerFrameData ret;
// Because why shouldn't a 64 button controller exist?
ret.supported_buttons = UINT64_MAX;
memset(ret.axis_data, 0,
sizeof(device::ControllerAxisData) * device::kMaxNumAxes);
ret.role = role;
ret.is_valid = true;
// Identity matrix.
ret.pose_data.device_to_origin[0] = 1;
ret.pose_data.device_to_origin[5] = 1;
ret.pose_data.device_to_origin[10] = 1;
ret.pose_data.device_to_origin[15] = 1;
return ret;
}
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_ #ifndef CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
#define CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_ #define CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
#include "base/containers/flat_map.h"
#include "device/vr/openvr/test/test_hook.h"
#include "device/vr/public/mojom/browser_test_interfaces.mojom.h" #include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
...@@ -26,9 +28,35 @@ class MockOpenVRDeviceHookBase : public device_test::mojom::XRTestHook { ...@@ -26,9 +28,35 @@ class MockOpenVRDeviceHookBase : public device_test::mojom::XRTestHook {
void WaitGetMagicWindowPose( void WaitGetMagicWindowPose(
device_test::mojom::XRTestHook::WaitGetMagicWindowPoseCallback callback) device_test::mojom::XRTestHook::WaitGetMagicWindowPoseCallback callback)
override; override;
void WaitGetControllerRoleForTrackedDeviceIndex(
unsigned int index,
device_test::mojom::XRTestHook::
WaitGetControllerRoleForTrackedDeviceIndexCallback callback) override;
void WaitGetTrackedDeviceClass(
unsigned int index,
device_test::mojom::XRTestHook::WaitGetTrackedDeviceClassCallback
callback) override;
void WaitGetControllerData(
unsigned int index,
device_test::mojom::XRTestHook::WaitGetControllerDataCallback callback)
override;
// MockOpenVRDeviceHookBase
unsigned int ConnectController(
const device::ControllerFrameData& initial_data);
void UpdateController(unsigned int index,
const device::ControllerFrameData& updated_data);
void DisconnectController(unsigned int index);
device::ControllerFrameData CreateValidController(
device::ControllerRole role);
void StopHooking(); void StopHooking();
protected:
device_test::mojom::TrackedDeviceClass
tracked_classes_[device::kMaxTrackedDevices];
base::flat_map<unsigned int, device::ControllerFrameData>
controller_data_map_;
private: private:
mojo::Binding<device_test::mojom::XRTestHook> binding_; mojo::Binding<device_test::mojom::XRTestHook> binding_;
device_test::mojom::XRTestHookRegistrationPtr test_hook_registration_; device_test::mojom::XRTestHookRegistrationPtr test_hook_registration_;
......
...@@ -2,8 +2,13 @@ ...@@ -2,8 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/run_loop.h"
#include "chrome/browser/vr/test/mock_openvr_device_hook_base.h"
#include "chrome/browser/vr/test/webvr_browser_test.h" #include "chrome/browser/vr/test/webvr_browser_test.h"
#include "chrome/browser/vr/test/webxr_vr_browser_test.h" #include "chrome/browser/vr/test/webxr_vr_browser_test.h"
#include "device/vr/openvr/test/test_hook.h"
#include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
#include "third_party/openvr/src/headers/openvr.h"
// Browser test equivalent of // Browser test equivalent of
// chrome/android/javatests/src/.../browser/vr/WebXrVrInputTest.java. // chrome/android/javatests/src/.../browser/vr/WebXrVrInputTest.java.
...@@ -30,4 +35,126 @@ IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard, ...@@ -30,4 +35,126 @@ IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
TestPresentationLocksFocusImpl(this, "webxr_test_presentation_locks_focus"); TestPresentationLocksFocusImpl(this, "webxr_test_presentation_locks_focus");
} }
class WebXrControllerInputOpenVRMock : public MockOpenVRDeviceHookBase {
public:
void OnFrameSubmitted(
device_test::mojom::SubmittedFrameDataPtr frame_data,
device_test::mojom::XRTestHook::OnFrameSubmittedCallback callback) final;
void WaitNumFrames(unsigned int num_frames) {
DCHECK(!wait_loop_);
target_submitted_frames_ = num_submitted_frames_ + num_frames;
wait_loop_ = new base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed);
wait_loop_->Run();
delete wait_loop_;
wait_loop_ = nullptr;
}
void ToggleTrigger(unsigned int index,
device::ControllerFrameData& controller_data) {
uint64_t trigger_mask = vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger);
controller_data.packet_number++;
controller_data.buttons_pressed ^= trigger_mask;
UpdateController(index, controller_data);
// TODO(https://crbug.com/887726): Figure out why waiting for OpenVR to grab
// the updated state instead of waiting for a number of frames causes frames
// to be submitted at an extremely slow rate. Once fixed, switch away from
// waiting on number of frames.
WaitNumFrames(30);
}
void PressReleaseTrigger(unsigned int index,
device::ControllerFrameData& controller_data) {
ToggleTrigger(index, controller_data);
ToggleTrigger(index, controller_data);
}
private:
base::RunLoop* wait_loop_ = nullptr;
unsigned int num_submitted_frames_ = 0;
unsigned int target_submitted_frames_ = 0;
};
void WebXrControllerInputOpenVRMock::OnFrameSubmitted(
device_test::mojom::SubmittedFrameDataPtr frame_data,
device_test::mojom::XRTestHook::OnFrameSubmittedCallback callback) {
num_submitted_frames_++;
if (wait_loop_ && target_submitted_frames_ == num_submitted_frames_) {
wait_loop_->Quit();
}
std::move(callback).Run();
}
// Test that OpenVR controller input is registered via WebXR's input method.
// Equivalent to
// WebXrVrInputTest#testControllerClicksRegisteredOnDaydream_WebXr.
IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
REQUIRES_GPU(TestControllerInputRegistered)) {
WebXrControllerInputOpenVRMock my_mock;
// Connect a controller.
auto controller_data = my_mock.CreateValidController(
device::ControllerRole::kControllerRoleRight);
unsigned int controller_index = my_mock.ConnectController(controller_data);
// Load the test page and enter presentation.
this->LoadUrlAndAwaitInitialization(
this->GetHtmlTestFile("test_webxr_input"));
this->EnterSessionWithUserGestureOrFail();
unsigned int num_iterations = 10;
this->RunJavaScriptOrFail("stepSetupListeners(" +
std::to_string(num_iterations) + ")");
// Press and unpress the controller's trigger a bunch of times and make sure
// they're all registered.
for (unsigned int i = 0; i < num_iterations; ++i) {
my_mock.PressReleaseTrigger(controller_index, controller_data);
// After each trigger release, wait for the JavaScript to receive the
// "select" event.
this->WaitOnJavaScriptStep();
}
this->EndTest();
}
// Test that OpenVR controller input is registered via the Gamepad API.
// Equivalent to
// WebXrVrInputTest#testControllerClicksRegisteredOnDaydream
IN_PROC_BROWSER_TEST_F(WebVrBrowserTestStandard,
REQUIRES_GPU(TestControllerInputRegistered)) {
WebXrControllerInputOpenVRMock my_mock;
// Connect a controller.
auto controller_data = my_mock.CreateValidController(
device::ControllerRole::kControllerRoleRight);
// openvr_gamepad_helper assumes axis index 1 is the trigger, so we need to
// set that here, otherwise it won't check whether it's pressed or not.
controller_data.axis_data[1].axis_type = vr::k_eControllerAxis_Trigger;
unsigned int controller_index = my_mock.ConnectController(controller_data);
// Load the test page and enter presentation.
this->LoadUrlAndAwaitInitialization(
this->GetHtmlTestFile("test_gamepad_button"));
this->EnterSessionWithUserGestureOrFail();
// We need to have this, otherwise the JavaScript side of the Gamepad API
// doesn't seem to pick up the correct button state? I.e. if we don't have
// this, openvr_gamepad_helper properly sets the gamepad's button state,
// but JavaScript still shows no buttons pressed.
// TODO(bsheedy): Figure out why this is the case.
my_mock.PressReleaseTrigger(controller_index, controller_data);
// Setting this in the Android version of the test needs to happen after a
// flakiness workaround. Coincidentally, it's also helpful for the different
// issue solved by the above PressReleaseTrigger, so make sure to set it here
// so that the above press/release isn't caught by the test code.
this->RunJavaScriptOrFail("canStartTest = true");
// Press and release the trigger, ensuring the Gamepad API detects both.
my_mock.ToggleTrigger(controller_index, controller_data);
this->WaitOnJavaScriptStep();
my_mock.ToggleTrigger(controller_index, controller_data);
this->WaitOnJavaScriptStep();
this->EndTest();
}
} // namespace vr } // namespace vr
...@@ -6,6 +6,37 @@ ...@@ -6,6 +6,37 @@
namespace device { namespace device {
// TODO(https://crbug.com/891832): Remove these as conversion functions as part
// of the switch to only mojom types.
ControllerRole MojoToDeviceControllerRole(
device_test::mojom::ControllerRole role) {
switch (role) {
case device_test::mojom::ControllerRole::kControllerRoleInvalid:
return device::kControllerRoleInvalid;
case device_test::mojom::ControllerRole::kControllerRoleLeft:
return device::kControllerRoleLeft;
case device_test::mojom::ControllerRole::kControllerRoleRight:
return device::kControllerRoleRight;
}
return device::kControllerRoleInvalid;
}
PoseFrameData MojoToDevicePoseFrameData(
device_test::mojom::PoseFrameDataPtr& pose) {
PoseFrameData ret = {};
ret.is_valid = !!pose->device_to_origin;
if (ret.is_valid) {
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
ret.device_to_origin[row * 4 + col] =
pose->device_to_origin->matrix().getFloat(row, col);
}
}
}
return ret;
}
XRTestHookWrapper::XRTestHookWrapper( XRTestHookWrapper::XRTestHookWrapper(
device_test::mojom::XRTestHookPtrInfo hook_info) device_test::mojom::XRTestHookPtrInfo hook_info)
: hook_info_(std::move(hook_info)) {} : hook_info_(std::move(hook_info)) {}
...@@ -56,18 +87,7 @@ PoseFrameData XRTestHookWrapper::WaitGetPresentingPose() { ...@@ -56,18 +87,7 @@ PoseFrameData XRTestHookWrapper::WaitGetPresentingPose() {
device_test::mojom::PoseFrameDataPtr pose; device_test::mojom::PoseFrameDataPtr pose;
hook_->WaitGetPresentingPose(&pose); hook_->WaitGetPresentingPose(&pose);
if (pose) { if (pose) {
PoseFrameData ret = {}; return MojoToDevicePoseFrameData(pose);
ret.is_valid = !!pose->device_to_origin;
if (ret.is_valid) {
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
ret.device_to_origin[row * 4 + col] =
pose->device_to_origin->matrix().getFloat(row, col);
}
}
}
return ret;
} }
} }
...@@ -79,17 +99,69 @@ PoseFrameData XRTestHookWrapper::WaitGetMagicWindowPose() { ...@@ -79,17 +99,69 @@ PoseFrameData XRTestHookWrapper::WaitGetMagicWindowPose() {
device_test::mojom::PoseFrameDataPtr pose; device_test::mojom::PoseFrameDataPtr pose;
hook_->WaitGetMagicWindowPose(&pose); hook_->WaitGetMagicWindowPose(&pose);
if (pose) { if (pose) {
PoseFrameData ret = {}; return MojoToDevicePoseFrameData(pose);
ret.is_valid = !!pose->device_to_origin; }
if (ret.is_valid) { }
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
ret.device_to_origin[row * 4 + col] =
pose->device_to_origin->matrix().getFloat(row, col);
}
}
}
return {};
}
ControllerRole XRTestHookWrapper::WaitGetControllerRoleForTrackedDeviceIndex(
unsigned int index) {
if (hook_) {
device_test::mojom::ControllerRole role;
hook_->WaitGetControllerRoleForTrackedDeviceIndex(index, &role);
return MojoToDeviceControllerRole(role);
}
return device::kControllerRoleInvalid;
}
TrackedDeviceClass XRTestHookWrapper::WaitGetTrackedDeviceClass(
unsigned int index) {
if (hook_) {
device_test::mojom::TrackedDeviceClass device_class;
hook_->WaitGetTrackedDeviceClass(index, &device_class);
switch (device_class) {
case device_test::mojom::TrackedDeviceClass::kTrackedDeviceInvalid:
return device::kTrackedDeviceInvalid;
case device_test::mojom::TrackedDeviceClass::kTrackedDeviceHmd:
return device::kTrackedDeviceHmd;
case device_test::mojom::TrackedDeviceClass::kTrackedDeviceController:
return device::kTrackedDeviceController;
case device_test::mojom::TrackedDeviceClass::kTrackedDeviceGenericTracker:
return device::kTrackedDeviceGenericTracker;
case device_test::mojom::TrackedDeviceClass::
kTrackedDeviceTrackingReference:
return device::kTrackedDeviceTrackingReference;
case device_test::mojom::TrackedDeviceClass::
kTrackedDeviceDisplayRedirect:
return device::kTrackedDeviceDisplayRedirect;
}
}
return device::kTrackedDeviceInvalid;
}
ControllerFrameData XRTestHookWrapper::WaitGetControllerData(
unsigned int index) {
if (hook_) {
device_test::mojom::ControllerFrameDataPtr data;
hook_->WaitGetControllerData(index, &data);
if (data) {
ControllerFrameData ret = {};
ret.packet_number = data->packet_number;
ret.buttons_pressed = data->buttons_pressed;
ret.buttons_touched = data->buttons_touched;
ret.supported_buttons = data->supported_buttons;
ret.pose_data = MojoToDevicePoseFrameData(data->pose_data);
ret.role = MojoToDeviceControllerRole(data->role);
ret.is_valid = data->is_valid;
for (unsigned int i = 0; i < device::kMaxNumAxes; ++i) {
ret.axis_data[i].x = data->axis_data[i]->x;
ret.axis_data[i].y = data->axis_data[i]->y;
ret.axis_data[i].axis_type = data->axis_data[i]->axis_type;
}
return ret; return ret;
} }
} }
......
...@@ -25,6 +25,10 @@ class XRTestHookWrapper : public OpenVRTestHook { ...@@ -25,6 +25,10 @@ class XRTestHookWrapper : public OpenVRTestHook {
DeviceConfig WaitGetDeviceConfig() override; DeviceConfig WaitGetDeviceConfig() override;
PoseFrameData WaitGetPresentingPose() override; PoseFrameData WaitGetPresentingPose() override;
PoseFrameData WaitGetMagicWindowPose() override; PoseFrameData WaitGetMagicWindowPose() override;
ControllerRole WaitGetControllerRoleForTrackedDeviceIndex(
unsigned int index) override;
TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) override;
ControllerFrameData WaitGetControllerData(unsigned int index) override;
void AttachCurrentThread() override; void AttachCurrentThread() override;
void DetachCurrentThread() override; void DetachCurrentThread() override;
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <wrl.h> #include <wrl.h>
#include <memory> #include <memory>
// TODO(https://crbug.com/892717): Update argument names to be consistent with
// Chromium style guidelines.
namespace vr { namespace vr {
class TestVRSystem : public IVRSystem { class TestVRSystem : public IVRSystem {
...@@ -96,17 +98,9 @@ class TestVRSystem : public IVRSystem { ...@@ -96,17 +98,9 @@ class TestVRSystem : public IVRSystem {
return 0; return 0;
} }
ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex( ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex(
TrackedDeviceIndex_t unDeviceIndex) override { TrackedDeviceIndex_t unDeviceIndex) override;
NOTIMPLEMENTED();
return TrackedControllerRole_Invalid;
}
ETrackedDeviceClass GetTrackedDeviceClass( ETrackedDeviceClass GetTrackedDeviceClass(
TrackedDeviceIndex_t unDeviceIndex) override { TrackedDeviceIndex_t unDeviceIndex) override;
// Not yet implemented, but avoid calling NOTIMPLEMENTED() because it floods
// logs, and will be called to enumerate input devices.
// TODO(crbug.com/863487) - implement this and test input.
return TrackedDeviceClass_Invalid;
}
bool IsTrackedDeviceConnected(TrackedDeviceIndex_t unDeviceIndex) override { bool IsTrackedDeviceConnected(TrackedDeviceIndex_t unDeviceIndex) override {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
return false; return false;
...@@ -125,17 +119,11 @@ class TestVRSystem : public IVRSystem { ...@@ -125,17 +119,11 @@ class TestVRSystem : public IVRSystem {
int32_t GetInt32TrackedDeviceProperty( int32_t GetInt32TrackedDeviceProperty(
TrackedDeviceIndex_t unDeviceIndex, TrackedDeviceIndex_t unDeviceIndex,
ETrackedDeviceProperty prop, ETrackedDeviceProperty prop,
ETrackedPropertyError* pError = 0L) override { ETrackedPropertyError* pError = 0L) override;
NOTIMPLEMENTED();
return 0;
}
uint64_t GetUint64TrackedDeviceProperty( uint64_t GetUint64TrackedDeviceProperty(
TrackedDeviceIndex_t unDeviceIndex, TrackedDeviceIndex_t unDeviceIndex,
ETrackedDeviceProperty prop, ETrackedDeviceProperty prop,
ETrackedPropertyError* pError = 0L) override { ETrackedPropertyError* pError = 0L) override;
NOTIMPLEMENTED();
return 0;
}
HmdMatrix34_t GetMatrix34TrackedDeviceProperty( HmdMatrix34_t GetMatrix34TrackedDeviceProperty(
TrackedDeviceIndex_t unDeviceIndex, TrackedDeviceIndex_t unDeviceIndex,
ETrackedDeviceProperty prop, ETrackedDeviceProperty prop,
...@@ -171,21 +159,15 @@ class TestVRSystem : public IVRSystem { ...@@ -171,21 +159,15 @@ class TestVRSystem : public IVRSystem {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
return {}; return {};
} }
bool GetControllerState(TrackedDeviceIndex_t unControllerDeviceIndex, bool GetControllerState(TrackedDeviceIndex_t controller_device_index,
VRControllerState_t* pControllerState, VRControllerState_t* controller_state,
uint32_t unControllerStateSize) override { uint32_t controller_state_size) override;
NOTIMPLEMENTED();
return false;
}
bool GetControllerStateWithPose( bool GetControllerStateWithPose(
ETrackingUniverseOrigin eOrigin, ETrackingUniverseOrigin origin,
TrackedDeviceIndex_t unControllerDeviceIndex, TrackedDeviceIndex_t device_controller_index,
VRControllerState_t* pControllerState, VRControllerState_t* controller_state,
uint32_t unControllerStateSize, uint32_t controller_state_size,
TrackedDevicePose_t* pTrackedDevicePose) override { TrackedDevicePose_t* tracked_device_pose) override;
NOTIMPLEMENTED();
return false;
}
void TriggerHapticPulse(TrackedDeviceIndex_t unControllerDeviceIndex, void TriggerHapticPulse(TrackedDeviceIndex_t unControllerDeviceIndex,
uint32_t unAxisId, uint32_t unAxisId,
unsigned short usDurationMicroSec) override { unsigned short usDurationMicroSec) override {
...@@ -506,6 +488,25 @@ bool TestVRSystem::PollNextEvent(VREvent_t*, unsigned int) { ...@@ -506,6 +488,25 @@ bool TestVRSystem::PollNextEvent(VREvent_t*, unsigned int) {
return false; return false;
} }
bool TestVRSystem::GetControllerState(
TrackedDeviceIndex_t controller_device_index,
VRControllerState_t* controller_state,
uint32_t controller_state_size) {
return g_test_helper.GetControllerState(controller_device_index,
controller_state);
}
bool TestVRSystem::GetControllerStateWithPose(
ETrackingUniverseOrigin origin,
TrackedDeviceIndex_t controller_device_index,
VRControllerState_t* controller_state,
uint32_t controller_state_size,
TrackedDevicePose_t* tracked_device_pose) {
g_test_helper.GetControllerState(controller_device_index, controller_state);
return g_test_helper.GetControllerPose(controller_device_index,
tracked_device_pose);
}
uint32_t TestVRSystem::GetStringTrackedDeviceProperty( uint32_t TestVRSystem::GetStringTrackedDeviceProperty(
TrackedDeviceIndex_t unDeviceIndex, TrackedDeviceIndex_t unDeviceIndex,
ETrackedDeviceProperty prop, ETrackedDeviceProperty prop,
...@@ -535,6 +536,38 @@ float TestVRSystem::GetFloatTrackedDeviceProperty( ...@@ -535,6 +536,38 @@ float TestVRSystem::GetFloatTrackedDeviceProperty(
return 0; return 0;
} }
int32_t TestVRSystem::GetInt32TrackedDeviceProperty(
TrackedDeviceIndex_t unDeviceIndex,
ETrackedDeviceProperty prop,
ETrackedPropertyError* pError) {
int32_t ret;
ETrackedPropertyError err =
g_test_helper.GetInt32TrackedDeviceProperty(unDeviceIndex, prop, ret);
if (err != TrackedProp_Success) {
NOTIMPLEMENTED();
}
if (pError) {
*pError = err;
}
return ret;
}
uint64_t TestVRSystem::GetUint64TrackedDeviceProperty(
TrackedDeviceIndex_t unDeviceIndex,
ETrackedDeviceProperty prop,
ETrackedPropertyError* pError) {
uint64_t ret;
ETrackedPropertyError err =
g_test_helper.GetUint64TrackedDeviceProperty(unDeviceIndex, prop, ret);
if (err != TrackedProp_Success) {
NOTIMPLEMENTED();
}
if (pError) {
*pError = err;
}
return ret;
}
HmdMatrix34_t TestVRSystem::GetSeatedZeroPoseToStandingAbsoluteTrackingPose() { HmdMatrix34_t TestVRSystem::GetSeatedZeroPoseToStandingAbsoluteTrackingPose() {
HmdMatrix34_t ret = {}; HmdMatrix34_t ret = {};
ret.m[0][0] = 1; ret.m[0][0] = 1;
...@@ -544,6 +577,16 @@ HmdMatrix34_t TestVRSystem::GetSeatedZeroPoseToStandingAbsoluteTrackingPose() { ...@@ -544,6 +577,16 @@ HmdMatrix34_t TestVRSystem::GetSeatedZeroPoseToStandingAbsoluteTrackingPose() {
return ret; return ret;
} }
ETrackedControllerRole TestVRSystem::GetControllerRoleForTrackedDeviceIndex(
TrackedDeviceIndex_t unDeviceIndex) {
return g_test_helper.GetControllerRoleForTrackedDeviceIndex(unDeviceIndex);
}
ETrackedDeviceClass TestVRSystem::GetTrackedDeviceClass(
TrackedDeviceIndex_t unDeviceIndex) {
return g_test_helper.GetTrackedDeviceClass(unDeviceIndex);
}
void TestVRCompositor::SuspendRendering(bool bSuspend) {} void TestVRCompositor::SuspendRendering(bool bSuspend) {}
void TestVRCompositor::SetTrackingSpace(ETrackingUniverseOrigin) {} void TestVRCompositor::SetTrackingSpace(ETrackingUniverseOrigin) {}
......
...@@ -159,6 +159,140 @@ vr::TrackedDevicePose_t TestHelper::GetPose(bool presenting) { ...@@ -159,6 +159,140 @@ vr::TrackedDevicePose_t TestHelper::GetPose(bool presenting) {
return TranslatePose(pose); return TranslatePose(pose);
} }
vr::ETrackedPropertyError TestHelper::GetInt32TrackedDeviceProperty(
unsigned int index,
ETrackedDeviceProperty prop,
int32_t& prop_value) {
vr::ETrackedPropertyError ret = vr::TrackedProp_Success;
prop_value = 0;
lock_.Acquire();
switch (prop) {
case vr::Prop_Axis0Type_Int32:
case vr::Prop_Axis1Type_Int32:
case vr::Prop_Axis2Type_Int32:
case vr::Prop_Axis3Type_Int32:
case vr::Prop_Axis4Type_Int32: {
auto controller_data = test_hook_->WaitGetControllerData(index);
if (!controller_data.is_valid) {
ret = vr::TrackedProp_WrongDeviceClass;
break;
}
prop_value = static_cast<vr::EVRControllerAxisType>(
controller_data.axis_data[prop - vr::Prop_Axis0Type_Int32].axis_type);
break;
}
default:
ret = vr::TrackedProp_UnknownProperty;
}
lock_.Release();
return ret;
}
vr::ETrackedPropertyError TestHelper::GetUint64TrackedDeviceProperty(
unsigned int index,
ETrackedDeviceProperty prop,
uint64_t& prop_value) {
vr::ETrackedPropertyError ret = vr::TrackedProp_Success;
prop_value = 0;
lock_.Acquire();
switch (prop) {
case vr::Prop_SupportedButtons_Uint64: {
auto controller_data = test_hook_->WaitGetControllerData(index);
if (!controller_data.is_valid) {
ret = vr::TrackedProp_WrongDeviceClass;
break;
}
prop_value = controller_data.supported_buttons;
break;
}
default:
ret = vr::TrackedProp_UnknownProperty;
}
lock_.Release();
return ret;
}
vr::ETrackedControllerRole TestHelper::GetControllerRoleForTrackedDeviceIndex(
unsigned int index) {
vr::ETrackedControllerRole ret = vr::TrackedControllerRole_Invalid;
lock_.Acquire();
if (test_hook_) {
switch (test_hook_->WaitGetControllerRoleForTrackedDeviceIndex(index)) {
case device::kControllerRoleInvalid:
break;
case device::kControllerRoleLeft:
ret = vr::TrackedControllerRole_LeftHand;
break;
case device::kControllerRoleRight:
ret = vr::TrackedControllerRole_RightHand;
break;
default:
NOTREACHED();
}
}
lock_.Release();
return ret;
}
vr::ETrackedDeviceClass TestHelper::GetTrackedDeviceClass(unsigned int index) {
vr::ETrackedDeviceClass tracked_class = vr::TrackedDeviceClass_Invalid;
lock_.Acquire();
if (test_hook_) {
switch (test_hook_->WaitGetTrackedDeviceClass(index)) {
case device::kTrackedDeviceInvalid:
break;
case device::kTrackedDeviceHmd:
tracked_class = vr::TrackedDeviceClass_HMD;
break;
case device::kTrackedDeviceController:
tracked_class = vr::TrackedDeviceClass_Controller;
break;
case device::kTrackedDeviceGenericTracker:
tracked_class = vr::TrackedDeviceClass_GenericTracker;
break;
case device::kTrackedDeviceTrackingReference:
tracked_class = vr::TrackedDeviceClass_TrackingReference;
break;
default:
NOTREACHED();
}
}
lock_.Release();
return tracked_class;
}
bool TestHelper::GetControllerState(unsigned int index,
vr::VRControllerState_t* controller_state) {
lock_.Acquire();
if (test_hook_) {
auto controller_data = test_hook_->WaitGetControllerData(index);
lock_.Release();
controller_state->unPacketNum = controller_data.packet_number;
controller_state->ulButtonPressed = controller_data.buttons_pressed;
controller_state->ulButtonTouched = controller_data.buttons_touched;
for (unsigned int i = 0; i < device::kMaxNumAxes; ++i) {
controller_state->rAxis[i].x = controller_data.axis_data[i].x;
controller_state->rAxis[i].y = controller_data.axis_data[i].y;
}
return controller_data.is_valid;
}
lock_.Release();
return false;
}
bool TestHelper::GetControllerPose(unsigned int index,
vr::TrackedDevicePose_t* controller_pose) {
lock_.Acquire();
if (test_hook_) {
auto controller_data = test_hook_->WaitGetControllerData(index);
lock_.Release();
*controller_pose = TranslatePose(controller_data.pose_data);
return controller_data.is_valid && controller_data.pose_data.is_valid;
}
lock_.Release();
return false;
}
void TestHelper::SetTestHook(device::OpenVRTestHook* hook) { void TestHelper::SetTestHook(device::OpenVRTestHook* hook) {
lock_.Acquire(); lock_.Acquire();
test_hook_ = hook; test_hook_ = hook;
......
...@@ -26,6 +26,21 @@ class TestHelper : public device::TestHookRegistration { ...@@ -26,6 +26,21 @@ class TestHelper : public device::TestHookRegistration {
TrackedDevicePose_t GetPose(bool presenting); TrackedDevicePose_t GetPose(bool presenting);
float GetInterpupillaryDistance(); float GetInterpupillaryDistance();
ProjectionRaw GetProjectionRaw(bool left); ProjectionRaw GetProjectionRaw(bool left);
ETrackedPropertyError GetInt32TrackedDeviceProperty(
unsigned int index,
ETrackedDeviceProperty prop,
int32_t& prop_value);
ETrackedPropertyError GetUint64TrackedDeviceProperty(
unsigned int index,
ETrackedDeviceProperty prop,
uint64_t& prop_value);
ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex(
unsigned int index);
ETrackedDeviceClass GetTrackedDeviceClass(unsigned int index);
bool GetControllerState(unsigned int index,
VRControllerState_t* controller_state);
bool GetControllerPose(unsigned int index,
TrackedDevicePose_t* controller_pose);
void TestFailure(); void TestFailure();
void AttachToCurrentThread(); void AttachToCurrentThread();
......
...@@ -5,10 +5,14 @@ ...@@ -5,10 +5,14 @@
#ifndef DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_ #ifndef DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
#define DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_ #define DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
#include <cstdint>
namespace device { namespace device {
// Update this string whenever either interface changes. // Update this string whenever either interface changes.
constexpr char kChromeOpenVRTestHookAPI[] = "ChromeTestHook_1"; constexpr char kChromeOpenVRTestHookAPI[] = "ChromeTestHook_2";
constexpr unsigned int kMaxTrackedDevices = 64;
constexpr unsigned int kMaxNumAxes = 5;
struct Color { struct Color {
unsigned char r; unsigned char r;
...@@ -44,6 +48,38 @@ struct DeviceConfig { ...@@ -44,6 +48,38 @@ struct DeviceConfig {
float viewport_right[4]; // raw projection right {left, right, top, bottom} float viewport_right[4]; // raw projection right {left, right, top, bottom}
}; };
struct ControllerAxisData {
float x = 0.0f;
float y = 0.0f;
unsigned int axis_type = 0;
};
enum TrackedDeviceClass {
kTrackedDeviceInvalid,
kTrackedDeviceHmd,
kTrackedDeviceController,
kTrackedDeviceGenericTracker,
kTrackedDeviceTrackingReference,
kTrackedDeviceDisplayRedirect
};
enum ControllerRole {
kControllerRoleInvalid,
kControllerRoleLeft,
kControllerRoleRight
};
struct ControllerFrameData {
unsigned int packet_number = 0;
uint64_t buttons_pressed = 0;
uint64_t buttons_touched = 0;
uint64_t supported_buttons = 0;
ControllerAxisData axis_data[kMaxNumAxes];
PoseFrameData pose_data = {};
ControllerRole role = kControllerRoleInvalid;
bool is_valid = false;
};
// Tests may implement this, and register it to control behavior of OpenVR. // Tests may implement this, and register it to control behavior of OpenVR.
class OpenVRTestHook { class OpenVRTestHook {
public: public:
...@@ -51,6 +87,10 @@ class OpenVRTestHook { ...@@ -51,6 +87,10 @@ class OpenVRTestHook {
virtual DeviceConfig WaitGetDeviceConfig() = 0; virtual DeviceConfig WaitGetDeviceConfig() = 0;
virtual PoseFrameData WaitGetPresentingPose() = 0; virtual PoseFrameData WaitGetPresentingPose() = 0;
virtual PoseFrameData WaitGetMagicWindowPose() = 0; virtual PoseFrameData WaitGetMagicWindowPose() = 0;
virtual ControllerRole WaitGetControllerRoleForTrackedDeviceIndex(
unsigned int index) = 0;
virtual TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) = 0;
virtual ControllerFrameData WaitGetControllerData(unsigned int index) = 0;
virtual void AttachCurrentThread() = 0; virtual void AttachCurrentThread() = 0;
virtual void DetachCurrentThread() = 0; virtual void DetachCurrentThread() = 0;
......
...@@ -47,15 +47,75 @@ struct DeviceConfig { ...@@ -47,15 +47,75 @@ struct DeviceConfig {
ProjectionRaw projection_right; ProjectionRaw projection_right;
}; };
struct ControllerAxisData {
float x;
float y;
uint8 axis_type; // Corresponds to OpenVR's EVRControllerAxisType
};
enum TrackedDeviceClass {
kTrackedDeviceInvalid,
kTrackedDeviceHmd,
kTrackedDeviceController,
kTrackedDeviceGenericTracker,
kTrackedDeviceTrackingReference,
kTrackedDeviceDisplayRedirect
};
enum ControllerRole {
kControllerRoleInvalid,
kControllerRoleLeft,
kControllerRoleRight
};
struct ControllerFrameData {
uint32 packet_number;
uint64 buttons_pressed;
uint64 buttons_touched;
uint64 supported_buttons;
array<ControllerAxisData, 5> axis_data;
PoseFrameData pose_data;
ControllerRole role = kControllerRoleInvalid;
bool is_valid;
};
// 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.
// It is only implemented when running in browser tests. // It is only implemented when running in browser tests.
interface XRTestHook { interface XRTestHook {
// Notifies the test anytime the XR runtime receives a frame with the data
// that was submitted.
[Sync] OnFrameSubmitted(SubmittedFrameData frame_data) => (); [Sync] OnFrameSubmitted(SubmittedFrameData frame_data) => ();
// Called by the XR runtime to retrieve the XR device's configuration set by
// the test.
[Sync] WaitGetDeviceConfig() => (DeviceConfig config); [Sync] WaitGetDeviceConfig() => (DeviceConfig config);
// Called by the XR runtime to retrieve the XR device's pose while in a
// presenting/exclusive session.
[Sync] WaitGetPresentingPose() => (PoseFrameData data); [Sync] WaitGetPresentingPose() => (PoseFrameData data);
// Called by the XR runtime to retrieve the XR device's pose while in a
// magic window/non-exclusive session.
[Sync] WaitGetMagicWindowPose() => (PoseFrameData data); [Sync] WaitGetMagicWindowPose() => (PoseFrameData data);
// Called by the XR runtime to retrieve the ControllerRole of the device
// that the test has registered at the given index, i.e. which hand the
// controller is mapped to.
[Sync] WaitGetControllerRoleForTrackedDeviceIndex(uint32 index) =>
(ControllerRole role);
// Called by the XR runtime to retrieve the class of the device that the test
// has registered at the given index, e.g. whether it is a controller or
// headset.
[Sync] WaitGetTrackedDeviceClass(uint32 index) =>
(TrackedDeviceClass device_class);
// Called by the XR runtime anytime it updates its controller data to retrieve
// the controller data of the device that the test has registered at the
// given index, e.g. its current position and pressed buttons.
[Sync] WaitGetControllerData(uint32 index) => (ControllerFrameData data);
}; };
// Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs // Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs
......
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