Bug 1658858 - Implement ImageRendering filtering parameter for SwCompositor. r=lsalzman
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 14 Aug 2020 00:56:22 +0000
changeset 544714 565097a8dd7db08d82151d40b7c3fcfdb6288c48
parent 544713 9a94d0040d2f9b0bf93fa02a624d5805e563e920
child 544715 33f1b6bf6b17cf0aaf020a5f3c3dede0a43a01f1
push id37700
push userdluca@mozilla.com
push dateSat, 15 Aug 2020 09:31:17 +0000
treeherdermozilla-central@7dcb2bda35c7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1658858
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1658858 - Implement ImageRendering filtering parameter for SwCompositor. r=lsalzman Differential Revision: https://phabricator.services.mozilla.com/D86890
gfx/webrender_bindings/src/swgl_bindings.rs
gfx/wr/swgl/src/gl.cc
gfx/wr/swgl/src/swgl_fns.rs
--- a/gfx/webrender_bindings/src/swgl_bindings.rs
+++ b/gfx/webrender_bindings/src/swgl_bindings.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use bindings::{GeckoProfilerThreadListener, WrCompositor};
-use gleam::{gl, gl::Gl};
+use gleam::{gl, gl::Gl, gl::GLenum};
 use std::cell::Cell;
 use std::collections::hash_map::HashMap;
 use std::os::raw::c_void;
 use std::ptr;
 use std::rc::Rc;
 use std::sync::{mpsc, Arc, Condvar, Mutex};
 use std::thread;
 use webrender::{
@@ -290,16 +290,17 @@ impl DrawTileHelper {
     fn draw(
         &self,
         viewport: &DeviceIntRect,
         dest: &DeviceIntRect,
         src: &DeviceIntRect,
         surface: &SwSurface,
         tile: &SwTile,
         flip_y: bool,
+        filter: GLenum,
     ) {
         let dx = dest.origin.x as f32 / viewport.size.width as f32;
         let dy = dest.origin.y as f32 / viewport.size.height as f32;
         let dw = dest.size.width as f32 / viewport.size.width as f32;
         let dh = dest.size.height as f32 / viewport.size.height as f32;
         self.gl.uniform_matrix_3fv(
             self.dest_matrix_loc,
             false,
@@ -318,16 +319,18 @@ impl DrawTileHelper {
         let sx = src.origin.x as f32 / surface.tile_size.width as f32;
         let sy = src.origin.y as f32 / surface.tile_size.height as f32;
         let sw = src.size.width as f32 / surface.tile_size.width as f32;
         let sh = src.size.height as f32 / surface.tile_size.height as f32;
         self.gl
             .uniform_matrix_3fv(self.tex_matrix_loc, false, &[sw, 0.0, 0.0, 0.0, sh, 0.0, sx, sy, 1.0]);
 
         self.gl.bind_texture(gl::TEXTURE_2D, tile.tex_id);
+        self.gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, filter as gl::GLint);
+        self.gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, filter as gl::GLint);
         self.gl.draw_arrays(gl::TRIANGLE_STRIP, 0, 4);
     }
 
     fn disable(&self) {
         self.gl.use_program(0);
         self.gl.bind_vertex_array(0);
     }
 }
@@ -338,16 +341,17 @@ struct SwCompositeJob {
     /// Locked texture that will be unlocked immediately following the job
     locked_src: swgl::LockedResource,
     /// Locked framebuffer that may be shared among many jobs
     locked_dst: swgl::LockedResource,
     src_rect: DeviceIntRect,
     dst_rect: DeviceIntRect,
     opaque: bool,
     flip_y: bool,
+    filter: GLenum,
 }
 
 /// The SwComposite thread processes a queue of composite jobs, also signaling
 /// via a condition when all available jobs have been processed, as tracked by
 /// the job count.
 struct SwCompositeThread {
     /// Queue of available composite jobs
     job_queue: mpsc::Sender<SwCompositeJob>,
@@ -389,16 +393,17 @@ impl SwCompositeThread {
                         job.src_rect.size.width,
                         job.src_rect.size.height,
                         job.dst_rect.origin.x,
                         job.dst_rect.origin.y,
                         job.dst_rect.size.width,
                         job.dst_rect.size.height,
                         job.opaque,
                         job.flip_y,
+                        job.filter,
                     );
                     // Release locked resources before modifying job count
                     drop(job);
                     // Decrement the job count. If applicable, signal that all jobs
                     // have been completed.
                     let mut count = info.job_count.lock().unwrap();
                     *count -= 1;
                     if *count <= 0 {
@@ -415,27 +420,29 @@ impl SwCompositeThread {
     fn queue_composite(
         &self,
         locked_src: swgl::LockedResource,
         locked_dst: swgl::LockedResource,
         src_rect: DeviceIntRect,
         dst_rect: DeviceIntRect,
         opaque: bool,
         flip_y: bool,
+        filter: GLenum,
     ) {
         // There are still tile updates happening, so send the job to the SwComposite thread.
         *self.job_count.lock().unwrap() += 1;
         self.job_queue
             .send(SwCompositeJob {
                 locked_src,
                 locked_dst,
                 src_rect,
                 dst_rect,
                 opaque,
                 flip_y,
+                filter,
             })
             .expect("Failing queuing SwComposite job");
     }
 
     /// Wait for all queued composition jobs to be processed by checking the done condition.
     fn wait_for_composites(&self) {
         let mut jobs = self.job_count.lock().unwrap();
         while *jobs > 0 {
@@ -444,17 +451,17 @@ impl SwCompositeThread {
     }
 }
 
 pub struct SwCompositor {
     gl: swgl::Context,
     native_gl: Option<Rc<dyn gl::Gl>>,
     compositor: Option<WrCompositor>,
     surfaces: HashMap<NativeSurfaceId, SwSurface>,
-    frame_surfaces: Vec<(NativeSurfaceId, CompositorSurfaceTransform, DeviceIntRect)>,
+    frame_surfaces: Vec<(NativeSurfaceId, CompositorSurfaceTransform, DeviceIntRect, GLenum)>,
     cur_tile: NativeTileId,
     draw_tile: Option<DrawTileHelper>,
     /// The maximum tile size required for any of the allocated surfaces.
     max_tile_size: DeviceIntSize,
     /// Reuse the same depth texture amongst all tiles in all surfaces.
     /// This depth texture must be big enough to accommodate the largest used
     /// tile size for any surface. The maximum requested tile size is tracked
     /// to ensure that this depth texture is at least that big.
@@ -537,17 +544,17 @@ impl SwCompositor {
     /// the order the surfaces were queued, so it is safe to ignore other possible sources
     /// of composition ordering dependencies, as the later queued tile will still be drawn
     /// later than the blocking tiles within that stable order. We assume that the tile's
     /// surface hasn't yet been added to the current frame list of surfaces to composite
     /// so that we only process potential blockers from surfaces that would come earlier
     /// in composition.
     fn get_overlaps(&self, overlap_rect: &DeviceIntRect) -> u32 {
         let mut overlaps = 0;
-        for &(ref id, ref transform, ref clip_rect) in &self.frame_surfaces {
+        for &(ref id, ref transform, ref clip_rect, _) in &self.frame_surfaces {
             // If the surface's clip rect doesn't overlap the tile's rect,
             // then there is no need to check any tiles within the surface.
             if !overlap_rect.intersects(clip_rect) {
                 continue;
             }
             if let Some(surface) = self.surfaces.get(id) {
                 for tile in &surface.tiles {
                     // If there is a deferred tile that might overlap the destination rectangle,
@@ -563,49 +570,56 @@ impl SwCompositor {
     }
 
     /// Helper function that queues a composite job to the current locked framebuffer
     fn queue_composite(
         &self,
         surface: &SwSurface,
         transform: &CompositorSurfaceTransform,
         clip_rect: &DeviceIntRect,
+        filter: GLenum,
         tile: &SwTile,
     ) {
         if let Some(ref composite_thread) = self.composite_thread {
             if let Some((src_rect, dst_rect, flip_y)) = tile.composite_rects(surface, transform, clip_rect) {
                 if let Some(texture) = self.gl.lock_texture(tile.color_id) {
                     let framebuffer = self.locked_framebuffer.clone().unwrap();
-                    composite_thread.queue_composite(texture, framebuffer, src_rect, dst_rect, surface.is_opaque, flip_y);
+                    composite_thread.queue_composite(texture, framebuffer, src_rect, dst_rect, surface.is_opaque, flip_y, filter);
                 }
             }
         }
     }
 
     /// If using the SwComposite thread, we need to compute an overlap count for all tiles
     /// within the surface being queued for composition this frame. If the tile is immediately
     /// ready to composite, then queue that now. Otherwise, set its draw order index for later
     /// composition.
-    fn init_composites(&mut self, id: &NativeSurfaceId, transform: &CompositorSurfaceTransform, clip_rect: &DeviceIntRect) {
+    fn init_composites(
+        &mut self,
+        id: &NativeSurfaceId,
+        transform: &CompositorSurfaceTransform,
+        clip_rect: &DeviceIntRect,
+        filter: GLenum,
+    ) {
         if self.composite_thread.is_none() {
             return;
         }
 
         if let Some(surface) = self.surfaces.get(&id) {
             for tile in &surface.tiles {
                 if let Some(overlap_rect) = tile.overlap_rect(surface, transform, clip_rect) {
                     let mut overlaps = self.get_overlaps(&overlap_rect);
                     // Record an extra overlap for an invalid tile to track the tile's dependency
                     // on its own future update.
                     if tile.invalid.get() {
                         overlaps += 1;
                     }
                     if overlaps == 0 {
                         // Not dependent on any tiles, so go ahead and composite now.
-                        self.queue_composite(surface, transform, clip_rect, tile);
+                        self.queue_composite(surface, transform, clip_rect, filter, tile);
                     } else {
                         // Has a dependency on some invalid tiles, so need to defer composition.
                         tile.overlaps.set(overlaps);
                     }
                 }
             }
         }
     }
@@ -616,45 +630,45 @@ impl SwCompositor {
         if self.composite_thread.is_none() {
             return;
         }
 
         // Look for the tile in the frame list and composite it if it has no dependencies.
         let mut frame_surfaces = self
             .frame_surfaces
             .iter()
-            .skip_while(|&(ref id, _, _)| *id != tile_id.surface_id);
+            .skip_while(|&(ref id, _, _, _)| *id != tile_id.surface_id);
         let overlap_rect = match frame_surfaces.next() {
-            Some(&(_, ref transform, ref clip_rect)) => {
+            Some(&(_, ref transform, ref clip_rect, filter)) => {
                 // Remove invalid tile's update dependency.
                 if tile.invalid.get() {
                     tile.overlaps.set(tile.overlaps.get() - 1);
                 }
                 // If the tile still has overlaps, keep deferring it till later.
                 if tile.overlaps.get() > 0 {
                     return;
                 }
                 // Otherwise, the tile's dependencies are all resolved, so composite it.
-                self.queue_composite(surface, transform, clip_rect, tile);
+                self.queue_composite(surface, transform, clip_rect, filter, tile);
                 // Finally, get the tile's overlap rect used for tracking dependencies
                 match tile.overlap_rect(surface, transform, clip_rect) {
                     Some(overlap_rect) => overlap_rect,
                     None => return,
                 }
             }
             None => return,
         };
 
         // Accumulate rects whose dependencies have been satisfied from this update.
         // Store the union of all these bounds to quickly reject unaffected tiles.
         let mut flushed_bounds = overlap_rect;
         let mut flushed_rects = vec![overlap_rect];
 
         // Check surfaces following the update in the frame list and see if they would overlap it.
-        for &(ref id, ref transform, ref clip_rect) in frame_surfaces {
+        for &(ref id, ref transform, ref clip_rect, filter) in frame_surfaces {
             // If the clip rect doesn't overlap the conservative bounds, we can skip the whole surface.
             if !flushed_bounds.intersects(clip_rect) {
                 continue;
             }
             if let Some(surface) = self.surfaces.get(&id) {
                 // Search through the surface's tiles for any blocked on this update and queue jobs for them.
                 for tile in &surface.tiles {
                     let mut overlaps = tile.overlaps.get();
@@ -677,17 +691,17 @@ impl SwCompositor {
                             overlaps -= 1;
                         }
                     }
                     if overlaps != tile.overlaps.get() {
                         // If the overlap count changed, this tile had a dependency on some flush rects.
                         // If the count hit zero, it is ready to composite.
                         tile.overlaps.set(overlaps);
                         if overlaps == 0 {
-                            self.queue_composite(surface, transform, clip_rect, tile);
+                            self.queue_composite(surface, transform, clip_rect, filter, tile);
                             // Record that the tile got flushed to update any downwind dependencies.
                             flushed_bounds = flushed_bounds.union(&overlap_rect);
                             flushed_rects.push(overlap_rect);
                         }
                     }
                 }
             }
         }
@@ -988,17 +1002,17 @@ impl Compositor for SwCompositor {
 
                 if let Some(compositor) = &mut self.compositor {
                     let info = compositor.bind(id, tile.dirty_rect, tile.valid_rect);
                     native_gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, info.fbo_id);
 
                     let viewport = dirty.translate(info.origin.to_vector());
                     let draw_tile = self.draw_tile.as_ref().unwrap();
                     draw_tile.enable(&viewport);
-                    draw_tile.draw(&viewport, &viewport, &dirty, &surface, &tile, false);
+                    draw_tile.draw(&viewport, &viewport, &dirty, &surface, &tile, false, gl::LINEAR);
                     draw_tile.disable();
 
                     native_gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, 0);
 
                     compositor.unbind();
                 }
 
                 native_gl.bind_texture(gl::TEXTURE_2D, 0);
@@ -1024,46 +1038,55 @@ impl Compositor for SwCompositor {
         transform: CompositorSurfaceTransform,
         clip_rect: DeviceIntRect,
         image_rendering: ImageRendering
     ) {
         if let Some(compositor) = &mut self.compositor {
             compositor.add_surface(id, transform, clip_rect, image_rendering);
         }
 
+        let filter = match image_rendering {
+            ImageRendering::Pixelated => {
+                gl::NEAREST
+            }
+            ImageRendering::Auto | ImageRendering::CrispEdges => {
+                gl::LINEAR
+            }
+        };
+
         // Compute overlap dependencies and issue any initial composite jobs for the SwComposite thread.
-        self.init_composites(&id, &transform, &clip_rect);
+        self.init_composites(&id, &transform, &clip_rect, filter);
 
-        self.frame_surfaces.push((id, transform, clip_rect));
+        self.frame_surfaces.push((id, transform, clip_rect, filter));
     }
 
     fn end_frame(&mut self) {
         if let Some(compositor) = &mut self.compositor {
             compositor.end_frame();
         } else if let Some(native_gl) = &self.native_gl {
             let (_, fw, fh, _) = self.gl.get_color_buffer(0, false);
             let viewport = DeviceIntRect::from_size(DeviceIntSize::new(fw, fh));
             let draw_tile = self.draw_tile.as_ref().unwrap();
             draw_tile.enable(&viewport);
             let mut blend = false;
             native_gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
-            for &(ref id, ref transform, ref clip_rect) in &self.frame_surfaces {
+            for &(ref id, ref transform, ref clip_rect, filter) in &self.frame_surfaces {
                 if let Some(surface) = self.surfaces.get(id) {
                     if surface.is_opaque {
                         if blend {
                             native_gl.disable(gl::BLEND);
                             blend = false;
                         }
                     } else if !blend {
                         native_gl.enable(gl::BLEND);
                         blend = true;
                     }
                     for tile in &surface.tiles {
                         if let Some((src_rect, dst_rect, flip_y)) = tile.composite_rects(surface, transform, clip_rect) {
-                            draw_tile.draw(&viewport, &dst_rect, &src_rect, surface, tile, flip_y);
+                            draw_tile.draw(&viewport, &dst_rect, &src_rect, surface, tile, flip_y, filter);
                         }
                     }
                 }
             }
             if blend {
                 native_gl.disable(gl::BLEND);
             }
             draw_tile.disable();
--- a/gfx/wr/swgl/src/gl.cc
+++ b/gfx/wr/swgl/src/gl.cc
@@ -4185,17 +4185,17 @@ void UnlockResource(LockedTexture* resou
 
 // Extension for optimized compositing of textures or framebuffers that may be
 // safely used across threads. The source and destination must be locked to
 // ensure that they can be safely accessed while the SWGL context might be used
 // by another thread.
 void Composite(LockedTexture* lockedDst, LockedTexture* lockedSrc, GLint srcX,
                GLint srcY, GLsizei srcWidth, GLsizei srcHeight, GLint dstX,
                GLint dstY, GLsizei dstWidth, GLsizei dstHeight,
-               GLboolean opaque, GLboolean flip) {
+               GLboolean opaque, GLboolean flip, GLenum filter) {
   if (!lockedDst || !lockedSrc) {
     return;
   }
   Texture& srctex = *lockedSrc;
   Texture& dsttex = *lockedDst;
   assert(srctex.bpp() == 4);
   const int bpp = 4;
   size_t src_stride = srctex.stride();
@@ -4219,17 +4219,16 @@ void Composite(LockedTexture* lockedDst,
   char* dest = dsttex.sample_ptr(dstX, flip ? dsttex.height - 1 - dstY : dstY);
   char* src = srctex.sample_ptr(srcX, srcY);
   if (flip) {
     dest_stride = -dest_stride;
   }
 
   IntRect srcReq = {srcX, srcY, srcX + srcWidth, srcY + srcHeight};
   IntRect dstReq = {dstX, dstY, dstX + dstWidth, dstY + dstHeight};
-  GLenum filter = GL_LINEAR;  // TODO
 
   if (opaque) {
     if (!srcReq.same_size(dstReq) && filter == GL_LINEAR) {
       linear_blit(srctex, srcReq, 0, dsttex, dstReq, 0, flip);
     } else {
       scale_blit(srctex, srcReq, 0, dsttex, dstReq, 0, flip);
     }
   } else {
--- a/gfx/wr/swgl/src/swgl_fns.rs
+++ b/gfx/wr/swgl/src/swgl_fns.rs
@@ -294,16 +294,17 @@ extern "C" {
         src_width: GLsizei,
         src_height: GLsizei,
         dst_x: GLint,
         dst_y: GLint,
         dst_width: GLsizei,
         dst_height: GLsizei,
         opaque: GLboolean,
         flip: GLboolean,
+        filter: GLenum,
     );
     fn CreateContext() -> *mut c_void;
     fn ReferenceContext(ctx: *mut c_void);
     fn DestroyContext(ctx: *mut c_void);
     fn MakeCurrent(ctx: *mut c_void);
 }
 
 #[derive(Clone)]
@@ -2296,31 +2297,33 @@ impl LockedResource {
         src_width: GLsizei,
         src_height: GLsizei,
         dst_x: GLint,
         dst_y: GLint,
         dst_width: GLsizei,
         dst_height: GLsizei,
         opaque: bool,
         flip: bool,
+        filter: GLenum,
     ) {
         unsafe {
             Composite(
                 self.0,
                 locked_src.0,
                 src_x,
                 src_y,
                 src_width,
                 src_height,
                 dst_x,
                 dst_y,
                 dst_width,
                 dst_height,
                 opaque as GLboolean,
                 flip as GLboolean,
+                filter,
             );
         }
     }
 }
 
 impl Clone for LockedResource {
     fn clone(&self) -> Self {
         unsafe { LockResource(self.0); }