Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use mipmaps and trilinear filtering for large, standalone textures. #2408

Merged
merged 1 commit into from Feb 12, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -70,6 +70,7 @@ pub enum DepthFunction {
pub enum TextureFilter {
Nearest,
Linear,
Trilinear,
}

#[derive(Debug)]
@@ -924,7 +925,9 @@ impl Device {
}

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],
@@ -941,15 +944,21 @@ impl Device {
}

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);
@@ -2229,6 +2238,11 @@ impl<'a> UploadTarget<'a> {
_ => 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 _);
@@ -857,11 +857,6 @@ impl ResourceCache {
}
};

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;
@@ -897,6 +892,29 @@ impl ResourceCache {
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,
@@ -384,7 +384,9 @@ impl TextureCache {
(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]
@@ -605,6 +607,8 @@ impl TextureCache {
(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!(),
};

@@ -645,6 +649,34 @@ impl TextureCache {
)
}

// 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.
@@ -658,27 +690,15 @@ impl TextureCache {
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(
Binary file not shown.
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.