mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #25259 - Manishearth:wpt-fixes, r=asajeffrey
Various webxr WPT fixes Needs https://github.com/servo/webxr/pull/108 r? @asajeffrey Went through most of the failing tests and fixed them. Many of the remaining ones fail due to unsupported features that I can slowly whittle away.
This commit is contained in:
commit
748edb2cd9
24 changed files with 212 additions and 180 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -6389,7 +6389,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webxr"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/webxr#29f4f1f28695553c45d6c020172048c0035c49ed"
|
||||
source = "git+https://github.com/servo/webxr#b79870164f518d90268aaaac1fb83059b83dceda"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"euclid",
|
||||
|
@ -6401,6 +6401,7 @@ dependencies = [
|
|||
"serde",
|
||||
"surfman",
|
||||
"surfman-chains",
|
||||
"time",
|
||||
"webxr-api",
|
||||
"winapi",
|
||||
"wio",
|
||||
|
@ -6409,7 +6410,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webxr-api"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/webxr#29f4f1f28695553c45d6c020172048c0035c49ed"
|
||||
source = "git+https://github.com/servo/webxr#b79870164f518d90268aaaac1fb83059b83dceda"
|
||||
dependencies = [
|
||||
"euclid",
|
||||
"ipc-channel",
|
||||
|
|
|
@ -20,7 +20,7 @@ use ipc_channel::ipc::IpcSender;
|
|||
use ipc_channel::router::ROUTER;
|
||||
use profile_traits::ipc;
|
||||
use std::rc::Rc;
|
||||
use webxr_api::{MockDeviceMsg, View, Views};
|
||||
use webxr_api::{MockDeviceMsg, MockViewInit, MockViewsInit};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct FakeXRDevice {
|
||||
|
@ -50,58 +50,57 @@ impl FakeXRDevice {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<Views> {
|
||||
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() != 3 ||
|
||||
right.viewOffset.position.len() != 3
|
||||
{
|
||||
pub fn view<Eye>(view: &FakeXRViewInit) -> Fallible<MockViewInit<Eye>> {
|
||||
if view.projectionMatrix.len() != 16 || view.viewOffset.position.len() != 3 {
|
||||
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 proj_l = Transform3D::from_array(proj_l);
|
||||
let v: Vec<_> = right.projectionMatrix.iter().map(|x| **x).collect();
|
||||
proj_r.copy_from_slice(&v);
|
||||
let proj_r = Transform3D::from_array(proj_r);
|
||||
let mut proj = [0.; 16];
|
||||
let v: Vec<_> = view.projectionMatrix.iter().map(|x| **x).collect();
|
||||
proj.copy_from_slice(&v);
|
||||
let projection = Transform3D::from_array(proj);
|
||||
|
||||
// spec defines offsets as origins, but mock API expects the inverse transform
|
||||
let offset_l = get_origin(&left.viewOffset)?.inverse();
|
||||
let offset_r = get_origin(&right.viewOffset)?.inverse();
|
||||
let transform = get_origin(&view.viewOffset)?.inverse();
|
||||
|
||||
let size_l = Size2D::new(views[0].resolution.width, views[0].resolution.height);
|
||||
let size_r = Size2D::new(views[1].resolution.width, views[1].resolution.height);
|
||||
|
||||
let origin_l = Point2D::new(0, 0);
|
||||
let origin_r = Point2D::new(size_l.width, 0);
|
||||
|
||||
let viewport_l = Rect::new(origin_l, size_l);
|
||||
let viewport_r = Rect::new(origin_r, size_r);
|
||||
|
||||
let left = View {
|
||||
projection: proj_l,
|
||||
transform: offset_l,
|
||||
viewport: viewport_l,
|
||||
let size = Size2D::new(view.resolution.width, view.resolution.height);
|
||||
let origin = match view.eye {
|
||||
XREye::Right => Point2D::new(size.width, 0),
|
||||
_ => Point2D::new(0, 0),
|
||||
};
|
||||
let right = View {
|
||||
projection: proj_r,
|
||||
transform: offset_r,
|
||||
viewport: viewport_r,
|
||||
let viewport = Rect::new(origin, size);
|
||||
|
||||
let fov = if let Some(ref fov) = view.fieldOfView {
|
||||
Some((
|
||||
fov.leftDegrees.to_radians(),
|
||||
fov.rightDegrees.to_radians(),
|
||||
fov.upDegrees.to_radians(),
|
||||
fov.downDegrees.to_radians(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Views::Stereo(left, right))
|
||||
|
||||
Ok(MockViewInit {
|
||||
projection,
|
||||
transform,
|
||||
viewport,
|
||||
fov,
|
||||
})
|
||||
}
|
||||
pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<MockViewsInit> {
|
||||
match views.len() {
|
||||
1 => Ok(MockViewsInit::Mono(view(&views[0])?)),
|
||||
2 => {
|
||||
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),
|
||||
};
|
||||
Ok(MockViewsInit::Stereo(view(left)?, view(right)?))
|
||||
},
|
||||
_ => Err(Error::NotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_origin<T, U>(
|
||||
|
@ -134,7 +133,7 @@ impl FakeXRDeviceMethods for FakeXRDevice {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-setviewerorigin
|
||||
fn SetViewerOrigin(
|
||||
&self,
|
||||
origin: &FakeXRRigidTransformInit,
|
||||
|
@ -142,7 +141,25 @@ impl FakeXRDeviceMethods for FakeXRDevice {
|
|||
) -> Fallible<()> {
|
||||
let _ = self
|
||||
.sender
|
||||
.send(MockDeviceMsg::SetViewerOrigin(get_origin(origin)?));
|
||||
.send(MockDeviceMsg::SetViewerOrigin(Some(get_origin(origin)?)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-clearviewerorigin
|
||||
fn ClearViewerOrigin(&self) {
|
||||
let _ = self.sender.send(MockDeviceMsg::SetViewerOrigin(None));
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-clearfloororigin
|
||||
fn ClearFloorOrigin(&self) {
|
||||
let _ = self.sender.send(MockDeviceMsg::SetFloorOrigin(None));
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-setfloororigin
|
||||
fn SetFloorOrigin(&self, origin: &FakeXRRigidTransformInit) -> Fallible<()> {
|
||||
let _ = self
|
||||
.sender
|
||||
.send(MockDeviceMsg::SetFloorOrigin(Some(get_origin(origin)?)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,11 @@ interface FakeXRDevice {
|
|||
// // behaves as if device was disconnected
|
||||
// Promise<void> disconnect();
|
||||
|
||||
// Sets the origin of the viewer
|
||||
[Throws] void setViewerOrigin(FakeXRRigidTransformInit origin, optional boolean emulatedPosition = false);
|
||||
void clearViewerOrigin();
|
||||
|
||||
[Throws] void setFloorOrigin(FakeXRRigidTransformInit origin);
|
||||
void clearFloorOrigin();
|
||||
|
||||
// // Simulates devices focusing and blurring sessions.
|
||||
// void simulateVisibilityChange(XRVisibilityState);
|
||||
|
@ -40,6 +43,8 @@ dictionary FakeXRViewInit {
|
|||
required FakeXRRigidTransformInit viewOffset;
|
||||
// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
|
||||
required FakeXRDeviceResolution resolution;
|
||||
|
||||
FakeXRFieldOfViewInit fieldOfView;
|
||||
};
|
||||
|
||||
// https://immersive-web.github.io/webxr/#xrviewport
|
||||
|
@ -56,3 +61,10 @@ dictionary FakeXRRigidTransformInit {
|
|||
required sequence<float> position;
|
||||
required sequence<float> orientation;
|
||||
};
|
||||
|
||||
dictionary FakeXRFieldOfViewInit {
|
||||
required float upDegrees;
|
||||
required float downDegrees;
|
||||
required float leftDegrees;
|
||||
required float rightDegrees;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,6 @@ dictionary XRRenderStateInit {
|
|||
[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"] interface XRRenderState {
|
||||
readonly attribute double depthNear;
|
||||
readonly attribute double depthFar;
|
||||
readonly attribute double inlineVerticalFieldOfView;
|
||||
readonly attribute double? inlineVerticalFieldOfView;
|
||||
readonly attribute XRWebGLLayer? baseLayer;
|
||||
};
|
||||
|
|
|
@ -162,12 +162,12 @@ impl XRMethods for XR {
|
|||
) -> Rc<Promise> {
|
||||
let promise = Promise::new_in_current_compartment(&self.global(), comp);
|
||||
|
||||
if !ScriptThread::is_user_interacting() {
|
||||
promise.reject_error(Error::Security);
|
||||
return promise;
|
||||
}
|
||||
|
||||
if mode != XRSessionMode::Inline {
|
||||
if !ScriptThread::is_user_interacting() {
|
||||
promise.reject_error(Error::Security);
|
||||
return promise;
|
||||
}
|
||||
|
||||
if self.pending_or_active_session() {
|
||||
promise.reject_error(Error::InvalidState);
|
||||
return promise;
|
||||
|
|
|
@ -77,7 +77,11 @@ impl XRFrameMethods for XRFrame {
|
|||
return Err(Error::InvalidState);
|
||||
}
|
||||
|
||||
let pose = reference.get_viewer_pose(&self.data);
|
||||
let pose = if let Some(pose) = reference.get_viewer_pose(&self.data) {
|
||||
pose
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(XRViewerPose::new(&self.global(), &self.session, pose)))
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
|||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::xrrigidtransform::XRRigidTransform;
|
||||
use crate::dom::xrsession::{cast_transform, ApiPose, ApiRigidTransform, ApiViewerPose, XRSession};
|
||||
use crate::dom::xrsession::{cast_transform, ApiPose, ApiViewerPose, XRSession};
|
||||
use crate::dom::xrspace::XRSpace;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::{RigidTransform3D, Vector3D};
|
||||
use euclid::RigidTransform3D;
|
||||
use webxr_api::Frame;
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -80,8 +80,8 @@ impl XRReferenceSpace {
|
|||
///
|
||||
/// This is equivalent to `get_pose(self).inverse() * get_pose(viewerSpace)` (in column vector notation),
|
||||
/// however we specialize it to be efficient
|
||||
pub fn get_viewer_pose(&self, base_pose: &Frame) -> ApiViewerPose {
|
||||
let pose = self.get_unoffset_viewer_pose(base_pose);
|
||||
pub fn get_viewer_pose(&self, base_pose: &Frame) -> Option<ApiViewerPose> {
|
||||
let pose = self.get_unoffset_viewer_pose(base_pose)?;
|
||||
// in column-vector notation,
|
||||
// get_viewer_pose(space) = get_pose(space).inverse() * get_pose(viewer_space)
|
||||
// = (get_unoffset_pose(space) * offset).inverse() * get_pose(viewer_space)
|
||||
|
@ -89,14 +89,13 @@ impl XRReferenceSpace {
|
|||
// = offset.inverse() * get_unoffset_viewer_pose(space)
|
||||
let offset = self.offset.transform();
|
||||
let inverse = offset.inverse();
|
||||
inverse.pre_transform(&pose)
|
||||
Some(inverse.pre_transform(&pose))
|
||||
}
|
||||
|
||||
/// Gets pose of the viewer with respect to this space
|
||||
///
|
||||
/// Does not apply originOffset, use get_viewer_pose instead if you need it
|
||||
pub fn get_unoffset_viewer_pose(&self, base_pose: &Frame) -> ApiViewerPose {
|
||||
let viewer_pose: ApiViewerPose = cast_transform(base_pose.transform);
|
||||
pub fn get_unoffset_viewer_pose(&self, base_pose: &Frame) -> Option<ApiViewerPose> {
|
||||
// all math is in column-vector notation
|
||||
// we use the following equation to verify correctness here:
|
||||
// get_viewer_pose(space) = get_pose(space).inverse() * get_pose(viewer_space)
|
||||
|
@ -105,25 +104,27 @@ impl XRReferenceSpace {
|
|||
// get_viewer_pose(eye_level) = get_pose(eye_level).inverse() * get_pose(viewer_space)
|
||||
// = I * viewer_pose
|
||||
// = viewer_pose
|
||||
let viewer_pose: ApiViewerPose = cast_transform(base_pose.transform?);
|
||||
|
||||
// we get viewer poses in eye-level space by default
|
||||
viewer_pose
|
||||
Some(viewer_pose)
|
||||
},
|
||||
XRReferenceSpaceType::Local_floor => {
|
||||
// XXXManishearth support getting floor info from stage parameters
|
||||
|
||||
// get_viewer_pose(floor_level) = get_pose(floor_level).inverse() * get_pose(viewer_space)
|
||||
// = Translate(-2).inverse() * viewer_pose
|
||||
// = Translate(2) * viewer_pose
|
||||
// = floor_to_native.inverse() * viewer_pose
|
||||
// = native_to_floor * viewer_pose
|
||||
let viewer_pose = base_pose.transform?;
|
||||
let native_to_floor = self
|
||||
.upcast::<XRSpace>()
|
||||
.session()
|
||||
.with_session(|s| s.floor_transform())?;
|
||||
|
||||
// assume approximate user height of 2 meters
|
||||
let floor_to_eye: ApiRigidTransform = Vector3D::new(0., 2., 0.).into();
|
||||
floor_to_eye.pre_transform(&viewer_pose)
|
||||
Some(cast_transform(native_to_floor.pre_transform(&viewer_pose)))
|
||||
},
|
||||
XRReferenceSpaceType::Viewer => {
|
||||
// This reference space follows the viewer around, so the viewer is
|
||||
// always at an identity transform with respect to it
|
||||
RigidTransform3D::identity()
|
||||
Some(RigidTransform3D::identity())
|
||||
},
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
@ -134,34 +135,34 @@ impl XRReferenceSpace {
|
|||
/// The reference origin used is common between all
|
||||
/// get_pose calls for spaces from the same device, so this can be used to compare
|
||||
/// with other spaces
|
||||
pub fn get_pose(&self, base_pose: &Frame) -> ApiPose {
|
||||
let pose = self.get_unoffset_pose(base_pose);
|
||||
pub fn get_pose(&self, base_pose: &Frame) -> Option<ApiPose> {
|
||||
let pose = self.get_unoffset_pose(base_pose)?;
|
||||
let offset = self.offset.transform();
|
||||
// pose is a transform from the unoffset space to native space,
|
||||
// offset is a transform from offset space to unoffset space,
|
||||
// we want a transform from unoffset space to native space,
|
||||
// which is pose * offset in column vector notation
|
||||
pose.pre_transform(&offset)
|
||||
Some(pose.pre_transform(&offset))
|
||||
}
|
||||
|
||||
/// Gets pose represented by this space
|
||||
///
|
||||
/// Does not apply originOffset, use get_viewer_pose instead if you need it
|
||||
pub fn get_unoffset_pose(&self, base_pose: &Frame) -> ApiPose {
|
||||
pub fn get_unoffset_pose(&self, base_pose: &Frame) -> Option<ApiPose> {
|
||||
match self.ty {
|
||||
XRReferenceSpaceType::Local => {
|
||||
// The eye-level pose is basically whatever the headset pose was at t=0, which
|
||||
// for most devices is (0, 0, 0)
|
||||
RigidTransform3D::identity()
|
||||
Some(RigidTransform3D::identity())
|
||||
},
|
||||
XRReferenceSpaceType::Local_floor => {
|
||||
// XXXManishearth support getting floor info from stage parameters
|
||||
|
||||
// Assume approximate height of 2m
|
||||
// the floor-level space is 2m below the eye-level space, which is (0, 0, 0)
|
||||
Vector3D::new(0., -2., 0.).into()
|
||||
let native_to_floor = self
|
||||
.upcast::<XRSpace>()
|
||||
.session()
|
||||
.with_session(|s| s.floor_transform())?;
|
||||
Some(cast_transform(native_to_floor.inverse()))
|
||||
},
|
||||
XRReferenceSpaceType::Viewer => cast_transform(base_pose.transform),
|
||||
XRReferenceSpaceType::Viewer => base_pose.transform.map(cast_transform),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct XRRenderState {
|
|||
reflector_: Reflector,
|
||||
depth_near: Cell<f64>,
|
||||
depth_far: Cell<f64>,
|
||||
inline_vertical_fov: Cell<f64>,
|
||||
inline_vertical_fov: Cell<Option<f64>>,
|
||||
layer: MutNullableDom<XRWebGLLayer>,
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ impl XRRenderState {
|
|||
pub fn new_inherited(
|
||||
depth_near: f64,
|
||||
depth_far: f64,
|
||||
inline_vertical_fov: f64,
|
||||
inline_vertical_fov: Option<f64>,
|
||||
layer: Option<&XRWebGLLayer>,
|
||||
) -> XRRenderState {
|
||||
XRRenderState {
|
||||
|
@ -41,7 +41,7 @@ impl XRRenderState {
|
|||
global: &GlobalScope,
|
||||
depth_near: f64,
|
||||
depth_far: f64,
|
||||
inline_vertical_fov: f64,
|
||||
inline_vertical_fov: Option<f64>,
|
||||
layer: Option<&XRWebGLLayer>,
|
||||
) -> DomRoot<XRRenderState> {
|
||||
reflect_dom_object(
|
||||
|
@ -73,7 +73,8 @@ impl XRRenderState {
|
|||
self.depth_far.set(depth)
|
||||
}
|
||||
pub fn set_inline_vertical_fov(&self, fov: f64) {
|
||||
self.inline_vertical_fov.set(fov)
|
||||
debug_assert!(self.inline_vertical_fov.get().is_some());
|
||||
self.inline_vertical_fov.set(Some(fov))
|
||||
}
|
||||
pub fn set_layer(&self, layer: Option<&XRWebGLLayer>) {
|
||||
self.layer.set(layer)
|
||||
|
@ -92,8 +93,8 @@ impl XRRenderStateMethods for XRRenderState {
|
|||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrrenderstate-inlineverticalfieldofview
|
||||
fn InlineVerticalFieldOfView(&self) -> Finite<f64> {
|
||||
Finite::wrap(self.inline_vertical_fov.get())
|
||||
fn GetInlineVerticalFieldOfView(&self) -> Option<Finite<f64>> {
|
||||
self.inline_vertical_fov.get().map(Finite::wrap)
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrrenderstate-baselayer
|
||||
|
|
|
@ -43,8 +43,10 @@ use dom_struct::dom_struct;
|
|||
use euclid::{Rect, RigidTransform3D, Transform3D};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use metrics::ToMs;
|
||||
use profile_traits::ipc;
|
||||
use std::cell::Cell;
|
||||
use std::f64::consts::{FRAC_PI_2, PI};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use webxr_api::{
|
||||
|
@ -72,7 +74,7 @@ pub struct XRSession {
|
|||
#[ignore_malloc_size_of = "closures are hard"]
|
||||
raf_callback_list: DomRefCell<Vec<(i32, Option<Rc<XRFrameRequestCallback>>)>>,
|
||||
#[ignore_malloc_size_of = "defined in ipc-channel"]
|
||||
raf_sender: DomRefCell<Option<IpcSender<(f64, Frame)>>>,
|
||||
raf_sender: DomRefCell<Option<IpcSender<Frame>>>,
|
||||
input_sources: Dom<XRInputSourceArray>,
|
||||
// Any promises from calling end()
|
||||
#[ignore_malloc_size_of = "promises are hard"]
|
||||
|
@ -115,8 +117,12 @@ impl XRSession {
|
|||
}
|
||||
|
||||
pub fn new(global: &GlobalScope, session: Session, mode: XRSessionMode) -> DomRoot<XRSession> {
|
||||
use std::f64::consts::FRAC_PI_2;
|
||||
let render_state = XRRenderState::new(global, 0.1, 1000.0, FRAC_PI_2, None);
|
||||
let ivfov = if mode == XRSessionMode::Inline {
|
||||
Some(FRAC_PI_2)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let render_state = XRRenderState::new(global, 0.1, 1000.0, ivfov, None);
|
||||
let input_sources = XRInputSourceArray::new(global);
|
||||
let ret = reflect_dom_object(
|
||||
Box::new(XRSession::new_inherited(
|
||||
|
@ -300,7 +306,7 @@ impl XRSession {
|
|||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#xr-animation-frame
|
||||
fn raf_callback(&self, (time, mut frame): (f64, Frame)) {
|
||||
fn raf_callback(&self, mut frame: Frame) {
|
||||
debug!("WebXR RAF callback");
|
||||
|
||||
// Step 1
|
||||
|
@ -333,6 +339,8 @@ impl XRSession {
|
|||
|
||||
// Step 4-5
|
||||
let mut callbacks = mem::replace(&mut *self.raf_callback_list.borrow_mut(), vec![]);
|
||||
let start = self.global().as_window().get_navigation_start();
|
||||
let time = (frame.time_ns - start).to_ms();
|
||||
|
||||
let frame = XRFrame::new(&self.global(), self, frame);
|
||||
// Step 6,7
|
||||
|
@ -376,7 +384,10 @@ impl XRSession {
|
|||
let near = *render_state.DepthNear() as f32;
|
||||
let far = *render_state.DepthFar() as f32;
|
||||
clip_planes.update(near, far);
|
||||
let top = *render_state.InlineVerticalFieldOfView() / 2.;
|
||||
let top = *render_state
|
||||
.GetInlineVerticalFieldOfView()
|
||||
.expect("IVFOV should be non null for inline sessions") /
|
||||
2.;
|
||||
let top = near * top.tan() as f32;
|
||||
let bottom = top;
|
||||
let left = top * size.width as f32 / size.height as f32;
|
||||
|
@ -451,7 +462,7 @@ impl XRSessionMethods for XRSession {
|
|||
}
|
||||
|
||||
// Step 4:
|
||||
if init.inlineVerticalFieldOfView.is_some() {
|
||||
if init.inlineVerticalFieldOfView.is_some() && self.is_immersive() {
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
|
||||
|
@ -459,13 +470,33 @@ impl XRSessionMethods for XRSession {
|
|||
.pending_render_state
|
||||
.or_init(|| self.active_render_state.get().clone_object());
|
||||
if let Some(near) = init.depthNear {
|
||||
pending.set_depth_near(*near);
|
||||
let mut near = *near;
|
||||
// Step 8 from #apply-the-pending-render-state
|
||||
// this may need to be changed if backends wish to impose
|
||||
// further constraints
|
||||
if near < 0. {
|
||||
near = 0.;
|
||||
}
|
||||
pending.set_depth_near(near);
|
||||
}
|
||||
if let Some(far) = init.depthFar {
|
||||
// Step 9 from #apply-the-pending-render-state
|
||||
// this may need to be changed if backends wish to impose
|
||||
// further constraints
|
||||
// currently the maximum is infinity, so we do nothing
|
||||
pending.set_depth_far(*far);
|
||||
}
|
||||
if let Some(fov) = init.inlineVerticalFieldOfView {
|
||||
pending.set_inline_vertical_fov(*fov);
|
||||
let mut fov = *fov;
|
||||
// Step 10 from #apply-the-pending-render-state
|
||||
// this may need to be changed if backends wish to impose
|
||||
// further constraints
|
||||
if fov < 0. {
|
||||
fov = 0.0001;
|
||||
} else if fov > PI {
|
||||
fov = PI - 0.0001;
|
||||
}
|
||||
pending.set_inline_vertical_fov(fov);
|
||||
}
|
||||
if let Some(ref layer) = init.baseLayer {
|
||||
pending.set_layer(Some(&layer))
|
||||
|
|
|
@ -68,7 +68,7 @@ impl XRSpace {
|
|||
/// with other spaces
|
||||
pub fn get_pose(&self, base_pose: &Frame) -> Option<ApiPose> {
|
||||
if let Some(reference) = self.downcast::<XRReferenceSpace>() {
|
||||
Some(reference.get_pose(base_pose))
|
||||
reference.get_pose(base_pose)
|
||||
} else if let Some(source) = self.input_source.get() {
|
||||
// XXXManishearth we should be able to request frame information
|
||||
// for inputs when necessary instead of always loading it
|
||||
|
|
|
@ -21,7 +21,6 @@ use crate::dom::promise::Promise;
|
|||
use crate::script_thread::ScriptThread;
|
||||
use crate::task_source::TaskSource;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::RigidTransform3D;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use profile_traits::ipc;
|
||||
|
@ -75,26 +74,26 @@ impl XRTestMethods for XRTest {
|
|||
|
||||
let origin = if let Some(ref o) = init.viewerOrigin {
|
||||
match get_origin(&o) {
|
||||
Ok(origin) => origin,
|
||||
Ok(origin) => Some(origin),
|
||||
Err(e) => {
|
||||
p.reject_error(e);
|
||||
return p;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
RigidTransform3D::identity()
|
||||
None
|
||||
};
|
||||
|
||||
let floor_origin = if let Some(ref o) = init.floorOrigin {
|
||||
match get_origin(&o) {
|
||||
Ok(origin) => origin,
|
||||
Ok(origin) => Some(origin),
|
||||
Err(e) => {
|
||||
p.reject_error(e);
|
||||
return p;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
RigidTransform3D::identity()
|
||||
None
|
||||
};
|
||||
|
||||
let views = match get_views(&init.views) {
|
||||
|
|
|
@ -20,7 +20,7 @@ use crate::dom::xrview::XRView;
|
|||
use crate::dom::xrviewport::XRViewport;
|
||||
use canvas_traits::webgl::WebGLFramebufferId;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::Size2D;
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use std::convert::TryInto;
|
||||
use webxr_api::SwapChainId as WebXRSwapChainId;
|
||||
use webxr_api::{Viewport, Views};
|
||||
|
@ -211,11 +211,13 @@ impl XRWebGLLayerMethods for XRWebGLLayer {
|
|||
let views = self.session.with_session(|s| s.views().clone());
|
||||
|
||||
let viewport = match (view.Eye(), views) {
|
||||
(XREye::None, Views::Inline) => {
|
||||
let origin = Point2D::new(0, 0);
|
||||
Rect::new(origin, self.size().cast())
|
||||
},
|
||||
(XREye::None, Views::Mono(view)) => view.viewport,
|
||||
(XREye::Left, Views::Stereo(view, _)) => view.viewport,
|
||||
(XREye::Right, Views::Stereo(_, view)) => view.viewport,
|
||||
// The spec doesn't really say what to do in this case
|
||||
// https://github.com/immersive-web/webxr/issues/769
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
|
@ -727760,7 +727760,7 @@
|
|||
"testharness"
|
||||
],
|
||||
"webxr/xrDevice_requestSession_no_mode.https.html": [
|
||||
"de77b38c868e3b5dad88b8eface770b16b5f16c1",
|
||||
"5ef249c58186131f8e467da48cfafebe65df9adf",
|
||||
"testharness"
|
||||
],
|
||||
"webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html": [
|
||||
|
@ -727872,7 +727872,7 @@
|
|||
"testharness"
|
||||
],
|
||||
"webxr/xrSession_requestAnimationFrame_timestamp.https.html": [
|
||||
"2796be9987b5f23e92d5f114fdaa972a5323a37c",
|
||||
"8f5c21cba4015da10b9031f1cf100138e958c46e",
|
||||
"testharness"
|
||||
],
|
||||
"webxr/xrSession_requestReferenceSpace.https.html": [
|
||||
|
@ -727928,7 +727928,7 @@
|
|||
"testharness"
|
||||
],
|
||||
"webxr/xrWebGLLayer_constructor.https.html": [
|
||||
"0584da79c12def757f951ec53c4c048f18c39b8c",
|
||||
"dda7ec80310f7ff8aac582a98410886ab4d1b81d",
|
||||
"testharness"
|
||||
],
|
||||
"webxr/xrWebGLLayer_framebuffer_draw.https.html": [
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[render_state_vertical_fov_immersive.https.html]
|
||||
[inlineVerticalFieldOfView is set appropriately on immersively sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[render_state_vertical_fov_inline.https.html]
|
||||
[inlineVerticalFieldOfView is set appropriately on inline sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[xrDevice_requestSession_no_mode.https.html]
|
||||
[Requesting a session with no mode rejects]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[xrDevice_requestSession_non_immersive_no_gesture.https.html]
|
||||
[Requesting non-immersive session outside of a user gesture succeeds]
|
||||
expected: FAIL
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
[xrSession_requestAnimationFrame_getViewerPose.https.html]
|
||||
[XRFrame getViewerPose updates on the next frame for non-immersive sessions]
|
||||
expected: FAIL
|
||||
|
||||
[XRFrame getViewerPose updates on the next frame for immersive sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
[xrSession_requestAnimationFrame_timestamp.https.html]
|
||||
expected: TIMEOUT
|
||||
[XRFrame getViewerPose updates on the next frame for immersive]
|
||||
expected: FAIL
|
||||
|
||||
[XRFrame getViewerPose updates on the next frame for non-immersive]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
[xrWebGLLayer_constructor.https.html]
|
||||
expected: ERROR
|
||||
[Ensure that XRWebGLLayer's constructor throws appropriate errors]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[xrWebGLLayer_viewports.https.html]
|
||||
[XRWebGLLayer reports a valid viewports for inline sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -11,11 +11,10 @@
|
|||
return navigator.xr.test.simulateDeviceConnection(VALID_NON_IMMERSIVE_DEVICE)
|
||||
.then( (controller) => new Promise((resolve) => {
|
||||
navigator.xr.test.simulateUserActivation( () => {
|
||||
resolve(promise_rejects(
|
||||
t,
|
||||
new TypeError(),
|
||||
navigator.xr.requestSession()
|
||||
))
|
||||
t.step_func(() => {
|
||||
assert_throws(new TypeError(), () => navigator.xr.requestSession())
|
||||
})
|
||||
resolve()
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -20,25 +20,23 @@ let testFunction = function(session, fakeDeviceController, t) {
|
|||
let counter = 0;
|
||||
let windowFrameTime = 0;
|
||||
let frameTime = 0;
|
||||
let lastFrameTime = 0;
|
||||
let firstFrame = true;
|
||||
|
||||
function onFrameFirst(time, xrFrame) {
|
||||
lastFrameTime = frameTime;
|
||||
frameTime = time;
|
||||
firstFrame = false;
|
||||
let now = performance.now();
|
||||
|
||||
t.step( () => {
|
||||
// This callback must be the first one called.
|
||||
assert_equals(counter, 0);
|
||||
|
||||
// window.requestAnimationFrame and session.requestAnimationFrame
|
||||
// should be providing timestamps that are on the same scale and
|
||||
// within a resonable margin of error of eachother. This means that
|
||||
// this frame's timestamp should be larger than one provided to a
|
||||
// previous window.requestAnimationFrame and should also be within
|
||||
// a sane delta of it. One minute is probably overly generous here,
|
||||
// but it will at least catch cases where the times are reported with
|
||||
// entirely different bases.
|
||||
assert_greater_than(frameTime, windowFrameTime);
|
||||
assert_approx_equals(frameTime, windowFrameTime, ONE_MINUTE);
|
||||
if (!firstFrame) {
|
||||
assert_greater_than(frameTime, lastFrameTime);
|
||||
assert_approx_equals(frameTime, lastFrameTime, TEN_SECONDS);
|
||||
}
|
||||
|
||||
// There's going to be some disparity between performance.now() and
|
||||
// the timestamp passed into the callback, but it shouldn't be huge.
|
||||
|
@ -47,6 +45,12 @@ let testFunction = function(session, fakeDeviceController, t) {
|
|||
assert_approx_equals(frameTime, now, TEN_SECONDS);
|
||||
});
|
||||
|
||||
if (firstFrame) {
|
||||
session.requestAnimationFrame(onFrameFirst);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
@ -65,21 +69,14 @@ let testFunction = function(session, fakeDeviceController, t) {
|
|||
// Make sure all the previous callbacks fired as expected.
|
||||
assert_equals(counter, 11);
|
||||
});
|
||||
|
||||
// Finished.
|
||||
resolve();
|
||||
}
|
||||
|
||||
window.requestAnimationFrame((time) => {
|
||||
windowFrameTime = time;
|
||||
|
||||
// Queue up several callbacks
|
||||
session.requestAnimationFrame(onFrameFirst);
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
session.requestAnimationFrame(onFrameSubsequent);
|
||||
}
|
||||
session.requestAnimationFrame(onFrameLast);
|
||||
});
|
||||
session.requestAnimationFrame(onFrameFirst);
|
||||
// Queue up several callbacks
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
session.requestAnimationFrame(onFrameSubsequent);
|
||||
}
|
||||
session.requestAnimationFrame(onFrameLast);
|
||||
|
||||
}));
|
||||
};
|
||||
|
|
|
@ -31,12 +31,14 @@ xr_promise_test("Ensure that XRWebGLLayer's constructor throws appropriate error
|
|||
navigator.xr.test.simulateUserActivation(() => {
|
||||
navigator.xr.requestSession('immersive-vr')
|
||||
.then((session) => {
|
||||
try {
|
||||
let webglLayerIncompatible = new XRWebGLLayer(session, gl);
|
||||
assert_unreached("XRWebGLLayer should fail when created with a context that is not XRCompatible")
|
||||
} catch (err) {
|
||||
assert_equals(err.name, "InvalidStateError");
|
||||
}
|
||||
t.step_func(() => {
|
||||
try {
|
||||
let webglLayerIncompatible = new XRWebGLLayer(session, gl);
|
||||
assert_unreached("XRWebGLLayer should fail when created with a context that is not XRCompatible")
|
||||
} catch (err) {
|
||||
assert_equals(err.name, "InvalidStateError");
|
||||
}
|
||||
})
|
||||
|
||||
gl.makeXRCompatible();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue