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:
bors-servo 2019-10-16 20:02:10 -04:00 committed by GitHub
commit 14240bee81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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
}