Skip to content

Commit

Permalink
Update FakeXRDeviceInterface to match webxr-test-api
Browse files Browse the repository at this point in the history
This updates the FakeXRDeviceInterface to support newly added methods in
the webxr-test-api:
* simulateVisibilityChange
* setBoundsGeometry
* setLocalToFloorLevelTransform
* clearLocalToFloorLevelTransform

and supports the setting of boundsCoordinates and
localToFloorLevelTransform in FakeXRDeviceInit.

The Bounds and LocalToFloorLevelTransform functions already existed on
internal tests, and as such a few of the tests that were ready were
ported to external wpts.

Note that simulateInputSourceConnection and disconnect will be
implemented by future work.

Bug:979316
Change-Id: Idfb8bb631bfc1f1cb13b571a90b1a41b4a6fe518
  • Loading branch information
alcooper91 authored and chromium-wpt-export-bot committed Jul 10, 2019
1 parent e0a9e5e commit 5e49100
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 8 deletions.
108 changes: 108 additions & 0 deletions resources/chromium/webxr-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
// This polyfill library implements the WebXR Test API as specified here:
// https://github.com/immersive-web/webxr-test-api


let default_standing = new gfx.mojom.Transform();
default_standing.matrix = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 1.65, 0, 1];
const default_stage_parameters = {
standingTransform: default_standing,
sizeX: 0,
sizeZ: 0,
bounds: null
};

class ChromeXRTest {
constructor() {
this.mockVRService_ = new MockVRService(mojo.frameInterfaces);
Expand Down Expand Up @@ -147,6 +160,8 @@ class MockRuntime {

this.pose_ = null;
this.next_frame_id_ = 0;
this.bounds_ = null;
this.send_pose_reset_ = false;

this.service_ = service;

Expand All @@ -168,6 +183,13 @@ class MockRuntime {
this.setViewerOrigin(fakeDeviceInit.viewerOrigin);
}

if (fakeDeviceInit.localToFloorLevelTransform != null) {
this.setLocalToFloorLevelTransform(fakeDeviceInit.localToFloorLevelTransform);
}

// This appropriately handles if the coordinates are null
this.setBoundsGeometry(fakeDeviceInit.boundsCoordinates);

this.setViews(fakeDeviceInit.views);
}

Expand Down Expand Up @@ -210,7 +232,91 @@ class MockRuntime {
this.pose_ = null;
}

simulateVisibilityChange(visibilityState) {
// TODO(https://crbug.com/982099): Chrome currently does not have a way for
// devices to bubble up any form of visibilityChange.
}

setBoundsGeometry(bounds) {
if (bounds == null) {
this.bounds_ = null;
} else if (bounds.length < 3) {
throw new Error("Bounds must have a length of at least 3");
} else {
this.bounds_ = bounds;
}

// We can only set bounds if we have stageParameters set; otherwise, we
// don't know the transform from local space to bounds space.
// We'll cache the bounds so that they can be set in the future if the
// floorLevel transform is set, but we won't update them just yet.
if (this.displayInfo_.stageParameters) {
this.displayInfo_.stageParameters.bounds = this.bounds_;

if (this.sessionClient_.ptr.isBound()) {
this.sessionClient_.onChanged(this.displayInfo_);
}
}
}

setLocalToFloorLevelTransform(transform) {
if (!this.displayInfo_.stageParameters) {
this.displayInfo_.stageParameters = default_stage_parameters;
this.displayInfo_.stageParameters.bounds = this.bounds_;
}

this.displayInfo_.stageParameters.standingTransform = new gfx.mojom.Transform();
this.displayInfo_.stageParameters.standingTransform.matrix =
this.getMatrixFromTransform(transform);

if (this.sessionClient_.ptr.isBound()) {
this.sessionClient_.onChanged(this.displayInfo_);
}
}

clearLocalToFloorLevelTransform() {
if (this.displayInfo_.stageParameters) {
this.displayInfo_.stageParameters = null;

if (this.sessionClient_.ptr.isBound()) {
this.sessionClient_.onChanged(this.displayInfo_);
}
}
}

simulateResetPose() {
this.send_pose_reset_ = true;
}

// Helper methods
getMatrixFromTransform(transform) {
let x = transform.orientation[0];
let y = transform.orientation[1];
let z = transform.orientation[2];
let w = transform.orientation[3];

let m11 = 1.0 - 2.0 * (y * y + z * z);
let m21 = 2.0 * (x * y + z * w);
let m31 = 2.0 * (x * z - y * w);

let m12 = 2.0 * (x * y - z * w);
let m22 = 1.0 - 2.0 * (x * x + z * z);
let m32 = 2.0 * (y * z + x * w);

let m13 = 2.0 * (x * z + y * w);
let m23 = 2.0 * (y * z - x * w);
let m33 = 1.0 - 2.0 * (x * x + y * y);

let m14 = transform.position[0];
let m24 = transform.position[1];
let m34 = transform.position[2];

// Column-major linearized order is expected.
return [m11, m21, m31, 0,
m12, m22, m32, 0,
m13, m23, m33, 0,
m14, m24, m34, 1];
}
getNonImmersiveDisplayInfo() {
let displayInfo = this.getImmersiveDisplayInfo();

Expand Down Expand Up @@ -297,6 +403,8 @@ class MockRuntime {
getFrameData() {
if (this.pose_) {
this.pose_.poseIndex++;
this.pose_.poseReset = this.send_pose_reset_;
this.send_pose_reset_ = false;
}

// Convert current document time to monotonic time.
Expand Down
43 changes: 43 additions & 0 deletions webxr/events_referenceSpace_reset.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants.js"></script>
<canvas id="webgl-canvas"></canvas>

<script>
let immersiveTestName = "XRSession resetpose from a device properly fires off " +
"the right events for immersive sessions";
let nonImmersiveTestName = "XRSession resetpose from a device properly fires off " +
"the right events for non-immersive sessions";

let watcherDone = new Event("watcherdone");

let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;

let testFunction = function(session, fakeDeviceController, t) {
let resetPromise = session.requestReferenceSpace('local')
.then((refSpace) => {
let eventWatcher = new EventWatcher(
t, refSpace, ["reset", "watcherdone"]);
refSpace.addEventListener("reset", (event) => {
assert_equals(event.referenceSpace, refSpace);
refSpace.dispatchEvent(watcherDone);
}, false);
return eventWatcher.wait_for(["reset", "watcherdone"]);
});

fakeDeviceController.simulateResetPose();

// The triggered resetPose event should arrive after the next Animation Frame
session.requestAnimationFrame(() => {});

return resetPromise;
};

xr_session_promise_test(
immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr');
xr_session_promise_test(
nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline');

</script>
25 changes: 17 additions & 8 deletions webxr/resources/webxr_test_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,25 @@ const VALID_POINTER_OFFSET = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1];
const VALID_GRIP_WITH_POINTER_OFFSET =
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 3, 3, 1];

const VALID_STAGE_TRANSFORM =
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.0, 1.65, -1.0, 1];
// A Valid Local to floor matrix/transform for when we don't care about specific
// values. Note that these should be identical, just different representations.
const VALID_LOCAL_TO_FLOOR_MATRIX = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
1, 1.65, -1, 1];

const VALID_LOCAL_TO_FLOOR_TRANSFORM = {
position: [1.0, 1.65, -1.0],
orientation: [0, 0, 0, 1]
};

const VALID_BOUNDS = [
{ x: 3.0, y: 0, z: -2.0 },
{ x: 3.5, y: 0, z: 0.0 },
{ x: 3.0, y: 0, z: 2.0 },
{ x: -3.0, y: 0, z: 2.0 },
{ x: -3.5, y: 0, z: 0.0 },
{ x: -3.0, y: 0, z: -2.0 }
{ x: 3.0, z: -2.0 },
{ x: 3.5, z: 0.0 },
{ x: 3.0, z: 2.0 },
{ x: -3.0, z: 2.0 },
{ x: -3.5, z: 0.0 },
{ x: -3.0, z: -2.0 }
];

const VALID_RESOLUTION = {
Expand Down
63 changes: 63 additions & 0 deletions webxr/xrBoundedReferenceSpace_updates.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants.js"></script>
<script src="resources/xr-test-asserts.js"></script>
<canvas></canvas>

<script>
let testName =
"'XRBoundedReferenceSpace updates properly when the changes are applied";

let fakeDeviceInitParams = {
supportsImmersive: true,
views: VALID_VIEWS,
viewerOrigin: IDENTITY_TRANSFORM,
localToFloorLevelTransform: VALID_LOCAL_TO_FLOOR_TRANSFORM
};

let testFunction = function(session, fakeDeviceController, t) {

return new Promise((resolve, reject) => {
session.requestReferenceSpace('bounded-floor')
.then((referenceSpace) => {
t.step(() => {
assert_unreached("Should not be able to get a bounded space until bounds set");
});
}).catch((err) => {
t.step(() => {
assert_equals(err.name, "NotSupportedError");
});

function onFrame(time, xrFrame) {
// After setting the bounds explicitly, we should be able to get a
// reference space
session.requestReferenceSpace('bounded-floor')
.then((referenceSpace) => {
t.step(() => {
assert_equals(referenceSpace.boundsGeometry.length, VALID_BOUNDS.length);
for (i = 0; i < VALID_BOUNDS.length; ++i) {
let valid_point = VALID_BOUNDS[i];
let bounds_point = referenceSpace.boundsGeometry[i];
assert_equals(valid_point.x, bounds_point.x);
assert_equals(bounds_point.y, 0.0);
assert_equals(valid_point.z, bounds_point.z);
assert_equals(bounds_point.w, 1.0);
}
});

resolve();
});
}

// Now set the bounds explicitly and check again on the next frame.
fakeDeviceController.setBoundsGeometry(VALID_BOUNDS);
session.requestAnimationFrame(onFrame);
});
});
};

xr_session_promise_test(testName, testFunction, fakeDeviceInitParams, 'immersive-vr');

</script>
70 changes: 70 additions & 0 deletions webxr/xrStationaryReferenceSpace_floorlevel_updates.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/webxr_util.js"></script>
<script src="resources/webxr_test_constants.js"></script>
<script src="resources/xr-test-asserts.js"></script>
<canvas></canvas>

<script>
let immersiveTestName = "'floor-level' XRStationaryReferenceSpace updates properly when " +
"the transform changes for immersive sessions";
let nonImmersiveTestName = "'floor-level' XRStationaryReferenceSpace updates properly when " +
"the transform changes for non-immersive sessions";

let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;

let testFunction = function(session, fakeDeviceController, t) {
// Don't need to request a frame/allow the stage updates to propagate before
// requesting local-floor because if the stage transform is set it just won't
// be emulated on the first frame, and we wait a frame before checking.
return session.requestReferenceSpace('local-floor')
.then((referenceSpace) => new Promise((resolve, reject) => {
function onFirstFrame(time, xrFrame) {
// On the first frame where the pose has been initialized, the stage
// should be using an emulated frame of reference because it has no
// stageParameters yet. So the pose should be ~1.5 meters off the floor.
t.step( () => {
let pose = xrFrame.getViewerPose(referenceSpace);

let poseMatrix = pose.transform.matrix;
assert_approx_equals(poseMatrix[12], 0.0, FLOAT_EPSILON);
assert_greater_than(poseMatrix[13], 1.0);
assert_approx_equals(poseMatrix[14], 0.0, FLOAT_EPSILON);

fakeDeviceController.setLocalToFloorLevelTransform(VALID_LOCAL_TO_FLOOR_TRANSFORM);

// Need to request one animation frame for the new stage transform to
// propagate before we check that it's what we expect.
session.requestAnimationFrame(() => {
session.requestAnimationFrame(onFrame);
});
});
}

function onFrame(time, xrFrame) {
t.step( () => {
// Check that stage transform was updated.
let pose = xrFrame.getViewerPose(referenceSpace);
assert_not_equals(pose, null);

let poseMatrix = pose.transform.matrix;
assert_matrix_approx_equals(poseMatrix, VALID_LOCAL_TO_FLOOR_MATRIX, FLOAT_EPSILON);
});

// Finished.
resolve();
}

// Need to wait one frame for the removal to propagate before we check that
// everything is at the expected emulated position.
session.requestAnimationFrame(() => {
session.requestAnimationFrame(onFirstFrame);
});
}));
};

xr_session_promise_test(immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr');
xr_session_promise_test(nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline');

</script>

0 comments on commit 5e49100

Please sign in to comment.