aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/canvas/Cargo.toml1
-rw-r--r--components/canvas/webgl_mode/inprocess.rs250
-rw-r--r--components/canvas/webgl_mode/mod.rs2
-rw-r--r--components/canvas/webgl_thread.rs197
-rw-r--r--components/canvas_traits/webgl_channel/mod.rs7
-rw-r--r--components/canvas_traits/webgl_channel/mpsc.rs4
-rw-r--r--components/constellation/constellation.rs10
-rw-r--r--components/constellation/pipeline.rs12
-rw-r--r--components/script/dom/bindings/trace.rs3
-rw-r--r--components/script/dom/vrdisplay.rs8
-rw-r--r--components/script/dom/webglrenderingcontext.rs108
-rw-r--r--components/script/dom/window.rs19
-rw-r--r--components/script/script_thread.rs7
-rw-r--r--components/script_traits/lib.rs4
-rw-r--r--components/servo/Cargo.toml2
-rw-r--r--components/servo/lib.rs176
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,
);
}