aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2019-07-11 19:41:04 -0400
committerJosh Matthews <josh@joshmatthews.net>2019-07-25 23:05:03 -0400
commit39d13d1fc85d3b38d04df689421fd0771c951b64 (patch)
tree2067c4e225e859a572a44ed733e0f9124c18c21c /components
parentad82d67b2e5ab56d046274ebb5436c3388ddf21a (diff)
downloadservo-39d13d1fc85d3b38d04df689421fd0771c951b64.tar.gz
servo-39d13d1fc85d3b38d04df689421fd0771c951b64.zip
Support running WebGL in its own thread or on the main thread.
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.rs195
-rw-r--r--components/canvas_traits/webgl_channel/mod.rs7
-rw-r--r--components/canvas_traits/webgl_channel/mpsc.rs4
-rw-r--r--components/servo/Cargo.toml2
-rw-r--r--components/servo/lib.rs159
8 files changed, 421 insertions, 199 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..715b6d8b8ca 100644
--- a/components/canvas/webgl_thread.rs
+++ b/components/canvas/webgl_thread.rs
@@ -5,6 +5,7 @@
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;
@@ -13,13 +14,17 @@ use ipc_channel::ipc::IpcSender;
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 +55,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 +67,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 +145,83 @@ 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 {
+ // Interpose a new channel in between the existing WebGL channel endpoints.
+ // This will bounce all WebGL messages through a second 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) = webgl_channel::<WebGLMsg>().unwrap();
+ let receiver = mem::replace(&mut init.receiver, from_router_receiver);
+
+ let thread_data = WebGLThread::new(init);
+
thread::Builder::new()
- .name("WebGLThread".to_owned())
+ .name("WebGL main thread pump".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;
- }
+ while let Ok(msg) = receiver.recv() {
+ let _ = from_router_sender.send(msg);
+ event_loop_waker.wake();
}
})
.expect("Thread spawning failed");
- result
+ WebGLMainThread {
+ thread_data,
+ 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("WebGL thread".to_owned())
+ .spawn(move || {
+ let mut data = WebGLThread::new(init);
+ data.process(EventLoop::Blocking, None);
+ })
+ .expect("Thread spawning failed");
+ }
+
+ 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 +266,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 +389,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 +426,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 +453,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 +478,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 +510,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 +529,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 +840,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/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..1ea0c10ab8e 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,13 +386,84 @@ 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);
+
+ // Initialize WebGL Thread entry point.
+ let webgl_result = gl_factory.map(|factory| {
+ let run_on_main_thread =
+ cfg!(windows) || std::env::var("SERVO_WEBGL_MAIN_THREAD").is_ok();
+
+ 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_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);
+
// Create the constellation, which maintains the engine
// pipelines, including the script and layout threads, as well
// as the navigation context.
@@ -403,13 +476,14 @@ 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,
);
// Send the constellation's swmanager sender to service worker manager thread
@@ -450,6 +524,7 @@ where
embedder_receiver: embedder_receiver,
embedder_events: Vec::new(),
profiler_enabled: false,
+ webgl_thread_data,
}
}
@@ -641,6 +716,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 +794,14 @@ 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>,
) -> (Sender<ConstellationMsg>, SWManagerSenders) {
// Global configuration options, parsed from the command line.
let opts = opts::get();
@@ -745,69 +825,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,