aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/vrdisplay.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/vrdisplay.rs')
-rw-r--r--components/script/dom/vrdisplay.rs840
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]),
- ]
-}