Test filtering changes. try: -b do -p linux64,win64 -u all[linux64-qr,windows10-64-qr] -t all[linux64-qr,windows10-64-qr]
Test filtering changes. try: -b do -p linux64,win64 -u all[linux64-qr,windows10-64-qr] -t all[linux64-qr,windows10-64-qr]
--- 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
);