mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #23777 - jdm:webgl-main-thread, r=asajeffrey
Support running WebGL in its own thread or on the main thread. This is the final missing piece to support WebGL in ANGLE on Windows. ANGLE doesn't support multiple GL contexts on separate threads using the same underlying Direct3d device, so we need to process all GL operations for WebGL on the same thread as the compositor. These changes try to retain enough flexibility to support both approaches so we can get WebGL working on Windows ASAP. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #23697 - [x] There are tests for these changes <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23777) <!-- Reviewable:end -->
This commit is contained in:
commit
8ec28978cd
164 changed files with 3105 additions and 552 deletions
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
|
||||
WebGLExternalImages::MainThread { ref textures } => {
|
||||
let textures = textures.borrow();
|
||||
let entry = textures
|
||||
.get(&WebGLContextId(id as usize))
|
||||
.expect("no texture entry???");
|
||||
(entry.0, entry.1)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn unlock(&mut self, id: u64) {
|
||||
self.webgl_channel
|
||||
.send(WebGLMsg::Unlock(WebGLContextId(id as usize)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
match *self {
|
||||
WebGLExternalImages::OffThread {
|
||||
ref webgl_channel, ..
|
||||
} => {
|
||||
webgl_channel
|
||||
.send(WebGLMsg::Unlock(WebGLContextId(id as usize)))
|
||||
.unwrap();
|
||||
},
|
||||
|
||||
/// Wrapper to send WebVR commands used in `WebGLThread`.
|
||||
struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>);
|
||||
|
||||
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 => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>), ()>
|
||||
|
|
|
@ -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>), ()> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue