mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #23485 - Manishearth:xrtest, r=asajeffrey
Basic XR Testing support This adds support for the XRTest and FakeXRDeviceController APIs from https://github.com/immersive-web/webxr-test-api, and plugs them into the `mock` backend of rust-webvr. Tested with [a modified webxr test page](https://github.com/immersive-web/webxr-test-api) r? @jdm @asajeffrey <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23485) <!-- Reviewable:end -->
This commit is contained in:
commit
7c8a4ecead
18 changed files with 306 additions and 13 deletions
104
components/script/dom/fakexrdevicecontroller.rs
Normal file
104
components/script/dom/fakexrdevicecontroller.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::FakeXRDeviceControllerBinding::{
|
||||
self, FakeXRDeviceControllerMethods, FakeXRRigidTransform, FakeXRViewInit,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::XRViewBinding::XREye;
|
||||
use crate::dom::bindings::error::{Error, Fallible};
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use dom_struct::dom_struct;
|
||||
use webvr_traits::{MockVRControlMsg, WebVREyeParameters, WebVRMsg};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct FakeXRDeviceController {
|
||||
reflector: Reflector,
|
||||
}
|
||||
|
||||
impl FakeXRDeviceController {
|
||||
pub fn new_inherited() -> FakeXRDeviceController {
|
||||
FakeXRDeviceController {
|
||||
reflector: Reflector::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(global: &GlobalScope) -> DomRoot<FakeXRDeviceController> {
|
||||
reflect_dom_object(
|
||||
Box::new(FakeXRDeviceController::new_inherited()),
|
||||
global,
|
||||
FakeXRDeviceControllerBinding::Wrap,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn send_msg(&self, msg: MockVRControlMsg) {
|
||||
self.global()
|
||||
.as_window()
|
||||
.webvr_thread()
|
||||
.unwrap()
|
||||
.send(WebVRMsg::MessageMockDisplay(msg))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl FakeXRDeviceControllerMethods for FakeXRDeviceController {
|
||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||
fn SetViews(&self, views: Vec<FakeXRViewInit>) -> Fallible<()> {
|
||||
if views.len() != 2 {
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
|
||||
let (left, right) = match (views[0].eye, views[1].eye) {
|
||||
(XREye::Left, XREye::Right) => (&views[0], &views[1]),
|
||||
(XREye::Right, XREye::Left) => (&views[1], &views[0]),
|
||||
_ => return Err(Error::NotSupported),
|
||||
};
|
||||
|
||||
if left.projectionMatrix.len() != 16 ||
|
||||
right.projectionMatrix.len() != 16 ||
|
||||
left.viewOffset.position.len() != 4 ||
|
||||
right.viewOffset.position.len() != 4
|
||||
{
|
||||
return Err(Error::Type("Incorrectly sized array".into()));
|
||||
}
|
||||
|
||||
let mut proj_l = [0.; 16];
|
||||
let mut proj_r = [0.; 16];
|
||||
let v: Vec<_> = left.projectionMatrix.iter().map(|x| **x).collect();
|
||||
proj_l.copy_from_slice(&v);
|
||||
let v: Vec<_> = right.projectionMatrix.iter().map(|x| **x).collect();
|
||||
proj_r.copy_from_slice(&v);
|
||||
|
||||
let mut params_l = WebVREyeParameters::default();
|
||||
let mut params_r = WebVREyeParameters::default();
|
||||
let v: Vec<_> = left.viewOffset.position.iter().map(|x| **x).collect();
|
||||
params_l.offset.copy_from_slice(&v);
|
||||
let v: Vec<_> = right.viewOffset.position.iter().map(|x| **x).collect();
|
||||
params_r.offset.copy_from_slice(&v);
|
||||
|
||||
self.send_msg(MockVRControlMsg::SetProjectionMatrices(proj_l, proj_r));
|
||||
self.send_msg(MockVRControlMsg::SetEyeParameters(params_l, params_r));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||
fn SetViewerOrigin(&self, origin: &FakeXRRigidTransform) -> Fallible<()> {
|
||||
if origin.position.len() != 4 || origin.orientation.len() != 4 {
|
||||
return Err(Error::Type("Incorrectly sized array".into()));
|
||||
}
|
||||
let mut position = [0.; 3];
|
||||
let mut orientation = [0.; 4];
|
||||
let v: Vec<_> = origin.position.iter().map(|x| **x).collect();
|
||||
position.copy_from_slice(&v[0..3]);
|
||||
let v: Vec<_> = origin.orientation.iter().map(|x| **x).collect();
|
||||
orientation.copy_from_slice(&v);
|
||||
self.send_msg(MockVRControlMsg::SetViewerPose(position, orientation));
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -301,6 +301,7 @@ pub mod eventsource;
|
|||
pub mod eventtarget;
|
||||
pub mod extendableevent;
|
||||
pub mod extendablemessageevent;
|
||||
pub mod fakexrdevicecontroller;
|
||||
pub mod file;
|
||||
pub mod filelist;
|
||||
pub mod filereader;
|
||||
|
@ -551,6 +552,7 @@ pub mod xrrigidtransform;
|
|||
pub mod xrsession;
|
||||
pub mod xrspace;
|
||||
pub mod xrstationaryreferencespace;
|
||||
pub mod xrtest;
|
||||
pub mod xrview;
|
||||
pub mod xrviewerpose;
|
||||
pub mod xrviewport;
|
||||
|
|
49
components/script/dom/webidls/FakeXRDeviceController.webidl
Normal file
49
components/script/dom/webidls/FakeXRDeviceController.webidl
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://github.com/immersive-web/webxr-test-api/
|
||||
|
||||
[Exposed=Window, Pref="dom.webxr.test"]
|
||||
interface FakeXRDeviceController {
|
||||
// Creates and attaches a XRFrameOfReference of the type specified to the device.
|
||||
// void setFrameOfReference(XRFrameOfReferenceType, FakeXRFrameOfReferenceInit);
|
||||
|
||||
// // Sets the values to be used for subsequent
|
||||
// // requestAnimationFrame() callbacks.
|
||||
[Throws] void setViews(sequence<FakeXRViewInit> views);
|
||||
|
||||
[Throws] void setViewerOrigin(FakeXRRigidTransform origin);
|
||||
|
||||
// Simulates the user activating the reset pose on a device.
|
||||
// void simulateResetPose();
|
||||
|
||||
// Simulates the platform ending the sessions.
|
||||
// void simulateForcedEndSessions();
|
||||
|
||||
// Simulates devices focusing and blurring sessions.
|
||||
// void simulateBlurSession(XRSession);
|
||||
// void simulateFocusSession(XRSession);
|
||||
|
||||
// void setBoundsGeometry(Array<FakeXRBoundsPoint> boundsCoodinates)l
|
||||
|
||||
// Promise<FakeXRInputSourceController>
|
||||
// simulateInputSourceConnection(FakeXRInputSourceInit);
|
||||
};
|
||||
|
||||
dictionary FakeXRViewInit {
|
||||
required XREye eye;
|
||||
// https://immersive-web.github.io/webxr/#view-projection-matrix
|
||||
required sequence<float> projectionMatrix;
|
||||
// https://immersive-web.github.io/webxr/#view-offset
|
||||
required FakeXRRigidTransform viewOffset;
|
||||
};
|
||||
|
||||
dictionary FakeXRBoundsPoint {
|
||||
double x; double z;
|
||||
};
|
||||
|
||||
dictionary FakeXRRigidTransform {
|
||||
required sequence<float> position;
|
||||
required sequence<float> orientation;
|
||||
};
|
|
@ -28,3 +28,8 @@ dictionary XRSessionCreationOptions {
|
|||
XRSessionMode mode = "inline";
|
||||
// XRPresentationContext outputContext;
|
||||
};
|
||||
|
||||
partial interface XR {
|
||||
// https://github.com/immersive-web/webxr-test-api/
|
||||
[SameObject, Pref="dom.webxr.test"] readonly attribute XRTest test;
|
||||
};
|
||||
|
|
25
components/script/dom/webidls/XRTest.webidl
Normal file
25
components/script/dom/webidls/XRTest.webidl
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://github.com/immersive-web/webxr-test-api/
|
||||
|
||||
[Exposed=Window, Pref="dom.webxr.test"]
|
||||
interface XRTest {
|
||||
// Simulates connecting a device to the system.
|
||||
// Used to instantiate a fake device for use in tests.
|
||||
Promise<FakeXRDeviceController> simulateDeviceConnection(FakeXRDeviceInit init);
|
||||
|
||||
// // Simulates a device being disconnected from the system.
|
||||
// Promise<void> simulateDeviceDisconnection(XRDevice);
|
||||
|
||||
// // Simulates a user activation (aka user gesture) for the current scope.
|
||||
// // The activation is only guaranteed to be valid in the provided function and only applies to WebXR
|
||||
// // Device API methods.
|
||||
// void simulateUserActivation(Function);
|
||||
};
|
||||
|
||||
dictionary FakeXRDeviceInit {
|
||||
// TODO: Subject to change to match spec changes.
|
||||
required boolean supportsImmersive;
|
||||
};
|
|
@ -21,6 +21,7 @@ use crate::dom::promise::Promise;
|
|||
use crate::dom::vrdisplay::VRDisplay;
|
||||
use crate::dom::vrdisplayevent::VRDisplayEvent;
|
||||
use crate::dom::xrsession::XRSession;
|
||||
use crate::dom::xrtest::XRTest;
|
||||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use profile_traits::ipc;
|
||||
|
@ -36,6 +37,7 @@ pub struct XR {
|
|||
gamepads: DomRefCell<Vec<Dom<Gamepad>>>,
|
||||
pending_immersive_session: Cell<bool>,
|
||||
active_immersive_session: MutNullableDom<VRDisplay>,
|
||||
test: MutNullableDom<XRTest>,
|
||||
}
|
||||
|
||||
impl XR {
|
||||
|
@ -46,6 +48,7 @@ impl XR {
|
|||
gamepads: DomRefCell::new(Vec::new()),
|
||||
pending_immersive_session: Cell::new(false),
|
||||
active_immersive_session: Default::default(),
|
||||
test: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +144,11 @@ impl XRMethods for XR {
|
|||
session.xr_present(promise.clone());
|
||||
promise
|
||||
}
|
||||
|
||||
// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||
fn Test(&self) -> DomRoot<XRTest> {
|
||||
self.test.or_init(|| XRTest::new(&self.global()))
|
||||
}
|
||||
}
|
||||
|
||||
impl XR {
|
||||
|
|
|
@ -99,7 +99,8 @@ impl XRSpace {
|
|||
orient[1] as f64,
|
||||
orient[2] as f64,
|
||||
orient[3] as f64,
|
||||
);
|
||||
)
|
||||
.normalize();
|
||||
RigidTransform3D::new(rotation, translation)
|
||||
}
|
||||
|
||||
|
|
66
components/script/dom/xrtest.rs
Normal file
66
components/script/dom/xrtest.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::XRTestBinding::{
|
||||
self, FakeXRDeviceInit, XRTestMethods,
|
||||
};
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::fakexrdevicecontroller::FakeXRDeviceController;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::promise::Promise;
|
||||
use dom_struct::dom_struct;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use webvr_traits::WebVRMsg;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct XRTest {
|
||||
reflector: Reflector,
|
||||
session_started: Cell<bool>,
|
||||
}
|
||||
|
||||
impl XRTest {
|
||||
pub fn new_inherited() -> XRTest {
|
||||
XRTest {
|
||||
reflector: Reflector::new(),
|
||||
session_started: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(global: &GlobalScope) -> DomRoot<XRTest> {
|
||||
reflect_dom_object(
|
||||
Box::new(XRTest::new_inherited()),
|
||||
global,
|
||||
XRTestBinding::Wrap,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl XRTestMethods for XRTest {
|
||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||
fn SimulateDeviceConnection(&self, init: &FakeXRDeviceInit) -> Rc<Promise> {
|
||||
let p = Promise::new(&self.global());
|
||||
|
||||
if !init.supportsImmersive || self.session_started.get() {
|
||||
p.reject_native(&());
|
||||
return p;
|
||||
}
|
||||
|
||||
self.session_started.set(true);
|
||||
self.global()
|
||||
.as_window()
|
||||
.webvr_thread()
|
||||
.unwrap()
|
||||
.send(WebVRMsg::CreateMockDisplay)
|
||||
.unwrap();
|
||||
p.resolve_native(&FakeXRDeviceController::new(&self.global()));
|
||||
|
||||
p
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue