From 0178fcae4ec29824a3f1d35969457bed1ca104e9 Mon Sep 17 00:00:00 2001 From: Alan Jeffrey Date: Fri, 26 Jul 2019 16:52:23 -0500 Subject: [PATCH] When using the WebGL external image API, use sync calls if we happen to be on the WebGL thread --- components/canvas/webgl_mode/inprocess.rs | 239 +++++++++------------- components/canvas/webgl_thread.rs | 167 ++++++++------- components/servo/lib.rs | 5 +- 3 files changed, 190 insertions(+), 221 deletions(-) diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs index d499a1cb148b..34a3f0eeb7a2 100644 --- a/components/canvas/webgl_mode/inprocess.rs +++ b/components/canvas/webgl_mode/inprocess.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::gl_context::GLContextFactory; -use crate::webgl_thread::{TexturesMap, WebGLMainThread, WebGLThread, WebGLThreadInit}; +use crate::webgl_thread::{WebGLMainThread, WebGLThread, WebGLThreadInit}; use canvas_traits::webgl::webgl_channel; use canvas_traits::webgl::DOMToTextureCommand; use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver}; @@ -13,8 +13,6 @@ 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}; @@ -25,20 +23,21 @@ pub struct WebGLThreads(WebGLSender); pub enum ThreadMode { MainThread(Box), - OffThread(Rc), + OffThread, } impl WebGLThreads { /// Creates a new WebGLThreads object pub fn new( gl_factory: GLContextFactory, + webrender_gl: Rc, webrender_api_sender: webrender_api::RenderApiSender, webvr_compositor: Option>, external_images: Arc>, mode: ThreadMode, ) -> ( WebGLThreads, - Option, + Option>, Box, Option>, ) { @@ -54,33 +53,24 @@ impl WebGLThreads { }; let output_handler = if pref!(dom.webgl.dom_to_texture.enabled) { - 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()) - }, - })) + Some(Box::new(OutputHandler::new( + webrender_gl.clone(), + sender.clone(), + ))) } else { None }; - let (external, webgl_thread) = match mode { + let external = WebGLExternalImages::new(webrender_gl, sender.clone()); + + let 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), - ) + let thread = WebGLThread::run_on_current_thread(init, event_loop_waker); + Some(thread) }, - - ThreadMode::OffThread(webrender_gl) => { + ThreadMode::OffThread => { WebGLThread::run_on_own_thread(init); - ( - WebGLExternalImages::new_off_thread(webrender_gl, sender.clone()), - None, - ) + None }, }; @@ -107,113 +97,90 @@ impl WebGLThreads { } /// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads. -enum WebGLExternalImages { - OffThread { - webrender_gl: Rc, - webgl_channel: WebGLSender, - // Used to avoid creating a new channel on each received WebRender request. - lock_channel: ( - WebGLSender<(u32, Size2D, usize)>, - WebGLReceiver<(u32, Size2D, usize)>, - ), - }, - MainThread { - textures: TexturesMap, - }, +struct WebGLExternalImages { + webrender_gl: Rc, + webgl_channel: WebGLSender, + // Used to avoid creating a new channel on each received WebRender request. + lock_channel: ( + WebGLSender<(u32, Size2D, usize)>, + WebGLReceiver<(u32, Size2D, usize)>, + ), } impl WebGLExternalImages { - fn new_off_thread(webrender_gl: Rc, channel: WebGLSender) -> Self { - WebGLExternalImages::OffThread { + fn new(webrender_gl: Rc, channel: WebGLSender) -> Self { + WebGLExternalImages { 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) { - 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) - }, + if let Some(main_thread) = WebGLMainThread::on_current_thread() { + // If we're on the same thread as WebGL, we can get the data directly + main_thread + .thread_data + .borrow_mut() + .handle_lock_unsync(WebGLContextId(id as usize)) + } else { + // 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) } } fn unlock(&mut self, id: u64) { - match *self { - WebGLExternalImages::OffThread { - ref webgl_channel, .. - } => { - webgl_channel - .send(WebGLMsg::Unlock(WebGLContextId(id as usize))) - .unwrap(); - }, - - WebGLExternalImages::MainThread { .. } => {}, + if let Some(main_thread) = WebGLMainThread::on_current_thread() { + // If we're on the same thread as WebGL, we can unlock directly + main_thread + .thread_data + .borrow_mut() + .handle_unlock(WebGLContextId(id as usize)) + } else { + self.webgl_channel + .send(WebGLMsg::Unlock(WebGLContextId(id as usize))) + .unwrap() } } } /// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait. type OutputHandlerData = Option<(u32, Size2D)>; -enum OutputHandler { - OffThread { - webrender_gl: Rc, - webgl_channel: WebGLSender, - // Used to avoid creating a new channel on each received WebRender request. - lock_channel: ( - WebGLSender, - WebGLReceiver, - ), - sync_objects: FnvHashMap, - }, - MainThread, +struct OutputHandler { + webrender_gl: Rc, + webgl_channel: WebGLSender, + // Used to avoid creating a new channel on each received WebRender request. + lock_channel: ( + WebGLSender, + WebGLReceiver, + ), + sync_objects: FnvHashMap, } impl OutputHandler { - fn new_off_thread(webrender_gl: Rc, channel: WebGLSender) -> Self { - OutputHandler::OffThread { + fn new(webrender_gl: Rc, channel: WebGLSender) -> Self { + OutputHandler { 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. @@ -222,49 +189,41 @@ impl webrender::OutputImageHandler for OutputHandler { &mut self, id: webrender_api::PipelineId, ) -> Option<(u32, webrender_api::units::FramebufferIntSize)> { - 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), - ) - }) - }, + // Insert a fence in the WR command queue + let gl_sync = self + .webrender_gl + .fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); + self.sync_objects.insert(id, gl_sync); + + let result = if let Some(main_thread) = WebGLMainThread::on_current_thread() { + main_thread + .thread_data + .borrow_mut() + .handle_dom_to_texture_lock(id, gl_sync as usize) + } else { + // 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() + }; - OutputHandler::MainThread => unimplemented!(), - } + result.map(|(tex_id, size)| { + ( + tex_id, + webrender_api::units::FramebufferIntSize::new(size.width, size.height), + ) + }) } fn unlock(&mut self, id: webrender_api::PipelineId) { - 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 => {}, + 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); } } } diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index 1d6051c7aa39..01262de02f26 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -15,8 +15,8 @@ use ipc_channel::router::ROUTER; use offscreen_gl_context::{DrawBuffer, GLContext, NativeGLContextMethods}; use pixels::{self, PixelFormat}; use std::borrow::Cow; +use std::cell::Cell; use std::cell::RefCell; -use std::collections::HashMap; use std::mem; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -80,12 +80,6 @@ pub(crate) struct WebGLThread { sender: WebGLSender, } -/// 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)>>>; - #[derive(PartialEq)] enum EventLoop { Blocking, @@ -105,26 +99,38 @@ pub(crate) struct WebGLThreadInit { /// 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, + pub(crate) thread_data: RefCell, + shut_down: Cell, } impl WebGLMainThread { /// Synchronously process all outstanding WebGL messages. - pub fn process(&mut self) { - if self.shut_down { + pub fn process(&self) { + if self.shut_down.get() { 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())) + self.thread_data.borrow_mut().bound_context_id = None; + self.shut_down.set( + !self + .thread_data + .borrow_mut() + .process(EventLoop::Nonblocking), + ); + } + + /// Returns the main GL thread if called from the main thread, + /// and `None` otherwise. + pub(crate) fn on_current_thread() -> Option> { + WEBGL_MAIN_THREAD.with(|main_thread| main_thread.borrow().clone()) } } +thread_local! { + static WEBGL_MAIN_THREAD: RefCell>> = RefCell::new(None); +} + impl WebGLThread { /// Create a new instance of WebGLThread. pub(crate) fn new( @@ -158,8 +164,7 @@ impl WebGLThread { pub(crate) fn run_on_current_thread( mut init: WebGLThreadInit, event_loop_waker: Box, - textures: TexturesMap, - ) -> WebGLMainThread { + ) -> Rc { 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 @@ -176,11 +181,17 @@ impl WebGLThread { ); } - WebGLMainThread { - thread_data: WebGLThread::new(init), - textures, - shut_down: false, - } + let result = Rc::new(WebGLMainThread { + thread_data: RefCell::new(WebGLThread::new(init)), + shut_down: Cell::new(false), + }); + + WEBGL_MAIN_THREAD.with(|main_thread| { + debug_assert!(main_thread.borrow().is_none()); + *main_thread.borrow_mut() = Some(result.clone()); + }); + + result } /// Perform all initialization required to run an instance of WebGLThread @@ -190,18 +201,18 @@ impl WebGLThread { .name("WebGL thread".to_owned()) .spawn(move || { let mut data = WebGLThread::new(init); - data.process(EventLoop::Blocking, None); + data.process(EventLoop::Blocking); }) .expect("Thread spawning failed"); } - fn process(&mut self, loop_type: EventLoop, textures: Option) -> bool { + fn process(&mut self, loop_type: EventLoop) -> 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()); + let exit = self.handle_msg(msg, &webgl_chan); if exit { return false; } @@ -210,16 +221,11 @@ impl WebGLThread { } /// Handles a generic WebGLMsg message - fn handle_msg( - &mut self, - msg: WebGLMsg, - webgl_chan: &WebGLChan, - textures: Option<&TexturesMap>, - ) -> bool { + fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool { trace!("processing {:?}", msg); match msg { WebGLMsg::CreateContext(version, size, attributes, result_sender) => { - let result = self.create_webgl_context(version, size, attributes, textures); + let result = self.create_webgl_context(version, size, attributes); result_sender .send(result.map(|(id, limits, share_mode)| { let data = Self::make_current_if_needed( @@ -264,10 +270,10 @@ impl WebGLThread { .unwrap(); }, WebGLMsg::ResizeContext(ctx_id, size, sender) => { - self.resize_webgl_context(ctx_id, size, sender, textures); + self.resize_webgl_context(ctx_id, size, sender); }, WebGLMsg::RemoveContext(ctx_id) => { - self.remove_webgl_context(ctx_id, textures); + self.remove_webgl_context(ctx_id); }, WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => { self.handle_webgl_command(ctx_id, command, backtrace); @@ -337,7 +343,7 @@ impl WebGLThread { context_id: WebGLContextId, sender: WebGLSender<(u32, Size2D, usize)>, ) { - sender.send(self.handle_lock_inner(context_id)).unwrap(); + sender.send(self.handle_lock_sync(context_id)).unwrap(); } /// handle_lock, but unconditionally IPC (used by webxr) @@ -346,12 +352,12 @@ impl WebGLThread { context_id: WebGLContextId, sender: IpcSender<(u32, Size2D, usize)>, ) { - sender.send(self.handle_lock_inner(context_id)).unwrap(); + sender.send(self.handle_lock_sync(context_id)).unwrap(); } /// Shared code between handle_lock and handle_lock_ipc, does the actual syncing/flushing /// but the caller must send the response back - fn handle_lock_inner(&mut self, context_id: WebGLContextId) -> (u32, Size2D, usize) { + fn handle_lock_sync(&mut self, context_id: WebGLContextId) -> (u32, Size2D, usize) { let data = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id) .expect("WebGLContext not found in a WebGLMsg::Lock message"); @@ -361,23 +367,38 @@ impl WebGLThread { // The related gl().wait_sync call is performed in the WR thread. See WebGLExternalImageApi for mor details. let gl_sync = data.ctx.gl().fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); info.gl_sync = Some(gl_sync); + debug_assert!(data.ctx.gl().get_error() == gl::NO_ERROR); + // It is important that the fence sync is properly flushed into the GPU's command queue. // Without proper flushing, the sync object may never be signaled. data.ctx.gl().flush(); + debug_assert!(data.ctx.gl().get_error() == gl::NO_ERROR); (info.texture_id, info.size, gl_sync as usize) } + /// A version of locking that doesn't return a GLsync object, + /// used when it's being called from the same thread. + pub(crate) fn handle_lock_unsync(&mut self, context_id: WebGLContextId) -> (u32, Size2D) { + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + info.render_state = ContextRenderState::Locked(None); + (info.texture_id, info.size) + } + /// Handles an unlock external callback received from webrender::ExternalImageHandler - fn handle_unlock(&mut self, context_id: WebGLContextId) { - let data = - Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id) - .expect("WebGLContext not found in a WebGLMsg::Unlock message"); + pub(crate) fn handle_unlock(&mut self, context_id: WebGLContextId) { let info = self.cached_context_info.get_mut(&context_id).unwrap(); info.render_state = ContextRenderState::Unlocked; if let Some(gl_sync) = info.gl_sync.take() { + let data = Self::make_current_if_needed( + context_id, + &self.contexts, + &mut self.bound_context_id, + ) + .expect("WebGLContext not found in a WebGLMsg::Unlock message"); // Release the GLSync object. data.ctx.gl().delete_sync(gl_sync); + debug_assert!(data.ctx.gl().get_error() == gl::NO_ERROR); } } @@ -387,7 +408,6 @@ impl WebGLThread { version: WebGLVersion, size: Size2D, 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. @@ -425,10 +445,6 @@ impl WebGLThread { }, ); - if let Some(ref textures) = textures { - textures.borrow_mut().insert(id, (texture_id, size)); - } - self.cached_context_info.insert( id, WebGLContextInfo { @@ -451,7 +467,6 @@ impl WebGLThread { context_id: WebGLContextId, size: Size2D, sender: WebGLSender>, - textures: Option<&TexturesMap>, ) { let data = Self::make_current_if_needed_mut( context_id, @@ -477,12 +492,6 @@ impl WebGLThread { 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`. @@ -508,7 +517,7 @@ impl WebGLThread { } /// Removes a WebGLContext and releases attached resources. - fn remove_webgl_context(&mut self, context_id: WebGLContextId, textures: Option<&TexturesMap>) { + fn remove_webgl_context(&mut self, context_id: WebGLContextId) { // Release webrender image keys. if let Some(info) = self.cached_context_info.remove(&context_id) { let mut txn = webrender_api::Transaction::new(); @@ -527,10 +536,6 @@ impl WebGLThread { // 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; } @@ -628,25 +633,7 @@ impl WebGLThread { self.webrender_api.send_transaction(document_id, txn); }, DOMToTextureCommand::Lock(pipeline_id, gl_sync, sender) => { - let contexts = &self.contexts; - let bound_context_id = &mut self.bound_context_id; - let result = self.dom_outputs.get(&pipeline_id).and_then(|dom_data| { - let data = Self::make_current_if_needed( - dom_data.context_id, - contexts, - bound_context_id, - ); - data.and_then(|data| { - // The next glWaitSync call is used to synchronize the two flows of - // OpenGL commands (WR and WebGL) in order to avoid using semi-ready WR textures. - // glWaitSync doesn't block WebGL CPU thread. - data.ctx - .gl() - .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED); - Some((dom_data.texture_id.get(), dom_data.size)) - }) - }); - + let result = self.handle_dom_to_texture_lock(pipeline_id, gl_sync); // Send the texture id and size to WR. sender.send(result).unwrap(); }, @@ -666,6 +653,28 @@ impl WebGLThread { } } + pub(crate) fn handle_dom_to_texture_lock( + &mut self, + pipeline_id: webrender_api::PipelineId, + gl_sync: usize, + ) -> Option<(u32, Size2D)> { + let contexts = &self.contexts; + let bound_context_id = &mut self.bound_context_id; + self.dom_outputs.get(&pipeline_id).and_then(|dom_data| { + let data = + Self::make_current_if_needed(dom_data.context_id, contexts, bound_context_id); + data.and_then(|data| { + // The next glWaitSync call is used to synchronize the two flows of + // OpenGL commands (WR and WebGL) in order to avoid using semi-ready WR textures. + // glWaitSync doesn't block WebGL CPU thread. + data.ctx + .gl() + .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED); + Some((dom_data.texture_id.get(), dom_data.size)) + }) + }) + } + /// Gets a reference to a GLContextWrapper for a given WebGLContextId and makes it current if required. fn make_current_if_needed<'a>( context_id: WebGLContextId, @@ -843,7 +852,7 @@ impl Drop for WebGLThread { // Call remove_context functions in order to correctly delete WebRender image keys. let context_ids: Vec = self.contexts.keys().map(|id| *id).collect(); for id in context_ids { - self.remove_webgl_context(id, None); + self.remove_webgl_context(id); } } } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index a6c3e7aa460b..d94700f11feb 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -227,7 +227,7 @@ pub struct Servo { embedder_receiver: EmbedderReceiver, embedder_events: Vec<(Option, EmbedderMsg)>, profiler_enabled: bool, - webgl_thread_data: Option, + webgl_thread_data: Option>, } #[derive(Clone)] @@ -420,13 +420,14 @@ where let webgl_result = gl_factory.map(|factory| { let (webgl_threads, thread_data, image_handler, output_handler) = WebGLThreads::new( factory, + window.gl(), 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()) + ThreadMode::OffThread }, );