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 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) {

View file

@ -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<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"]
@ -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::<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 {

View file

@ -42,7 +42,13 @@ impl XRViewerPose {
) -> DomRoot<XRViewerPose> {
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))
},

View file

@ -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<WebXRSwapChainId>,
context: Dom<WebGLRenderingContext>,
session: Dom<XRSession>,
/// 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<WebXRSwapChainId>,
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<WebXRSwapChainId>,
session: &XRSession,
context: &WebGLRenderingContext,
init: &XRWebGLLayerInit,
@ -89,6 +89,7 @@ impl XRWebGLLayer {
context: &WebGLRenderingContext,
init: &XRWebGLLayerInit,
) -> Fallible<DomRoot<Self>> {
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 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) =
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 sessions 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<i32> {
if let Some(framebuffer) = self.framebuffer {
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, 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