mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Auto merge of #25144 - Manishearth:inline, r=asajeffrey
Support inline sessions (with spatial tracking!) This assumes that your WebXR backend can tolerate being spawned multiple times in inline mode. Currently there is only one backend that allows inline mode (headless), and it works there. This can be improved with https://github.com/servo/webxr/issues/30 . Todo: - [ ] Add a default inline device to webxr so that there is always a tracking-free inline session available (followup: https://github.com/servo/webxr/issues/101) - [x] WPT update - [ ] Make inline with spatial tracking a feature request (followup: https://github.com/servo/servo/issues/24270) fixes https://github.com/servo/servo/issues/24186 Depends on https://github.com/servo/webxr/pull/100
This commit is contained in:
commit
073194a618
25 changed files with 210 additions and 146 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -6391,7 +6391,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webxr"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/webxr#e44552df536a6f424d58ccd068aa0301fee5fa1e"
|
||||
source = "git+https://github.com/servo/webxr#29f4f1f28695553c45d6c020172048c0035c49ed"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"euclid",
|
||||
|
@ -6411,7 +6411,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webxr-api"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/webxr#e44552df536a6f424d58ccd068aa0301fee5fa1e"
|
||||
source = "git+https://github.com/servo/webxr#29f4f1f28695553c45d6c020172048c0035c49ed"
|
||||
dependencies = [
|
||||
"euclid",
|
||||
"ipc-channel",
|
||||
|
|
|
@ -61,7 +61,7 @@ use cssparser::RGBA;
|
|||
use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
|
||||
use embedder_traits::{EventLoopWaker, MediaMetadata};
|
||||
use encoding_rs::{Decoder, Encoding};
|
||||
use euclid::default::{Point2D, Rect, Rotation3D, Transform2D, Transform3D};
|
||||
use euclid::default::{Point2D, Rect, Rotation3D, Transform2D};
|
||||
use euclid::Length as EuclidLength;
|
||||
use html5ever::buffer_queue::BufferQueue;
|
||||
use html5ever::{LocalName, Namespace, Prefix, QualName};
|
||||
|
@ -534,7 +534,7 @@ unsafe_no_jsmanaged_fields!(Mutex<MediaFrameRenderer>);
|
|||
unsafe_no_jsmanaged_fields!(ResourceFetchTiming);
|
||||
unsafe_no_jsmanaged_fields!(Timespec);
|
||||
unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
|
||||
unsafe_no_jsmanaged_fields!(Rotation3D<f64>, Transform2D<f32>, Transform3D<f64>);
|
||||
unsafe_no_jsmanaged_fields!(Rotation3D<f64>, Transform2D<f32>);
|
||||
unsafe_no_jsmanaged_fields!(Point2D<f32>, Rect<Au>);
|
||||
unsafe_no_jsmanaged_fields!(Rect<f32>);
|
||||
unsafe_no_jsmanaged_fields!(CascadeData);
|
||||
|
@ -665,6 +665,20 @@ unsafe impl<T, U> JSTraceable for euclid::RigidTransform3D<f64, T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> JSTraceable for euclid::Transform3D<f32, T, U> {
|
||||
#[inline]
|
||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> JSTraceable for euclid::Transform3D<f64, T, U> {
|
||||
#[inline]
|
||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> JSTraceable for EuclidLength<u64, T> {
|
||||
#[inline]
|
||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
dictionary XRRenderStateInit {
|
||||
double depthNear;
|
||||
double depthFar;
|
||||
double inlineVerticalFieldOfView;
|
||||
XRWebGLLayer baseLayer;
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"] interface XRRenderState {
|
||||
readonly attribute double depthNear;
|
||||
readonly attribute double depthFar;
|
||||
readonly attribute double inlineVerticalFieldOfView;
|
||||
readonly attribute XRWebGLLayer? baseLayer;
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ interface XRWebGLLayer {
|
|||
readonly attribute boolean stencil;
|
||||
readonly attribute boolean alpha;
|
||||
|
||||
readonly attribute WebGLFramebuffer framebuffer;
|
||||
readonly attribute WebGLFramebuffer? framebuffer;
|
||||
readonly attribute unsigned long framebufferWidth;
|
||||
readonly attribute unsigned long framebufferHeight;
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ pub struct XR {
|
|||
gamepads: DomRefCell<Vec<Dom<Gamepad>>>,
|
||||
pending_immersive_session: Cell<bool>,
|
||||
active_immersive_session: MutNullableDom<XRSession>,
|
||||
active_inline_sessions: DomRefCell<Vec<Dom<XRSession>>>,
|
||||
test: MutNullableDom<XRTest>,
|
||||
}
|
||||
|
||||
|
@ -53,6 +54,7 @@ impl XR {
|
|||
gamepads: DomRefCell::new(Vec::new()),
|
||||
pending_immersive_session: Cell::new(false),
|
||||
active_immersive_session: Default::default(),
|
||||
active_inline_sessions: DomRefCell::new(Vec::new()),
|
||||
test: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +88,9 @@ impl XR {
|
|||
self.active_immersive_session.set(None);
|
||||
}
|
||||
}
|
||||
// XXXManishearth when we support inline sessions we should remove them too
|
||||
self.active_inline_sessions
|
||||
.borrow_mut()
|
||||
.retain(|sess| Dom::from_ref(&**sess) != Dom::from_ref(session));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,12 +167,14 @@ impl XRMethods for XR {
|
|||
return promise;
|
||||
}
|
||||
|
||||
if self.pending_or_active_session() {
|
||||
promise.reject_error(Error::InvalidState);
|
||||
return promise;
|
||||
}
|
||||
if mode != XRSessionMode::Inline {
|
||||
if self.pending_or_active_session() {
|
||||
promise.reject_error(Error::InvalidState);
|
||||
return promise;
|
||||
}
|
||||
|
||||
self.set_pending();
|
||||
self.set_pending();
|
||||
}
|
||||
|
||||
let promise = Promise::new_in_current_compartment(&self.global(), comp);
|
||||
let mut trusted = Some(TrustedPromise::new(promise.clone()));
|
||||
|
@ -193,7 +199,7 @@ impl XRMethods for XR {
|
|||
};
|
||||
let _ = task_source.queue_with_canceller(
|
||||
task!(request_session: move || {
|
||||
this.root().session_obtained(message, trusted.root());
|
||||
this.root().session_obtained(message, trusted.root(), mode);
|
||||
}),
|
||||
&canceller,
|
||||
);
|
||||
|
@ -211,7 +217,12 @@ impl XRMethods for XR {
|
|||
}
|
||||
|
||||
impl XR {
|
||||
fn session_obtained(&self, response: Result<Session, XRError>, promise: Rc<Promise>) {
|
||||
fn session_obtained(
|
||||
&self,
|
||||
response: Result<Session, XRError>,
|
||||
promise: Rc<Promise>,
|
||||
mode: XRSessionMode,
|
||||
) {
|
||||
let session = match response {
|
||||
Ok(session) => session,
|
||||
Err(_) => {
|
||||
|
@ -220,8 +231,14 @@ impl XR {
|
|||
},
|
||||
};
|
||||
|
||||
let session = XRSession::new(&self.global(), session);
|
||||
self.set_active_immersive_session(&session);
|
||||
let session = XRSession::new(&self.global(), session, mode);
|
||||
if mode == XRSessionMode::Inline {
|
||||
self.active_inline_sessions
|
||||
.borrow_mut()
|
||||
.push(Dom::from_ref(&*session));
|
||||
} else {
|
||||
self.set_active_immersive_session(&session);
|
||||
}
|
||||
promise.resolve_native(&session);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ pub struct XRRenderState {
|
|||
reflector_: Reflector,
|
||||
depth_near: Cell<f64>,
|
||||
depth_far: Cell<f64>,
|
||||
inline_vertical_fov: Cell<f64>,
|
||||
layer: MutNullableDom<XRWebGLLayer>,
|
||||
}
|
||||
|
||||
|
@ -24,12 +25,14 @@ impl XRRenderState {
|
|||
pub fn new_inherited(
|
||||
depth_near: f64,
|
||||
depth_far: f64,
|
||||
inline_vertical_fov: f64,
|
||||
layer: Option<&XRWebGLLayer>,
|
||||
) -> XRRenderState {
|
||||
XRRenderState {
|
||||
reflector_: Reflector::new(),
|
||||
depth_near: Cell::new(depth_near),
|
||||
depth_far: Cell::new(depth_far),
|
||||
inline_vertical_fov: Cell::new(inline_vertical_fov),
|
||||
layer: MutNullableDom::new(layer),
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +41,16 @@ impl XRRenderState {
|
|||
global: &GlobalScope,
|
||||
depth_near: f64,
|
||||
depth_far: f64,
|
||||
inline_vertical_fov: f64,
|
||||
layer: Option<&XRWebGLLayer>,
|
||||
) -> DomRoot<XRRenderState> {
|
||||
reflect_dom_object(
|
||||
Box::new(XRRenderState::new_inherited(depth_near, depth_far, layer)),
|
||||
Box::new(XRRenderState::new_inherited(
|
||||
depth_near,
|
||||
depth_far,
|
||||
inline_vertical_fov,
|
||||
layer,
|
||||
)),
|
||||
global,
|
||||
XRRenderStateBinding::Wrap,
|
||||
)
|
||||
|
@ -52,6 +61,7 @@ impl XRRenderState {
|
|||
&self.global(),
|
||||
self.depth_near.get(),
|
||||
self.depth_far.get(),
|
||||
self.inline_vertical_fov.get(),
|
||||
self.layer.get().as_ref().map(|x| &**x),
|
||||
)
|
||||
}
|
||||
|
@ -62,6 +72,9 @@ impl XRRenderState {
|
|||
pub fn set_depth_far(&self, depth: f64) {
|
||||
self.depth_far.set(depth)
|
||||
}
|
||||
pub fn set_inline_vertical_fov(&self, fov: f64) {
|
||||
self.inline_vertical_fov.set(fov)
|
||||
}
|
||||
pub fn set_layer(&self, layer: Option<&XRWebGLLayer>) {
|
||||
self.layer.set(layer)
|
||||
}
|
||||
|
@ -78,6 +91,11 @@ impl XRRenderStateMethods for XRRenderState {
|
|||
Finite::wrap(self.depth_far.get())
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrrenderstate-inlineverticalfieldofview
|
||||
fn InlineVerticalFieldOfView(&self) -> Finite<f64> {
|
||||
Finite::wrap(self.inline_vertical_fov.get())
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrrenderstate-baselayer
|
||||
fn GetBaseLayer(&self) -> Option<DomRoot<XRWebGLLayer>> {
|
||||
self.layer.get()
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::dom::bindings::cell::DomRefCell;
|
|||
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::XRBinding::XRSessionMode;
|
||||
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
|
||||
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit;
|
||||
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods;
|
||||
|
@ -39,7 +40,7 @@ use crate::dom::xrspace::XRSpace;
|
|||
use crate::dom::xrwebgllayer::XRWebGLLayer;
|
||||
use crate::task_source::TaskSource;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::RigidTransform3D;
|
||||
use euclid::{Rect, RigidTransform3D, Transform3D};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use profile_traits::ipc;
|
||||
|
@ -47,8 +48,8 @@ use std::cell::Cell;
|
|||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use webxr_api::{
|
||||
self, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind, Session,
|
||||
Visibility,
|
||||
self, util, Display, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind,
|
||||
Session, View, Viewer, Visibility,
|
||||
};
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -56,6 +57,7 @@ pub struct XRSession {
|
|||
eventtarget: EventTarget,
|
||||
base_layer: MutNullableDom<XRWebGLLayer>,
|
||||
blend_mode: XREnvironmentBlendMode,
|
||||
mode: XRSessionMode,
|
||||
visibility_state: Cell<XRVisibilityState>,
|
||||
viewer_space: MutNullableDom<XRSpace>,
|
||||
#[ignore_malloc_size_of = "defined in webxr"]
|
||||
|
@ -63,6 +65,8 @@ pub struct XRSession {
|
|||
frame_requested: Cell<bool>,
|
||||
pending_render_state: MutNullableDom<XRRenderState>,
|
||||
active_render_state: MutDom<XRRenderState>,
|
||||
/// Cached projection matrix for inline sessions
|
||||
inline_projection_matrix: DomRefCell<Transform3D<f32, Viewer, Display>>,
|
||||
|
||||
next_raf_id: Cell<i32>,
|
||||
#[ignore_malloc_size_of = "closures are hard"]
|
||||
|
@ -85,17 +89,20 @@ impl XRSession {
|
|||
session: Session,
|
||||
render_state: &XRRenderState,
|
||||
input_sources: &XRInputSourceArray,
|
||||
mode: XRSessionMode,
|
||||
) -> XRSession {
|
||||
XRSession {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
base_layer: Default::default(),
|
||||
blend_mode: session.environment_blend_mode().into(),
|
||||
mode,
|
||||
visibility_state: Cell::new(XRVisibilityState::Visible),
|
||||
viewer_space: Default::default(),
|
||||
session: DomRefCell::new(session),
|
||||
frame_requested: Cell::new(false),
|
||||
pending_render_state: MutNullableDom::new(None),
|
||||
active_render_state: MutDom::new(render_state),
|
||||
inline_projection_matrix: Default::default(),
|
||||
|
||||
next_raf_id: Cell::new(0),
|
||||
raf_callback_list: DomRefCell::new(vec![]),
|
||||
|
@ -107,14 +114,16 @@ impl XRSession {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new(global: &GlobalScope, session: Session) -> DomRoot<XRSession> {
|
||||
let render_state = XRRenderState::new(global, 0.1, 1000.0, None);
|
||||
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 input_sources = XRInputSourceArray::new(global);
|
||||
let ret = reflect_dom_object(
|
||||
Box::new(XRSession::new_inherited(
|
||||
session,
|
||||
&render_state,
|
||||
&input_sources,
|
||||
mode,
|
||||
)),
|
||||
global,
|
||||
XRSessionBinding::Wrap,
|
||||
|
@ -134,6 +143,10 @@ impl XRSession {
|
|||
self.ended.get()
|
||||
}
|
||||
|
||||
pub fn is_immersive(&self) -> bool {
|
||||
self.mode != XRSessionMode::Inline
|
||||
}
|
||||
|
||||
fn setup_raf_loop(&self) {
|
||||
assert!(
|
||||
self.raf_sender.borrow().is_none(),
|
||||
|
@ -298,9 +311,12 @@ impl XRSession {
|
|||
self.active_render_state.set(&pending);
|
||||
// Step 6-7: XXXManishearth handle inlineVerticalFieldOfView
|
||||
|
||||
// XXXManishearth handle inline sessions and composition disabled flag
|
||||
let swap_chain_id = pending.GetBaseLayer().map(|layer| layer.swap_chain_id());
|
||||
self.session.borrow_mut().set_swap_chain(swap_chain_id);
|
||||
if self.is_immersive() {
|
||||
let swap_chain_id = pending.GetBaseLayer().map(|layer| layer.swap_chain_id());
|
||||
self.session.borrow_mut().set_swap_chain(swap_chain_id);
|
||||
} else {
|
||||
self.update_inline_projection_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
for event in frame.events.drain(..) {
|
||||
|
@ -333,8 +349,10 @@ impl XRSession {
|
|||
self.outside_raf.set(true);
|
||||
|
||||
frame.set_active(false);
|
||||
base_layer.swap_buffers();
|
||||
self.session.borrow_mut().render_animation_frame();
|
||||
if self.is_immersive() {
|
||||
base_layer.swap_buffers();
|
||||
self.session.borrow_mut().render_animation_frame();
|
||||
}
|
||||
self.request_new_xr_frame();
|
||||
|
||||
// If the canvas element is attached to the DOM, it is now dirty,
|
||||
|
@ -345,6 +363,44 @@ impl XRSession {
|
|||
.upcast::<Node>()
|
||||
.dirty(NodeDamage::OtherNodeDamage);
|
||||
}
|
||||
|
||||
fn update_inline_projection_matrix(&self) {
|
||||
debug_assert!(!self.is_immersive());
|
||||
let render_state = self.active_render_state.get();
|
||||
let size = if let Some(base) = render_state.GetBaseLayer() {
|
||||
base.size()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let mut clip_planes = util::ClipPlanes::default();
|
||||
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 = near * top.tan() as f32;
|
||||
let bottom = top;
|
||||
let left = top * size.width as f32 / size.height as f32;
|
||||
let right = left;
|
||||
let matrix = util::frustum_to_projection_matrix(left, right, top, bottom, clip_planes);
|
||||
*self.inline_projection_matrix.borrow_mut() = matrix;
|
||||
}
|
||||
|
||||
/// Constructs a View suitable for inline sessions using the inlineVerticalFieldOfView and canvas size
|
||||
pub fn inline_view(&self) -> View<Viewer> {
|
||||
debug_assert!(!self.is_immersive());
|
||||
let size = self
|
||||
.active_render_state
|
||||
.get()
|
||||
.GetBaseLayer()
|
||||
.expect("Must never construct views when base layer is not set")
|
||||
.size();
|
||||
View {
|
||||
// Inline views have no offset
|
||||
transform: RigidTransform3D::identity(),
|
||||
projection: *self.inline_projection_matrix.borrow(),
|
||||
viewport: Rect::from_size(size.to_i32()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XRSessionMethods for XRSession {
|
||||
|
@ -394,9 +450,10 @@ impl XRSessionMethods for XRSession {
|
|||
}
|
||||
}
|
||||
|
||||
// XXXManishearth step 4:
|
||||
// If newState’s inlineVerticalFieldOfView is set and session is an
|
||||
// immersive session, throw an InvalidStateError and abort these steps.
|
||||
// Step 4:
|
||||
if init.inlineVerticalFieldOfView.is_some() {
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
|
||||
let pending = self
|
||||
.pending_render_state
|
||||
|
@ -407,6 +464,9 @@ impl XRSessionMethods for XRSession {
|
|||
if let Some(far) = init.depthFar {
|
||||
pending.set_depth_far(*far);
|
||||
}
|
||||
if let Some(fov) = init.inlineVerticalFieldOfView {
|
||||
pending.set_inline_vertical_fov(*fov);
|
||||
}
|
||||
if let Some(ref layer) = init.baseLayer {
|
||||
pending.set_layer(Some(&layer))
|
||||
}
|
||||
|
@ -416,7 +476,6 @@ impl XRSessionMethods for XRSession {
|
|||
.borrow_mut()
|
||||
.update_clip_planes(*pending.DepthNear() as f32, *pending.DepthFar() as f32);
|
||||
}
|
||||
// XXXManishearth handle inlineVerticalFieldOfView
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -481,6 +540,19 @@ impl XRSessionMethods for XRSession {
|
|||
fn End(&self) -> Rc<Promise> {
|
||||
let global = self.global();
|
||||
let p = Promise::new(&global);
|
||||
if self.ended.get() && self.end_promises.borrow().is_empty() {
|
||||
// If the session has completely ended and all end promises have been resolved,
|
||||
// don't queue up more end promises
|
||||
//
|
||||
// We need to check for end_promises being empty because `ended` is set
|
||||
// before everything has been completely shut down, and we do not want to
|
||||
// prematurely resolve the promise then
|
||||
//
|
||||
// However, if end_promises is empty, then all end() promises have already resolved,
|
||||
// so the session has completely shut down and we should not queue up more promises
|
||||
p.resolve_native(&());
|
||||
return p;
|
||||
}
|
||||
self.end_promises.borrow_mut().push(p.clone());
|
||||
// This is duplicated in event_callback since this should
|
||||
// happen ASAP for end() but can happen later if the device
|
||||
|
|
|
@ -25,14 +25,12 @@ use euclid::RigidTransform3D;
|
|||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use profile_traits::ipc;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use webxr_api::{self, Error as XRError, MockDeviceInit, MockDeviceMsg};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct XRTest {
|
||||
reflector: Reflector,
|
||||
session_started: Cell<bool>,
|
||||
devices_connected: DomRefCell<Vec<Dom<FakeXRDevice>>>,
|
||||
}
|
||||
|
||||
|
@ -40,7 +38,6 @@ impl XRTest {
|
|||
pub fn new_inherited() -> XRTest {
|
||||
XRTest {
|
||||
reflector: Reflector::new(),
|
||||
session_started: Cell::new(false),
|
||||
devices_connected: DomRefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
@ -76,11 +73,6 @@ impl XRTestMethods for XRTest {
|
|||
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;
|
||||
}
|
||||
|
||||
let origin = if let Some(ref o) = init.viewerOrigin {
|
||||
match get_origin(&o) {
|
||||
Ok(origin) => origin,
|
||||
|
@ -121,8 +113,6 @@ impl XRTestMethods for XRTest {
|
|||
floor_origin,
|
||||
};
|
||||
|
||||
self.session_started.set(true);
|
||||
|
||||
let global = self.global();
|
||||
let window = global.as_window();
|
||||
let this = Trusted::new(self);
|
||||
|
|
|
@ -42,6 +42,13 @@ impl XRViewerPose {
|
|||
) -> DomRoot<XRViewerPose> {
|
||||
rooted_vec!(let mut views);
|
||||
session.with_session(|s| match s.views() {
|
||||
Views::Inline => views.push(XRView::new(
|
||||
global,
|
||||
session,
|
||||
&session.inline_view(),
|
||||
XREye::None,
|
||||
&pose,
|
||||
)),
|
||||
Views::Mono(view) => {
|
||||
views.push(XRView::new(global, session, &view, XREye::None, &pose))
|
||||
},
|
||||
|
|
|
@ -20,9 +20,10 @@ use crate::dom::xrview::XRView;
|
|||
use crate::dom::xrviewport::XRViewport;
|
||||
use canvas_traits::webgl::WebGLFramebufferId;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::Size2D;
|
||||
use std::convert::TryInto;
|
||||
use webxr_api::SwapChainId as WebXRSwapChainId;
|
||||
use webxr_api::Views;
|
||||
use webxr_api::{Viewport, Views};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct XRWebGLLayer {
|
||||
|
@ -32,19 +33,20 @@ pub struct XRWebGLLayer {
|
|||
stencil: bool,
|
||||
alpha: bool,
|
||||
#[ignore_malloc_size_of = "ids don't malloc"]
|
||||
swap_chain_id: WebXRSwapChainId,
|
||||
swap_chain_id: Option<WebXRSwapChainId>,
|
||||
context: Dom<WebGLRenderingContext>,
|
||||
session: Dom<XRSession>,
|
||||
framebuffer: Dom<WebGLFramebuffer>,
|
||||
/// If none, this is an inline session (the composition disabled flag is true)
|
||||
framebuffer: Option<Dom<WebGLFramebuffer>>,
|
||||
}
|
||||
|
||||
impl XRWebGLLayer {
|
||||
pub fn new_inherited(
|
||||
swap_chain_id: WebXRSwapChainId,
|
||||
swap_chain_id: Option<WebXRSwapChainId>,
|
||||
session: &XRSession,
|
||||
context: &WebGLRenderingContext,
|
||||
init: &XRWebGLLayerInit,
|
||||
framebuffer: &WebGLFramebuffer,
|
||||
framebuffer: Option<&WebGLFramebuffer>,
|
||||
) -> XRWebGLLayer {
|
||||
XRWebGLLayer {
|
||||
reflector_: Reflector::new(),
|
||||
|
@ -55,17 +57,17 @@ impl XRWebGLLayer {
|
|||
swap_chain_id,
|
||||
context: Dom::from_ref(context),
|
||||
session: Dom::from_ref(session),
|
||||
framebuffer: Dom::from_ref(framebuffer),
|
||||
framebuffer: framebuffer.map(Dom::from_ref),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
global: &GlobalScope,
|
||||
swap_chain_id: WebXRSwapChainId,
|
||||
swap_chain_id: Option<WebXRSwapChainId>,
|
||||
session: &XRSession,
|
||||
context: &WebGLRenderingContext,
|
||||
init: &XRWebGLLayerInit,
|
||||
framebuffer: &WebGLFramebuffer,
|
||||
framebuffer: Option<&WebGLFramebuffer>,
|
||||
) -> DomRoot<XRWebGLLayer> {
|
||||
reflect_dom_object(
|
||||
Box::new(XRWebGLLayer::new_inherited(
|
||||
|
@ -87,6 +89,7 @@ impl XRWebGLLayer {
|
|||
context: &WebGLRenderingContext,
|
||||
init: &XRWebGLLayerInit,
|
||||
) -> Fallible<DomRoot<Self>> {
|
||||
let framebuffer;
|
||||
// Step 2
|
||||
if session.is_ended() {
|
||||
return Err(Error::InvalidState);
|
||||
|
@ -95,9 +98,15 @@ impl XRWebGLLayer {
|
|||
// XXXManishearth step 4: check XR compat flag for immersive sessions
|
||||
|
||||
// Step 9.2. "Initialize layer’s framebuffer to a new opaque framebuffer created with context."
|
||||
let size = session.with_session(|session| session.recommended_framebuffer_resolution());
|
||||
let (swap_chain_id, framebuffer) =
|
||||
WebGLFramebuffer::maybe_new_webxr(session, context, size).ok_or(Error::Operation)?;
|
||||
let (swap_chain_id, framebuffer) = if session.is_immersive() {
|
||||
let size = session.with_session(|session| session.recommended_framebuffer_resolution());
|
||||
let (swap_chain_id, fb) = WebGLFramebuffer::maybe_new_webxr(session, context, size)
|
||||
.ok_or(Error::Operation)?;
|
||||
framebuffer = fb;
|
||||
(Some(swap_chain_id), Some(&*framebuffer))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// Step 9.3. "Allocate and initialize resources compatible with session’s XR device,
|
||||
// including GPU accessible memory buffers, as required to support the compositing of layer."
|
||||
|
@ -115,12 +124,13 @@ impl XRWebGLLayer {
|
|||
session,
|
||||
context,
|
||||
init,
|
||||
&framebuffer,
|
||||
framebuffer,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn swap_chain_id(&self) -> WebXRSwapChainId {
|
||||
self.swap_chain_id
|
||||
.expect("swap_chain_id must not be called for inline sessions")
|
||||
}
|
||||
|
||||
pub fn session(&self) -> &XRSession {
|
||||
|
@ -128,10 +138,27 @@ impl XRWebGLLayer {
|
|||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
if let WebGLFramebufferId::Opaque(id) = self.framebuffer.id() {
|
||||
if let WebGLFramebufferId::Opaque(id) = self
|
||||
.framebuffer
|
||||
.as_ref()
|
||||
.expect("swap_buffers must not be called for inline sessions")
|
||||
.id()
|
||||
{
|
||||
self.context.swap_buffers(Some(id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Size2D<u32, Viewport> {
|
||||
if let Some(framebuffer) = self.framebuffer.as_ref() {
|
||||
let size = framebuffer.size().unwrap_or((0, 0));
|
||||
Size2D::new(
|
||||
size.0.try_into().unwrap_or(0),
|
||||
size.1.try_into().unwrap_or(0),
|
||||
)
|
||||
} else {
|
||||
Size2D::from_untyped(self.context.Canvas().get_size())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XRWebGLLayerMethods for XRWebGLLayer {
|
||||
|
@ -161,28 +188,18 @@ impl XRWebGLLayerMethods for XRWebGLLayer {
|
|||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebuffer
|
||||
fn Framebuffer(&self) -> DomRoot<WebGLFramebuffer> {
|
||||
DomRoot::from_ref(&self.framebuffer)
|
||||
fn GetFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
|
||||
self.framebuffer.as_ref().map(|x| DomRoot::from_ref(&**x))
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferwidth
|
||||
fn FramebufferWidth(&self) -> u32 {
|
||||
self.framebuffer
|
||||
.size()
|
||||
.unwrap_or((0, 0))
|
||||
.0
|
||||
.try_into()
|
||||
.unwrap_or(0)
|
||||
self.size().width
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight
|
||||
fn FramebufferHeight(&self) -> u32 {
|
||||
self.framebuffer
|
||||
.size()
|
||||
.unwrap_or((0, 0))
|
||||
.1
|
||||
.try_into()
|
||||
.unwrap_or(0)
|
||||
self.size().height
|
||||
}
|
||||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
|
||||
|
|
|
@ -727667,7 +727667,7 @@
|
|||
"testharness"
|
||||
],
|
||||
"webxr/xrView_oneframeupdate.https.html": [
|
||||
"8edb2c0b265f18479644f312f91478d6029e3cd5",
|
||||
"438d3dbaa36a14d6850adf83e3279e23c00157a4",
|
||||
"testharness"
|
||||
],
|
||||
"webxr/xrView_sameObject.https.html": [
|
||||
|
|
|
@ -107,9 +107,6 @@
|
|||
[XRReferenceSpace interface: attribute onreset]
|
||||
expected: FAIL
|
||||
|
||||
[XRRenderState interface: attribute inlineVerticalFieldOfView]
|
||||
expected: FAIL
|
||||
|
||||
[XRRay interface: attribute origin]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -169,3 +166,4 @@
|
|||
|
||||
[WebGLRenderingContext includes WebGLRenderingContextBase: member names are unique]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[xrDevice_requestSession_immersive.https.html]
|
||||
[Tests requestSession ignores unknown optionalFeatures]
|
||||
expected: FAIL
|
||||
|
||||
[Tests requestSession accepts XRSessionInit dictionary]
|
||||
expected: FAIL
|
||||
|
|
@ -2,12 +2,3 @@
|
|||
[Tests requestSession ignores unknown optionalFeatures]
|
||||
expected: FAIL
|
||||
|
||||
[Tests requestSession accepts XRSessionInit dictionary with empty feature lists]
|
||||
expected: FAIL
|
||||
|
||||
[Tests requestSession ignores unknown objects in optionalFeatures]
|
||||
expected: FAIL
|
||||
|
||||
[Tests requestSession ignores unknown strings in optionalFeatures]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[xrFrame_getPose.https.html]
|
||||
[XRFrame.getPose works for non-immersive sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[xrFrame_lifetime.https.html]
|
||||
[XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[xrSession_end.https.html]
|
||||
[end event fires when non-immersive session ends]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[xrSession_requestAnimationFrame_callback_calls.https.html]
|
||||
[XRSession requestAnimationFrame calls the provided callback a non-immersive session]
|
||||
expected: FAIL
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
[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: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,34 +1,7 @@
|
|||
[xrSession_requestReferenceSpace_features.https.html]
|
||||
[Non-immersive session rejects unbounded space even when requested]
|
||||
expected: FAIL
|
||||
|
||||
[Immersive session supports local space by default]
|
||||
expected: FAIL
|
||||
|
||||
[Non-immersive session supports local-floor space when required]
|
||||
expected: FAIL
|
||||
|
||||
[Immersive session rejects local-floor space if not requested]
|
||||
expected: FAIL
|
||||
|
||||
[Immersive session supports local-floor space when required]
|
||||
expected: FAIL
|
||||
|
||||
[Non-immersive session rejects bounded-floor space even when requested]
|
||||
expected: FAIL
|
||||
|
||||
[Non-immersive session supports local space when optional]
|
||||
expected: FAIL
|
||||
|
||||
[Immersive session supports local-floor space when optional]
|
||||
expected: FAIL
|
||||
|
||||
[Non-immersive session supports local space when required]
|
||||
expected: FAIL
|
||||
|
||||
[Non-immersive session rejects local space if not requested]
|
||||
expected: FAIL
|
||||
|
||||
[Immersive session supports viewer space by default]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[xrSession_viewer_referenceSpace.https.html]
|
||||
[Identity reference space provides correct poses for immersive sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[xrView_eyes.https.html]
|
||||
[XRView.eye is correct for non-immersive sessions]
|
||||
expected: FAIL
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
[xrView_oneframeupdate.https.html]
|
||||
expected: ERROR
|
||||
[XRView projection matrices update near and far depths on the next frame]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[xrWebGLLayer_opaque_framebuffer.https.html]
|
||||
[Ensure that the framebuffer given by the WebGL layer is opaque for non-immersive]
|
||||
expected: FAIL
|
||||
|
|
@ -76,7 +76,7 @@ let testFunction = function(session, fakeDeviceController, t) {
|
|||
counter++;
|
||||
}
|
||||
|
||||
session.requestAnimationFrame(onFrame);
|
||||
session.requestAnimationFrame(t.step_func(onFrame));
|
||||
}));
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue