Give inline sessions the correct projection matrices

This commit is contained in:
Manish Goregaokar 2019-12-05 15:08:09 -08:00
parent a1c1a16e98
commit 8aaa8493a7
4 changed files with 108 additions and 27 deletions

View file

@ -61,7 +61,7 @@ use cssparser::RGBA;
use devtools_traits::{CSSError, TimelineMarkerType, WorkerId}; use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
use embedder_traits::{EventLoopWaker, MediaMetadata}; use embedder_traits::{EventLoopWaker, MediaMetadata};
use encoding_rs::{Decoder, Encoding}; 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 euclid::Length as EuclidLength;
use html5ever::buffer_queue::BufferQueue; use html5ever::buffer_queue::BufferQueue;
use html5ever::{LocalName, Namespace, Prefix, QualName}; 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!(ResourceFetchTiming);
unsafe_no_jsmanaged_fields!(Timespec); unsafe_no_jsmanaged_fields!(Timespec);
unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext); 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!(Point2D<f32>, Rect<Au>);
unsafe_no_jsmanaged_fields!(Rect<f32>); unsafe_no_jsmanaged_fields!(Rect<f32>);
unsafe_no_jsmanaged_fields!(CascadeData); 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> { unsafe impl<T> JSTraceable for EuclidLength<u64, T> {
#[inline] #[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) { unsafe fn trace(&self, _trc: *mut JSTracer) {

View file

@ -40,7 +40,7 @@ use crate::dom::xrspace::XRSpace;
use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::dom::xrwebgllayer::XRWebGLLayer;
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::RigidTransform3D; use euclid::{Rect, RigidTransform3D, Transform3D};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use profile_traits::ipc; use profile_traits::ipc;
@ -48,8 +48,8 @@ use std::cell::Cell;
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use webxr_api::{ use webxr_api::{
self, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind, Session, self, util, Display, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind,
Visibility, Session, View, Viewer, Visibility,
}; };
#[dom_struct] #[dom_struct]
@ -65,6 +65,8 @@ pub struct XRSession {
frame_requested: Cell<bool>, frame_requested: Cell<bool>,
pending_render_state: MutNullableDom<XRRenderState>, pending_render_state: MutNullableDom<XRRenderState>,
active_render_state: MutDom<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>, next_raf_id: Cell<i32>,
#[ignore_malloc_size_of = "closures are hard"] #[ignore_malloc_size_of = "closures are hard"]
@ -100,6 +102,7 @@ impl XRSession {
frame_requested: Cell::new(false), frame_requested: Cell::new(false),
pending_render_state: MutNullableDom::new(None), pending_render_state: MutNullableDom::new(None),
active_render_state: MutDom::new(render_state), active_render_state: MutDom::new(render_state),
inline_projection_matrix: Default::default(),
next_raf_id: Cell::new(0), next_raf_id: Cell::new(0),
raf_callback_list: DomRefCell::new(vec![]), raf_callback_list: DomRefCell::new(vec![]),
@ -140,6 +143,10 @@ impl XRSession {
self.ended.get() self.ended.get()
} }
pub fn is_immersive(&self) -> bool {
self.mode != XRSessionMode::Inline
}
fn setup_raf_loop(&self) { fn setup_raf_loop(&self) {
assert!( assert!(
self.raf_sender.borrow().is_none(), self.raf_sender.borrow().is_none(),
@ -304,9 +311,12 @@ impl XRSession {
self.active_render_state.set(&pending); self.active_render_state.set(&pending);
// Step 6-7: XXXManishearth handle inlineVerticalFieldOfView // Step 6-7: XXXManishearth handle inlineVerticalFieldOfView
// XXXManishearth handle inline sessions and composition disabled flag if self.is_immersive() {
let swap_chain_id = pending.GetBaseLayer().map(|layer| layer.swap_chain_id()); let swap_chain_id = pending.GetBaseLayer().map(|layer| layer.swap_chain_id());
self.session.borrow_mut().set_swap_chain(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(..) { for event in frame.events.drain(..) {
@ -339,8 +349,10 @@ impl XRSession {
self.outside_raf.set(true); self.outside_raf.set(true);
frame.set_active(false); frame.set_active(false);
base_layer.swap_buffers(); if self.is_immersive() {
self.session.borrow_mut().render_animation_frame(); base_layer.swap_buffers();
self.session.borrow_mut().render_animation_frame();
}
self.request_new_xr_frame(); self.request_new_xr_frame();
// If the canvas element is attached to the DOM, it is now dirty, // If the canvas element is attached to the DOM, it is now dirty,
@ -351,6 +363,44 @@ impl XRSession {
.upcast::<Node>() .upcast::<Node>()
.dirty(NodeDamage::OtherNodeDamage); .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 { impl XRSessionMethods for XRSession {

View file

@ -42,7 +42,13 @@ impl XRViewerPose {
) -> DomRoot<XRViewerPose> { ) -> DomRoot<XRViewerPose> {
rooted_vec!(let mut views); rooted_vec!(let mut views);
session.with_session(|s| match s.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::Mono(view) => {
views.push(XRView::new(global, session, &view, XREye::None, &pose)) views.push(XRView::new(global, session, &view, XREye::None, &pose))
}, },

View file

@ -20,10 +20,10 @@ use crate::dom::xrview::XRView;
use crate::dom::xrviewport::XRViewport; use crate::dom::xrviewport::XRViewport;
use canvas_traits::webgl::WebGLFramebufferId; use canvas_traits::webgl::WebGLFramebufferId;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Size2D; use euclid::Size2D;
use std::convert::TryInto; use std::convert::TryInto;
use webxr_api::SwapChainId as WebXRSwapChainId; use webxr_api::SwapChainId as WebXRSwapChainId;
use webxr_api::Views; use webxr_api::{Viewport, Views};
#[dom_struct] #[dom_struct]
pub struct XRWebGLLayer { pub struct XRWebGLLayer {
@ -33,7 +33,7 @@ pub struct XRWebGLLayer {
stencil: bool, stencil: bool,
alpha: bool, alpha: bool,
#[ignore_malloc_size_of = "ids don't malloc"] #[ignore_malloc_size_of = "ids don't malloc"]
swap_chain_id: WebXRSwapChainId, swap_chain_id: Option<WebXRSwapChainId>,
context: Dom<WebGLRenderingContext>, context: Dom<WebGLRenderingContext>,
session: Dom<XRSession>, session: Dom<XRSession>,
/// If none, this is an inline session (the composition disabled flag is true) /// If none, this is an inline session (the composition disabled flag is true)
@ -42,7 +42,7 @@ pub struct XRWebGLLayer {
impl XRWebGLLayer { impl XRWebGLLayer {
pub fn new_inherited( pub fn new_inherited(
swap_chain_id: WebXRSwapChainId, swap_chain_id: Option<WebXRSwapChainId>,
session: &XRSession, session: &XRSession,
context: &WebGLRenderingContext, context: &WebGLRenderingContext,
init: &XRWebGLLayerInit, init: &XRWebGLLayerInit,
@ -63,7 +63,7 @@ impl XRWebGLLayer {
pub fn new( pub fn new(
global: &GlobalScope, global: &GlobalScope,
swap_chain_id: WebXRSwapChainId, swap_chain_id: Option<WebXRSwapChainId>,
session: &XRSession, session: &XRSession,
context: &WebGLRenderingContext, context: &WebGLRenderingContext,
init: &XRWebGLLayerInit, init: &XRWebGLLayerInit,
@ -89,6 +89,7 @@ impl XRWebGLLayer {
context: &WebGLRenderingContext, context: &WebGLRenderingContext,
init: &XRWebGLLayerInit, init: &XRWebGLLayerInit,
) -> Fallible<DomRoot<Self>> { ) -> Fallible<DomRoot<Self>> {
let framebuffer;
// Step 2 // Step 2
if session.is_ended() { if session.is_ended() {
return Err(Error::InvalidState); return Err(Error::InvalidState);
@ -97,9 +98,15 @@ impl XRWebGLLayer {
// XXXManishearth step 4: check XR compat flag for immersive sessions // XXXManishearth step 4: check XR compat flag for immersive sessions
// Step 9.2. "Initialize layers framebuffer to a new opaque framebuffer created with context." // Step 9.2. "Initialize layers framebuffer to a new opaque framebuffer created with context."
let size = session.with_session(|session| session.recommended_framebuffer_resolution()); let (swap_chain_id, framebuffer) = if session.is_immersive() {
let (swap_chain_id, framebuffer) = let size = session.with_session(|session| session.recommended_framebuffer_resolution());
WebGLFramebuffer::maybe_new_webxr(session, context, size).ok_or(Error::Operation)?; 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 sessions XR device, // Step 9.3. "Allocate and initialize resources compatible with sessions XR device,
// including GPU accessible memory buffers, as required to support the compositing of layer." // including GPU accessible memory buffers, as required to support the compositing of layer."
@ -117,12 +124,13 @@ impl XRWebGLLayer {
session, session,
context, context,
init, init,
Some(&framebuffer), framebuffer,
)) ))
} }
pub fn swap_chain_id(&self) -> WebXRSwapChainId { pub fn swap_chain_id(&self) -> WebXRSwapChainId {
self.swap_chain_id self.swap_chain_id
.expect("swap_chain_id must not be called for inline sessions")
} }
pub fn session(&self) -> &XRSession { pub fn session(&self) -> &XRSession {
@ -133,19 +141,22 @@ impl XRWebGLLayer {
if let WebGLFramebufferId::Opaque(id) = self if let WebGLFramebufferId::Opaque(id) = self
.framebuffer .framebuffer
.as_ref() .as_ref()
.expect("Must have framebuffer") .expect("swap_buffers must not be called for inline sessions")
.id() .id()
{ {
self.context.swap_buffers(Some(id)); self.context.swap_buffers(Some(id));
} }
} }
fn size(&self) -> Size2D<i32> { pub fn size(&self) -> Size2D<u32, Viewport> {
if let Some(framebuffer) = self.framebuffer { if let Some(framebuffer) = self.framebuffer.as_ref() {
let size = framebuffer.size().unwrap_or((0, 0)); 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 { } 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 /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferwidth
fn FramebufferWidth(&self) -> u32 { fn FramebufferWidth(&self) -> u32 {
self.size().width.try_into().unwrap_or(0) self.size().width
} }
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight
fn FramebufferHeight(&self) -> u32 { fn FramebufferHeight(&self) -> u32 {
self.size().height.try_into().unwrap_or(0) self.size().height
} }
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport