From d81dcd13e4e14a8c80b7c9d475feb58c258babc0 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Wed, 9 May 2018 15:08:40 +0800 Subject: [PATCH] shaped-texture: Disable mipmapping during animation This avoids overwhelming the GPU with trying to update mipmaps at a high rate. Because doing so could easily cause a reduction in the compositor frame rate and thus actually reduce visual quality. In the case of a window that is constantly animating in the overview, this reduces mutter's render time by around 20%-30%. (cherry picked from commit c9c32835409046556148d850796210c605ad9998) --- src/compositor/meta-shaped-texture.c | 88 ++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index bc9c7a6df..6f4d3acbb 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -38,6 +38,20 @@ #include "meta-cullable.h" +/* MAX_MIPMAPPING_FPS needs to be as small as possible for the best GPU + * performance, but higher than the refresh rate of commonly slow updating + * windows like top or a blinking cursor, so that such windows do get + * mipmapped. + */ +#define MAX_MIPMAPPING_FPS 5 +#define MIN_MIPMAP_AGE_USEC (G_USEC_PER_SEC / MAX_MIPMAPPING_FPS) + +/* MIN_FAST_UPDATES_BEFORE_UNMIPMAP allows windows to update themselves + * occasionally without causing mipmapping to be disabled, so long as such + * an update takes fewer update_area calls than: + */ +#define MIN_FAST_UPDATES_BEFORE_UNMIPMAP 20 + static void meta_shaped_texture_dispose (GObject *object); static void meta_shaped_texture_paint (ClutterActor *actor); @@ -95,6 +109,11 @@ struct _MetaShapedTexturePrivate guint tex_width, tex_height; guint fallback_width, fallback_height; + gint64 prev_invalidation, last_invalidation; + guint fast_updates; + guint remipmap_timeout_id; + gint64 earliest_remipmap; + guint create_mipmaps : 1; }; @@ -191,6 +210,12 @@ meta_shaped_texture_dispose (GObject *object) MetaShapedTexture *self = (MetaShapedTexture *) object; MetaShapedTexturePrivate *priv = self->priv; + if (priv->remipmap_timeout_id) + { + g_source_remove (priv->remipmap_timeout_id); + priv->remipmap_timeout_id = 0; + } + if (priv->paint_tower) meta_texture_tower_free (priv->paint_tower); priv->paint_tower = NULL; @@ -372,6 +397,21 @@ set_cogl_texture (MetaShapedTexture *stex, meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex); } +static gboolean +texture_is_idle_and_not_mipmapped (gpointer user_data) +{ + MetaShapedTexture *stex = META_SHAPED_TEXTURE (user_data); + MetaShapedTexturePrivate *priv = stex->priv; + + if ((g_get_monotonic_time () - priv->earliest_remipmap) < 0) + return G_SOURCE_CONTINUE; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (stex)); + priv->remipmap_timeout_id = 0; + + return G_SOURCE_REMOVE; +} + static void meta_shaped_texture_paint (ClutterActor *actor) { @@ -381,9 +421,10 @@ meta_shaped_texture_paint (ClutterActor *actor) guchar opacity; CoglContext *ctx; CoglFramebuffer *fb; - CoglTexture *paint_tex; + CoglTexture *paint_tex = NULL; ClutterActorBox alloc; CoglPipelineFilter filter; + gint64 now = g_get_monotonic_time (); if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) return; @@ -406,13 +447,34 @@ meta_shaped_texture_paint (ClutterActor *actor) * Setting the texture quality to high without SGIS_generate_mipmap * support for TFP textures will result in fallbacks to XGetImage. */ - if (priv->create_mipmaps) - paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); - else - paint_tex = COGL_TEXTURE (priv->texture); + if (priv->create_mipmaps && priv->last_invalidation) + { + gint64 age = now - priv->last_invalidation; + + if (age >= MIN_MIPMAP_AGE_USEC || + priv->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP) + paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); + } if (paint_tex == NULL) - return; + { + paint_tex = COGL_TEXTURE (priv->texture); + + if (paint_tex == NULL) + return; + + if (priv->create_mipmaps) + { + /* Minus 1000 to ensure we don't fail the age test in timeout */ + priv->earliest_remipmap = now + MIN_MIPMAP_AGE_USEC - 1000; + + if (!priv->remipmap_timeout_id) + priv->remipmap_timeout_id = + g_timeout_add (MIN_MIPMAP_AGE_USEC / 1000, + texture_is_idle_and_not_mipmapped, + stex); + } + } tex_width = priv->tex_width; tex_height = priv->tex_height; @@ -723,6 +785,20 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, meta_texture_tower_update_area (priv->paint_tower, x, y, width, height); + priv->prev_invalidation = priv->last_invalidation; + priv->last_invalidation = g_get_monotonic_time (); + + if (priv->prev_invalidation) + { + gint64 interval = priv->last_invalidation - priv->prev_invalidation; + gboolean fast_update = interval < MIN_MIPMAP_AGE_USEC; + + if (!fast_update) + priv->fast_updates = 0; + else if (priv->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP) + priv->fast_updates++; + } + unobscured_region = effective_unobscured_region (stex); if (unobscured_region) {