diff --git a/Cargo.lock b/Cargo.lock index 27e82a3c7d..716e3e3a81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,7 +118,7 @@ name = "cgl" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -308,7 +308,7 @@ name = "direct-composition" version = "0.1.0" dependencies = [ "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "mozangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.57.2", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -473,7 +473,7 @@ dependencies = [ [[package]] name = "gleam" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1463,7 +1463,7 @@ dependencies = [ "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1493,7 +1493,7 @@ dependencies = [ "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.57.2", @@ -1593,7 +1593,7 @@ dependencies = [ "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "font-loader 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1724,7 +1724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gif 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3414b424657317e708489d2857d9575f4403698428b040b609b9d1c1a84a2c" "checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a" -"checksum gleam 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ba6539d49223f6bca4f076d9490c001bdbfe07d59cb0ad4079033c75bdc92d" +"checksum gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2260952cc0393ca6f183e1a91a035c65c85ddb02505f3d53e5a775eb05946f44" "checksum glutin 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70c5fe78efbd5a3b243a804ea1032053c584510f8822819f94cfb29b2100317" "checksum half 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d5c5f71a723d10dfc58927cbed37c3071a50afc7f073d86fd7d3e5727db890f" "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 7e7aff31b4..38b5b685f9 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -27,7 +27,7 @@ byteorder = "1.0" cfg-if = "0.1.2" euclid = "0.19" fxhash = "0.2.1" -gleam = "0.6.2" +gleam = "0.6.3" image = { optional = true, version = "0.19" } lazy_static = "1" log = "0.4" diff --git a/webrender/src/debug_render.rs b/webrender/src/debug_render.rs index b3bd6520d9..254ff26e4e 100644 --- a/webrender/src/debug_render.rs +++ b/webrender/src/debug_render.rs @@ -123,7 +123,7 @@ impl DebugRenderer { let line_vao = device.create_vao(&DESC_COLOR); let tri_vao = device.create_vao(&DESC_COLOR); - let font_texture = device.create_texture::( + let font_texture = device.create_texture( TextureTarget::Array, ImageFormat::R8, debug_font_data::BMP_WIDTH, @@ -131,7 +131,10 @@ impl DebugRenderer { TextureFilter::Linear, None, 1, - Some(&debug_font_data::FONT_BITMAP), + ); + device.upload_texture_immediate( + &font_texture, + &debug_font_data::FONT_BITMAP ); Ok(DebugRenderer { diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index ddec23feec..e87ae7b873 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -14,6 +14,7 @@ use internal_types::{FastHashMap, RenderTargetInfo}; use log::Level; use smallvec::SmallVec; use std::cell::RefCell; +use std::cmp; use std::fs::File; use std::io::Read; use std::marker::PhantomData; @@ -45,12 +46,6 @@ impl Add for FrameId { } } -const GL_FORMAT_RGBA: gl::GLuint = gl::RGBA; - -const GL_FORMAT_BGRA_GL: gl::GLuint = gl::BGRA; - -const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT; - const SHADER_VERSION_GL: &str = "#version 150\n"; const SHADER_VERSION_GLES: &str = "#version 300 es\n"; @@ -737,7 +732,8 @@ pub struct Device { #[cfg(feature = "debug_renderer")] capabilities: Capabilities, - bgra_format: gl::GLuint, + bgra_format_internal: gl::GLuint, + bgra_format_external: gl::GLuint, // debug inside_frame: bool, @@ -753,6 +749,12 @@ pub struct Device { // frames and GPU frames. frame_id: FrameId, + /// Whether glTexStorage* is supported. We prefer this over glTexImage* + /// because it guarantees that mipmaps won't be generated (which they + /// otherwise are on some drivers, particularly ANGLE), If it's not + /// supported, we fall back to glTexImage*. + supports_texture_storage: bool, + // GL extensions extensions: Vec, } @@ -781,14 +783,33 @@ impl Device { extensions.push(gl.get_string_i(gl::EXTENSIONS, i)); } + // Our common-case image data in Firefox is BGRA, so we make an effort + // to use BGRA as the internal texture storage format to avoid the need + // to swizzle during upload. Currently we only do this on GLES (and thus + // for Windows, via ANGLE). + // + // On Mac, Apple docs [1] claim that BGRA is a more efficient internal + // format, so we may want to consider doing that at some point, since it + // would give us both a more efficient internal format and avoid the + // swizzling in the common case. + // + // We also need our internal format types to be sized, since glTexStorage* + // will reject non-sized internal format types. + // + // [1] https://developer.apple.com/library/archive/documentation/ + // GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/ + // opengl_texturedata.html#//apple_ref/doc/uid/TP40001987-CH407-SW22 let supports_bgra = supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888"); - let bgra_format = match gl.get_type() { - gl::GlType::Gl => GL_FORMAT_BGRA_GL, - gl::GlType::Gles => if supports_bgra { - GL_FORMAT_BGRA_GLES - } else { - GL_FORMAT_RGBA - } + let (bgra_format_internal, bgra_format_external) = if supports_bgra { + assert_eq!(gl.get_type(), gl::GlType::Gles, "gleam only detects bgra on gles"); + (gl::BGRA8_EXT, gl::BGRA_EXT) + } else { + (gl::RGBA8, gl::BGRA) + }; + + let supports_texture_storage = match gl.get_type() { + gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_texture_storage"), + gl::GlType::Gles => true, }; Device { @@ -805,7 +826,8 @@ impl Device { supports_multisampling: false, //TODO }, - bgra_format, + bgra_format_internal, + bgra_format_external, bound_textures: [0; 16], bound_program: 0, @@ -821,6 +843,7 @@ impl Device { cached_programs, frame_id: FrameId(0), extensions, + supports_texture_storage, } } @@ -1182,7 +1205,7 @@ impl Device { } } - pub fn create_texture( + pub fn create_texture( &mut self, target: TextureTarget, format: ImageFormat, @@ -1191,7 +1214,6 @@ impl Device { filter: TextureFilter, render_target: Option, layer_count: i32, - pixels: Option<&[T]>, ) -> Texture { debug_assert!(self.inside_frame); @@ -1220,36 +1242,69 @@ impl Device { // Allocate storage. let desc = self.gl_describe_format(texture.format); - match texture.target { - gl::TEXTURE_2D_ARRAY => { + let is_array = match texture.target { + gl::TEXTURE_2D_ARRAY => true, + gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => false, + _ => panic!("BUG: Unexpected texture target!"), + }; + assert!(is_array || texture.layer_count == 1); + + // Firefox doesn't use mipmaps, but Servo uses them for standalone image + // textures images larger than 512 pixels. This is the only case where + // we set the filter to trilinear. + let mipmap_levels = if texture.filter == TextureFilter::Trilinear { + let max_dimension = cmp::max(width, height); + ((max_dimension) as f64).log2() as gl::GLint + 1 + } else { + 1 + }; + + // Use glTexStorage where available, since it avoids allocating + // unnecessary mipmap storage and generally improves performance with + // stronger invariants. + match (self.supports_texture_storage, is_array) { + (true, true) => + self.gl.tex_storage_3d( + gl::TEXTURE_2D_ARRAY, + mipmap_levels, + desc.internal, + texture.width as gl::GLint, + texture.height as gl::GLint, + texture.layer_count, + ), + (true, false) => + self.gl.tex_storage_2d( + texture.target, + mipmap_levels, + desc.internal, + texture.width as gl::GLint, + texture.height as gl::GLint, + ), + (false, true) => self.gl.tex_image_3d( gl::TEXTURE_2D_ARRAY, 0, - desc.internal, - texture.width as _, - texture.height as _, + desc.internal as gl::GLint, + texture.width as gl::GLint, + texture.height as gl::GLint, texture.layer_count, 0, desc.external, desc.pixel_type, - pixels.map(texels_to_u8_slice), - ) - } - gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { - assert_eq!(texture.layer_count, 1); + None, + ), + (false, false) => self.gl.tex_image_2d( texture.target, 0, - desc.internal, - texture.width as _, - texture.height as _, + desc.internal as gl::GLint, + texture.width as gl::GLint, + texture.height as gl::GLint, 0, desc.external, desc.pixel_type, - pixels.map(texels_to_u8_slice), - ) - }, - _ => panic!("BUG: Unexpected texture target!"), + None, + ), } // Set up FBOs, if required. @@ -1302,6 +1357,26 @@ impl Device { self.bind_read_target(None); } + /// Notifies the device that the contents of a render target are no longer + /// needed. + pub fn invalidate_render_target(&mut self, texture: &Texture) { + let attachments: &[gl::GLenum] = if texture.has_depth() { + &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] + } else { + &[gl::COLOR_ATTACHMENT0] + }; + + let original_bound_fbo = self.bound_draw_fbo; + for fbo_id in texture.fbo_ids.iter() { + // Note: The invalidate extension may not be supported, in which + // case this is a no-op. That's ok though, because it's just a + // hint. + self.bind_external_draw_target(*fbo_id); + self.gl.invalidate_framebuffer(gl::FRAMEBUFFER, attachments); + } + self.bind_external_draw_target(original_bound_fbo); + } + /// Notifies the device that a render target is about to be reused. /// /// This method adds or removes a depth target as necessary. @@ -1579,7 +1654,7 @@ impl Device { TextureUploader { target: UploadTarget { gl: &*self.gl, - bgra_format: self.bgra_format, + bgra_format: self.bgra_format_external, texture, }, buffer, @@ -1587,6 +1662,45 @@ impl Device { } } + /// Performs an immediate (non-PBO) texture upload. + pub fn upload_texture_immediate( + &mut self, + texture: &Texture, + pixels: &[T] + ) { + self.bind_texture(DEFAULT_TEXTURE, texture); + let desc = self.gl_describe_format(texture.format); + match texture.target { + gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => + self.gl.tex_sub_image_2d( + texture.target, + 0, + 0, + 0, + texture.width as gl::GLint, + texture.height as gl::GLint, + desc.external, + desc.pixel_type, + texels_to_u8_slice(pixels), + ), + gl::TEXTURE_2D_ARRAY => + self.gl.tex_sub_image_3d( + texture.target, + 0, + 0, + 0, + 0, + texture.width as gl::GLint, + texture.height as gl::GLint, + texture.layer_count as gl::GLint, + desc.external, + desc.pixel_type, + texels_to_u8_slice(pixels), + ), + _ => panic!("BUG: Unexpected texture target!"), + } + } + #[cfg(any(feature = "debug_renderer", feature = "capture"))] pub fn read_pixels(&mut self, img_desc: &ImageDescriptor) -> Vec { let desc = self.gl_describe_format(img_desc.format); @@ -1613,7 +1727,7 @@ impl Device { ReadPixelsFormat::Rgba8 => { (4, FormatDesc { external: gl::RGBA, - internal: gl::RGBA8 as _, + internal: gl::RGBA8, pixel_type: gl::UNSIGNED_BYTE, }) } @@ -2170,38 +2284,34 @@ impl Device { fn gl_describe_format(&self, format: ImageFormat) -> FormatDesc { match format { ImageFormat::R8 => FormatDesc { - internal: gl::R8 as _, + internal: gl::R8, external: gl::RED, pixel_type: gl::UNSIGNED_BYTE, }, ImageFormat::R16 => FormatDesc { - internal: gl::R16 as _, + internal: gl::R16, external: gl::RED, pixel_type: gl::UNSIGNED_SHORT, }, ImageFormat::BGRA8 => { - let external = self.bgra_format; FormatDesc { - internal: match self.gl.get_type() { - gl::GlType::Gl => gl::RGBA as _, - gl::GlType::Gles => external as _, - }, - external, + internal: self.bgra_format_internal, + external: self.bgra_format_external, pixel_type: gl::UNSIGNED_BYTE, } }, ImageFormat::RGBAF32 => FormatDesc { - internal: gl::RGBA32F as _, + internal: gl::RGBA32F, external: gl::RGBA, pixel_type: gl::FLOAT, }, ImageFormat::RGBAI32 => FormatDesc { - internal: gl::RGBA32I as _, + internal: gl::RGBA32I, external: gl::RGBA_INTEGER, pixel_type: gl::INT, }, ImageFormat::RG8 => FormatDesc { - internal: gl::RG8 as _, + internal: gl::RG8, external: gl::RG, pixel_type: gl::UNSIGNED_BYTE, }, @@ -2210,7 +2320,7 @@ impl Device { } struct FormatDesc { - internal: gl::GLint, + internal: gl::GLenum, external: gl::GLuint, pixel_type: gl::GLuint, } diff --git a/webrender/src/gpu_glyph_renderer.rs b/webrender/src/gpu_glyph_renderer.rs index bdfa6d719d..20d80093c1 100644 --- a/webrender/src/gpu_glyph_renderer.rs +++ b/webrender/src/gpu_glyph_renderer.rs @@ -62,8 +62,8 @@ impl GpuGlyphRenderer { TextureFilter::Linear, None, 1, - Some(area_lut_pixels) ); + device.upload_texture_immediate(&area_lut_texture, area_lut_pixels); let vector_stencil_vao = device.create_vao_with_new_instances(&renderer::desc::VECTOR_STENCIL, prim_vao); @@ -110,7 +110,7 @@ impl Renderer { let _timer = self.gpu_profile.start_timer(GPU_TAG_GLYPH_STENCIL); - let texture = self.device.create_texture::( + let texture = self.device.create_texture( TextureTarget::Default, ImageFormat::RGBAF32, target_size.width, @@ -120,7 +120,6 @@ impl Renderer { has_depth: false, }), 1, - None ); // Initialize temporary framebuffer. @@ -188,8 +187,8 @@ impl Renderer { TextureFilter::Nearest, None, 1, - Some(&path_info_texels) ); + self.device.upload_texture_immediate(&path_info_texture, &path_info_texels); self.gpu_glyph_renderer.vector_stencil.bind(&mut self.device, projection, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index be495ce42c..c8abff9d41 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -813,7 +813,7 @@ struct TextureResolver { impl TextureResolver { fn new(device: &mut Device) -> TextureResolver { let dummy_cache_texture = device - .create_texture::( + .create_texture( TextureTarget::Array, ImageFormat::BGRA8, 1, @@ -821,7 +821,6 @@ impl TextureResolver { TextureFilter::Linear, None, 1, - None, ); TextureResolver { @@ -855,9 +854,11 @@ impl TextureResolver { fn end_frame(&mut self, device: &mut Device, frame_id: FrameId) { // return the cached targets to the pool - self.end_pass(None, None); + self.end_pass(device, None, None); // return the saved targets as well - self.render_target_pool.extend(self.saved_targets.drain(..)); + while let Some(target) = self.saved_targets.pop() { + self.return_to_pool(device, target); + } // GC the render target pool. // @@ -871,6 +872,12 @@ impl TextureResolver { self.retain_targets(device, |texture| texture.used_recently(frame_id, 30)); } + /// Transfers ownership of a render target back to the pool. + fn return_to_pool(&mut self, device: &mut Device, target: Texture) { + device.invalidate_render_target(&target); + self.render_target_pool.push(target); + } + /// Drops all targets from the render target pool that do not satisfy the predicate. pub fn retain_targets bool>(&mut self, device: &mut Device, f: F) { // We can't just use retain() because `Texture` requires manual cleanup. @@ -887,6 +894,7 @@ impl TextureResolver { fn end_pass( &mut self, + device: &mut Device, a8_texture: Option, rgba8_texture: Option, ) { @@ -899,7 +907,7 @@ impl TextureResolver { assert_eq!(self.saved_targets.len(), index.0); self.saved_targets.push(at.texture); } else { - self.render_target_pool.push(at.texture); + self.return_to_pool(device, at.texture); } } if let Some(at) = self.prev_pass_alpha.take() { @@ -907,7 +915,7 @@ impl TextureResolver { assert_eq!(self.saved_targets.len(), index.0); self.saved_targets.push(at.texture); } else { - self.render_target_pool.push(at.texture); + self.return_to_pool(device, at.texture); } } @@ -1103,7 +1111,7 @@ impl GpuCacheTexture { } // Create the new texture. - let mut texture = device.create_texture::( + let mut texture = device.create_texture( TextureTarget::Default, ImageFormat::RGBAF32, new_size.width, @@ -1111,7 +1119,6 @@ impl GpuCacheTexture { TextureFilter::Nearest, rt_info, 1, - None, ); // Blit the contents of the previous texture, if applicable. @@ -1400,7 +1407,7 @@ impl VertexDataTexture { } let new_height = (needed_height + 127) & !127; - let texture = device.create_texture::( + let texture = device.create_texture( TextureTarget::Default, self.format, width, @@ -1408,7 +1415,6 @@ impl VertexDataTexture { TextureFilter::Nearest, None, 1, - None, ); self.texture = Some(texture); } @@ -1756,7 +1762,7 @@ impl Renderer { 21, ]; - let mut texture = device.create_texture::( + let mut texture = device.create_texture( TextureTarget::Default, ImageFormat::R8, 8, @@ -1764,8 +1770,8 @@ impl Renderer { TextureFilter::Nearest, None, 1, - Some(&dither_matrix), ); + device.upload_texture_immediate(&texture, &dither_matrix); Some(texture) } else { @@ -2774,7 +2780,7 @@ impl Renderer { // // Ensure no PBO is bound when creating the texture storage, // or GL will attempt to read data from there. - let texture = self.device.create_texture::( + let texture = self.device.create_texture( TextureTarget::Array, format, width, @@ -2782,7 +2788,6 @@ impl Renderer { filter, render_target, layer_count, - None, ); self.texture_resolver.texture_cache_map.insert(update.id, texture); } @@ -3815,7 +3820,7 @@ impl Renderer { t } else { counters.targets_created.inc(); - let mut t = self.device.create_texture::( + self.device.create_texture( TextureTarget::Array, list.format, list.max_size.width, @@ -3823,9 +3828,7 @@ impl Renderer { TextureFilter::Linear, Some(rt_info), list.targets.len() as _, - None, - ); - t + ) }; list.check_ready(&texture); @@ -4016,6 +4019,7 @@ impl Renderer { }; self.texture_resolver.end_pass( + &mut self.device, cur_alpha, cur_color, ); @@ -4763,8 +4767,8 @@ impl Renderer { plain.filter, plain.render_target, plain.size.2, - Some(texels.as_slice()), ); + device.upload_texture_immediate(&texture, &texels); (texture, texels) }