aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/xrsession.rs
diff options
context:
space:
mode:
authorDaniel Adams <70986246+msub2@users.noreply.github.com>2024-08-16 17:36:52 -1000
committerGitHub <noreply@github.com>2024-08-17 03:36:52 +0000
commit20273b062af969152635306c3df2a3a1364ac4d1 (patch)
treeec58d2b37e920958aebe9130e6581d0a0faa0924 /components/script/dom/xrsession.rs
parentf0045a76866f2d56d6a01aaf93ec20177dad778a (diff)
downloadservo-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.rs126
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.