diff options
author | Daniel Adams <70986246+msub2@users.noreply.github.com> | 2024-08-16 17:36:52 -1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-17 03:36:52 +0000 |
commit | 20273b062af969152635306c3df2a3a1364ac4d1 (patch) | |
tree | ec58d2b37e920958aebe9130e6581d0a0faa0924 /components/script/dom/xrsession.rs | |
parent | f0045a76866f2d56d6a01aaf93ec20177dad778a (diff) | |
download | servo-20273b062af969152635306c3df2a3a1364ac4d1.tar.gz servo-20273b062af969152635306c3df2a3a1364ac4d1.zip |
webxr: Update XRSession to latest spec (#33059)
* Add missing XRSession members, initial implementations
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Implement supportedFramerates getter
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Implement framerate changes, add spec links
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update WPT expectations
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* ./mach fmt
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add missing spec link
Signed-off-by: Daniel Adams <msub2official@gmail.com>
---------
Signed-off-by: Daniel Adams <msub2official@gmail.com>
Diffstat (limited to 'components/script/dom/xrsession.rs')
-rw-r--r-- | components/script/dom/xrsession.rs | 126 |
1 files changed, 125 insertions, 1 deletions
diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 9b42c146a0f..6cfd8fda59e 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -5,15 +5,19 @@ use std::cell::Cell; use std::collections::HashMap; use std::f64::consts::{FRAC_PI_2, PI}; -use std::mem; use std::rc::Rc; +use std::{mem, ptr}; use dom_struct::dom_struct; use euclid::{RigidTransform3D, Transform3D, Vector3D}; use ipc_channel::ipc::IpcReceiver; use ipc_channel::router::ROUTER; +use js::jsapi::JSObject; +use js::jsval::JSVal; +use js::typedarray::Float32Array; use metrics::ToMs; use profile_traits::ipc; +use servo_atoms::Atom; use webxr_api::{ self, util, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode, Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray, @@ -21,6 +25,7 @@ use webxr_api::{ }; use super::bindings::trace::HashMapTracedValues; +use crate::dom::bindings::buffer_source::create_buffer_source; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods; @@ -39,9 +44,11 @@ use crate::dom::bindings::codegen::Bindings::XRSessionBinding::{ use crate::dom::bindings::codegen::Bindings::XRSystemBinding::XRSessionMode; use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; +use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -56,6 +63,7 @@ use crate::dom::xrrenderstate::XRRenderState; use crate::dom::xrsessionevent::XRSessionEvent; use crate::dom::xrspace::XRSpace; use crate::realms::InRealm; +use crate::script_runtime::JSContext; use crate::task_source::TaskSource; #[dom_struct] @@ -97,6 +105,9 @@ pub struct XRSession { #[ignore_malloc_size_of = "defined in webxr"] #[no_trace] input_frames: DomRefCell<HashMap<InputId, InputFrame>>, + framerate: Cell<f32>, + #[ignore_malloc_size_of = "promises are hard"] + update_framerate_promise: DomRefCell<Option<Rc<Promise>>>, } impl XRSession { @@ -128,6 +139,8 @@ impl XRSession { pending_hit_test_promises: DomRefCell::new(HashMapTracedValues::new()), outside_raf: Cell::new(true), input_frames: DomRefCell::new(HashMap::new()), + framerate: Cell::new(0.0), + update_framerate_promise: DomRefCell::new(None), } } @@ -543,6 +556,24 @@ impl XRSession { _ => self.session.borrow_mut().apply_event(event), } } + + /// <https://www.w3.org/TR/webxr/#apply-the-nominal-frame-rate> + fn apply_nominal_framerate(&self, rate: f32) { + if self.framerate.get() == rate || self.ended.get() { + return; + } + + self.framerate.set(rate); + + let event = XRSessionEvent::new( + &self.global(), + Atom::from("frameratechange"), + false, + false, + self, + ); + event.upcast::<Event>().fire(self.upcast()); + } } impl XRSessionMethods for XRSession { @@ -581,6 +612,9 @@ impl XRSessionMethods for XRSession { SetOninputsourceschange ); + // https://www.w3.org/TR/webxr/#dom-xrsession-onframeratechange + event_handler!(frameratechange, GetOnframeratechange, SetOnframeratechange); + // https://immersive-web.github.io/webxr/#dom-xrsession-renderstate fn RenderState(&self) -> DomRoot<XRRenderState> { self.active_render_state.get() @@ -881,6 +915,96 @@ impl XRSessionMethods for XRSession { // this should always be world space XRInteractionMode::World_space } + + /// <https://www.w3.org/TR/webxr/#dom-xrsession-framerate> + fn GetFrameRate(&self) -> Option<Finite<f32>> { + let session = self.session.borrow(); + if self.mode == XRSessionMode::Inline || session.supported_frame_rates().is_empty() { + None + } else { + Finite::new(self.framerate.get()) + } + } + + /// <https://www.w3.org/TR/webxr/#dom-xrsession-supportedframerates> + fn GetSupportedFrameRates(&self, cx: JSContext) -> Option<Float32Array> { + let session = self.session.borrow(); + if self.mode == XRSessionMode::Inline || session.supported_frame_rates().is_empty() { + None + } else { + let framerates = session.supported_frame_rates(); + rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>()); + Some( + create_buffer_source(cx, framerates, array.handle_mut()) + .expect("Failed to construct supported frame rates array"), + ) + } + } + + /// <https://www.w3.org/TR/webxr/#dom-xrsession-enabledfeatures> + fn EnabledFeatures(&self, cx: JSContext) -> JSVal { + let session = self.session.borrow(); + let features = session.granted_features(); + to_frozen_array(features, cx) + } + + /// <https://www.w3.org/TR/webxr/#dom-xrsession-issystemkeyboardsupported> + fn IsSystemKeyboardSupported(&self) -> bool { + // Support for this only exists on Meta headsets (no desktop support) + // so this will always be false until that changes + false + } + + /// <https://www.w3.org/TR/webxr/#dom-xrsession-updatetargetframerate> + fn UpdateTargetFrameRate(&self, rate: Finite<f32>, comp: InRealm) -> Rc<Promise> { + let mut session = self.session.borrow_mut(); + let supported_frame_rates = session.supported_frame_rates(); + let promise = Promise::new_in_current_realm(comp); + + if self.mode == XRSessionMode::Inline || + supported_frame_rates.is_empty() || + self.ended.get() + { + promise.reject_error(Error::InvalidState); + return promise; + } + + if !supported_frame_rates.contains(&*rate) { + promise.reject_error(Error::Type("Provided framerate not supported".into())); + return promise; + } + + *self.update_framerate_promise.borrow_mut() = Some(promise.clone()); + + let this = Trusted::new(self); + let global = self.global(); + let window = global.as_window(); + let (task_source, canceller) = window + .task_manager() + .dom_manipulation_task_source_with_canceller(); + let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap(); + + ROUTER.add_route( + receiver.to_opaque(), + Box::new(move |message| { + let this = this.clone(); + let _ = task_source.queue_with_canceller( + task!(update_session_framerate: move || { + let session = this.root(); + session.apply_nominal_framerate(message.to().unwrap()); + if let Some(promise) = session.update_framerate_promise.borrow_mut().take() { + promise.resolve_native(&()); + }; + }), + &canceller, + ); + }), + ); + + session.update_frame_rate(*rate, sender); + + promise + } } // The pose of an object in native-space. Should never be exposed. |