Auto merge of #22649 - Manishearth:xr-promises, r=jdm

Make XR/VR presenting code async

Previously we only pretended to be async, we returned promises that were already resolved because we synchronously blocked on the channel.

This:

 - Moves XR presentation to session creation (where it belongs, see https://github.com/immersive-web/webxr/issues/453)
 - Factors out common presentation code in a way that supports asynchronously resolving promises
 - Uses this for `VRDisplay::RequestPresent()` and `XR::RequestSession()`

r? @jdm

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22649)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-01-08 20:42:37 -05:00 committed by GitHub
commit 44cbe969e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 58 deletions

View file

@ -15,7 +15,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCallback; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCallback;
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite; use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{DomRoot, MutDom, MutNullableDom}; use crate::dom::bindings::root::{DomRoot, MutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
@ -34,7 +34,7 @@ use crate::dom::xrframe::XRFrame;
use crate::dom::xrsession::XRSession; use crate::dom::xrsession::XRSession;
use crate::script_runtime::CommonScriptMsg; use crate::script_runtime::CommonScriptMsg;
use crate::script_runtime::ScriptThreadEventCategory::WebVREvent; use crate::script_runtime::ScriptThreadEventCategory::WebVREvent;
use crate::task_source::TaskSourceName; use crate::task_source::{TaskSource, TaskSourceName};
use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand}; use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand};
use crossbeam_channel::{unbounded, Sender}; use crossbeam_channel::{unbounded, Sender};
use dom_struct::dom_struct; use dom_struct::dom_struct;
@ -347,35 +347,9 @@ impl VRDisplayMethods for VRDisplay {
}, },
}; };
// WebVR spec: Repeat calls while already presenting will update the VRLayers being displayed. self.request_present(layer_bounds, Some(&layer_ctx), Some(promise.clone()), |p| {
if self.presenting.get() { p.resolve_native(&())
*self.layer.borrow_mut() = layer_bounds; });
self.layer_ctx.set(Some(&layer_ctx));
promise.resolve_native(&());
return promise;
}
// Request Present
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
self.webvr_thread()
.send(WebVRMsg::RequestPresent(
self.global().pipeline_id(),
self.display.borrow().display_id,
sender,
))
.unwrap();
match receiver.recv().unwrap() {
Ok(()) => {
*self.layer.borrow_mut() = layer_bounds;
self.layer_ctx.set(Some(&layer_ctx));
self.init_present();
promise.resolve_native(&());
},
Err(e) => {
promise.reject_native(&e);
},
}
promise promise
} }
@ -464,6 +438,64 @@ impl VRDisplay {
} }
} }
pub fn request_present<F>(
&self,
layer_bounds: WebVRLayer,
ctx: Option<&WebGLRenderingContext>,
promise: Option<Rc<Promise>>,
resolve: F,
) where
F: FnOnce(Rc<Promise>) + Send + 'static,
{
// WebVR spec: Repeat calls while already presenting will update the VRLayers being displayed.
if self.presenting.get() {
*self.layer.borrow_mut() = layer_bounds;
self.layer_ctx.set(ctx);
promise.map(resolve);
return;
}
// Request Present
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
self.webvr_thread()
.send(WebVRMsg::RequestPresent(
self.global().pipeline_id(),
self.display.borrow().display_id,
sender,
))
.unwrap();
let promise = promise.map(TrustedPromise::new);
let this = Trusted::new(self);
let ctx = ctx.map(|c| Trusted::new(c));
let global = self.global();
let window = global.as_window();
let (task_source, canceller) = window
.task_manager()
.dom_manipulation_task_source_with_canceller();
thread::spawn(move || {
let recv = receiver.recv().unwrap();
let _ = task_source.queue_with_canceller(
task!(vr_presenting: move || {
let this = this.root();
let promise = promise.map(|p| p.root());
let ctx = ctx.map(|c| c.root());
match recv {
Ok(()) => {
*this.layer.borrow_mut() = layer_bounds;
this.layer_ctx.set(ctx.as_ref().map(|c| &**c));
this.init_present();
promise.map(resolve);
},
Err(e) => {
promise.map(|p| p.reject_native(&e));
},
}
}),
&canceller,
);
});
}
pub fn handle_webvr_event(&self, event: &WebVRDisplayEvent) { pub fn handle_webvr_event(&self, event: &WebVRDisplayEvent) {
match *event { match *event {
WebVRDisplayEvent::Connect(ref display) => { WebVRDisplayEvent::Connect(ref display) => {
@ -688,30 +720,19 @@ impl VRDisplay {
// XR stuff // XR stuff
// XXXManishearth eventually we should share as much logic as possible // XXXManishearth eventually we should share as much logic as possible
impl VRDisplay { impl VRDisplay {
pub fn xr_present(&self, session: &XRSession, ctx: &WebGLRenderingContext) { pub fn xr_present(
&self,
session: &XRSession,
ctx: Option<&WebGLRenderingContext>,
promise: Option<Rc<Promise>>,
) {
let layer_bounds = WebVRLayer::default(); let layer_bounds = WebVRLayer::default();
self.xr_session.set(Some(session)); self.xr_session.set(Some(session));
if self.presenting.get() { let session = Trusted::new(session);
*self.layer.borrow_mut() = layer_bounds; self.request_present(layer_bounds, ctx, promise, move |p| {
self.layer_ctx.set(Some(&ctx)); let session = session.root();
return; p.resolve_native(&session);
} });
// Request Present
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
self.webvr_thread()
.send(WebVRMsg::RequestPresent(
self.global().pipeline_id(),
self.display.borrow().display_id,
sender,
))
.unwrap();
if let Ok(()) = receiver.recv().unwrap() {
*self.layer.borrow_mut() = layer_bounds;
self.layer_ctx.set(Some(&ctx));
self.init_present();
}
} }
pub fn xr_raf(&self, callback: Rc<XRFrameRequestCallback>) -> u32 { pub fn xr_raf(&self, callback: Rc<XRFrameRequestCallback>) -> u32 {

View file

@ -95,10 +95,7 @@ impl XRMethods for XR {
} }
let session = XRSession::new(&self.global(), &displays[0]); let session = XRSession::new(&self.global(), &displays[0]);
promise.resolve_native(&session); session.xr_present(promise.clone());
// whether or not we should initiate presentation is unclear
// https://github.com/immersive-web/webxr/issues/453
promise promise
} }
} }

View file

@ -14,6 +14,7 @@ use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::vrdisplay::VRDisplay; use crate::dom::vrdisplay::VRDisplay;
use crate::dom::xrlayer::XRLayer; use crate::dom::xrlayer::XRLayer;
use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::dom::xrwebgllayer::XRWebGLLayer;
@ -43,6 +44,10 @@ impl XRSession {
XRSessionBinding::Wrap, XRSessionBinding::Wrap,
) )
} }
pub fn xr_present(&self, p: Rc<Promise>) {
self.display.xr_present(self, None, Some(p));
}
} }
impl XRSessionMethods for XRSession { impl XRSessionMethods for XRSession {
@ -76,7 +81,7 @@ impl XRSessionMethods for XRSession {
self.base_layer.set(layer); self.base_layer.set(layer);
if let Some(layer) = layer { if let Some(layer) = layer {
let layer = layer.downcast::<XRWebGLLayer>().unwrap(); let layer = layer.downcast::<XRWebGLLayer>().unwrap();
self.display.xr_present(&self, &layer.Context()); self.display.xr_present(&self, Some(&layer.Context()), None);
} else { } else {
// steps unknown // steps unknown
// https://github.com/immersive-web/webxr/issues/453 // https://github.com/immersive-web/webxr/issues/453