[mutter] shaped-texture: Disable mipmapping during animation



commit c9c32835409046556148d850796210c605ad9998
Author: Daniel van Vugt <daniel van vugt canonical com>
Date:   Wed May 9 15:08:40 2018 +0800

    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%.

 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 98346c6ae..0240c067c 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;
@@ -758,6 +820,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)
     {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]