Test filtering changes. try: -b do -p linux64,win64 -u all[linux64-qr,windows10-64-qr] -t all[linux64-qr,windows10-64-qr]
authorGlenn Watson <github@intuitionlibrary.com>
Mon, 12 Feb 2018 10:08:46 +1000
changeset 1424756 7467e8a890bd16eeaac598c9c241cc77036512a7
parent 1424693 b600272d00bb410771ae45e967b1053f77d6bdb4
child 1606764 3a33d8ef02a3e527dea3b379aba147c003c7afd0
push id251788
push usergwatson@mozilla.com
push dateMon, 12 Feb 2018 00:09:26 +0000
treeherdertry@7467e8a890bd [default view] [failures only]
milestone60.0a1
Test filtering changes. try: -b do -p linux64,win64 -u all[linux64-qr,windows10-64-qr] -t all[linux64-qr,windows10-64-qr]
gfx/webrender/src/device.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/texture_cache.rs
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -65,16 +65,17 @@ pub enum DepthFunction {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum TextureFilter {
     Nearest,
     Linear,
+    Trilinear,
 }
 
 #[derive(Debug)]
 pub enum VertexAttributeKind {
     F32,
     U8Norm,
     U16Norm,
     I32,
@@ -919,42 +920,50 @@ impl Device {
 
         if self.bound_program != program.id {
             self.gl.use_program(program.id);
             self.bound_program = program.id;
         }
     }
 
     pub fn create_texture(
-        &mut self, target: TextureTarget, format: ImageFormat,
+        &mut self,
+        target: TextureTarget,
+        format: ImageFormat,
     ) -> Texture {
         Texture {
             id: self.gl.gen_textures(1)[0],
             target: get_gl_target(target),
             width: 0,
             height: 0,
             layer_count: 0,
             format,
             filter: TextureFilter::Nearest,
             render_target: None,
             fbo_ids: vec![],
             depth_rb: None,
         }
     }
 
     fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
-        let filter = match filter {
+        let mag_filter = match filter {
+            TextureFilter::Nearest => gl::NEAREST,
+            TextureFilter::Linear | TextureFilter::Trilinear => gl::LINEAR,
+        };
+
+        let min_filter = match filter {
             TextureFilter::Nearest => gl::NEAREST,
             TextureFilter::Linear => gl::LINEAR,
+            TextureFilter::Trilinear => gl::LINEAR_MIPMAP_LINEAR,
         };
 
         self.gl
-            .tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, filter as gl::GLint);
+            .tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, mag_filter as gl::GLint);
         self.gl
-            .tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, filter as gl::GLint);
+            .tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, min_filter as gl::GLint);
 
         self.gl
             .tex_parameter_i(target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
         self.gl
             .tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
     }
 
     /// Resizes a texture with enabled render target views,
@@ -2211,14 +2220,19 @@ impl<'a> UploadTarget<'a> {
                     gl_format,
                     data_type,
                     chunk.offset,
                 );
             }
             _ => panic!("BUG: Unexpected texture target!"),
         }
 
+        // If using tri-linear filtering, build the mip-map chain for this texture.
+        if self.texture.filter == TextureFilter::Trilinear {
+            self.gl.generate_mipmap(self.texture.target);
+        }
+
         // Reset row length to 0, otherwise the stride would apply to all texture uploads.
         if chunk.stride.is_some() {
             self.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as _);
         }
     }
 }
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -866,21 +866,16 @@ impl ResourceCache {
                         }
                         Err(BlobImageError::Other(msg)) => {
                             panic!("Vector image error {}", msg);
                         }
                     }
                 }
             };
 
-            let filter = match request.rendering {
-                ImageRendering::Pixelated => TextureFilter::Nearest,
-                ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
-            };
-
             let descriptor = if let Some(tile) = request.tile {
                 let tile_size = image_template.tiling.unwrap();
                 let image_descriptor = &image_template.descriptor;
 
                 let (actual_width, actual_height) =
                     compute_tile_size(image_descriptor, tile_size, tile);
 
                 // The tiled image could be stored on the CPU as one large image or be
@@ -906,16 +901,39 @@ impl ResourceCache {
                     offset,
                     format: image_descriptor.format,
                     is_opaque: image_descriptor.is_opaque,
                 }
             } else {
                 image_template.descriptor.clone()
             };
 
+            let filter = match request.rendering {
+                ImageRendering::Pixelated => {
+                    TextureFilter::Nearest
+                }
+                ImageRendering::Auto | ImageRendering::CrispEdges => {
+                    // If the texture uses linear filtering, enable mipmaps and
+                    // trilinear filtering, for better image quality. We only
+                    // support this for now on textures that are not placed
+                    // into the shared cache. This accounts for any image
+                    // that is > 512 in either dimension, so it should cover
+                    // the most important use cases. We may want to support
+                    // mip-maps on shared cache items in the future.
+                    if !self.texture_cache.is_allowed_in_shared_cache(
+                        TextureFilter::Linear,
+                        &descriptor,
+                    ) {
+                        TextureFilter::Trilinear
+                    } else {
+                        TextureFilter::Linear
+                    }
+                }
+            };
+
             let entry = self.cached_images.get_mut(&request).as_mut().unwrap();
             self.texture_cache.update(
                 &mut entry.texture_cache_handle,
                 descriptor,
                 filter,
                 Some(image_data),
                 [0.0; 3],
                 image_template.dirty_rect,
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -379,17 +379,19 @@ impl TextureCache {
         region_index: u16
     ) -> &mut TextureRegion {
         let texture_array = match (format, filter) {
             (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
             (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
             (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
             (ImageFormat::RGBAF32, _) |
             (ImageFormat::RG8, _) |
-            (ImageFormat::R8, TextureFilter::Nearest) => unreachable!(),
+            (ImageFormat::R8, TextureFilter::Nearest) |
+            (ImageFormat::R8, TextureFilter::Trilinear) |
+            (ImageFormat::BGRA8, TextureFilter::Trilinear) => unreachable!(),
         };
 
         &mut texture_array.regions[region_index as usize]
     }
 
     // Check if a given texture handle has a valid allocation
     // in the texture cache.
     pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool {
@@ -600,16 +602,18 @@ impl TextureCache {
     ) -> Option<CacheEntry> {
         // Work out which cache it goes in, based on format.
         let texture_array = match (descriptor.format, filter) {
             (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
             (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
             (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
             (ImageFormat::RGBAF32, _) |
             (ImageFormat::R8, TextureFilter::Nearest) |
+            (ImageFormat::R8, TextureFilter::Trilinear) |
+            (ImageFormat::BGRA8, TextureFilter::Trilinear) |
             (ImageFormat::RG8, _) => unreachable!(),
         };
 
         // Lazy initialize this texture array if required.
         if texture_array.texture_id.is_none() {
             let texture_id = self.cache_textures.allocate(descriptor.format);
 
             let update_op = TextureUpdate {
@@ -640,50 +644,66 @@ impl TextureCache {
         texture_array.alloc(
             descriptor.width,
             descriptor.height,
             user_data,
             self.frame_id,
         )
     }
 
+    // Returns true if the given image descriptor *may* be
+    // placed in the shared texture cache.
+    pub fn is_allowed_in_shared_cache(
+        &self,
+        filter: TextureFilter,
+        descriptor: &ImageDescriptor,
+    ) -> bool {
+        let mut allowed_in_shared_cache = true;
+
+        // TODO(gw): For now, anything that requests nearest filtering and isn't BGRA8
+        //           just fails to allocate in a texture page, and gets a standalone
+        //           texture. This is probably rare enough that it can be fixed up later.
+        if filter == TextureFilter::Nearest &&
+           descriptor.format != ImageFormat::BGRA8 {
+            allowed_in_shared_cache = false;
+        }
+
+        // Anything larger than 512 goes in a standalone texture.
+        // TODO(gw): If we find pages that suffer from batch breaks in this
+        //           case, add support for storing these in a standalone
+        //           texture array.
+        if descriptor.width > 512 || descriptor.height > 512 {
+            allowed_in_shared_cache = false;
+        }
+
+        allowed_in_shared_cache
+    }
+
     // Allocate storage for a given image. This attempts to allocate
     // from the shared cache, but falls back to standalone texture
     // if the image is too large, or the cache is full.
     fn allocate(
         &mut self,
         handle: &mut TextureCacheHandle,
         descriptor: ImageDescriptor,
         filter: TextureFilter,
         user_data: [f32; 3],
     ) {
         assert!(descriptor.width > 0 && descriptor.height > 0);
 
         // Work out if this image qualifies to go in the shared (batching) cache.
-        let mut allowed_in_shared_cache = true;
+        let allowed_in_shared_cache = self.is_allowed_in_shared_cache(
+            filter,
+            &descriptor,
+        );
         let mut allocated_in_shared_cache = true;
         let mut new_cache_entry = None;
         let size = DeviceUintSize::new(descriptor.width, descriptor.height);
         let frame_id = self.frame_id;
 
-        // TODO(gw): For now, anything that requests nearest filtering and isn't BGRA8
-        //           just fails to allocate in a texture page, and gets a standalone
-        //           texture. This is probably rare enough that it can be fixed up later.
-        if filter == TextureFilter::Nearest && descriptor.format != ImageFormat::BGRA8 {
-            allowed_in_shared_cache = false;
-        }
-
-        // Anything larger than 512 goes in a standalone texture.
-        // TODO(gw): If we find pages that suffer from batch breaks in this
-        //           case, add support for storing these in a standalone
-        //           texture array.
-        if descriptor.width > 512 || descriptor.height > 512 {
-            allowed_in_shared_cache = false;
-        }
-
         // If it's allowed in the cache, see if there is a spot for it.
         if allowed_in_shared_cache {
             new_cache_entry = self.allocate_from_shared_cache(
                 &descriptor,
                 filter,
                 user_data
             );