diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/canvas/Cargo.toml | 1 | ||||
-rw-r--r-- | components/canvas/webgl_mode/inprocess.rs | 250 | ||||
-rw-r--r-- | components/canvas/webgl_mode/mod.rs | 2 | ||||
-rw-r--r-- | components/canvas/webgl_thread.rs | 197 | ||||
-rw-r--r-- | components/canvas_traits/webgl_channel/mod.rs | 7 | ||||
-rw-r--r-- | components/canvas_traits/webgl_channel/mpsc.rs | 4 | ||||
-rw-r--r-- | components/constellation/constellation.rs | 10 | ||||
-rw-r--r-- | components/constellation/pipeline.rs | 12 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 3 | ||||
-rw-r--r-- | components/script/dom/vrdisplay.rs | 8 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 108 | ||||
-rw-r--r-- | components/script/dom/window.rs | 19 | ||||
-rw-r--r-- | components/script/script_thread.rs | 7 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 4 | ||||
-rw-r--r-- | components/servo/Cargo.toml | 2 | ||||
-rw-r--r-- | components/servo/lib.rs | 176 |
16 files changed, 589 insertions, 221 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index dbc3b2a7200..03a77826ac7 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -21,6 +21,7 @@ azure = {git = "https://github.com/servo/rust-azure", optional = true} byteorder = "1" canvas_traits = {path = "../canvas_traits"} cssparser = "0.25" +embedder_traits = {path = "../embedder_traits"} euclid = "0.20" fnv = "1.0" gleam = "0.6.7" diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs index 76d468390db..d499a1cb148 100644 --- a/components/canvas/webgl_mode/inprocess.rs +++ b/components/canvas/webgl_mode/inprocess.rs @@ -3,15 +3,19 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::gl_context::GLContextFactory; -use crate::webgl_thread::WebGLThread; +use crate::webgl_thread::{TexturesMap, WebGLMainThread, WebGLThread, WebGLThreadInit}; use canvas_traits::webgl::webgl_channel; use canvas_traits::webgl::DOMToTextureCommand; use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver}; -use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler}; +use canvas_traits::webgl::{WebGLSender, WebVRRenderHandler}; +use embedder_traits::EventLoopWaker; use euclid::default::Size2D; use fnv::FnvHashMap; use gleam::gl; use servo_config::pref; +use std::cell::RefCell; +use std::collections::HashMap; +use std::default::Default; use std::rc::Rc; use std::sync::{Arc, Mutex}; use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry}; @@ -19,37 +23,70 @@ use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry /// WebGL Threading API entry point that lives in the constellation. pub struct WebGLThreads(WebGLSender<WebGLMsg>); +pub enum ThreadMode { + MainThread(Box<dyn EventLoopWaker>), + OffThread(Rc<dyn gl::Gl>), +} + impl WebGLThreads { /// Creates a new WebGLThreads object pub fn new( gl_factory: GLContextFactory, - webrender_gl: Rc<dyn gl::Gl>, webrender_api_sender: webrender_api::RenderApiSender, webvr_compositor: Option<Box<dyn WebVRRenderHandler>>, external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, + mode: ThreadMode, ) -> ( WebGLThreads, + Option<WebGLMainThread>, Box<dyn WebrenderExternalImageApi>, Option<Box<dyn webrender::OutputImageHandler>>, ) { + let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap(); // This implementation creates a single `WebGLThread` for all the pipelines. - let channel = WebGLThread::start( + let init = WebGLThreadInit { gl_factory, webrender_api_sender, - webvr_compositor.map(|c| WebVRRenderWrapper(c)), + webvr_compositor, external_images, - ); + sender: sender.clone(), + receiver, + }; + let output_handler = if pref!(dom.webgl.dom_to_texture.enabled) { - Some(Box::new(OutputHandler::new( - webrender_gl.clone(), - channel.clone(), - ))) + Some(Box::new(match mode { + ThreadMode::MainThread(..) => OutputHandler::new_main_thread(), + ThreadMode::OffThread(ref webrender_gl) => { + OutputHandler::new_off_thread(webrender_gl.clone(), sender.clone()) + }, + })) } else { None }; - let external = WebGLExternalImages::new(webrender_gl, channel.clone()); + + let (external, webgl_thread) = match mode { + ThreadMode::MainThread(event_loop_waker) => { + let textures = Rc::new(RefCell::new(HashMap::new())); + let thread_data = + WebGLThread::run_on_current_thread(init, event_loop_waker, textures.clone()); + ( + WebGLExternalImages::new_main_thread(textures), + Some(thread_data), + ) + }, + + ThreadMode::OffThread(webrender_gl) => { + WebGLThread::run_on_own_thread(init); + ( + WebGLExternalImages::new_off_thread(webrender_gl, sender.clone()), + None, + ) + }, + }; + ( - WebGLThreads(channel), + WebGLThreads(sender), + webgl_thread, Box::new(external), output_handler.map(|b| b as Box<_>), ) @@ -70,88 +107,113 @@ impl WebGLThreads { } /// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads. -struct WebGLExternalImages { - webrender_gl: Rc<dyn gl::Gl>, - webgl_channel: WebGLSender<WebGLMsg>, - // Used to avoid creating a new channel on each received WebRender request. - lock_channel: ( - WebGLSender<(u32, Size2D<i32>, usize)>, - WebGLReceiver<(u32, Size2D<i32>, usize)>, - ), +enum WebGLExternalImages { + OffThread { + webrender_gl: Rc<dyn gl::Gl>, + webgl_channel: WebGLSender<WebGLMsg>, + // Used to avoid creating a new channel on each received WebRender request. + lock_channel: ( + WebGLSender<(u32, Size2D<i32>, usize)>, + WebGLReceiver<(u32, Size2D<i32>, usize)>, + ), + }, + MainThread { + textures: TexturesMap, + }, } impl WebGLExternalImages { - fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self { - Self { + fn new_off_thread(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self { + WebGLExternalImages::OffThread { webrender_gl, webgl_channel: channel, lock_channel: webgl_channel().unwrap(), } } + + fn new_main_thread(textures: TexturesMap) -> Self { + WebGLExternalImages::MainThread { textures } + } } impl WebrenderExternalImageApi for WebGLExternalImages { fn lock(&mut self, id: u64) -> (u32, Size2D<i32>) { - // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue. - // The WebGLMsg::Lock message inserts a fence in the WebGL command queue. - self.webgl_channel - .send(WebGLMsg::Lock( - WebGLContextId(id as usize), - self.lock_channel.0.clone(), - )) - .unwrap(); - let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap(); - // The next glWaitSync call is run on the WR thread and it's used to synchronize the two - // flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture. - // glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem. - self.webrender_gl - .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED); - (image_id, size) - } + match *self { + WebGLExternalImages::OffThread { + ref webgl_channel, + ref webrender_gl, + ref lock_channel, + } => { + // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue. + // The WebGLMsg::Lock message inserts a fence in the WebGL command queue. + webgl_channel + .send(WebGLMsg::Lock( + WebGLContextId(id as usize), + lock_channel.0.clone(), + )) + .unwrap(); + let (image_id, size, gl_sync) = lock_channel.1.recv().unwrap(); + // The next glWaitSync call is run on the WR thread and it's used to synchronize the two + // flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture. + // glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem. + webrender_gl.wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED); + (image_id, size) + }, - fn unlock(&mut self, id: u64) { - self.webgl_channel - .send(WebGLMsg::Unlock(WebGLContextId(id as usize))) - .unwrap(); + WebGLExternalImages::MainThread { ref textures } => { + let textures = textures.borrow(); + let entry = textures + .get(&WebGLContextId(id as usize)) + .expect("no texture entry???"); + (entry.0, entry.1) + }, + } } -} -/// Wrapper to send WebVR commands used in `WebGLThread`. -struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>); + fn unlock(&mut self, id: u64) { + match *self { + WebGLExternalImages::OffThread { + ref webgl_channel, .. + } => { + webgl_channel + .send(WebGLMsg::Unlock(WebGLContextId(id as usize))) + .unwrap(); + }, -impl WebVRRenderHandler for WebVRRenderWrapper { - fn handle( - &mut self, - gl: &dyn gl::Gl, - command: WebVRCommand, - texture: Option<(u32, Size2D<i32>)>, - ) { - self.0.handle(gl, command, texture); + WebGLExternalImages::MainThread { .. } => {}, + } } } /// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait. type OutputHandlerData = Option<(u32, Size2D<i32>)>; -struct OutputHandler { - webrender_gl: Rc<dyn gl::Gl>, - webgl_channel: WebGLSender<WebGLMsg>, - // Used to avoid creating a new channel on each received WebRender request. - lock_channel: ( - WebGLSender<OutputHandlerData>, - WebGLReceiver<OutputHandlerData>, - ), - sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>, +enum OutputHandler { + OffThread { + webrender_gl: Rc<dyn gl::Gl>, + webgl_channel: WebGLSender<WebGLMsg>, + // Used to avoid creating a new channel on each received WebRender request. + lock_channel: ( + WebGLSender<OutputHandlerData>, + WebGLReceiver<OutputHandlerData>, + ), + sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>, + }, + MainThread, } impl OutputHandler { - fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self { - Self { + fn new_off_thread(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self { + OutputHandler::OffThread { webrender_gl, webgl_channel: channel, lock_channel: webgl_channel().unwrap(), sync_objects: Default::default(), } } + + fn new_main_thread() -> Self { + OutputHandler::MainThread + } } /// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization. @@ -160,29 +222,49 @@ impl webrender::OutputImageHandler for OutputHandler { &mut self, id: webrender_api::PipelineId, ) -> Option<(u32, webrender_api::units::FramebufferIntSize)> { - // Insert a fence in the WR command queue - let gl_sync = self - .webrender_gl - .fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); - // The lock command adds a WaitSync call on the WebGL command flow. - let command = DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone()); - self.webgl_channel - .send(WebGLMsg::DOMToTextureCommand(command)) - .unwrap(); - self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| { - ( - tex_id, - webrender_api::units::FramebufferIntSize::new(size.width, size.height), - ) - }) + match *self { + OutputHandler::OffThread { + ref webrender_gl, + ref lock_channel, + ref webgl_channel, + .. + } => { + // Insert a fence in the WR command queue + let gl_sync = webrender_gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); + // The lock command adds a WaitSync call on the WebGL command flow. + let command = + DOMToTextureCommand::Lock(id, gl_sync as usize, lock_channel.0.clone()); + webgl_channel + .send(WebGLMsg::DOMToTextureCommand(command)) + .unwrap(); + lock_channel.1.recv().unwrap().map(|(tex_id, size)| { + ( + tex_id, + webrender_api::units::FramebufferIntSize::new(size.width, size.height), + ) + }) + }, + + OutputHandler::MainThread => unimplemented!(), + } } fn unlock(&mut self, id: webrender_api::PipelineId) { - if let Some(gl_sync) = self.sync_objects.remove(&id) { - // Flush the Sync object into the GPU's command queue to guarantee that it it's signaled. - self.webrender_gl.flush(); - // Mark the sync object for deletion. - self.webrender_gl.delete_sync(gl_sync); + match *self { + OutputHandler::OffThread { + ref webrender_gl, + ref mut sync_objects, + .. + } => { + if let Some(gl_sync) = sync_objects.remove(&id) { + // Flush the Sync object into the GPU's command queue to guarantee that it it's signaled. + webrender_gl.flush(); + // Mark the sync object for deletion. + webrender_gl.delete_sync(gl_sync); + } + }, + + OutputHandler::MainThread => {}, } } } diff --git a/components/canvas/webgl_mode/mod.rs b/components/canvas/webgl_mode/mod.rs index b16ddc31c2e..5aa85947d21 100644 --- a/components/canvas/webgl_mode/mod.rs +++ b/components/canvas/webgl_mode/mod.rs @@ -3,4 +3,4 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ mod inprocess; -pub use self::inprocess::WebGLThreads; +pub use self::inprocess::{ThreadMode, WebGLThreads}; diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index ce73b03cf09..1d6051c7aa3 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -5,21 +5,27 @@ use super::gl_context::{map_attrs_to_script_attrs, GLContextFactory, GLContextWrapper}; use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; use canvas_traits::webgl::*; +use embedder_traits::EventLoopWaker; use euclid::default::Size2D; use fnv::FnvHashMap; use gleam::gl; use half::f16; -use ipc_channel::ipc::IpcSender; +use ipc_channel::ipc::{self, IpcSender, OpaqueIpcMessage}; +use ipc_channel::router::ROUTER; use offscreen_gl_context::{DrawBuffer, GLContext, NativeGLContextMethods}; use pixels::{self, PixelFormat}; use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::mem; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::thread; use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; /// WebGL Threading API entry point that lives in the constellation. /// It allows to get a WebGLThread handle for each script pipeline. -pub use crate::webgl_mode::WebGLThreads; +pub use crate::webgl_mode::{ThreadMode, WebGLThreads}; struct GLContextData { ctx: GLContextWrapper, @@ -50,7 +56,7 @@ impl Default for GLState { /// A WebGLThread manages the life cycle and message multiplexing of /// a set of WebGLContexts living in the same thread. -pub struct WebGLThread<VR: WebVRRenderHandler + 'static> { +pub(crate) struct WebGLThread { /// Factory used to create a new GLContext shared with the WR/Main thread. gl_factory: GLContextFactory, /// Channel used to generate/update or delete `webrender_api::ImageKey`s. @@ -62,20 +68,74 @@ pub struct WebGLThread<VR: WebVRRenderHandler + 'static> { /// Current bound context. bound_context_id: Option<WebGLContextId>, /// Handler user to send WebVR commands. - webvr_compositor: Option<VR>, + webvr_compositor: Option<Box<dyn WebVRRenderHandler>>, /// Texture ids and sizes used in DOM to texture outputs. dom_outputs: FnvHashMap<webrender_api::PipelineId, DOMToTextureData>, /// List of registered webrender external images. /// We use it to get an unique ID for new WebGLContexts. external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, + /// The receiver that will be used for processing WebGL messages. + receiver: WebGLReceiver<WebGLMsg>, + /// The receiver that should be used to send WebGL messages for processing. + sender: WebGLSender<WebGLMsg>, } -impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { - pub fn new( - gl_factory: GLContextFactory, - webrender_api_sender: webrender_api::RenderApiSender, - webvr_compositor: Option<VR>, - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, +/// A map of GL contexts to backing textures and their sizes. +/// Only used for accessing this information when the WebGL processing is run +/// on the main thread and the compositor needs access to this information +/// synchronously. +pub(crate) type TexturesMap = Rc<RefCell<HashMap<WebGLContextId, (u32, Size2D<i32>)>>>; + +#[derive(PartialEq)] +enum EventLoop { + Blocking, + Nonblocking, +} + +/// The data required to initialize an instance of the WebGLThread type. +pub(crate) struct WebGLThreadInit { + pub gl_factory: GLContextFactory, + pub webrender_api_sender: webrender_api::RenderApiSender, + pub webvr_compositor: Option<Box<dyn WebVRRenderHandler>>, + pub external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, + pub sender: WebGLSender<WebGLMsg>, + pub receiver: WebGLReceiver<WebGLMsg>, +} + +/// The extra data required to run an instance of WebGLThread when it is +/// not running in its own thread. +pub struct WebGLMainThread { + thread_data: WebGLThread, + shut_down: bool, + textures: TexturesMap, +} + +impl WebGLMainThread { + /// Synchronously process all outstanding WebGL messages. + pub fn process(&mut self) { + if self.shut_down { + return; + } + + // Any context could be current when we start. + self.thread_data.bound_context_id = None; + self.shut_down = !self + .thread_data + .process(EventLoop::Nonblocking, Some(self.textures.clone())) + } +} + +impl WebGLThread { + /// Create a new instance of WebGLThread. + pub(crate) fn new( + WebGLThreadInit { + gl_factory, + webrender_api_sender, + webvr_compositor, + external_images, + sender, + receiver, + }: WebGLThreadInit, ) -> Self { WebGLThread { gl_factory, @@ -86,49 +146,80 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { webvr_compositor, dom_outputs: Default::default(), external_images, + sender, + receiver, } } - /// Creates a new `WebGLThread` and returns a Sender to - /// communicate with it. - pub fn start( - gl_factory: GLContextFactory, - webrender_api_sender: webrender_api::RenderApiSender, - webvr_compositor: Option<VR>, - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - ) -> WebGLSender<WebGLMsg> { - let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap(); - let result = sender.clone(); + /// Perform all initialization required to run an instance of WebGLThread + /// concurrently on the current thread. Returns a `WebGLMainThread` instance + /// that can be used to process any outstanding WebGL messages at any given + /// point in time. + pub(crate) fn run_on_current_thread( + mut init: WebGLThreadInit, + event_loop_waker: Box<dyn EventLoopWaker>, + textures: TexturesMap, + ) -> WebGLMainThread { + if let WebGLReceiver::Ipc(ref mut receiver) = init.receiver { + // Interpose a new channel in between the existing WebGL channel endpoints. + // This will bounce all WebGL messages through the router thread adding a small + // delay, but this will also ensure that the main thread will wake up and + // process the WebGL message when it arrives. + let (from_router_sender, from_router_receiver) = ipc::channel::<WebGLMsg>().unwrap(); + let old_receiver = mem::replace(receiver, from_router_receiver); + ROUTER.add_route( + old_receiver.to_opaque(), + Box::new(move |msg: OpaqueIpcMessage| { + let _ = from_router_sender.send(msg.to().unwrap()); + event_loop_waker.wake(); + }), + ); + } + + WebGLMainThread { + thread_data: WebGLThread::new(init), + textures, + shut_down: false, + } + } + + /// Perform all initialization required to run an instance of WebGLThread + /// in parallel on its own dedicated thread. + pub(crate) fn run_on_own_thread(init: WebGLThreadInit) { thread::Builder::new() - .name("WebGLThread".to_owned()) + .name("WebGL thread".to_owned()) .spawn(move || { - let mut renderer = WebGLThread::new( - gl_factory, - webrender_api_sender, - webvr_compositor, - external_images, - ); - let webgl_chan = WebGLChan(sender); - loop { - let msg = receiver.recv().unwrap(); - let exit = renderer.handle_msg(msg, &webgl_chan); - if exit { - return; - } - } + let mut data = WebGLThread::new(init); + data.process(EventLoop::Blocking, None); }) .expect("Thread spawning failed"); + } - result + fn process(&mut self, loop_type: EventLoop, textures: Option<TexturesMap>) -> bool { + let webgl_chan = WebGLChan(self.sender.clone()); + while let Ok(msg) = match loop_type { + EventLoop::Blocking => self.receiver.recv(), + EventLoop::Nonblocking => self.receiver.try_recv(), + } { + let exit = self.handle_msg(msg, &webgl_chan, textures.as_ref()); + if exit { + return false; + } + } + true } /// Handles a generic WebGLMsg message - #[inline] - fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool { + fn handle_msg( + &mut self, + msg: WebGLMsg, + webgl_chan: &WebGLChan, + textures: Option<&TexturesMap>, + ) -> bool { trace!("processing {:?}", msg); match msg { WebGLMsg::CreateContext(version, size, attributes, result_sender) => { - let result = self.create_webgl_context(version, size, attributes); + let result = self.create_webgl_context(version, size, attributes, textures); result_sender .send(result.map(|(id, limits, share_mode)| { let data = Self::make_current_if_needed( @@ -173,10 +264,10 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { .unwrap(); }, WebGLMsg::ResizeContext(ctx_id, size, sender) => { - self.resize_webgl_context(ctx_id, size, sender); + self.resize_webgl_context(ctx_id, size, sender, textures); }, WebGLMsg::RemoveContext(ctx_id) => { - self.remove_webgl_context(ctx_id); + self.remove_webgl_context(ctx_id, textures); }, WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => { self.handle_webgl_command(ctx_id, command, backtrace); @@ -296,6 +387,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { version: WebGLVersion, size: Size2D<u32>, attributes: GLContextAttributes, + textures: Option<&TexturesMap>, ) -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode), String> { // Creating a new GLContext may make the current bound context_id dirty. // Clear it to ensure that make_current() is called in subsequent commands. @@ -332,6 +424,11 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { state: Default::default(), }, ); + + if let Some(ref textures) = textures { + textures.borrow_mut().insert(id, (texture_id, size)); + } + self.cached_context_info.insert( id, WebGLContextInfo { @@ -354,6 +451,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { context_id: WebGLContextId, size: Size2D<u32>, sender: WebGLSender<Result<(), String>>, + textures: Option<&TexturesMap>, ) { let data = Self::make_current_if_needed_mut( context_id, @@ -378,6 +476,13 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { // Update webgl texture size. Texture id may change too. info.texture_id = texture_id; info.size = real_size; + + if let Some(ref textures) = textures { + textures + .borrow_mut() + .insert(context_id, (texture_id, real_size)); + } + // Update WR image if needed. Resize image updates are only required for SharedTexture mode. // Readback mode already updates the image every frame to send the raw pixels. // See `handle_update_wr_image`. @@ -403,7 +508,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { } /// Removes a WebGLContext and releases attached resources. - fn remove_webgl_context(&mut self, context_id: WebGLContextId) { + fn remove_webgl_context(&mut self, context_id: WebGLContextId, textures: Option<&TexturesMap>) { // Release webrender image keys. if let Some(info) = self.cached_context_info.remove(&context_id) { let mut txn = webrender_api::Transaction::new(); @@ -422,6 +527,10 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { // Release GL context. self.contexts.remove(&context_id); + if let Some(ref textures) = textures { + textures.borrow_mut().remove(&context_id); + } + // Removing a GLContext may make the current bound context_id dirty. self.bound_context_id = None; } @@ -729,12 +838,12 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> { } } -impl<VR: WebVRRenderHandler + 'static> Drop for WebGLThread<VR> { +impl Drop for WebGLThread { fn drop(&mut self) { // Call remove_context functions in order to correctly delete WebRender image keys. let context_ids: Vec<WebGLContextId> = self.contexts.keys().map(|id| *id).collect(); for id in context_ids { - self.remove_webgl_context(id); + self.remove_webgl_context(id, None); } } } diff --git a/components/canvas_traits/webgl_channel/mod.rs b/components/canvas_traits/webgl_channel/mod.rs index c9b2137a287..45c76d54605 100644 --- a/components/canvas_traits/webgl_channel/mod.rs +++ b/components/canvas_traits/webgl_channel/mod.rs @@ -71,6 +71,13 @@ where WebGLReceiver::Mpsc(ref receiver) => receiver.recv().map_err(|_| ()), } } + + pub fn try_recv(&self) -> Result<T, ()> { + match *self { + WebGLReceiver::Ipc(ref receiver) => receiver.try_recv().map_err(|_| ()), + WebGLReceiver::Mpsc(ref receiver) => receiver.try_recv().map_err(|_| ()), + } + } } pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()> diff --git a/components/canvas_traits/webgl_channel/mpsc.rs b/components/canvas_traits/webgl_channel/mpsc.rs index 0bf845308d3..0dd063967ea 100644 --- a/components/canvas_traits/webgl_channel/mpsc.rs +++ b/components/canvas_traits/webgl_channel/mpsc.rs @@ -47,6 +47,10 @@ impl<T> WebGLReceiver<T> { pub fn recv(&self) -> Result<T, mpsc::RecvError> { self.0.recv() } + #[inline] + pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> { + self.0.try_recv() + } } pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()> { diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 9a4d8b3e93f..6b87dca2036 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -112,7 +112,7 @@ use compositing::compositor_thread::Msg as ToCompositorMsg; use compositing::SendableFrameTree; use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg}; -use embedder_traits::{Cursor, EmbedderMsg, EmbedderProxy}; +use embedder_traits::{Cursor, EmbedderMsg, EmbedderProxy, EventLoopWaker}; use euclid::{default::Size2D as UntypedSize2D, Scale, Size2D}; use gfx::font_cache_thread::FontCacheThread; use gfx_traits::Epoch; @@ -416,6 +416,9 @@ pub struct Constellation<Message, LTF, STF> { /// Application window's GL Context for Media player player_context: WindowGLContext, + + /// Mechanism to force the compositor to process events. + event_loop_waker: Option<Box<dyn EventLoopWaker>>, } /// State needed to construct a constellation. @@ -469,6 +472,9 @@ pub struct InitialConstellationState { /// Application window's GL Context for Media player pub player_context: WindowGLContext, + + /// Mechanism to force the compositor to process events. + pub event_loop_waker: Option<Box<dyn EventLoopWaker>>, } /// Data needed for webdriver @@ -767,6 +773,7 @@ where enable_canvas_antialiasing, glplayer_threads: state.glplayer_threads, player_context: state.player_context, + event_loop_waker: state.event_loop_waker, }; constellation.run(); @@ -1009,6 +1016,7 @@ where webvr_chan: self.webvr_chan.clone(), webxr_registry: self.webxr_registry.clone(), player_context: self.player_context.clone(), + event_loop_waker: self.event_loop_waker.as_ref().map(|w| (*w).clone_box()), }); let pipeline = match result { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index d2c006d368c..43c6a13524f 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -11,6 +11,7 @@ use compositing::CompositionPipeline; use compositing::CompositorProxy; use crossbeam_channel::Sender; use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg}; +use embedder_traits::EventLoopWaker; use euclid::{Scale, Size2D}; use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -195,6 +196,9 @@ pub struct InitialPipelineState { /// Application window's GL Context for Media player pub player_context: WindowGLContext, + + /// Mechanism to force the compositor to process events. + pub event_loop_waker: Option<Box<dyn EventLoopWaker>>, } pub struct NewPipeline { @@ -327,7 +331,11 @@ impl Pipeline { let register = state .background_monitor_register .expect("Couldn't start content, no background monitor has been initiated"); - unprivileged_pipeline_content.start_all::<Message, LTF, STF>(false, register); + unprivileged_pipeline_content.start_all::<Message, LTF, STF>( + false, + register, + state.event_loop_waker, + ); None }; @@ -524,6 +532,7 @@ impl UnprivilegedPipelineContent { self, wait_for_completion: bool, background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, + event_loop_waker: Option<Box<dyn EventLoopWaker>>, ) where LTF: LayoutThreadFactory<Message = Message>, STF: ScriptThreadFactory<Message = Message>, @@ -566,6 +575,7 @@ impl UnprivilegedPipelineContent { webrender_api_sender: self.webrender_api_sender.clone(), layout_is_busy: layout_thread_busy_flag.clone(), player_context: self.player_context.clone(), + event_loop_waker, }, self.load_data.clone(), self.opts.profile_script_events, diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index bad7ebaa4bb..9fcc61dc8b6 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -54,6 +54,7 @@ use canvas_traits::webgl::{WebGLShaderId, WebGLTextureId, WebGLVersion, WebGLVer use crossbeam_channel::{Receiver, Sender}; use cssparser::RGBA; use devtools_traits::{CSSError, TimelineMarkerType, WorkerId}; +use embedder_traits::EventLoopWaker; use encoding_rs::{Decoder, Encoding}; use euclid::default::{Point2D, Rect, Rotation3D, Transform2D, Transform3D}; use euclid::Length as EuclidLength; @@ -146,7 +147,7 @@ pub unsafe trait JSTraceable { unsafe fn trace(&self, trc: *mut JSTracer); } -unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>); +unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>); unsafe_no_jsmanaged_fields!(CSSError); diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index 462b1651eb3..81a263d3394 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -32,11 +32,11 @@ use crate::dom::vreyeparameters::VREyeParameters; use crate::dom::vrframedata::VRFrameData; use crate::dom::vrpose::VRPose; use crate::dom::vrstageparameters::VRStageParameters; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{WebGLMessageSender, WebGLRenderingContext}; use crate::script_runtime::CommonScriptMsg; use crate::script_runtime::ScriptThreadEventCategory::WebVREvent; use crate::task_source::{TaskSource, TaskSourceName}; -use canvas_traits::webgl::{webgl_channel, WebGLMsgSender, WebGLReceiver, WebVRCommand}; +use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand}; use crossbeam_channel::{unbounded, Sender}; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; @@ -102,7 +102,7 @@ struct VRRAFUpdate { depth_near: f64, depth_far: f64, /// WebGL API sender - api_sender: Option<WebGLMsgSender>, + api_sender: Option<WebGLMessageSender>, /// Number uniquely identifying the WebGL context /// so that we may setup/tear down VR compositors as things change context_id: usize, @@ -583,7 +583,7 @@ impl VRDisplay { .fire(self.global().upcast::<EventTarget>()); } - fn api_sender(&self) -> Option<WebGLMsgSender> { + fn api_sender(&self) -> Option<WebGLMessageSender> { self.layer_ctx.get().map(|c| c.webgl_sender()) } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 1240caccf7c..743a2adec84 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -54,12 +54,13 @@ use backtrace::Backtrace; use canvas_traits::webgl::WebGLError::*; use canvas_traits::webgl::{ webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLLimits, GlType, - Parameter, TexDataType, TexFormat, TexParameter, WebGLCommand, WebGLCommandBacktrace, - WebGLContextShareMode, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, - WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSender, WebGLVersion, WebVRCommand, - YAxisTreatment, + Parameter, TexDataType, TexFormat, TexParameter, WebGLChan, WebGLCommand, + WebGLCommandBacktrace, WebGLContextId, WebGLContextShareMode, WebGLError, + WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId, WebGLResult, + WebGLSLVersion, WebGLSendResult, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment, }; use dom_struct::dom_struct; +use embedder_traits::EventLoopWaker; use euclid::default::{Point2D, Rect, Size2D}; use ipc_channel::ipc::{self, IpcSharedMemory}; use js::jsapi::{JSContext, JSObject, Type}; @@ -79,6 +80,7 @@ use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; use std::rc::Rc; +use webrender_api::ImageKey; // From the GLES 2.0.25 spec, page 85: // @@ -135,7 +137,7 @@ bitflags! { pub struct WebGLRenderingContext { reflector_: Reflector, #[ignore_malloc_size_of = "Channels are hard"] - webgl_sender: WebGLMsgSender, + webgl_sender: WebGLMessageSender, #[ignore_malloc_size_of = "Defined in webrender"] webrender_image: Cell<Option<webrender_api::ImageKey>>, share_mode: WebGLContextShareMode, @@ -197,7 +199,10 @@ impl WebGLRenderingContext { let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units; Self { reflector_: Reflector::new(), - webgl_sender: ctx_data.sender, + webgl_sender: WebGLMessageSender::new( + ctx_data.sender, + window.get_event_loop_waker(), + ), webrender_image: Cell::new(None), share_mode: ctx_data.share_mode, webgl_version, @@ -319,7 +324,7 @@ impl WebGLRenderingContext { } } - pub fn webgl_sender(&self) -> WebGLMsgSender { + pub(crate) fn webgl_sender(&self) -> WebGLMessageSender { self.webgl_sender.clone() } @@ -4288,3 +4293,92 @@ impl TexPixels { } } } + +#[derive(JSTraceable)] +pub(crate) struct WebGLCommandSender { + sender: WebGLChan, + waker: Option<Box<dyn EventLoopWaker>>, +} + +impl WebGLCommandSender { + pub fn new(sender: WebGLChan, waker: Option<Box<dyn EventLoopWaker>>) -> WebGLCommandSender { + WebGLCommandSender { sender, waker } + } + + pub fn send(&self, msg: WebGLMsg) -> WebGLSendResult { + let result = self.sender.send(msg); + if let Some(ref waker) = self.waker { + waker.wake(); + } + result + } +} + +#[derive(JSTraceable, MallocSizeOf)] +pub(crate) struct WebGLMessageSender { + sender: WebGLMsgSender, + #[ignore_malloc_size_of = "traits are cumbersome"] + waker: Option<Box<dyn EventLoopWaker>>, +} + +impl Clone for WebGLMessageSender { + fn clone(&self) -> WebGLMessageSender { + WebGLMessageSender { + sender: self.sender.clone(), + waker: self.waker.as_ref().map(|w| (*w).clone_box()), + } + } +} + +impl WebGLMessageSender { + fn wake_after_send<F: FnOnce() -> WebGLSendResult>(&self, f: F) -> WebGLSendResult { + let result = f(); + if let Some(ref waker) = self.waker { + waker.wake(); + } + result + } + + pub fn new( + sender: WebGLMsgSender, + waker: Option<Box<dyn EventLoopWaker>>, + ) -> WebGLMessageSender { + WebGLMessageSender { sender, waker } + } + + pub fn context_id(&self) -> WebGLContextId { + self.sender.context_id() + } + + pub fn send(&self, msg: WebGLCommand, backtrace: WebGLCommandBacktrace) -> WebGLSendResult { + self.wake_after_send(|| self.sender.send(msg, backtrace)) + } + + pub fn send_vr(&self, command: WebVRCommand) -> WebGLSendResult { + self.wake_after_send(|| self.sender.send_vr(command)) + } + + pub fn send_resize( + &self, + size: Size2D<u32>, + sender: WebGLSender<Result<(), String>>, + ) -> WebGLSendResult { + self.wake_after_send(|| self.sender.send_resize(size, sender)) + } + + pub fn send_remove(&self) -> WebGLSendResult { + self.wake_after_send(|| self.sender.send_remove()) + } + + pub fn send_update_wr_image(&self, sender: WebGLSender<ImageKey>) -> WebGLSendResult { + self.wake_after_send(|| self.sender.send_update_wr_image(sender)) + } + + pub fn send_dom_to_texture(&self, command: DOMToTextureCommand) -> WebGLSendResult { + self.wake_after_send(|| self.sender.send_dom_to_texture(command)) + } + + pub fn webxr_external_image_api(&self) -> impl webxr_api::WebGLExternalImageApi { + self.sender.webxr_external_image_api() + } +} diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index ccbd08e1ef7..b33a5c5a126 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -50,6 +50,7 @@ use crate::dom::promise::Promise; use crate::dom::screen::Screen; use crate::dom::storage::Storage; use crate::dom::testrunner::TestRunner; +use crate::dom::webglrenderingcontext::WebGLCommandSender; use crate::dom::windowproxy::WindowProxy; use crate::dom::worklet::Worklet; use crate::dom::workletglobalscope::WorkletGlobalScopeType; @@ -73,7 +74,7 @@ use crossbeam_channel::{unbounded, Sender, TryRecvError}; use cssparser::{Parser, ParserInput, SourceLocation}; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; use dom_struct::dom_struct; -use embedder_traits::EmbedderMsg; +use embedder_traits::{EmbedderMsg, EventLoopWaker}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use ipc_channel::ipc::{channel, IpcSender}; @@ -326,6 +327,10 @@ pub struct Window { /// Window's GL context from application #[ignore_malloc_size_of = "defined in script_thread"] player_context: WindowGLContext, + + /// A mechanism to force the compositor to process events. + #[ignore_malloc_size_of = "traits are cumbersome"] + event_loop_waker: Option<Box<dyn EventLoopWaker>>, } impl Window { @@ -432,8 +437,10 @@ impl Window { self.current_viewport.clone().get() } - pub fn webgl_chan(&self) -> Option<WebGLChan> { - self.webgl_chan.clone() + pub(crate) fn webgl_chan(&self) -> Option<WebGLCommandSender> { + self.webgl_chan + .as_ref() + .map(|chan| WebGLCommandSender::new(chan.clone(), self.get_event_loop_waker())) } pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> { @@ -498,6 +505,10 @@ impl Window { pub fn get_player_context(&self) -> WindowGLContext { self.player_context.clone() } + + pub fn get_event_loop_waker(&self) -> Option<Box<dyn EventLoopWaker>> { + self.event_loop_waker.as_ref().map(|w| (*w).clone_box()) + } } // https://html.spec.whatwg.org/multipage/#atob @@ -2087,6 +2098,7 @@ impl Window { replace_surrogates: bool, user_agent: Cow<'static, str>, player_context: WindowGLContext, + event_loop_waker: Option<Box<dyn EventLoopWaker>>, ) -> DomRoot<Self> { let layout_rpc: Box<dyn LayoutRPC + Send> = { let (rpc_send, rpc_recv) = unbounded(); @@ -2169,6 +2181,7 @@ impl Window { userscripts_path, replace_surrogates, player_context, + event_loop_waker, }); unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 81ef7d9493c..77e037406f7 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -93,7 +93,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::CSSError; use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo}; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; -use embedder_traits::EmbedderMsg; +use embedder_traits::{EmbedderMsg, EventLoopWaker}; use euclid::default::{Point2D, Rect}; use euclid::Vector2D; use headers::ReferrerPolicy as ReferrerPolicyHeader; @@ -684,6 +684,9 @@ pub struct ScriptThread { /// Application window's GL Context for Media player player_context: WindowGLContext, + + /// A mechanism to force the compositor's event loop to process events. + event_loop_waker: Option<Box<dyn EventLoopWaker>>, } /// In the event of thread panic, all data on the stack runs its destructor. However, there @@ -1314,6 +1317,7 @@ impl ScriptThread { replace_surrogates, user_agent, player_context: state.player_context, + event_loop_waker: state.event_loop_waker, } } @@ -3142,6 +3146,7 @@ impl ScriptThread { self.replace_surrogates, self.user_agent.clone(), self.player_context.clone(), + self.event_loop_waker.as_ref().map(|w| (*w).clone_box()), ); // Initialize the browsing context for the window. diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 19c8a41e8c5..08d59b04573 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -24,7 +24,7 @@ use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; -use embedder_traits::Cursor; +use embedder_traits::{Cursor, EventLoopWaker}; use euclid::{ default::{Point2D, Rect}, Length, Scale, Size2D, Vector2D, @@ -666,6 +666,8 @@ pub struct InitialScriptState { pub layout_is_busy: Arc<AtomicBool>, /// Application window's GL Context for Media player pub player_context: WindowGLContext, + /// Mechanism to force the compositor to process events. + pub event_loop_waker: Option<Box<dyn EventLoopWaker>>, } /// This trait allows creating a `ScriptThread` without depending on the `script` diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index cc158af843f..d6758c4fb61 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -60,9 +60,9 @@ layout_thread_2020 = {path = "../layout_thread_2020", optional = true} log = "0.4" media = {path = "../media"} msg = {path = "../msg"} -offscreen_gl_context = "0.23" net = {path = "../net"} net_traits = {path = "../net_traits"} +offscreen_gl_context = "0.23" profile = {path = "../profile"} profile_traits = {path = "../profile_traits"} script = {path = "../script"} diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 42b52985b59..a6c3e7aa460 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -65,7 +65,7 @@ fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {} use bluetooth::BluetoothThreadFactory; use bluetooth_traits::BluetoothRequest; use canvas::gl_context::{CloneableDispatcher, GLContextFactory}; -use canvas::webgl_thread::WebGLThreads; +use canvas::webgl_thread::{ThreadMode, WebGLMainThread, WebGLThreads}; use compositing::compositor_thread::{ CompositorProxy, CompositorReceiver, InitialCompositorState, Msg, }; @@ -117,6 +117,7 @@ use std::rc::Rc; use webrender::{RendererKind, ShaderPrecacheFlags}; use webrender_traits::{WebrenderExternalImageHandlers, WebrenderImageHandlerType}; use webvr::{VRServiceManager, WebVRCompositorHandler, WebVRThread}; +use webvr_traits::WebVRMsg; pub use gleam::gl; pub use keyboard_types; @@ -226,6 +227,7 @@ pub struct Servo<Window: WindowMethods + 'static + ?Sized> { embedder_receiver: EmbedderReceiver, embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>, profiler_enabled: bool, + webgl_thread_data: Option<WebGLMainThread>, } #[derive(Clone)] @@ -384,11 +386,93 @@ where None }; + let (webvr_chan, webvr_constellation_sender, webvr_compositor) = + if let Some(services) = webvr_services { + // WebVR initialization + let (mut handler, sender) = WebVRCompositorHandler::new(); + let (webvr_thread, constellation_sender) = WebVRThread::spawn(sender, services); + handler.set_webvr_thread_sender(webvr_thread.clone()); + ( + Some(webvr_thread), + Some(constellation_sender), + Some(handler), + ) + } else { + (None, None, None) + }; + + // GLContext factory used to create WebGL Contexts + let gl_factory = if opts.should_use_osmesa() { + GLContextFactory::current_osmesa_handle() + } else { + let dispatcher = + Box::new(MainThreadDispatcher::new(compositor_proxy.clone())) as Box<_>; + GLContextFactory::current_native_handle(dispatcher, window.gl().get_type()) + }; + + let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new(); + let mut external_image_handlers = Box::new(external_image_handlers); + + let run_webgl_on_main_thread = + cfg!(windows) || std::env::var("SERVO_WEBGL_MAIN_THREAD").is_ok(); + + // Initialize WebGL Thread entry point. + let webgl_result = gl_factory.map(|factory| { + let (webgl_threads, thread_data, image_handler, output_handler) = WebGLThreads::new( + factory, + webrender_api_sender.clone(), + webvr_compositor.map(|c| c as Box<_>), + external_images.clone(), + if run_webgl_on_main_thread { + ThreadMode::MainThread(embedder.create_event_loop_waker()) + } else { + ThreadMode::OffThread(window.gl()) + }, + ); + + // Set webrender external image handler for WebGL textures + external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL); + + // Set DOM to texture handler, if enabled. + if let Some(output_handler) = output_handler { + webrender.set_output_image_handler(output_handler); + } + + (webgl_threads, thread_data) + }); + let (webgl_threads, webgl_thread_data) = match webgl_result { + Some((a, b)) => (Some(a), b), + None => (None, None), + }; + + let glplayer_threads = match window.get_gl_context() { + GlContext::Unknown => None, + _ => { + let (glplayer_threads, image_handler) = GLPlayerThreads::new(external_images); + external_image_handlers + .set_handler(image_handler, WebrenderImageHandlerType::Media); + Some(glplayer_threads) + }, + }; + let player_context = WindowGLContext { gl_context: window.get_gl_context(), native_display: window.get_native_display(), gl_api: window.get_gl_api(), - glplayer_chan: None, + glplayer_chan: glplayer_threads.as_ref().map(GLPlayerThreads::pipeline), + }; + + webrender.set_external_image_handler(external_image_handlers); + + // When webgl execution occurs on the main thread, and the script thread + // lives in the same process, then the script thread needs the ability to + // wake up the main thread's event loop when webgl commands need processing. + // When there are multiple processes, this is handled automatically by + // the IPC receiving handler instead. + let event_loop_waker = if run_webgl_on_main_thread && !opts.multiprocess { + Some(embedder.create_event_loop_waker()) + } else { + None }; // Create the constellation, which maintains the engine @@ -403,13 +487,15 @@ where mem_profiler_chan.clone(), debugger_chan, devtools_chan, - &mut webrender, webrender_document, webrender_api_sender, - window.gl(), - webvr_services, webxr_main_thread.registry(), player_context, + webgl_threads, + webvr_chan, + webvr_constellation_sender, + glplayer_threads, + event_loop_waker, ); // Send the constellation's swmanager sender to service worker manager thread @@ -450,6 +536,7 @@ where embedder_receiver: embedder_receiver, embedder_events: Vec::new(), profiler_enabled: false, + webgl_thread_data, } } @@ -641,6 +728,10 @@ where } pub fn handle_events(&mut self, events: Vec<WindowEvent>) { + if let Some(ref mut webgl_thread) = self.webgl_thread_data { + webgl_thread.process(); + } + if self.compositor.receive_messages() { self.receive_messages(); } @@ -715,13 +806,15 @@ fn create_constellation( mem_profiler_chan: mem::ProfilerChan, debugger_chan: Option<debugger::Sender>, devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>, - webrender: &mut webrender::Renderer, webrender_document: webrender_api::DocumentId, webrender_api_sender: webrender_api::RenderApiSender, - window_gl: Rc<dyn gl::Gl>, - webvr_services: Option<VRServiceManager>, webxr_registry: webxr_api::Registry, player_context: WindowGLContext, + webgl_threads: Option<WebGLThreads>, + webvr_chan: Option<IpcSender<WebVRMsg>>, + webvr_constellation_sender: Option<Sender<Sender<ConstellationMsg>>>, + glplayer_threads: Option<GLPlayerThreads>, + event_loop_waker: Option<Box<dyn EventLoopWaker>>, ) -> (Sender<ConstellationMsg>, SWManagerSenders) { // Global configuration options, parsed from the command line. let opts = opts::get(); @@ -745,69 +838,6 @@ fn create_constellation( let resource_sender = public_resource_threads.sender(); - let (webvr_chan, webvr_constellation_sender, webvr_compositor) = - if let Some(services) = webvr_services { - // WebVR initialization - let (mut handler, sender) = WebVRCompositorHandler::new(); - let (webvr_thread, constellation_sender) = WebVRThread::spawn(sender, services); - handler.set_webvr_thread_sender(webvr_thread.clone()); - ( - Some(webvr_thread), - Some(constellation_sender), - Some(handler), - ) - } else { - (None, None, None) - }; - - // GLContext factory used to create WebGL Contexts - let gl_factory = if opts.should_use_osmesa() { - GLContextFactory::current_osmesa_handle() - } else { - let dispatcher = Box::new(MainThreadDispatcher::new(compositor_proxy.clone())) as Box<_>; - GLContextFactory::current_native_handle(dispatcher, window_gl.get_type()) - }; - - let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new(); - let mut external_image_handlers = Box::new(external_image_handlers); - - // Initialize WebGL Thread entry point. - let webgl_threads = gl_factory.map(|factory| { - let (webgl_threads, image_handler, output_handler) = WebGLThreads::new( - factory, - window_gl, - webrender_api_sender.clone(), - webvr_compositor.map(|c| c as Box<_>), - external_images.clone(), - ); - - // Set webrender external image handler for WebGL textures - external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL); - - // Set DOM to texture handler, if enabled. - if let Some(output_handler) = output_handler { - webrender.set_output_image_handler(output_handler); - } - - webgl_threads - }); - - let glplayer_threads = match player_context.gl_context { - GlContext::Unknown => None, - _ => { - let (glplayer_threads, image_handler) = GLPlayerThreads::new(external_images); - external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::Media); - Some(glplayer_threads) - }, - }; - - webrender.set_external_image_handler(external_image_handlers); - - let player_context = WindowGLContext { - glplayer_chan: glplayer_threads.as_ref().map(|threads| threads.pipeline()), - ..player_context - }; - let initial_state = InitialConstellationState { compositor_proxy, embedder_proxy, @@ -826,6 +856,7 @@ fn create_constellation( webxr_registry, glplayer_threads, player_context, + event_loop_waker, }; let (constellation_chan, from_swmanager_sender) = Constellation::< script_layout_interface::message::Msg, @@ -930,7 +961,8 @@ pub fn run_content_process(token: String) { layout_thread::LayoutThread, script::script_thread::ScriptThread>( true, - background_hang_monitor_register + background_hang_monitor_register, + None, ); } |