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:
bors-servo 2019-12-11 21:04:47 -05:00 committed by GitHub
commit 073194a618
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 210 additions and 146 deletions

4
Cargo.lock generated
View file

@ -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",

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

@ -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;
};

View file

@ -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;

View file

@ -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);
}

View file

@ -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()

View file

@ -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 newStates 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

View file

@ -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);

View file

@ -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))
},

View file

@ -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 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."
@ -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

View file

@ -727667,7 +727667,7 @@
"testharness"
],
"webxr/xrView_oneframeupdate.https.html": [
"8edb2c0b265f18479644f312f91478d6029e3cd5",
"438d3dbaa36a14d6850adf83e3279e23c00157a4",
"testharness"
],
"webxr/xrView_sameObject.https.html": [

View file

@ -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

View file

@ -1,7 +0,0 @@
[xrDevice_requestSession_immersive.https.html]
[Tests requestSession ignores unknown optionalFeatures]
expected: FAIL
[Tests requestSession accepts XRSessionInit dictionary]
expected: FAIL

View file

@ -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

View file

@ -1,4 +0,0 @@
[xrFrame_getPose.https.html]
[XRFrame.getPose works for non-immersive sessions]
expected: FAIL

View file

@ -1,4 +0,0 @@
[xrFrame_lifetime.https.html]
[XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions]
expected: FAIL

View file

@ -1,4 +0,0 @@
[xrSession_end.https.html]
[end event fires when non-immersive session ends]
expected: FAIL

View file

@ -1,4 +0,0 @@
[xrSession_requestAnimationFrame_callback_calls.https.html]
[XRSession requestAnimationFrame calls the provided callback a non-immersive session]
expected: FAIL

View file

@ -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

View file

@ -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

View file

@ -1,4 +0,0 @@
[xrSession_viewer_referenceSpace.https.html]
[Identity reference space provides correct poses for immersive sessions]
expected: FAIL

View file

@ -1,4 +0,0 @@
[xrView_eyes.https.html]
[XRView.eye is correct for non-immersive sessions]
expected: FAIL

View file

@ -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

View file

@ -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

View file

@ -76,7 +76,7 @@ let testFunction = function(session, fakeDeviceController, t) {
counter++;
}
session.requestAnimationFrame(onFrame);
session.requestAnimationFrame(t.step_func(onFrame));
}));
};