From 1dc7636135678d49a1e25f15c260f2734747e59b Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 14 Jan 2019 19:15:33 -0800 Subject: [PATCH 01/11] Add XRRenderState --- components/script/dom/mod.rs | 1 + .../script/dom/webidls/XRRenderState.webidl | 17 +++++ components/script/dom/xrrenderstate.rs | 64 +++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 components/script/dom/webidls/XRRenderState.webidl create mode 100644 components/script/dom/xrrenderstate.rs diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 00486b9033f..15c2e51057c 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -538,6 +538,7 @@ pub mod xr; pub mod xrframe; pub mod xrlayer; pub mod xrreferencespace; +pub mod xrrenderstate; pub mod xrrigidtransform; pub mod xrsession; pub mod xrspace; diff --git a/components/script/dom/webidls/XRRenderState.webidl b/components/script/dom/webidls/XRRenderState.webidl new file mode 100644 index 00000000000..99e66dbe113 --- /dev/null +++ b/components/script/dom/webidls/XRRenderState.webidl @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://immersive-web.github.io/webxr/#xrrenderstate-interface + +dictionary XRRenderStateInit { + double depthNear = 0.1; + double depthFar = 1000.0; + XRLayer? baseLayer = null; +}; + +[SecureContext, Exposed=Window] interface XRRenderState { + readonly attribute double depthNear; + readonly attribute double depthFar; + readonly attribute XRLayer? baseLayer; +}; \ No newline at end of file diff --git a/components/script/dom/xrrenderstate.rs b/components/script/dom/xrrenderstate.rs new file mode 100644 index 00000000000..562e4231919 --- /dev/null +++ b/components/script/dom/xrrenderstate.rs @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::{self, XRRenderStateMethods}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::reflector::Reflector; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::xrlayer::XRLayer; + +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct XRRenderState { + reflector_: Reflector, + depth_near: Cell, + depth_far: Cell, + layer: MutNullableDom, +} + +impl XRRenderState { + pub fn new_inherited( + depth_near: f64, + depth_far: f64, + layer: Option<&XRLayer>, + ) -> XRRenderState { + XRRenderState { + reflector_: Reflector::new(), + depth_near: Cell::new(depth_near), + depth_far: Cell::new(depth_far), + layer: MutNullableDom::new(layer), + } + } + + pub fn new( + global: &GlobalScope, + depth_near: f64, + depth_far: f64, + layer: Option<&XRLayer>, + ) -> DomRoot { + reflect_dom_object( + Box::new(XRRenderState::new_inherited(depth_near, depth_far, layer)), + global, + XRRenderStateBinding::Wrap, + ) + } +} + +impl XRRenderStateMethods for XRRenderState { + fn DepthNear(&self) -> Finite { + Finite::wrap(self.depth_near.get()) + } + + fn DepthFar(&self) -> Finite { + Finite::wrap(self.depth_far.get()) + } + + fn GetBaseLayer(&self) -> Option> { + self.layer.get() + } +} From 401b470e901a4ed47e538478de796cbc87cceac7 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 14 Jan 2019 19:33:52 -0800 Subject: [PATCH 02/11] Hook XRRenderState into XRSession --- .../script/dom/webidls/XRRenderState.webidl | 2 +- .../script/dom/webidls/XRSession.webidl | 4 +- components/script/dom/xrrenderstate.rs | 3 ++ components/script/dom/xrsession.rs | 44 +++---------------- 4 files changed, 12 insertions(+), 41 deletions(-) diff --git a/components/script/dom/webidls/XRRenderState.webidl b/components/script/dom/webidls/XRRenderState.webidl index 99e66dbe113..3cf622cfb9b 100644 --- a/components/script/dom/webidls/XRRenderState.webidl +++ b/components/script/dom/webidls/XRRenderState.webidl @@ -14,4 +14,4 @@ dictionary XRRenderStateInit { readonly attribute double depthNear; readonly attribute double depthFar; readonly attribute XRLayer? baseLayer; -}; \ No newline at end of file +}; diff --git a/components/script/dom/webidls/XRSession.webidl b/components/script/dom/webidls/XRSession.webidl index fa8d13b7cb3..3184a1b7787 100644 --- a/components/script/dom/webidls/XRSession.webidl +++ b/components/script/dom/webidls/XRSession.webidl @@ -19,9 +19,7 @@ interface XRSession : EventTarget { // readonly attribute XRPresentationContext outputContext; readonly attribute XREnvironmentBlendMode environmentBlendMode; - attribute double depthNear; - attribute double depthFar; - attribute XRLayer? baseLayer; + readonly attribute XRRenderState renderState; // // Methods Promise requestReferenceSpace(XRReferenceSpaceOptions options); diff --git a/components/script/dom/xrrenderstate.rs b/components/script/dom/xrrenderstate.rs index 562e4231919..cc0739396e2 100644 --- a/components/script/dom/xrrenderstate.rs +++ b/components/script/dom/xrrenderstate.rs @@ -50,14 +50,17 @@ impl XRRenderState { } impl XRRenderStateMethods for XRRenderState { + /// https://immersive-web.github.io/webxr/#dom-xrrenderstate-depthnear fn DepthNear(&self) -> Finite { Finite::wrap(self.depth_near.get()) } + /// https://immersive-web.github.io/webxr/#dom-xrrenderstate-depthfar fn DepthFar(&self) -> Finite { Finite::wrap(self.depth_far.get()) } + /// https://immersive-web.github.io/webxr/#dom-xrrenderstate-baselayer fn GetBaseLayer(&self) -> Option> { self.layer.get() } diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index f756b0e5fa5..b4e6f7c728e 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -14,8 +14,7 @@ use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMe use crate::dom::bindings::error::Error; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::reflector::reflect_dom_object; -use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::reflector::{DomObject, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -25,6 +24,7 @@ use crate::dom::xrlayer::XRLayer; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrstationaryreferencespace::XRStationaryReferenceSpace; use crate::dom::xrwebgllayer::XRWebGLLayer; +use crate::dom::xrrenderstate::XRRenderState; use dom_struct::dom_struct; use std::rc::Rc; @@ -61,46 +61,16 @@ impl XRSession { } impl XRSessionMethods for XRSession { - /// https://immersive-web.github.io/webxr/#dom-xrsession-depthnear - fn DepthNear(&self) -> Finite { - self.display.DepthNear() - } - - /// https://immersive-web.github.io/webxr/#dom-xrsession-depthfar - fn DepthFar(&self) -> Finite { - self.display.DepthFar() - } - - /// https://immersive-web.github.io/webxr/#dom-xrsession-depthnear - fn SetDepthNear(&self, d: Finite) { - self.display.SetDepthNear(d) - } - - /// https://immersive-web.github.io/webxr/#dom-xrsession-depthfar - fn SetDepthFar(&self, d: Finite) { - self.display.SetDepthFar(d) - } - /// https://immersive-web.github.io/webxr/#dom-xrsession-mode fn Mode(&self) -> XRSessionMode { XRSessionMode::Immersive_vr } - /// https://immersive-web.github.io/webxr/#dom-xrsession-baselayer - fn SetBaseLayer(&self, layer: Option<&XRLayer>) { - self.base_layer.set(layer); - if let Some(layer) = layer { - let layer = layer.downcast::().unwrap(); - self.display.xr_present(&self, Some(&layer.Context()), None); - } else { - // steps unknown - // https://github.com/immersive-web/webxr/issues/453 - } - } - - /// https://immersive-web.github.io/webxr/#dom-xrsession-baselayer - fn GetBaseLayer(&self) -> Option> { - self.base_layer.get() + // https://immersive-web.github.io/webxr/#dom-xrsession-renderstate + fn RenderState(&self) -> DomRoot { + // XXXManishearth maybe cache this + XRRenderState::new(&self.global(), *self.display.DepthNear(), *self.display.DepthFar(), + self.base_layer.get().as_ref().map(|l| &**l)) } /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe From e20909cf43b2c37bf927cbf76a5874b3e7532370 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 00:22:20 -0800 Subject: [PATCH 03/11] Allow VR RAF loop to handle changes in the layer, support pending renderstates --- components/script/dom/vrdisplay.rs | 186 ++++++++++++++---- .../script/dom/webidls/XRRenderState.webidl | 6 +- .../script/dom/webidls/XRSession.webidl | 1 + components/script/dom/xrsession.rs | 21 +- 4 files changed, 170 insertions(+), 44 deletions(-) diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index e806afb7a22..db474f967bb 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -13,7 +13,9 @@ use crate::dom::bindings::codegen::Bindings::VRLayerBinding::VRLayer; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::FrameRequestCallback; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCallback; +use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods; use crate::dom::bindings::error::Error; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; @@ -34,10 +36,11 @@ use crate::dom::vrstageparameters::VRStageParameters; use crate::dom::webglrenderingcontext::WebGLRenderingContext; use crate::dom::xrframe::XRFrame; use crate::dom::xrsession::XRSession; +use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::script_runtime::CommonScriptMsg; use crate::script_runtime::ScriptThreadEventCategory::WebVREvent; use crate::task_source::{TaskSource, TaskSourceName}; -use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand}; +use canvas_traits::webgl::{webgl_channel, WebGLMsgSender, WebGLReceiver, WebVRCommand}; use crossbeam_channel::{unbounded, Sender}; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; @@ -74,6 +77,10 @@ pub struct VRDisplay { raf_callback_list: DomRefCell>)>>, #[ignore_malloc_size_of = "closures are hard"] xr_raf_callback_list: DomRefCell>)>>, + /// When there isn't any layer_ctx the RAF thread needs to be "woken up" + raf_wakeup_sender: DomRefCell>>, + #[ignore_malloc_size_of = "Rc is hard"] + pending_renderstate_updates: DomRefCell)>>, // Compositor VRFrameData synchonization frame_data_status: Cell, #[ignore_malloc_size_of = "closures are hard"] @@ -88,6 +95,7 @@ pub struct VRDisplay { unsafe_no_jsmanaged_fields!(WebVRDisplayData); unsafe_no_jsmanaged_fields!(WebVRFrameData); unsafe_no_jsmanaged_fields!(WebVRLayer); +unsafe_no_jsmanaged_fields!(VRFrameDataStatus); #[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)] enum VRFrameDataStatus { @@ -96,7 +104,18 @@ enum VRFrameDataStatus { Exit, } -unsafe_no_jsmanaged_fields!(VRFrameDataStatus); +#[derive(Clone, MallocSizeOf)] +struct VRRAFUpdate { + depth_near: f64, + depth_far: f64, + /// WebGL API sender + api_sender: Option, + /// Number uniquely identifying the WebGL context + /// so that we may setup/tear down VR compositors as things change + context_id: usize, +} + +type VRRAFUpdateSender = Sender>; impl VRDisplay { fn new_inherited(global: &GlobalScope, display: WebVRDisplayData) -> VRDisplay { @@ -130,6 +149,8 @@ impl VRDisplay { next_raf_id: Cell::new(1), raf_callback_list: DomRefCell::new(vec![]), xr_raf_callback_list: DomRefCell::new(vec![]), + raf_wakeup_sender: DomRefCell::new(None), + pending_renderstate_updates: DomRefCell::new(vec![]), frame_data_status: Cell::new(VRFrameDataStatus::Waiting), frame_data_receiver: DomRefCell::new(None), running_display_raf: Cell::new(false), @@ -567,6 +588,63 @@ impl VRDisplay { .fire(self.global().upcast::()); } + fn api_sender(&self) -> Option { + self.layer_ctx.get().map(|c| c.webgl_sender()) + } + + fn context_id(&self) -> usize { + self.layer_ctx + .get() + .map(|c| &*c as *const WebGLRenderingContext as usize) + .unwrap_or(0) + } + + fn vr_raf_update(&self) -> VRRAFUpdate { + VRRAFUpdate { + depth_near: self.depth_near.get(), + depth_far: self.depth_far.get(), + api_sender: self.api_sender(), + context_id: self.context_id(), + } + } + + pub fn queue_renderstate(&self, state: &XRRenderStateInit, promise: Rc) { + // can't clone dictionaries + let new_state = XRRenderStateInit { + depthNear: state.depthNear, + depthFar: state.depthFar, + baseLayer: state.baseLayer.clone(), + }; + self.pending_renderstate_updates.borrow_mut().push((new_state, promise)) + } + + fn process_renderstate_queue(&self) { + let mut updates = self.pending_renderstate_updates.borrow_mut(); + if updates.is_empty() { + return; + } + + debug_assert!(self.xr_session.get().is_some()); + for update in updates.drain(..) { + if let Some(near) = update.0.depthNear { + self.depth_near.set(*near); + } + if let Some(far) = update.0.depthFar { + self.depth_far.set(*far); + } + if let Some(ref layer) = update.0.baseLayer { + self.xr_session.get().unwrap().set_layer(&layer); + let layer = layer.downcast::().unwrap(); + self.layer_ctx.set(Some(&layer.Context())); + } + update.1.resolve_native(&()); + } + + if let Some(ref wakeup) = *self.raf_wakeup_sender.borrow() { + let _ = wakeup.send(()); + } + } + fn init_present(&self) { self.presenting.set(true); let xr = self.global().as_window().Navigator().Xr(); @@ -575,13 +653,18 @@ impl VRDisplay { *self.frame_data_receiver.borrow_mut() = Some(sync_receiver); let display_id = self.display.borrow().display_id; - let api_sender = self.layer_ctx.get().unwrap().webgl_sender(); + let mut api_sender = self.api_sender(); + let mut context_id = self.context_id(); let js_sender = self.global().script_chan(); let address = Trusted::new(&*self); - let near_init = self.depth_near.get(); - let far_init = self.depth_far.get(); + let mut near = self.depth_near.get(); + let mut far = self.depth_far.get(); let pipeline_id = self.global().pipeline_id(); + let (raf_sender, raf_receiver) = unbounded(); + let (wakeup_sender, wakeup_receiver) = unbounded(); + *self.raf_wakeup_sender.borrow_mut() = Some(wakeup_sender); + // The render loop at native headset frame rate is implemented using a dedicated thread. // Every loop iteration syncs pose data with the HMD, submits the pixels to the display and waits for Vsync. // Both the requestAnimationFrame call of a VRDisplay in the JavaScript thread and the VRSyncPoses call @@ -591,40 +674,68 @@ impl VRDisplay { thread::Builder::new() .name("WebVR_RAF".into()) .spawn(move || { - let (raf_sender, raf_receiver) = unbounded(); - let mut near = near_init; - let mut far = far_init; - // Initialize compositor - api_sender - .send_vr(WebVRCommand::Create(display_id)) - .unwrap(); - loop { - // Run RAF callbacks on JavaScript thread - let this = address.clone(); - let sender = raf_sender.clone(); - let task = Box::new(task!(handle_vrdisplay_raf: move || { - this.root().handle_raf(&sender); - })); - // NOTE: WebVR spec doesn't specify what task source we should use. Is - // dom-manipulation a good choice long term? - js_sender - .send(CommonScriptMsg::Task( - WebVREvent, - task, - Some(pipeline_id), - TaskSourceName::DOMManipulation, - )) + if let Some(ref api_sender) = api_sender { + api_sender + .send_vr(WebVRCommand::Create(display_id)) .unwrap(); + } + loop { + if let Some(ref api_sender) = api_sender { + // Run RAF callbacks on JavaScript thread + let this = address.clone(); + let sender = raf_sender.clone(); + let task = Box::new(task!(handle_vrdisplay_raf: move || { + this.root().handle_raf(&sender); + })); + // NOTE: WebVR spec doesn't specify what task source we should use. Is + // dom-manipulation a good choice long term? + js_sender + .send(CommonScriptMsg::Task( + WebVREvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )) + .unwrap(); - // Run Sync Poses in parallell on Render thread - let msg = WebVRCommand::SyncPoses(display_id, near, far, sync_sender.clone()); - api_sender.send_vr(msg).unwrap(); + // Run Sync Poses in parallell on Render thread + let msg = + WebVRCommand::SyncPoses(display_id, near, far, sync_sender.clone()); + api_sender.send_vr(msg).unwrap(); + } else { + let _ = wakeup_receiver.recv(); + let sender = raf_sender.clone(); + let this = address.clone(); + let task = Box::new(task!(flush_renderstate_queue: move || { + let this = this.root(); + this.process_renderstate_queue(); + sender.send(Ok(this.vr_raf_update())).unwrap(); + })); + js_sender + .send(CommonScriptMsg::Task( + WebVREvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )) + .unwrap(); + } // Wait until both SyncPoses & RAF ends - if let Ok(depth) = raf_receiver.recv().unwrap() { - near = depth.0; - far = depth.1; + if let Ok(update) = raf_receiver.recv().unwrap() { + near = update.depth_near; + far = update.depth_far; + if update.context_id != context_id { + if let Some(ref api_sender) = update.api_sender { + api_sender + .send_vr(WebVRCommand::Create(display_id)) + .unwrap(); + } + context_id = update.context_id; + } + + api_sender = update.api_sender; } else { // Stop thread // ExitPresent called or some error happened @@ -677,7 +788,7 @@ impl VRDisplay { self.frame_data_status.set(status); } - fn handle_raf(&self, end_sender: &Sender>) { + fn handle_raf(&self, end_sender: &VRRAFUpdateSender) { self.frame_data_status.set(VRFrameDataStatus::Waiting); let now = self.global().as_window().Performance().Now(); @@ -717,12 +828,11 @@ impl VRDisplay { } } + self.process_renderstate_queue(); match self.frame_data_status.get() { VRFrameDataStatus::Synced => { // Sync succeeded. Notify RAF thread. - end_sender - .send(Ok((self.depth_near.get(), self.depth_far.get()))) - .unwrap(); + end_sender.send(Ok(self.vr_raf_update())).unwrap(); }, VRFrameDataStatus::Exit | VRFrameDataStatus::Waiting => { // ExitPresent called or some error ocurred. diff --git a/components/script/dom/webidls/XRRenderState.webidl b/components/script/dom/webidls/XRRenderState.webidl index 3cf622cfb9b..feaf6160db3 100644 --- a/components/script/dom/webidls/XRRenderState.webidl +++ b/components/script/dom/webidls/XRRenderState.webidl @@ -5,9 +5,9 @@ // https://immersive-web.github.io/webxr/#xrrenderstate-interface dictionary XRRenderStateInit { - double depthNear = 0.1; - double depthFar = 1000.0; - XRLayer? baseLayer = null; + double depthNear; + double depthFar; + XRLayer baseLayer; }; [SecureContext, Exposed=Window] interface XRRenderState { diff --git a/components/script/dom/webidls/XRSession.webidl b/components/script/dom/webidls/XRSession.webidl index 3184a1b7787..5f2cfb23929 100644 --- a/components/script/dom/webidls/XRSession.webidl +++ b/components/script/dom/webidls/XRSession.webidl @@ -26,6 +26,7 @@ interface XRSession : EventTarget { // FrozenArray getInputSources(); + Promise updateRenderState(optional XRRenderStateInit state); long requestAnimationFrame(XRFrameRequestCallback callback); void cancelAnimationFrame(long handle); diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index b4e6f7c728e..52ed2e3526f 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -4,6 +4,7 @@ use crate::dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods; use crate::dom::bindings::codegen::Bindings::XRBinding::XRSessionMode; +use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit; use crate::dom::bindings::codegen::Bindings::XRSessionBinding; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XREnvironmentBlendMode; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCallback; @@ -23,7 +24,6 @@ use crate::dom::vrdisplay::VRDisplay; use crate::dom::xrlayer::XRLayer; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrstationaryreferencespace::XRStationaryReferenceSpace; -use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::dom::xrrenderstate::XRRenderState; use dom_struct::dom_struct; use std::rc::Rc; @@ -58,6 +58,10 @@ impl XRSession { pub fn xr_present(&self, p: Rc) { self.display.xr_present(self, None, Some(p)); } + + pub fn set_layer(&self, layer: &XRLayer) { + self.base_layer.set(Some(layer)) + } } impl XRSessionMethods for XRSession { @@ -69,8 +73,19 @@ impl XRSessionMethods for XRSession { // https://immersive-web.github.io/webxr/#dom-xrsession-renderstate fn RenderState(&self) -> DomRoot { // XXXManishearth maybe cache this - XRRenderState::new(&self.global(), *self.display.DepthNear(), *self.display.DepthFar(), - self.base_layer.get().as_ref().map(|l| &**l)) + XRRenderState::new( + &self.global(), + *self.display.DepthNear(), + *self.display.DepthFar(), + self.base_layer.get().as_ref().map(|l| &**l), + ) + } + + /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe + fn UpdateRenderState(&self, init: &XRRenderStateInit) -> Rc { + let p = Promise::new(&self.global()); + self.display.queue_renderstate(init, p.clone()); + p } /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe From 87558c3a17307df22f63aaa4af4a9fa1ab716d4a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 13:10:11 -0800 Subject: [PATCH 04/11] Shut down old VR compositor when the layer is replaced --- components/script/dom/vrdisplay.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index db474f967bb..99b082b703a 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -732,6 +732,12 @@ impl VRDisplay { .send_vr(WebVRCommand::Create(display_id)) .unwrap(); } + if let Some(ref api_sender) = api_sender { + // shut down old vr compositor + api_sender + .send_vr(WebVRCommand::Release(display_id)) + .unwrap(); + } context_id = update.context_id; } From 1a64b217af14bc1e2b287e4edb09109412486a91 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 13:14:13 -0800 Subject: [PATCH 05/11] Improve handling of missing layer_ctx --- components/script/dom/vrdisplay.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index 99b082b703a..597d80d3343 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -437,7 +437,8 @@ impl VRDisplayMethods for VRDisplay { let display_id = self.display.borrow().display_id; let layer = self.layer.borrow(); let msg = WebVRCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds); - self.layer_ctx.get().unwrap().send_vr_command(msg); + self.layer_ctx.get().expect("SubmitFrame can only be called when there is a webgl layer") + .send_vr_command(msg); } // https://w3c.github.io/webvr/spec/1.1/#dom-vrdisplay-getlayers @@ -757,12 +758,12 @@ impl VRDisplay { let xr = self.global().as_window().Navigator().Xr(); xr.deactivate_session(); *self.frame_data_receiver.borrow_mut() = None; - - let api_sender = self.layer_ctx.get().unwrap().webgl_sender(); - let display_id = self.display.borrow().display_id; - api_sender - .send_vr(WebVRCommand::Release(display_id)) - .unwrap(); + if let Some(api_sender) = self.api_sender() { + let display_id = self.display.borrow().display_id; + api_sender + .send_vr(WebVRCommand::Release(display_id)) + .unwrap(); + } } // Only called when the JSContext is destroyed while presenting. From d0a6884b2c44789aa518ca499a6f4218faf15e59 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 14:06:22 -0800 Subject: [PATCH 06/11] Include webxr pref in prefs file --- resources/prefs.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/prefs.json b/resources/prefs.json index 85ae67919ad..a9478ae44c1 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -28,6 +28,7 @@ "dom.webrtc.enabled": false, "dom.webvr.enabled": false, "dom.webvr.event_polling_interval": 500, + "dom.webxr.enabled": false, "dom.webvr.test": false, "dom.worklet.timeout_ms": 10, "gfx.subpixel-text-antialiasing.enabled": true, From 798c88917bbfe1fcffeba678153e8aa2d238bbf8 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 14:13:58 -0800 Subject: [PATCH 07/11] Trigger wakeup when queuing, not when processing --- components/script/dom/vrdisplay.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index 597d80d3343..93749a6777e 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -437,8 +437,10 @@ impl VRDisplayMethods for VRDisplay { let display_id = self.display.borrow().display_id; let layer = self.layer.borrow(); let msg = WebVRCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds); - self.layer_ctx.get().expect("SubmitFrame can only be called when there is a webgl layer") - .send_vr_command(msg); + self.layer_ctx + .get() + .expect("SubmitFrame can only be called when there is a webgl layer") + .send_vr_command(msg); } // https://w3c.github.io/webvr/spec/1.1/#dom-vrdisplay-getlayers @@ -616,16 +618,19 @@ impl VRDisplay { depthFar: state.depthFar, baseLayer: state.baseLayer.clone(), }; - self.pending_renderstate_updates.borrow_mut().push((new_state, promise)) + self.pending_renderstate_updates + .borrow_mut() + .push((new_state, promise)); + + if let Some(ref wakeup) = *self.raf_wakeup_sender.borrow() { + let _ = wakeup.send(()); + } } fn process_renderstate_queue(&self) { let mut updates = self.pending_renderstate_updates.borrow_mut(); - if updates.is_empty() { - return; - } - debug_assert!(self.xr_session.get().is_some()); + debug_assert!(updates.is_empty() || self.xr_session.get().is_some()); for update in updates.drain(..) { if let Some(near) = update.0.depthNear { self.depth_near.set(*near); @@ -640,10 +645,6 @@ impl VRDisplay { } update.1.resolve_native(&()); } - - if let Some(ref wakeup) = *self.raf_wakeup_sender.borrow() { - let _ = wakeup.send(()); - } } fn init_present(&self) { From 9ea28c4c63694220d6ac1fd22629087e84caaa1a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 14:47:03 -0800 Subject: [PATCH 08/11] Flush renderstate queue before presenting --- components/script/dom/vrdisplay.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index 93749a6777e..02aaeada707 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -651,6 +651,7 @@ impl VRDisplay { self.presenting.set(true); let xr = self.global().as_window().Navigator().Xr(); xr.set_active_immersive_session(&self); + self.process_renderstate_queue(); let (sync_sender, sync_receiver) = webgl_channel().unwrap(); *self.frame_data_receiver.borrow_mut() = Some(sync_receiver); From 0038ddf51ecd04887ab4ac00d2644874c68a3df0 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 15 Jan 2019 16:18:02 -0800 Subject: [PATCH 09/11] Avoid double-spawning RAF thread --- components/script/dom/vrdisplay.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index 02aaeada707..789f0e5936e 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -61,6 +61,7 @@ pub struct VRDisplay { depth_near: Cell, depth_far: Cell, presenting: Cell, + has_raf_thread: Cell, left_eye_params: MutDom, right_eye_params: MutDom, capabilities: MutDom, @@ -130,6 +131,7 @@ impl VRDisplay { depth_near: Cell::new(0.01), depth_far: Cell::new(10000.0), presenting: Cell::new(false), + has_raf_thread: Cell::new(false), left_eye_params: MutDom::new(&*VREyeParameters::new( display.left_eye_parameters.clone(), &global, @@ -652,6 +654,10 @@ impl VRDisplay { let xr = self.global().as_window().Navigator().Xr(); xr.set_active_immersive_session(&self); self.process_renderstate_queue(); + if self.has_raf_thread.get() { + return; + } + self.has_raf_thread.set(true); let (sync_sender, sync_receiver) = webgl_channel().unwrap(); *self.frame_data_receiver.borrow_mut() = Some(sync_receiver); @@ -760,6 +766,7 @@ impl VRDisplay { let xr = self.global().as_window().Navigator().Xr(); xr.deactivate_session(); *self.frame_data_receiver.borrow_mut() = None; + self.has_raf_thread.set(false); if let Some(api_sender) = self.api_sender() { let display_id = self.display.borrow().display_id; api_sender From 922883f35ad6a0f1f85556717e55c901abb9bd92 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 25 Mar 2019 09:13:56 -0700 Subject: [PATCH 10/11] Pref-gate XRRenderState --- components/script/dom/webidls/XRRenderState.webidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/script/dom/webidls/XRRenderState.webidl b/components/script/dom/webidls/XRRenderState.webidl index feaf6160db3..195d5acf3ac 100644 --- a/components/script/dom/webidls/XRRenderState.webidl +++ b/components/script/dom/webidls/XRRenderState.webidl @@ -10,7 +10,7 @@ dictionary XRRenderStateInit { XRLayer baseLayer; }; -[SecureContext, Exposed=Window] interface XRRenderState { +[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"] interface XRRenderState { readonly attribute double depthNear; readonly attribute double depthFar; readonly attribute XRLayer? baseLayer; From 1e1f527c8271463adea170eb9e93508fa0c261b5 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 25 Mar 2019 09:14:29 -0700 Subject: [PATCH 11/11] Formatting changes --- components/script/dom/xrsession.rs | 7 ++----- resources/prefs.json | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 52ed2e3526f..575d0be2977 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -11,11 +11,8 @@ use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCal use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRReferenceSpaceOptions; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRReferenceSpaceType; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRSessionMethods; -use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods; use crate::dom::bindings::error::Error; -use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::num::Finite; -use crate::dom::bindings::reflector::{DomObject, reflect_dom_object}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -23,8 +20,8 @@ use crate::dom::promise::Promise; use crate::dom::vrdisplay::VRDisplay; use crate::dom::xrlayer::XRLayer; use crate::dom::xrreferencespace::XRReferenceSpace; -use crate::dom::xrstationaryreferencespace::XRStationaryReferenceSpace; use crate::dom::xrrenderstate::XRRenderState; +use crate::dom::xrstationaryreferencespace::XRStationaryReferenceSpace; use dom_struct::dom_struct; use std::rc::Rc; diff --git a/resources/prefs.json b/resources/prefs.json index a9478ae44c1..1ca4197be44 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -28,8 +28,8 @@ "dom.webrtc.enabled": false, "dom.webvr.enabled": false, "dom.webvr.event_polling_interval": 500, - "dom.webxr.enabled": false, "dom.webvr.test": false, + "dom.webxr.enabled": false, "dom.worklet.timeout_ms": 10, "gfx.subpixel-text-antialiasing.enabled": true, "js.asmjs.enabled": true,