From e3fd03765dfeb908705b9de1ef6fd0d107deadc5 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 14 Nov 2018 08:45:17 -0500 Subject: [PATCH 1/3] Use local offscreen_gl_context. --- Cargo.lock | 8 +++----- Cargo.toml | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 555d81f5236c..cbd8afb49a48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,7 +359,7 @@ dependencies = [ "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.21.1", "pixels 0.0.1", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", @@ -378,7 +378,7 @@ dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.0.1", - "offscreen_gl_context 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.21.1", "pixels 0.0.1", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2707,7 +2707,6 @@ dependencies = [ [[package]] name = "offscreen_gl_context" version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3246,7 +3245,7 @@ dependencies = [ "msg 0.0.1", "net_traits 0.0.1", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.21.1", "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5011,7 +5010,6 @@ dependencies = [ "checksum objc 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5ffd1ab984e2a5ed8a222a6b567d38a69c1d04d64b19eb7c2b10794c6af9f76c" "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_id 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4730aa1c64d722db45f7ccc4113a3e2c465d018de6db4d3e7dfe031e8c8a297" -"checksum offscreen_gl_context 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e86c90338962922a5f623128079b5c01d03784c8fd0809691f4eba233d69a1c" "checksum openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6c24d3508b4fb6da175c10baac54c578b33f09c89ae90c6fe9788b3b4768efdc" "checksum openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)" = "912f301a749394e1025d9dcddef6106ddee9252620e6d0a0e5f8d0681de9b129" "checksum ordered-float 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a3c8db0fca1fdb34404f0b1286db252f23930b9f7a481e376c16c0d5c309d4" diff --git a/Cargo.toml b/Cargo.toml index f20f4d1194ed..3c8219a791d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ opt-level = 3 # # [patch."https://github.com/servo/"] # = { path = "/path/to/local/checkout" } +offscreen_gl_context = { path = "../rust-offscreen-rendering-context" } From 486cacf490339da9d18ad99d8684b5c0be0eff1e Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 14 Nov 2018 18:06:35 -0500 Subject: [PATCH 2/3] Remove offscreen_gl_context dependency from canvas_traits and script. --- Cargo.lock | 2 - components/canvas/gl_context.rs | 49 +++++++++++++++++-- components/canvas/webgl_thread.rs | 6 +-- components/canvas_traits/Cargo.toml | 1 - components/canvas_traits/webgl.rs | 25 +++++++++- components/script/Cargo.toml | 1 - components/script/dom/bindings/trace.rs | 2 +- components/script/dom/htmlcanvaselement.rs | 3 +- .../script/dom/webgl2renderingcontext.rs | 3 +- .../script/dom/webglrenderingcontext.rs | 10 ++-- components/script/dom/webglshader.rs | 3 +- 11 files changed, 80 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbd8afb49a48..9b3c0d85abea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,7 +378,6 @@ dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of 0.0.1", "malloc_size_of_derive 0.0.1", - "offscreen_gl_context 0.21.1", "pixels 0.0.1", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3245,7 +3244,6 @@ dependencies = [ "msg 0.0.1", "net_traits 0.0.1", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.21.1", "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/canvas/gl_context.rs b/components/canvas/gl_context.rs index 093d1837920c..ad12b410ba57 100644 --- a/components/canvas/gl_context.rs +++ b/components/canvas/gl_context.rs @@ -3,14 +3,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use super::webgl_thread::{GLState, WebGLImpl}; -use canvas_traits::webgl::{WebGLCommand, WebGLCommandBacktrace, WebGLVersion}; +use canvas_traits::webgl::{WebGLCommand, WebGLCommandBacktrace, WebGLVersion, GLContextAttributes, GLLimits}; use compositing::compositor_thread::{self, CompositorProxy}; use euclid::Size2D; use gleam::gl; use offscreen_gl_context::{ - ColorAttachmentType, GLContext, GLContextAttributes, GLContextDispatcher, + ColorAttachmentType, GLContext, GLContextAttributes as RawGLContextAttributes, GLContextDispatcher, }; -use offscreen_gl_context::{GLLimits, GLVersion}; +use offscreen_gl_context::{GLLimits as RawGLLimits, GLVersion}; use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle, NativeGLContextMethods}; use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle}; use std::sync::{Arc, Mutex}; @@ -52,6 +52,7 @@ impl GLContextFactory { size: Size2D, attributes: GLContextAttributes, ) -> Result { + let attributes = map_attrs(attributes); Ok(match *self { GLContextFactory::Native(ref handle, ref dispatcher) => { let dispatcher = dispatcher.as_ref().map(|d| Box::new(d.clone()) as Box<_>); @@ -88,6 +89,7 @@ impl GLContextFactory { size: Size2D, attributes: GLContextAttributes, ) -> Result { + let attributes = map_attrs(attributes); Ok(match *self { GLContextFactory::Native(..) => { GLContextWrapper::Native(GLContext::new_shared_with_dispatcher( @@ -189,7 +191,7 @@ impl GLContextWrapper { let limits = ctx.borrow_limits().clone(); - (real_size, texture_id, limits) + (real_size, texture_id, map_limits(limits)) }, GLContextWrapper::OSMesa(ref ctx) => { let (real_size, texture_id) = { @@ -202,7 +204,7 @@ impl GLContextWrapper { let limits = ctx.borrow_limits().clone(); - (real_size, texture_id, limits) + (real_size, texture_id, map_limits(limits)) }, } } @@ -243,3 +245,40 @@ impl GLContextDispatcher for MainThreadDispatcher { .send(compositor_thread::Msg::Dispatch(f)); } } + +fn map_limits(limits: RawGLLimits) -> GLLimits { + GLLimits { + max_vertex_attribs: limits.max_vertex_attribs, + max_tex_size: limits.max_tex_size, + max_cube_map_tex_size: limits.max_cube_map_tex_size, + max_combined_texture_image_units: limits.max_combined_texture_image_units, + max_fragment_uniform_vectors: limits.max_fragment_uniform_vectors, + max_renderbuffer_size: limits.max_renderbuffer_size, + max_texture_image_units: limits.max_texture_image_units, + max_varying_vectors: limits.max_varying_vectors, + max_vertex_texture_image_units: limits.max_vertex_texture_image_units, + max_vertex_uniform_vectors: limits.max_vertex_uniform_vectors, + } +} + +pub fn map_attrs(attrs: GLContextAttributes) -> RawGLContextAttributes { + RawGLContextAttributes { + alpha: attrs.alpha, + depth: attrs.depth, + stencil: attrs.stencil, + antialias: attrs.antialias, + premultiplied_alpha: attrs.premultiplied_alpha, + preserve_drawing_buffer: attrs.preserve_drawing_buffer, + } +} + +pub fn map_attrs_to_script_attrs(attrs: RawGLContextAttributes) -> GLContextAttributes { + GLContextAttributes { + alpha: attrs.alpha, + depth: attrs.depth, + stencil: attrs.stencil, + antialias: attrs.antialias, + premultiplied_alpha: attrs.premultiplied_alpha, + preserve_drawing_buffer: attrs.preserve_drawing_buffer, + } +} diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index 8fc311a1982f..460fac69e196 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -2,14 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use super::gl_context::{GLContextFactory, GLContextWrapper}; +use super::gl_context::{GLContextFactory, GLContextWrapper, map_attrs_to_script_attrs}; use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; use canvas_traits::webgl::*; use euclid::Size2D; use fnv::FnvHashMap; use gleam::gl; use half::f16; -use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods}; +use offscreen_gl_context::{GLContext, NativeGLContextMethods}; use pixels::{self, PixelFormat}; use std::borrow::Cow; use std::thread; @@ -755,7 +755,7 @@ impl WebGLImpl { ) { match command { WebGLCommand::GetContextAttributes(ref sender) => { - sender.send(*ctx.borrow_attributes()).unwrap() + sender.send(map_attrs_to_script_attrs(*ctx.borrow_attributes())).unwrap() }, WebGLCommand::ActiveTexture(target) => ctx.gl().active_texture(target), WebGLCommand::AttachShader(program_id, shader_id) => { diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml index 59a11368c5be..5cffee6c0c0b 100644 --- a/components/canvas_traits/Cargo.toml +++ b/components/canvas_traits/Cargo.toml @@ -21,7 +21,6 @@ gleam = "0.6.7" lazy_static = "1" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } -offscreen_gl_context = {version = "0.21", features = ["serde"]} pixels = {path = "../pixels"} serde = "1.0" serde_bytes = "0.10" diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index 3ef88844e50a..7ebda459a177 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -5,7 +5,6 @@ use euclid::{Rect, Size2D}; use gleam::gl; use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSharedMemory}; -use offscreen_gl_context::{GLContextAttributes, GLLimits}; use pixels::PixelFormat; use serde_bytes::ByteBuf; use std::borrow::Cow; @@ -776,3 +775,27 @@ pub enum YAxisTreatment { AsIs, Flipped, } + +#[derive(Copy, Clone, Debug, Deserialize, Serialize)] +pub struct GLContextAttributes { + pub alpha: bool, + pub depth: bool, + pub stencil: bool, + pub antialias: bool, + pub premultiplied_alpha: bool, + pub preserve_drawing_buffer: bool, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GLLimits { + pub max_vertex_attribs: u32, + pub max_tex_size: u32, + pub max_cube_map_tex_size: u32, + pub max_combined_texture_image_units: u32, + pub max_fragment_uniform_vectors: u32, + pub max_renderbuffer_size: u32, + pub max_texture_image_units: u32, + pub max_varying_vectors: u32, + pub max_vertex_texture_image_units: u32, + pub max_vertex_uniform_vectors: u32 +} diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index b3206a55f872..80cf5a3e4cbd 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -77,7 +77,6 @@ mime_guess = "2.0.0-alpha.6" msg = {path = "../msg"} net_traits = {path = "../net_traits"} num-traits = "0.2" -offscreen_gl_context = {version = "0.21", features = ["serde"]} parking_lot = "0.6" phf = "0.7.18" pixels = {path = "../pixels"} diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 1b430cb12ff3..94638609958a 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -39,6 +39,7 @@ use canvas_traits::webgl::{WebGLBufferId, WebGLChan, WebGLContextShareMode, WebG use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsgSender, WebGLPipeline, WebGLProgramId}; use canvas_traits::webgl::{WebGLReceiver, WebGLRenderbufferId, WebGLSLVersion, WebGLSender}; use canvas_traits::webgl::{WebGLShaderId, WebGLTextureId, WebGLVersion, WebGLVertexArrayId}; +use canvas_traits::webgl::{GLLimits}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::error::Error; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; @@ -81,7 +82,6 @@ use net_traits::response::HttpsState; use net_traits::response::{Response, ResponseBody}; use net_traits::storage_thread::StorageType; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads}; -use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::reporter::CSSErrorReporter; diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 1dbb0806728d..a84a8017ff82 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -4,7 +4,7 @@ use base64; use canvas_traits::canvas::{CanvasId, CanvasMsg, FromScriptMsg}; -use canvas_traits::webgl::WebGLVersion; +use canvas_traits::webgl::{WebGLVersion, GLContextAttributes}; use crate::dom::attr::Attr; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding; @@ -40,7 +40,6 @@ use ipc_channel::ipc::IpcSharedMemory; use js::error::throw_type_error; use js::jsapi::JSContext; use js::rust::HandleValue; -use offscreen_gl_context::GLContextAttributes; use profile_traits::ipc; use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; use servo_config::prefs::PREFS; diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 658df0adc854..d145148f2b1e 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /// https://www.khronos.org/registry/webgl/specs/latest/2.0/webgl.idl -use canvas_traits::webgl::WebGLVersion; +use canvas_traits::webgl::{WebGLVersion, GLContextAttributes}; use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding; use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextMethods; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; @@ -37,7 +37,6 @@ use js::jsapi::{JSContext, JSObject}; use js::jsval::JSVal; use js::rust::CustomAutoRooterGuard; use js::typedarray::ArrayBufferView; -use offscreen_gl_context::GLContextAttributes; use script_layout_interface::HTMLCanvasDataSource; use std::ptr::NonNull; diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index c7896b42436b..92df4ea08bd1 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -6,10 +6,11 @@ use backtrace::Backtrace; use canvas_traits::webgl::WebGLError::*; use canvas_traits::webgl::{ - webgl_channel, AlphaTreatment, DOMToTextureCommand, Parameter, TexDataType, TexFormat, - TexParameter, WebGLCommand, WebGLCommandBacktrace, WebGLContextShareMode, WebGLError, - WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId, WebGLResult, - WebGLSLVersion, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment, + webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLLimits, + Parameter, TexDataType, TexFormat, TexParameter, WebGLCommand, WebGLCommandBacktrace, + WebGLContextShareMode, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, + WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSender, WebGLVersion, WebVRCommand, + YAxisTreatment, }; use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants; @@ -67,7 +68,6 @@ use js::typedarray::{ }; use js::typedarray::{TypedArray, TypedArrayElementCreator}; use net_traits::image_cache::ImageResponse; -use offscreen_gl_context::{GLContextAttributes, GLLimits}; use pixels::{self, PixelFormat}; use script_layout_interface::HTMLCanvasDataSource; use serde::{Deserialize, Serialize}; diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 0c17aa4e5d58..31f8dc7a26a2 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -4,7 +4,7 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl use canvas_traits::webgl::{webgl_channel, WebGLVersion}; -use canvas_traits::webgl::{WebGLCommand, WebGLError}; +use canvas_traits::webgl::{WebGLCommand, WebGLError, GLLimits}; use canvas_traits::webgl::{WebGLResult, WebGLSLVersion, WebGLShaderId}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::WebGLShaderBinding; @@ -19,7 +19,6 @@ use crate::dom::webglobject::WebGLObject; use crate::dom::webglrenderingcontext::WebGLRenderingContext; use dom_struct::dom_struct; use mozangle::shaders::{BuiltInResources, Output, ShaderValidator}; -use offscreen_gl_context::GLLimits; use std::cell::Cell; use std::os::raw::c_int; use std::sync::{Once, ONCE_INIT}; From 3c229f1df44e104ad96414918c1445e7d6b3e863 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 14 Nov 2018 08:45:25 -0500 Subject: [PATCH 3/3] webgl: Swap the current draw buffer every time a webgl canvas is marked dirty. Refactors the WebGL thread and canvas rendering context to reflect the fact that WebRender image keys are immutable. Any readback that is necessary occurs right before the draw buffer is swapped. --- components/canvas/gl_context.rs | 9 ++ components/canvas/webgl_thread.rs | 133 +++++++++++------- components/canvas_traits/webgl.rs | 15 +- .../script/dom/webglrenderingcontext.rs | 38 +++-- 4 files changed, 118 insertions(+), 77 deletions(-) diff --git a/components/canvas/gl_context.rs b/components/canvas/gl_context.rs index ad12b410ba57..8074c597cc4b 100644 --- a/components/canvas/gl_context.rs +++ b/components/canvas/gl_context.rs @@ -178,6 +178,15 @@ impl GLContextWrapper { } } + /// Swap the backing texture for the draw buffer, returning the id of the texture + /// now used for reading. + pub fn swap_draw_buffer(&mut self) -> u32 { + match *self { + GLContextWrapper::Native(ref mut ctx) => ctx.swap_draw_buffer().unwrap(), + GLContextWrapper::OSMesa(ref mut ctx) => ctx.swap_draw_buffer().unwrap(), + } + } + pub fn get_info(&self) -> (Size2D, u32, GLLimits) { match *self { GLContextWrapper::Native(ref ctx) => { diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index 460fac69e196..6c0ae7811c52 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -9,6 +9,7 @@ use euclid::Size2D; use fnv::FnvHashMap; use gleam::gl; use half::f16; +use ipc_channel::ipc; use offscreen_gl_context::{GLContext, NativeGLContextMethods}; use pixels::{self, PixelFormat}; use std::borrow::Cow; @@ -119,7 +120,7 @@ impl WebGLThread { WebGLMsg::CreateContext(version, size, attributes, result_sender) => { let result = self.create_webgl_context(version, size, attributes); result_sender - .send(result.map(|(id, limits, share_mode)| { + .send(result.map(|(id, limits, share_mode, receiver)| { let data = Self::make_current_if_needed( id, &self.contexts, @@ -141,6 +142,7 @@ impl WebGLThread { limits, share_mode, glsl_version, + ready_to_present: receiver, } })) .unwrap(); @@ -154,6 +156,9 @@ impl WebGLThread { WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => { self.handle_webgl_command(ctx_id, command, backtrace); }, + WebGLMsg::PresentWebGLFrame(ctx_id) => { + self.handle_present_webgl_frame(ctx_id); + } WebGLMsg::WebVRCommand(ctx_id, command) => { self.handle_webvr_command(ctx_id, command); }, @@ -177,6 +182,62 @@ impl WebGLThread { false } + fn handle_present_webgl_frame( + &mut self, + context_id: WebGLContextId, + ) { + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + let new_texture = match Self::make_current_if_needed_mut( + context_id, + &mut self.contexts, + &mut self.bound_context_id, + ) { + Some(data) => { + match info.share_mode { + WebGLContextShareMode::Readback => { + // Obtain the pixels for the current frame and notify webrender. + let pixels = Self::raw_pixels(&data.ctx, info.size); + if let Some(image_key) = info.image_key { + Self::update_wr_readback_image( + &self.webrender_api, + info.size, + info.alpha, + image_key, + pixels, + ); + } else { + info.image_key = Some(Self::create_wr_readback_image( + &self.webrender_api, + info.size, + info.alpha, + pixels, + )); + } + } + WebGLContextShareMode::SharedTexture if info.image_key.is_none() => { + info.image_key = Some(Self::create_wr_external_image( + &self.webrender_api, + info.size, + info.alpha, + context_id + )); + } + WebGLContextShareMode::SharedTexture => {} + } + + // Swap the backing textures now that this frame is complete. + data.ctx.swap_draw_buffer() + } + None => return, + }; + + // Store the texture that represents the complete frame. + info.texture_id = new_texture; + if let Some(ref sender) = info.present_notifier { + let _ = sender.send(()); + } + } + /// Handles a WebGLCommand for a specific WebGLContext fn handle_webgl_command( &mut self, @@ -248,7 +309,7 @@ impl WebGLThread { version: WebGLVersion, size: Size2D, attributes: GLContextAttributes, - ) -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode), String> { + ) -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode, Option>), 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. self.bound_context_id = None; @@ -279,6 +340,13 @@ impl WebGLThread { state: Default::default(), }, ); + let (notifier_sender, notifier_receiver) = match share_mode { + WebGLContextShareMode::SharedTexture => (None, None), + WebGLContextShareMode::Readback => { + let (sender, receiver) = ipc::channel().unwrap(); + (Some(sender), Some(receiver)) + } + }; self.cached_context_info.insert( id, WebGLContextInfo { @@ -288,10 +356,11 @@ impl WebGLThread { image_key: None, share_mode, gl_sync: None, + present_notifier: notifier_sender, }, ); - Ok((id, limits, share_mode)) + Ok((id, limits, share_mode, notifier_receiver)) } /// Resizes a WebGLContext @@ -358,62 +427,16 @@ impl WebGLThread { self.bound_context_id = None; } - /// Handles the creation/update of webrender_api::ImageKeys for a specific WebGLContext. - /// This method is invoked from a UpdateWebRenderImage message sent by the layout thread. - /// If SharedTexture is used the UpdateWebRenderImage message is sent only after a WebGLContext creation. - /// If Readback is used UpdateWebRenderImage message is sent always on each layout iteration in order to - /// submit the updated raw pixels. + /// Handles the retrieval of webrender_api::ImageKeys for a specific WebGLContext. + /// This method is invoked from a UpdateWebRenderImage message sent by the layout thread, + /// only once after a WebGL frame is presented to the compositor for the first time. fn handle_update_wr_image( &mut self, context_id: WebGLContextId, sender: WebGLSender, ) { - let info = self.cached_context_info.get_mut(&context_id).unwrap(); - let webrender_api = &self.webrender_api; - - let image_key = match info.share_mode { - WebGLContextShareMode::SharedTexture => { - let size = info.size; - let alpha = info.alpha; - // Reuse existing ImageKey or generate a new one. - // When using a shared texture ImageKeys are only generated after a WebGLContext creation. - *info.image_key.get_or_insert_with(|| { - Self::create_wr_external_image(webrender_api, size, alpha, context_id) - }) - }, - WebGLContextShareMode::Readback => { - let pixels = Self::raw_pixels(&self.contexts[&context_id].ctx, info.size); - match info.image_key.clone() { - Some(image_key) => { - // ImageKey was already created, but WR Images must - // be updated every frame in readback mode to send the new raw pixels. - Self::update_wr_readback_image( - webrender_api, - info.size, - info.alpha, - image_key, - pixels, - ); - - image_key - }, - None => { - // Generate a new ImageKey for Readback mode. - let image_key = Self::create_wr_readback_image( - webrender_api, - info.size, - info.alpha, - pixels, - ); - info.image_key = Some(image_key); - image_key - }, - } - }, - }; - - // Send the ImageKey to the Layout thread. - sender.send(image_key).unwrap(); + let info = self.cached_context_info.get(&context_id).unwrap(); + sender.send(info.image_key.expect("no image key for webgl context?")); } fn handle_dom_to_texture(&mut self, command: DOMToTextureCommand) { @@ -685,6 +708,8 @@ struct WebGLContextInfo { share_mode: WebGLContextShareMode, /// GLSync Object used for a correct synchronization with Webrender external image callbacks. gl_sync: Option, + /// Signal that the webgl thread is ready to present a new frame to the compositor. + present_notifier: Option>, } /// This trait is used as a bridge between the `WebGLThreads` implementation and diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index 7ebda459a177..ba20142deaf6 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -4,7 +4,7 @@ use euclid::{Rect, Size2D}; use gleam::gl; -use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSharedMemory}; +use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSharedMemory, IpcReceiver}; use pixels::PixelFormat; use serde_bytes::ByteBuf; use std::borrow::Cow; @@ -46,6 +46,8 @@ pub enum WebGLMsg { ResizeContext(WebGLContextId, Size2D, WebGLSender>), /// Drops a WebGLContext. RemoveContext(WebGLContextId), + /// Present a WebGL frame to the compositor. + PresentWebGLFrame(WebGLContextId), /// Runs a WebGLCommand in a specific WebGLContext. WebGLCommand(WebGLContextId, WebGLCommand, WebGLCommandBacktrace), /// Runs a WebVRCommand in a specific WebGLContext. @@ -70,7 +72,7 @@ pub enum WebGLMsg { } /// Contains the WebGLCommand sender and information about a WebGLContext -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct WebGLCreateContextResult { /// Sender instance to send commands to the specific WebGLContext pub sender: WebGLMsgSender, @@ -80,6 +82,8 @@ pub struct WebGLCreateContextResult { pub share_mode: WebGLContextShareMode, /// The GLSL version supported by the context. pub glsl_version: WebGLSLVersion, + /// The WebGL renderer is ready to present another frame. + pub ready_to_present: Option>, } #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)] @@ -138,6 +142,13 @@ impl WebGLMsgSender { .send(WebGLMsg::WebGLCommand(self.ctx_id, command, backtrace)) } + /// Instruct the webgl renderer to present the current frame to the compositor. + #[inline] + pub fn send_present_frame(&self) -> WebGLSendResult { + self.sender + .send(WebGLMsg::PresentWebGLFrame(self.ctx_id)) + } + /// Send a WebVRCommand message #[inline] pub fn send_vr(&self, command: WebVRCommand) -> WebGLSendResult { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 92df4ea08bd1..75db7b710f48 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -162,6 +162,8 @@ pub struct WebGLRenderingContext { default_vao: DomOnceCell, current_vao: MutNullableDom, textures: Textures, + #[ignore_malloc_size_of = "channels are hard"] + ready_to_present: Option>, } impl WebGLRenderingContext { @@ -217,6 +219,7 @@ impl WebGLRenderingContext { default_vao: Default::default(), current_vao: Default::default(), textures: Textures::new(max_combined_texture_image_units), + ready_to_present: ctx_data.ready_to_present, } }) } @@ -435,8 +438,8 @@ impl WebGLRenderingContext { } fn mark_as_dirty(&self) { - // If we don't have a bound framebuffer, then don't mark the canvas - // as dirty. + // If we don't have a bound framebuffer, we want to ensure that the current + // frame contents are presented to the compositor. if self.bound_framebuffer.get().is_none() { self.canvas .upcast::() @@ -783,26 +786,19 @@ impl WebGLRenderingContext { } pub fn layout_handle(&self) -> webrender_api::ImageKey { - match self.share_mode { - WebGLContextShareMode::SharedTexture => { - // WR using ExternalTexture requires a single update message. - self.webrender_image.get().unwrap_or_else(|| { - let (sender, receiver) = webgl_channel().unwrap(); - self.webgl_sender.send_update_wr_image(sender).unwrap(); - let image_key = receiver.recv().unwrap(); - self.webrender_image.set(Some(image_key)); - - image_key - }) - }, - WebGLContextShareMode::Readback => { - // WR using Readback requires to update WR image every frame - // in order to send the new raw pixels. - let (sender, receiver) = webgl_channel().unwrap(); - self.webgl_sender.send_update_wr_image(sender).unwrap(); - receiver.recv().unwrap() - }, + self.webgl_sender.send_present_frame(); + if let Some(ref rx) = self.ready_to_present { + // Wait for a notification that the frame has been presented. + let _ = rx.recv(); } + + self.webrender_image.get().unwrap_or_else(|| { + let (sender, receiver) = webgl_channel().unwrap(); + self.webgl_sender.send_update_wr_image(sender).unwrap(); + let image_key = receiver.recv().unwrap(); + self.webrender_image.set(Some(image_key)); + image_key + }) } // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/