mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #24452 - Manishearth:raf-lifecycle, r=asajeffrey
Always request new XR frames Currently we only request XR frames when there is a rAF callback queued up. This is incorrect, see https://immersive-web.github.io/webxr/#xr-animation-frame With this change we always request updated frame state regardless of whether or not the user has queued up callbacks. In addition to being spec-correct, this means that we can reliably listen for events in the OpenXR backend in the rAF loop itself, instead of having to set up a second spinning loop for when the rAF is inactive. This lets us implement visibility states for openxr without having to worry about the user never receiving the wakeup call because the rAF loop isn't pumping. See also: https://github.com/immersive-web/webxr/issues/877 r? @asajeffrey
This commit is contained in:
commit
14240bee81
1 changed files with 39 additions and 42 deletions
|
@ -119,6 +119,7 @@ impl XRSession {
|
|||
);
|
||||
input_sources.set_initial_inputs(&ret);
|
||||
ret.attach_event_handler();
|
||||
ret.setup_raf_loop();
|
||||
ret
|
||||
}
|
||||
|
||||
|
@ -131,6 +132,43 @@ impl XRSession {
|
|||
self.ended.get()
|
||||
}
|
||||
|
||||
fn setup_raf_loop(&self) {
|
||||
assert!(
|
||||
self.raf_sender.borrow().is_none(),
|
||||
"RAF loop already set up"
|
||||
);
|
||||
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();
|
||||
*self.raf_sender.borrow_mut() = Some(sender);
|
||||
ROUTER.add_route(
|
||||
receiver.to_opaque(),
|
||||
Box::new(move |message| {
|
||||
let this = this.clone();
|
||||
let _ = task_source.queue_with_canceller(
|
||||
task!(xr_raf_callback: move || {
|
||||
this.root().raf_callback(message.to().unwrap());
|
||||
}),
|
||||
&canceller,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
self.request_new_xr_frame();
|
||||
}
|
||||
|
||||
/// Requests a new https://immersive-web.github.io/webxr/#xr-animation-frame
|
||||
///
|
||||
/// This happens regardless of the presense of rAF callbacks
|
||||
fn request_new_xr_frame(&self) {
|
||||
let sender = self.raf_sender.borrow().clone().unwrap();
|
||||
self.session.borrow_mut().request_animation_frame(sender);
|
||||
}
|
||||
|
||||
fn attach_event_handler(&self) {
|
||||
let this = Trusted::new(self);
|
||||
let global = self.global();
|
||||
|
@ -291,6 +329,7 @@ impl XRSession {
|
|||
|
||||
frame.set_active(false);
|
||||
self.session.borrow_mut().render_animation_frame();
|
||||
self.request_new_xr_frame();
|
||||
|
||||
// If the canvas element is attached to the DOM, it is now dirty,
|
||||
// and we need to trigger a reflow.
|
||||
|
@ -368,9 +407,6 @@ impl XRSessionMethods for XRSession {
|
|||
|
||||
/// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe
|
||||
fn RequestAnimationFrame(&self, callback: Rc<XRFrameRequestCallback>) -> i32 {
|
||||
// We only need to send a message once, until a raf callback executes.
|
||||
let should_send = self.raf_callback_list.borrow().is_empty();
|
||||
|
||||
// queue up RAF callback, obtain ID
|
||||
let raf_id = self.next_raf_id.get();
|
||||
self.next_raf_id.set(raf_id + 1);
|
||||
|
@ -378,45 +414,6 @@ impl XRSessionMethods for XRSession {
|
|||
.borrow_mut()
|
||||
.push((raf_id, Some(callback)));
|
||||
|
||||
// set up listener for response, if necessary
|
||||
if self.raf_sender.borrow().is_none() {
|
||||
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();
|
||||
*self.raf_sender.borrow_mut() = Some(sender);
|
||||
ROUTER.add_route(
|
||||
receiver.to_opaque(),
|
||||
Box::new(move |message| {
|
||||
let this = this.clone();
|
||||
let _ = task_source.queue_with_canceller(
|
||||
task!(xr_raf_callback: move || {
|
||||
this.root().raf_callback(message.to().unwrap());
|
||||
}),
|
||||
&canceller,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if should_send {
|
||||
// If our callback list is empty, it either means this is the first request,
|
||||
// or raf_callback executed, in which case we should
|
||||
// send a message to request an animation frame.
|
||||
//
|
||||
// This prevents multiple messages being sent for a single call to raf_callback,
|
||||
// and multiple message are unnecessary,
|
||||
// since one call will already deal with multiple potentially enqueued callbacks.
|
||||
//
|
||||
// Allowing multiple messages could keep the main-thread,
|
||||
// where the session thread might be running, looping on incoming messages.
|
||||
let sender = self.raf_sender.borrow().clone().unwrap();
|
||||
self.session.borrow_mut().request_animation_frame(sender);
|
||||
}
|
||||
|
||||
raf_id
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue