Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update FakeXRDeviceInterface to match webxr-test-api #17740

Merged
merged 1 commit into from
Jul 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>