diff options
Diffstat (limited to 'components/script/dom/vrdisplay.rs')
-rw-r--r-- | components/script/dom/vrdisplay.rs | 840 |
1 files changed, 0 insertions, 840 deletions
diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs deleted file mode 100644 index 2d59aa6a844..00000000000 --- a/components/script/dom/vrdisplay.rs +++ /dev/null @@ -1,840 +0,0 @@ -/* 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::callback::ExceptionHandling; -use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods; -use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods; -use crate::dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods; -use crate::dom::bindings::codegen::Bindings::VRDisplayBinding::VREye; -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::error::Error; -use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::num::Finite; -use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; -use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; -use crate::dom::bindings::root::{DomRoot, MutDom, MutNullableDom}; -use crate::dom::bindings::str::DOMString; -use crate::dom::event::Event; -use crate::dom::eventtarget::EventTarget; -use crate::dom::promise::Promise; -use crate::dom::vrdisplaycapabilities::VRDisplayCapabilities; -use crate::dom::vrdisplayevent::VRDisplayEvent; -use crate::dom::vreyeparameters::VREyeParameters; -use crate::dom::vrframedata::VRFrameData; -use crate::dom::vrpose::VRPose; -use crate::dom::vrstageparameters::VRStageParameters; -use crate::dom::webglrenderingcontext::{WebGLMessageSender, WebGLRenderingContext}; -use crate::dom::window::Window; -use crate::realms::InRealm; -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 crossbeam_channel::{unbounded, Sender}; -use dom_struct::dom_struct; -use ipc_channel::ipc::IpcSender; -use msg::constellation_msg::PipelineId; -use profile_traits::ipc; -use std::cell::Cell; -use std::mem; -use std::rc::Rc; -use std::thread; -use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRPoseInformation}; -use webvr_traits::{WebVRLayer, WebVRMsg}; - -#[dom_struct] -pub struct VRDisplay { - eventtarget: EventTarget, - #[ignore_malloc_size_of = "Defined in rust-webvr"] - display: DomRefCell<WebVRDisplayData>, - depth_near: Cell<f64>, - depth_far: Cell<f64>, - presenting: Cell<bool>, - has_raf_thread: Cell<bool>, - left_eye_params: MutDom<VREyeParameters>, - right_eye_params: MutDom<VREyeParameters>, - capabilities: MutDom<VRDisplayCapabilities>, - stage_params: MutNullableDom<VRStageParameters>, - #[ignore_malloc_size_of = "Defined in rust-webvr"] - frame_data: DomRefCell<WebVRFrameData>, - #[ignore_malloc_size_of = "Defined in rust-webvr"] - layer: DomRefCell<WebVRLayer>, - layer_ctx: MutNullableDom<WebGLRenderingContext>, - #[ignore_malloc_size_of = "Defined in rust-webvr"] - next_raf_id: Cell<u32>, - /// List of request animation frame callbacks - #[ignore_malloc_size_of = "closures are hard"] - raf_callback_list: DomRefCell<Vec<(u32, Option<Rc<FrameRequestCallback>>)>>, - /// When there isn't any layer_ctx the RAF thread needs to be "woken up" - raf_wakeup_sender: DomRefCell<Option<Sender<()>>>, - #[ignore_malloc_size_of = "Rc is hard"] - pending_renderstate_updates: DomRefCell<Vec<(XRRenderStateInit, Rc<Promise>)>>, - // Compositor VRFrameData synchonization - frame_data_status: Cell<VRFrameDataStatus>, - #[ignore_malloc_size_of = "closures are hard"] - frame_data_receiver: DomRefCell<Option<WebGLReceiver<Result<WebVRPoseInformation, ()>>>>, - running_display_raf: Cell<bool>, - paused: Cell<bool>, - stopped_on_pause: Cell<bool>, - #[ignore_malloc_size_of = "channels are hard"] - webvr_thread: IpcSender<WebVRMsg>, - pipeline: PipelineId, -} - -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 { - Waiting, - Synced, - Exit, -} - -#[derive(Clone, MallocSizeOf)] -struct VRRAFUpdate { - depth_near: f64, - depth_far: f64, - /// WebGL API sender - api_sender: Option<WebGLMessageSender>, - /// Number uniquely identifying the WebGL context - /// so that we may setup/tear down VR compositors as things change - context_id: usize, -} - -type VRRAFUpdateSender = Sender<Result<VRRAFUpdate, ()>>; - -impl VRDisplay { - fn new_inherited(global: &Window, display: WebVRDisplayData) -> VRDisplay { - let stage = match display.stage_parameters { - Some(ref params) => Some(VRStageParameters::new(params.clone(), &global)), - None => None, - }; - - VRDisplay { - eventtarget: EventTarget::new_inherited(), - display: DomRefCell::new(display.clone()), - 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, - )), - right_eye_params: MutDom::new(&*VREyeParameters::new( - display.right_eye_parameters.clone(), - &global, - )), - capabilities: MutDom::new(&*VRDisplayCapabilities::new( - display.capabilities.clone(), - &global, - )), - stage_params: MutNullableDom::new(stage.as_deref()), - frame_data: DomRefCell::new(Default::default()), - layer: DomRefCell::new(Default::default()), - layer_ctx: MutNullableDom::default(), - next_raf_id: Cell::new(1), - 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), - // Some VR implementations (e.g. Daydream) can be paused in some life cycle situations - // such as showing and hiding the controller pairing screen. - paused: Cell::new(false), - // This flag is set when the Display was presenting when it received a VR Pause event. - // When the VR Resume event is received and the flag is set, VR presentation automatically restarts. - stopped_on_pause: Cell::new(false), - webvr_thread: global.webvr_thread().expect("webvr is disabled"), - pipeline: global.pipeline_id(), - } - } - - pub fn new(global: &Window, display: WebVRDisplayData) -> DomRoot<VRDisplay> { - reflect_dom_object(Box::new(VRDisplay::new_inherited(&global, display)), global) - } -} - -impl Drop for VRDisplay { - fn drop(&mut self) { - if self.presenting.get() { - self.force_stop_present(); - } - } -} - -impl VRDisplayMethods for VRDisplay { - // https://w3c.github.io/webvr/#dom-vrdisplay-isconnected - fn IsConnected(&self) -> bool { - self.display.borrow().connected - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-ispresenting - fn IsPresenting(&self) -> bool { - self.presenting.get() - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-capabilities - fn Capabilities(&self) -> DomRoot<VRDisplayCapabilities> { - DomRoot::from_ref(&*self.capabilities.get()) - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-stageparameters - fn GetStageParameters(&self) -> Option<DomRoot<VRStageParameters>> { - self.stage_params.get().map(|s| DomRoot::from_ref(&*s)) - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-geteyeparameters - fn GetEyeParameters(&self, eye: VREye) -> DomRoot<VREyeParameters> { - match eye { - VREye::Left => DomRoot::from_ref(&*self.left_eye_params.get()), - VREye::Right => DomRoot::from_ref(&*self.right_eye_params.get()), - } - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-displayid - fn DisplayId(&self) -> u32 { - self.display.borrow().display_id - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-displayname - fn DisplayName(&self) -> DOMString { - DOMString::from(self.display.borrow().display_name.clone()) - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-getframedata-framedata-framedata - fn GetFrameData(&self, frame_data: &VRFrameData) -> bool { - // If presenting we use a synced data with compositor for the whole frame. - // Frame data is only synced with compositor when GetFrameData is called from - // inside the VRDisplay.requestAnimationFrame. This is checked using the running_display_raf property. - // This check avoids data race conditions when calling GetFrameData from outside of the - // VRDisplay.requestAnimationFrame callbacks and fixes a possible deadlock during the interval - // when the requestAnimationFrame is moved from window to VRDisplay. - if self.presenting.get() && self.running_display_raf.get() { - if self.frame_data_status.get() == VRFrameDataStatus::Waiting { - self.sync_frame_data(); - } - frame_data.update(&self.frame_data.borrow()); - return true; - } - - // If not presenting we fetch inmediante VRFrameData - let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread - .send(WebVRMsg::GetFrameData( - self.global().pipeline_id(), - self.DisplayId(), - self.depth_near.get(), - self.depth_far.get(), - sender, - )) - .unwrap(); - return match receiver.recv().unwrap() { - Ok(data) => { - frame_data.update(&data); - true - }, - Err(e) => { - error!("WebVR::GetFrameData: {:?}", e); - false - }, - }; - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-getpose - fn GetPose(&self) -> DomRoot<VRPose> { - VRPose::new(&self.global(), &self.frame_data.borrow().pose) - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-resetpose - fn ResetPose(&self) { - let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread - .send(WebVRMsg::ResetPose( - self.global().pipeline_id(), - self.DisplayId(), - sender, - )) - .unwrap(); - if let Ok(data) = receiver.recv().unwrap() { - // Some VRDisplay data might change after calling ResetPose() - *self.display.borrow_mut() = data; - } - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-depthnear - fn DepthNear(&self) -> Finite<f64> { - Finite::wrap(self.depth_near.get()) - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-depthnear - fn SetDepthNear(&self, value: Finite<f64>) { - self.depth_near.set(*value); - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-depthfar - fn DepthFar(&self) -> Finite<f64> { - Finite::wrap(self.depth_far.get()) - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-depthfar - fn SetDepthFar(&self, value: Finite<f64>) { - self.depth_far.set(*value); - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-requestanimationframe - fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 { - if self.presenting.get() { - let raf_id = self.next_raf_id.get(); - self.next_raf_id.set(raf_id + 1); - self.raf_callback_list - .borrow_mut() - .push((raf_id, Some(callback))); - raf_id - } else { - // WebVR spec: When a VRDisplay is not presenting it should - // fallback to window.requestAnimationFrame. - self.global().as_window().RequestAnimationFrame(callback) - } - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-cancelanimationframe - fn CancelAnimationFrame(&self, handle: u32) { - if self.presenting.get() { - let mut list = self.raf_callback_list.borrow_mut(); - if let Some(pair) = list.iter_mut().find(|pair| pair.0 == handle) { - pair.1 = None; - } - } else { - // WebVR spec: When a VRDisplay is not presenting it should - // fallback to window.cancelAnimationFrame. - self.global().as_window().CancelAnimationFrame(handle); - } - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-requestpresent - fn RequestPresent(&self, layers: Vec<VRLayer>, comp: InRealm) -> Rc<Promise> { - let promise = Promise::new_in_current_realm(&self.global(), comp); - // TODO: WebVR spec: this method must be called in response to a user gesture - - // WebVR spec: If canPresent is false the promise MUST be rejected - if !self.display.borrow().capabilities.can_present { - let msg = "VRDisplay canPresent is false".to_string(); - promise.reject_native(&msg); - return promise; - } - - // Current WebVRSpec only allows 1 VRLayer if the VRDevice can present. - // Future revisions of this spec may allow multiple layers to enable more complex rendering effects - // such as compositing WebGL and DOM elements together. - // That functionality is not allowed by this revision of the spec. - if layers.len() != 1 { - let msg = "The number of layers must be 1".to_string(); - promise.reject_native(&msg); - return promise; - } - - // Parse and validate received VRLayer - let layer = validate_layer(&layers[0]); - - let layer_bounds; - let layer_ctx; - - match layer { - Ok((bounds, ctx)) => { - layer_bounds = bounds; - layer_ctx = ctx; - }, - Err(msg) => { - let msg = msg.to_string(); - promise.reject_native(&msg); - return promise; - }, - }; - - // WebVR spec: Repeat calls while already presenting will update the VRLayers being displayed. - if self.presenting.get() { - *self.layer.borrow_mut() = layer_bounds; - self.layer_ctx.set(Some(&layer_ctx)); - promise.resolve_native(&()); - return promise; - } - - let xr = self.global().as_window().Navigator().Xr(); - - if xr.pending_or_active_session() { - // WebVR spec doesn't mandate anything here, however - // the WebXR spec expects there to be only one immersive XR session at a time, - // and WebVR is deprecated - promise.reject_error(Error::InvalidState); - return promise; - } - - self.request_present(layer_bounds, Some(&layer_ctx), Some(promise.clone()), |p| { - p.resolve_native(&()) - }); - promise - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-exitpresent - fn ExitPresent(&self, comp: InRealm) -> Rc<Promise> { - let promise = Promise::new_in_current_realm(&self.global(), comp); - - // WebVR spec: If the VRDisplay is not presenting the promise MUST be rejected. - if !self.presenting.get() { - let msg = "VRDisplay is not presenting".to_string(); - promise.reject_native(&msg); - return promise; - } - - // Exit present - let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread - .send(WebVRMsg::ExitPresent( - self.global().pipeline_id(), - self.display.borrow().display_id, - Some(sender), - )) - .unwrap(); - match receiver.recv().unwrap() { - Ok(()) => { - self.stop_present(); - promise.resolve_native(&()); - }, - Err(e) => { - promise.reject_native(&e); - }, - } - - promise - } - - // https://w3c.github.io/webvr/#dom-vrdisplay-submitframe - fn SubmitFrame(&self) { - if !self.presenting.get() { - warn!("VRDisplay not presenting"); - return; - } - - 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); - } - - // https://w3c.github.io/webvr/spec/1.1/#dom-vrdisplay-getlayers - fn GetLayers(&self) -> Vec<VRLayer> { - // WebVR spec: MUST return an empty array if the VRDisplay is not currently presenting - if !self.presenting.get() { - return Vec::new(); - } - - let layer = self.layer.borrow(); - - vec![VRLayer { - leftBounds: Some(bounds_to_vec(&layer.left_bounds)), - rightBounds: Some(bounds_to_vec(&layer.right_bounds)), - source: self.layer_ctx.get().map(|ctx| ctx.Canvas()), - }] - } -} - -impl VRDisplay { - pub fn update_display(&self, display: &WebVRDisplayData) { - *self.display.borrow_mut() = display.clone(); - if let Some(ref stage) = display.stage_parameters { - if self.stage_params.get().is_none() { - let params = Some(VRStageParameters::new( - stage.clone(), - &self.global().as_window(), - )); - self.stage_params.set(params.as_deref()); - } else { - self.stage_params.get().unwrap().update(&stage); - } - } else { - self.stage_params.set(None); - } - } - - pub fn request_present<F>( - &self, - layer_bounds: WebVRLayer, - ctx: Option<&WebGLRenderingContext>, - promise: Option<Rc<Promise>>, - resolve: F, - ) where - F: FnOnce(Rc<Promise>) + Send + 'static, - { - // Request Present - let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread - .send(WebVRMsg::RequestPresent( - self.global().pipeline_id(), - self.display.borrow().display_id, - sender, - )) - .unwrap(); - let promise = promise.map(TrustedPromise::new); - let this = Trusted::new(self); - let ctx = ctx.map(|c| Trusted::new(c)); - let global = self.global(); - let window = global.as_window(); - let (task_source, canceller) = window - .task_manager() - .dom_manipulation_task_source_with_canceller(); - thread::spawn(move || { - let recv = receiver.recv().unwrap(); - let _ = task_source.queue_with_canceller( - task!(vr_presenting: move || { - let this = this.root(); - let promise = promise.map(|p| p.root()); - let ctx = ctx.map(|c| c.root()); - match recv { - Ok(()) => { - *this.layer.borrow_mut() = layer_bounds; - this.layer_ctx.set(ctx.as_deref()); - this.init_present(); - promise.map(resolve); - }, - Err(e) => { - promise.map(|p| p.reject_native(&e)); - }, - } - }), - &canceller, - ); - }); - } - - pub fn handle_webvr_event(&self, event: &WebVRDisplayEvent) { - match *event { - WebVRDisplayEvent::Connect(ref display) => { - self.update_display(&display); - }, - WebVRDisplayEvent::Disconnect(_id) => { - self.display.borrow_mut().connected = false; - }, - WebVRDisplayEvent::Activate(ref display, _) | - WebVRDisplayEvent::Deactivate(ref display, _) | - WebVRDisplayEvent::Blur(ref display) | - WebVRDisplayEvent::Focus(ref display) => { - self.update_display(&display); - self.notify_event(&event); - }, - WebVRDisplayEvent::PresentChange(ref display, presenting) => { - self.update_display(&display); - self.presenting.set(presenting); - self.notify_event(&event); - }, - WebVRDisplayEvent::Change(ref display) => { - // Change event doesn't exist in WebVR spec. - // So we update display data but don't notify JS. - self.update_display(&display); - }, - WebVRDisplayEvent::Pause(_) => { - if self.paused.get() { - return; - } - self.paused.set(true); - if self.presenting.get() { - self.stop_present(); - self.stopped_on_pause.set(true); - } - }, - WebVRDisplayEvent::Resume(_) => { - self.paused.set(false); - if self.stopped_on_pause.get() { - self.stopped_on_pause.set(false); - self.init_present(); - } - }, - WebVRDisplayEvent::Exit(_) => { - self.stopped_on_pause.set(false); - if self.presenting.get() { - self.stop_present(); - } - }, - }; - } - - fn notify_event(&self, event: &WebVRDisplayEvent) { - let root = DomRoot::from_ref(&*self); - let event = VRDisplayEvent::new_from_webvr(&self.global(), &root, &event); - event - .upcast::<Event>() - .fire(self.global().upcast::<EventTarget>()); - } - - fn api_sender(&self) -> Option<WebGLMessageSender> { - 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(), - } - } - - fn init_present(&self) { - self.presenting.set(true); - 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); - - let display_id = self.display.borrow().display_id; - 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 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 - // in the Webrender thread are executed in parallel. This allows to get some JavaScript code executed ahead. - // while the render thread is syncing the VRFrameData to be used for the current frame. - // This thread runs until the user calls ExitPresent, the tab is closed or some unexpected error happened. - thread::Builder::new() - .name("WebVR_RAF".into()) - .spawn(move || { - // Initialize compositor - 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, - false, - 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(); - 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(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(); - } - 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; - } - - api_sender = update.api_sender; - } else { - // Stop thread - // ExitPresent called or some error happened - return; - } - } - }) - .expect("Thread spawning failed"); - } - - fn stop_present(&self) { - self.presenting.set(false); - *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 - .send_vr(WebVRCommand::Release(display_id)) - .unwrap(); - } - } - - // Only called when the JSContext is destroyed while presenting. - // In this case we don't want to wait for WebVR Thread response. - fn force_stop_present(&self) { - self.webvr_thread - .send(WebVRMsg::ExitPresent( - self.global().pipeline_id(), - self.display.borrow().display_id, - None, - )) - .unwrap(); - self.stop_present(); - } - - fn sync_frame_data(&self) { - let status = if let Some(receiver) = self.frame_data_receiver.borrow().as_ref() { - match receiver.recv().unwrap() { - Ok(pose) => { - *self.frame_data.borrow_mut() = pose.frame.block(); - VRFrameDataStatus::Synced - }, - Err(()) => VRFrameDataStatus::Exit, - } - } else { - VRFrameDataStatus::Exit - }; - - self.frame_data_status.set(status); - } - - fn handle_raf(&self, end_sender: &VRRAFUpdateSender) { - self.frame_data_status.set(VRFrameDataStatus::Waiting); - - let now = self.global().as_window().Performance().Now(); - - self.running_display_raf.set(true); - let mut callbacks = mem::replace(&mut *self.raf_callback_list.borrow_mut(), vec![]); - // Call registered VRDisplay.requestAnimationFrame callbacks. - for (_, callback) in callbacks.drain(..) { - if let Some(callback) = callback { - let _ = callback.Call__(Finite::wrap(*now), ExceptionHandling::Report); - } - } - - self.running_display_raf.set(false); - if self.frame_data_status.get() == VRFrameDataStatus::Waiting { - // User didn't call getFrameData while presenting. - // We automatically reads the pending VRFrameData to avoid overflowing the IPC-Channel buffers. - // Show a warning as the WebVR Spec recommends. - warn!("WebVR: You should call GetFrameData while presenting"); - self.sync_frame_data(); - } - - match self.frame_data_status.get() { - VRFrameDataStatus::Synced => { - // Sync succeeded. Notify RAF thread. - end_sender.send(Ok(self.vr_raf_update())).unwrap(); - }, - VRFrameDataStatus::Exit | VRFrameDataStatus::Waiting => { - // ExitPresent called or some error ocurred. - // Notify VRDisplay RAF thread to stop. - end_sender.send(Err(())).unwrap(); - }, - } - } -} - -// WebVR Spec: If the number of values in the leftBounds/rightBounds arrays -// is not 0 or 4 for any of the passed layers the promise is rejected -fn parse_bounds(src: &Option<Vec<Finite<f32>>>, dst: &mut [f32; 4]) -> Result<(), &'static str> { - match *src { - Some(ref values) => { - if values.len() == 0 { - return Ok(()); - } - if values.len() != 4 { - return Err( - "The number of values in the leftBounds/rightBounds arrays must be 0 or 4", - ); - } - for i in 0..4 { - dst[i] = *values[i]; - } - Ok(()) - }, - None => Ok(()), - } -} - -fn validate_layer( - layer: &VRLayer, -) -> Result<(WebVRLayer, DomRoot<WebGLRenderingContext>), &'static str> { - let ctx = layer - .source - .as_ref() - .map(|ref s| s.get_base_webgl_context()) - .unwrap_or(None); - if let Some(ctx) = ctx { - let mut data = WebVRLayer::default(); - parse_bounds(&layer.leftBounds, &mut data.left_bounds)?; - parse_bounds(&layer.rightBounds, &mut data.right_bounds)?; - Ok((data, ctx)) - } else { - Err("VRLayer source must be a WebGL Context") - } -} - -fn bounds_to_vec(src: &[f32; 4]) -> Vec<Finite<f32>> { - vec![ - Finite::wrap(src[0]), - Finite::wrap(src[1]), - Finite::wrap(src[2]), - Finite::wrap(src[3]), - ] -} |