diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index e8818d316ac..9dad0da9cd3 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -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); unsafe_no_jsmanaged_fields!(ResourceFetchTiming); unsafe_no_jsmanaged_fields!(Timespec); unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext); -unsafe_no_jsmanaged_fields!(Rotation3D, Transform2D, Transform3D); +unsafe_no_jsmanaged_fields!(Rotation3D, Transform2D); unsafe_no_jsmanaged_fields!(Point2D, Rect); unsafe_no_jsmanaged_fields!(Rect); unsafe_no_jsmanaged_fields!(CascadeData); @@ -665,6 +665,20 @@ unsafe impl JSTraceable for euclid::RigidTransform3D { } } +unsafe impl JSTraceable for euclid::Transform3D { + #[inline] + unsafe fn trace(&self, _trc: *mut JSTracer) { + // Do nothing + } +} + +unsafe impl JSTraceable for euclid::Transform3D { + #[inline] + unsafe fn trace(&self, _trc: *mut JSTracer) { + // Do nothing + } +} + unsafe impl JSTraceable for EuclidLength { #[inline] unsafe fn trace(&self, _trc: *mut JSTracer) { diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index d2b31b692e1..1ffe7ff77c3 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -40,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; @@ -48,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] @@ -65,6 +65,8 @@ pub struct XRSession { frame_requested: Cell, pending_render_state: MutNullableDom, active_render_state: MutDom, + /// Cached projection matrix for inline sessions + inline_projection_matrix: DomRefCell>, next_raf_id: Cell, #[ignore_malloc_size_of = "closures are hard"] @@ -100,6 +102,7 @@ impl XRSession { 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![]), @@ -140,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(), @@ -304,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(..) { @@ -339,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, @@ -351,6 +363,44 @@ impl XRSession { .upcast::() .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 { + 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 { diff --git a/components/script/dom/xrviewerpose.rs b/components/script/dom/xrviewerpose.rs index 1a5aacd0c91..0e64613294b 100644 --- a/components/script/dom/xrviewerpose.rs +++ b/components/script/dom/xrviewerpose.rs @@ -42,7 +42,13 @@ impl XRViewerPose { ) -> DomRoot { rooted_vec!(let mut views); session.with_session(|s| match s.views() { - Views::Inline => unimplemented!(), + 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)) }, diff --git a/components/script/dom/xrwebgllayer.rs b/components/script/dom/xrwebgllayer.rs index 2d39a47286b..752e2ea384a 100644 --- a/components/script/dom/xrwebgllayer.rs +++ b/components/script/dom/xrwebgllayer.rs @@ -20,10 +20,10 @@ use crate::dom::xrview::XRView; use crate::dom::xrviewport::XRViewport; use canvas_traits::webgl::WebGLFramebufferId; use dom_struct::dom_struct; -use euclid::default::Size2D; +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 { @@ -33,7 +33,7 @@ pub struct XRWebGLLayer { stencil: bool, alpha: bool, #[ignore_malloc_size_of = "ids don't malloc"] - swap_chain_id: WebXRSwapChainId, + swap_chain_id: Option, context: Dom, session: Dom, /// If none, this is an inline session (the composition disabled flag is true) @@ -42,7 +42,7 @@ pub struct XRWebGLLayer { impl XRWebGLLayer { pub fn new_inherited( - swap_chain_id: WebXRSwapChainId, + swap_chain_id: Option, session: &XRSession, context: &WebGLRenderingContext, init: &XRWebGLLayerInit, @@ -63,7 +63,7 @@ impl XRWebGLLayer { pub fn new( global: &GlobalScope, - swap_chain_id: WebXRSwapChainId, + swap_chain_id: Option, session: &XRSession, context: &WebGLRenderingContext, init: &XRWebGLLayerInit, @@ -89,6 +89,7 @@ impl XRWebGLLayer { context: &WebGLRenderingContext, init: &XRWebGLLayerInit, ) -> Fallible> { + let framebuffer; // Step 2 if session.is_ended() { return Err(Error::InvalidState); @@ -97,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." @@ -117,12 +124,13 @@ impl XRWebGLLayer { session, context, init, - Some(&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 { @@ -133,19 +141,22 @@ impl XRWebGLLayer { if let WebGLFramebufferId::Opaque(id) = self .framebuffer .as_ref() - .expect("Must have framebuffer") + .expect("swap_buffers must not be called for inline sessions") .id() { self.context.swap_buffers(Some(id)); } } - fn size(&self) -> Size2D { - if let Some(framebuffer) = self.framebuffer { + pub fn size(&self) -> Size2D { + if let Some(framebuffer) = self.framebuffer.as_ref() { let size = framebuffer.size().unwrap_or((0, 0)); - Size2D::new(size.0, size.1) + Size2D::new( + size.0.try_into().unwrap_or(0), + size.1.try_into().unwrap_or(0), + ) } else { - self.context.Canvas().get_size() + Size2D::from_untyped(self.context.Canvas().get_size()) } } } @@ -183,12 +194,12 @@ impl XRWebGLLayerMethods for XRWebGLLayer { /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferwidth fn FramebufferWidth(&self) -> u32 { - self.size().width.try_into().unwrap_or(0) + self.size().width } /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight fn FramebufferHeight(&self) -> u32 { - self.size().height.try_into().unwrap_or(0) + self.size().height } /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport