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>
This commit is contained in:
Daniel Adams 2024-08-16 17:36:52 -10:00 committed by GitHub
parent f0045a7686
commit 20273b062a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 132 additions and 62 deletions

View file

@ -117,7 +117,7 @@ DOMInterfaces = {
}, },
'XRSession': { 'XRSession': {
'inRealms': ['UpdateRenderState', 'RequestReferenceSpace'], 'inRealms': ['UpdateRenderState', 'RequestReferenceSpace', 'UpdateTargetFrameRate'],
}, },
'Bluetooth': { 'Bluetooth': {

View file

@ -27,11 +27,16 @@ callback XRFrameRequestCallback = undefined (DOMHighResTimeStamp time, XRFrame f
interface XRSession : EventTarget { interface XRSession : EventTarget {
// Attributes // Attributes
readonly attribute XRVisibilityState visibilityState; readonly attribute XRVisibilityState visibilityState;
readonly attribute float? frameRate;
readonly attribute Float32Array? supportedFrameRates;
[SameObject] readonly attribute XRRenderState renderState; [SameObject] readonly attribute XRRenderState renderState;
[SameObject] readonly attribute XRInputSourceArray inputSources; [SameObject] readonly attribute XRInputSourceArray inputSources;
readonly attribute /*FrozenArray<DOMString>*/ any enabledFeatures;
readonly attribute boolean isSystemKeyboardSupported;
// Methods // Methods
[Throws] undefined updateRenderState(optional XRRenderStateInit state = {}); [Throws] undefined updateRenderState(optional XRRenderStateInit state = {});
Promise<undefined> updateTargetFrameRate(float rate);
Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceType type); Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceType type);
long requestAnimationFrame(XRFrameRequestCallback callback); long requestAnimationFrame(XRFrameRequestCallback callback);
@ -49,6 +54,7 @@ interface XRSession : EventTarget {
attribute EventHandler onsqueezestart; attribute EventHandler onsqueezestart;
attribute EventHandler onsqueezeend; attribute EventHandler onsqueezeend;
attribute EventHandler onvisibilitychange; attribute EventHandler onvisibilitychange;
attribute EventHandler onframeratechange;
// AR Module // AR Module
// Attributes // Attributes

View file

@ -5,15 +5,19 @@
use std::cell::Cell; use std::cell::Cell;
use std::collections::HashMap; use std::collections::HashMap;
use std::f64::consts::{FRAC_PI_2, PI}; use std::f64::consts::{FRAC_PI_2, PI};
use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::{mem, ptr};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::{RigidTransform3D, Transform3D, Vector3D}; use euclid::{RigidTransform3D, Transform3D, Vector3D};
use ipc_channel::ipc::IpcReceiver; use ipc_channel::ipc::IpcReceiver;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use js::jsapi::JSObject;
use js::jsval::JSVal;
use js::typedarray::Float32Array;
use metrics::ToMs; use metrics::ToMs;
use profile_traits::ipc; use profile_traits::ipc;
use servo_atoms::Atom;
use webxr_api::{ use webxr_api::{
self, util, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode, self, util, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode,
Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray, Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray,
@ -21,6 +25,7 @@ use webxr_api::{
}; };
use super::bindings::trace::HashMapTracedValues; use super::bindings::trace::HashMapTracedValues;
use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods; 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::codegen::Bindings::XRSystemBinding::XRSessionMode;
use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; 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::event::Event;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
@ -56,6 +63,7 @@ use crate::dom::xrrenderstate::XRRenderState;
use crate::dom::xrsessionevent::XRSessionEvent; use crate::dom::xrsessionevent::XRSessionEvent;
use crate::dom::xrspace::XRSpace; use crate::dom::xrspace::XRSpace;
use crate::realms::InRealm; use crate::realms::InRealm;
use crate::script_runtime::JSContext;
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
#[dom_struct] #[dom_struct]
@ -97,6 +105,9 @@ pub struct XRSession {
#[ignore_malloc_size_of = "defined in webxr"] #[ignore_malloc_size_of = "defined in webxr"]
#[no_trace] #[no_trace]
input_frames: DomRefCell<HashMap<InputId, InputFrame>>, 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 { impl XRSession {
@ -128,6 +139,8 @@ impl XRSession {
pending_hit_test_promises: DomRefCell::new(HashMapTracedValues::new()), pending_hit_test_promises: DomRefCell::new(HashMapTracedValues::new()),
outside_raf: Cell::new(true), outside_raf: Cell::new(true),
input_frames: DomRefCell::new(HashMap::new()), 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), _ => 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 { impl XRSessionMethods for XRSession {
@ -581,6 +612,9 @@ impl XRSessionMethods for XRSession {
SetOninputsourceschange SetOninputsourceschange
); );
// https://www.w3.org/TR/webxr/#dom-xrsession-onframeratechange
event_handler!(frameratechange, GetOnframeratechange, SetOnframeratechange);
// https://immersive-web.github.io/webxr/#dom-xrsession-renderstate // https://immersive-web.github.io/webxr/#dom-xrsession-renderstate
fn RenderState(&self) -> DomRoot<XRRenderState> { fn RenderState(&self) -> DomRoot<XRRenderState> {
self.active_render_state.get() self.active_render_state.get()
@ -881,6 +915,96 @@ impl XRSessionMethods for XRSession {
// this should always be world space // this should always be world space
XRInteractionMode::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. // The pose of an object in native-space. Should never be exposed.

View file

@ -1,16 +1,4 @@
[depth_sensing_preferences.https.html] [depth_sensing_preferences.https.html]
[depthSensing - Required - Fully populated grants session]
expected: FAIL
[depthSensing - Required - Empty usage grants session]
expected: FAIL
[depthSensing - Required - Empty format grants session]
expected: FAIL
[depthSensing - Required - Empty usage and format grants session]
expected: FAIL
[depthSensing - Required - Missing usage rejects session] [depthSensing - Required - Missing usage rejects session]
expected: FAIL expected: FAIL

View file

@ -335,18 +335,6 @@
[XRWebGLLayer interface: xrWebGLLayer must inherit property "fixedFoveation" with the proper type] [XRWebGLLayer interface: xrWebGLLayer must inherit property "fixedFoveation" with the proper type]
expected: FAIL expected: FAIL
[XRSession interface: attribute frameRate]
expected: FAIL
[XRSession interface: attribute supportedFrameRates]
expected: FAIL
[XRSession interface: operation updateTargetFrameRate(float)]
expected: FAIL
[XRSession interface: attribute onframeratechange]
expected: FAIL
[XRSession interface: xrSession must inherit property "frameRate" with the proper type] [XRSession interface: xrSession must inherit property "frameRate" with the proper type]
expected: FAIL expected: FAIL
@ -365,15 +353,9 @@
[XRFrame interface: attribute predictedDisplayTime] [XRFrame interface: attribute predictedDisplayTime]
expected: FAIL expected: FAIL
[XRSession interface: attribute enabledFeatures]
expected: FAIL
[XRSession interface: xrSession must inherit property "enabledFeatures" with the proper type] [XRSession interface: xrSession must inherit property "enabledFeatures" with the proper type]
expected: FAIL expected: FAIL
[XRSession interface: attribute isSystemKeyboardSupported]
expected: FAIL
[XRSession interface: xrSession must inherit property "isSystemKeyboardSupported" with the proper type] [XRSession interface: xrSession must inherit property "isSystemKeyboardSupported" with the proper type]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[depth_sensing_preferences.https.html] [depth_sensing_preferences.https.html]
[depthSensing - Required - Fully populated grants session]
expected: FAIL
[depthSensing - Required - Empty usage grants session]
expected: FAIL
[depthSensing - Required - Empty format grants session]
expected: FAIL
[depthSensing - Required - Empty usage and format grants session]
expected: FAIL
[depthSensing - Required - Missing usage rejects session] [depthSensing - Required - Missing usage rejects session]
expected: FAIL expected: FAIL

View file

@ -278,18 +278,6 @@
[XRWebGLLayer interface: xrWebGLLayer must inherit property "fixedFoveation" with the proper type] [XRWebGLLayer interface: xrWebGLLayer must inherit property "fixedFoveation" with the proper type]
expected: FAIL expected: FAIL
[XRSession interface: attribute frameRate]
expected: FAIL
[XRSession interface: attribute supportedFrameRates]
expected: FAIL
[XRSession interface: operation updateTargetFrameRate(float)]
expected: FAIL
[XRSession interface: attribute onframeratechange]
expected: FAIL
[XRSession interface: xrSession must inherit property "frameRate" with the proper type] [XRSession interface: xrSession must inherit property "frameRate" with the proper type]
expected: FAIL expected: FAIL
@ -308,15 +296,9 @@
[XRFrame interface: attribute predictedDisplayTime] [XRFrame interface: attribute predictedDisplayTime]
expected: FAIL expected: FAIL
[XRSession interface: attribute enabledFeatures]
expected: FAIL
[XRSession interface: xrSession must inherit property "enabledFeatures" with the proper type] [XRSession interface: xrSession must inherit property "enabledFeatures" with the proper type]
expected: FAIL expected: FAIL
[XRSession interface: attribute isSystemKeyboardSupported]
expected: FAIL
[XRSession interface: xrSession must inherit property "isSystemKeyboardSupported" with the proper type] [XRSession interface: xrSession must inherit property "isSystemKeyboardSupported" with the proper type]
expected: FAIL expected: FAIL