From 7a0bc5af7f4f25c84df9e65b2a86b83f33074933 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Mon, 10 Feb 2020 17:07:48 +0800 Subject: [PATCH] background: Limit mipmap levels to avoid loss of visible detail When the wallpaper image is larger than the monitor resolution we already use mipmapping to scale it down smoothly in hardware. We use `GL_TEXTURE_MIN_FILTER` = `GL_LINEAR_MIPMAP_LINEAR` for the highest quality scaling that GL can do. However that option is designed for 3D use cases where the mipmap level is changing over time or space. Since our wallpaper is not changing distance from us we can improve the rendering quality even more than `GL_LINEAR_MIPMAP_LINEAR`. To do this we now set `GL_TEXTURE_MAX_LEVEL` (if available) to limit the mipmap level or blurriness level to the lowest resolution (highest level) that is still equal to or higher than the monitor itself. This way we get the benefits of mipmapping (downscaling in hardware) *and* retain the maximum possible sharpness for the monitor resolution -- something that `GL_LINEAR_MIPMAP_LINEAR` alone doesn't do. Example: Monitor is 1920x1080 Wallpaper photo is 4000x3000 Mipmaps stored on the GPU are 4000x3000, 2000x1500, 1000x750, ... Before: You would see an average of the 2000x1500 and 1000x750 images. After: You will now only see the 2000x1500 image, linearly sampled. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1003 --- src/compositor/meta-background.c | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c index 30be33261..4927ae638 100644 --- a/src/compositor/meta-background.c +++ b/src/compositor/meta-background.c @@ -747,6 +747,25 @@ get_wrap_mode (GDesktopBackgroundStyle style) } } +static int +get_best_mipmap_level (CoglTexture *texture, + int visible_width, + int visible_height) +{ + int mipmap_width = cogl_texture_get_width (texture); + int mipmap_height = cogl_texture_get_height (texture); + int halves = 0; + + while (mipmap_width >= visible_width && mipmap_height >= visible_height) + { + halves++; + mipmap_width /= 2; + mipmap_height /= 2; + } + + return MAX (0, halves - 1); +} + CoglTexture * meta_background_get_texture (MetaBackground *self, int monitor_index, @@ -854,10 +873,17 @@ meta_background_get_texture (MetaBackground *self, if (texture2 != NULL && self->blend_factor != 0.0) { CoglPipeline *pipeline = create_pipeline (PIPELINE_REPLACE); + int mipmap_level; + + mipmap_level = get_best_mipmap_level (texture2, + texture_width, + texture_height); + cogl_pipeline_set_color4f (pipeline, self->blend_factor, self->blend_factor, self->blend_factor, self->blend_factor); cogl_pipeline_set_layer_texture (pipeline, 0, texture2); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (self->style)); + cogl_pipeline_set_layer_max_mipmap_level (pipeline, 0, mipmap_level); bare_region_visible = draw_texture (self, monitor->fbo, pipeline, @@ -876,6 +902,12 @@ meta_background_get_texture (MetaBackground *self, if (texture1 != NULL && self->blend_factor != 1.0) { CoglPipeline *pipeline = create_pipeline (PIPELINE_ADD); + int mipmap_level; + + mipmap_level = get_best_mipmap_level (texture1, + texture_width, + texture_height); + cogl_pipeline_set_color4f (pipeline, (1 - self->blend_factor), (1 - self->blend_factor), @@ -883,6 +915,7 @@ meta_background_get_texture (MetaBackground *self, (1 - self->blend_factor));; cogl_pipeline_set_layer_texture (pipeline, 0, texture1); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (self->style)); + cogl_pipeline_set_layer_max_mipmap_level (pipeline, 0, mipmap_level); bare_region_visible = bare_region_visible || draw_texture (self, monitor->fbo, pipeline,