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
This commit is contained in:
Daniel van Vugt 2020-02-10 17:07:48 +08:00 committed by Jonas Ådahl
parent c5fbab6bad
commit 7a0bc5af7f

View File

@ -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 * CoglTexture *
meta_background_get_texture (MetaBackground *self, meta_background_get_texture (MetaBackground *self,
int monitor_index, int monitor_index,
@ -854,10 +873,17 @@ meta_background_get_texture (MetaBackground *self,
if (texture2 != NULL && self->blend_factor != 0.0) if (texture2 != NULL && self->blend_factor != 0.0)
{ {
CoglPipeline *pipeline = create_pipeline (PIPELINE_REPLACE); 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, cogl_pipeline_set_color4f (pipeline,
self->blend_factor, self->blend_factor, self->blend_factor, self->blend_factor); self->blend_factor, self->blend_factor, self->blend_factor, self->blend_factor);
cogl_pipeline_set_layer_texture (pipeline, 0, texture2); 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_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, bare_region_visible = draw_texture (self,
monitor->fbo, pipeline, monitor->fbo, pipeline,
@ -876,6 +902,12 @@ meta_background_get_texture (MetaBackground *self,
if (texture1 != NULL && self->blend_factor != 1.0) if (texture1 != NULL && self->blend_factor != 1.0)
{ {
CoglPipeline *pipeline = create_pipeline (PIPELINE_ADD); 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, cogl_pipeline_set_color4f (pipeline,
(1 - self->blend_factor), (1 - self->blend_factor),
(1 - self->blend_factor), (1 - self->blend_factor),
@ -883,6 +915,7 @@ meta_background_get_texture (MetaBackground *self,
(1 - self->blend_factor));; (1 - self->blend_factor));;
cogl_pipeline_set_layer_texture (pipeline, 0, texture1); 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_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, bare_region_visible = bare_region_visible || draw_texture (self,
monitor->fbo, pipeline, monitor->fbo, pipeline,