[gnome-shell] blur-effect: Use ClutterBlurNode



commit f7019bdd0de73209dc40960904248de10421000a
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Wed Dec 9 19:20:16 2020 -0300

    blur-effect: Use ClutterBlurNode
    
    With ClutterBlurNode available, we can remove our own implementation
    and delegate the blur shader and framebuffers. This simply replaces
    the pair of layer nodes (vblur and hblur) with a ClutterBlurNode,
    and removes all dead code.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1528>

 src/shell-blur-effect.c | 295 ++++++------------------------------------------
 1 file changed, 36 insertions(+), 259 deletions(-)
---
diff --git a/src/shell-blur-effect.c b/src/shell-blur-effect.c
index 7785d02961..7003b68f7c 100644
--- a/src/shell-blur-effect.c
+++ b/src/shell-blur-effect.c
@@ -26,7 +26,7 @@
  * SECTION:shell-blur-effect
  * @short_description: Blur effect for actors
  *
- * #ShellBlurEffect is a moderately fast gaussian blur implementation. It also has
+ * #ShellBlurEffect is a blur implementation based on Clutter. It also has
  * an optional brightness property.
  *
  * # Modes
@@ -38,89 +38,8 @@
  * @SHELL_BLUR_MODE_BACKGROUND can be computationally expensive, since the contents
  * beneath the actor cannot be cached, so beware of the performance implications
  * of using this blur mode.
- *
- * # Optimizations
- *
- * There are a number of optimizations in place to make this blur implementation
- * real-time. All in all, the implementation performs best when using large
- * blur-radii that allow downscaling the texture to smaller sizes, at small
- * radii where no downscaling is possible this can easily halve the framerate.
- *
- * ## Multipass
- *
- * It is implemented in 2 passes: vertical and horizontal.
- *
- * ## Downscaling
- *
- * #ShellBlurEffect uses dynamic downscaling to speed up blurring. Downscaling
- * happens in factors of 2 (the image is downscaled either by 2, 4, 8, 16, …) and
- * depends on the blur radius, the actor size, among others.
- *
- * The actor is drawn into a downscaled framebuffer; the blur passes are applied
- * on the downscaled actor contents; and finally, the blurred contents are drawn
- * upscaled again.
- *
- * ## Hardware Interpolation
- *
- * This blur implementation cuts down the number of sampling operations by
- * exploiting the hardware interpolation that is performed when sampling between
- * pixel boundaries. This technique is described at:
- *
- * http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
- *
- * ## Incremental gauss-factor calculation
- *
- * The kernel values for the gaussian kernel are computed incrementally instead
- * of running the expensive calculations multiple times inside the blur shader.
- * The implementation is based on the algorithm presented by K. Turkowski in
- * GPU Gems 3, chapter 40:
- *
- * https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch40.html
- *
  */
 
-static const gchar *gaussian_blur_glsl_declarations =
-"uniform float sigma;                                                      \n"
-"uniform float pixel_step;                                                 \n"
-"uniform int vertical;                                                     \n";
-
-static const gchar *gaussian_blur_glsl =
-"  int horizontal = 1 - vertical;                                          \n"
-"                                                                          \n"
-"  vec2 uv = vec2 (cogl_tex_coord.st);                                     \n"
-"                                                                          \n"
-"  vec3 gauss_coefficient;                                                 \n"
-"  gauss_coefficient.x = 1.0 / (sqrt (2.0 * 3.14159265) * sigma);          \n"
-"  gauss_coefficient.y = exp (-0.5 / (sigma * sigma));                     \n"
-"  gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;        \n"
-"                                                                          \n"
-"  float gauss_coefficient_total = gauss_coefficient.x;                    \n"
-"                                                                          \n"
-"  vec4 ret = texture2D (cogl_sampler, uv) * gauss_coefficient.x;          \n"
-"  gauss_coefficient.xy *= gauss_coefficient.yz;                           \n"
-"                                                                          \n"
-"  int n_steps = int (ceil (3 * sigma));                                   \n"
-"                                                                          \n"
-"  for (int i = 1; i < n_steps; i += 2) {                                  \n"
-"    float coefficient_subtotal = gauss_coefficient.x;                     \n"
-"    gauss_coefficient.xy *= gauss_coefficient.yz;                         \n"
-"    coefficient_subtotal += gauss_coefficient.x;                          \n"
-"                                                                          \n"
-"    float gauss_ratio = gauss_coefficient.x / coefficient_subtotal;       \n"
-"                                                                          \n"
-"    float foffset = float (i) + gauss_ratio;                              \n"
-"    vec2 offset = vec2 (foffset * pixel_step * float (horizontal),        \n"
-"                        foffset * pixel_step * float (vertical));         \n"
-"                                                                          \n"
-"    ret += texture2D (cogl_sampler, uv + offset) * coefficient_subtotal;  \n"
-"    ret += texture2D (cogl_sampler, uv - offset) * coefficient_subtotal;  \n"
-"                                                                          \n"
-"    gauss_coefficient_total += 2.0 * coefficient_subtotal;                \n"
-"    gauss_coefficient.xy *= gauss_coefficient.yz;                         \n"
-"  }                                                                       \n"
-"                                                                          \n"
-"  cogl_texel = ret / gauss_coefficient_total;                             \n";
-
 static const gchar *brightness_glsl_declarations =
 "uniform float brightness;                                                 \n";
 
@@ -130,12 +49,6 @@ static const gchar *brightness_glsl =
 #define MIN_DOWNSCALE_SIZE 256.f
 #define MAX_SIGMA 6.f
 
-typedef enum
-{
-  VERTICAL,
-  HORIZONTAL,
-} BlurType;
-
 typedef enum
 {
   ACTOR_PAINTED = 1 << 0,
@@ -149,23 +62,12 @@ typedef struct
   CoglTexture *texture;
 } FramebufferData;
 
-typedef struct
-{
-  FramebufferData data;
-  BlurType type;
-  int sigma_uniform;
-  int pixel_step_uniform;
-  int vertical_uniform;
-} BlurData;
-
 struct _ShellBlurEffect
 {
   ClutterEffect parent_instance;
 
   ClutterActor *actor;
 
-  BlurData blur[2];
-
   unsigned int tex_width;
   unsigned int tex_height;
 
@@ -219,29 +121,6 @@ create_base_pipeline (void)
   return cogl_pipeline_copy (base_pipeline);
 }
 
-static CoglPipeline*
-create_blur_pipeline (void)
-{
-  static CoglPipeline *blur_pipeline = NULL;
-
-  if (G_UNLIKELY (blur_pipeline == NULL))
-    {
-      CoglSnippet *snippet;
-
-      blur_pipeline = create_base_pipeline ();
-
-      snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
-                                  gaussian_blur_glsl_declarations,
-                                  NULL);
-      cogl_snippet_set_replace (snippet, gaussian_blur_glsl);
-      cogl_pipeline_add_layer_snippet (blur_pipeline, 0, snippet);
-      cogl_object_unref (snippet);
-    }
-
-  return cogl_pipeline_copy (blur_pipeline);
-}
-
-
 static CoglPipeline*
 create_brightness_pipeline (void)
 {
@@ -263,59 +142,17 @@ create_brightness_pipeline (void)
   return cogl_pipeline_copy (brightness_pipeline);
 }
 
-static void
-setup_blur (BlurData *blur,
-            BlurType  type)
-{
-  blur->type = type;
-  blur->data.pipeline = create_blur_pipeline ();
-
-  blur->sigma_uniform =
-    cogl_pipeline_get_uniform_location (blur->data.pipeline, "sigma");
-  blur->pixel_step_uniform =
-    cogl_pipeline_get_uniform_location (blur->data.pipeline, "pixel_step");
-  blur->vertical_uniform =
-    cogl_pipeline_get_uniform_location (blur->data.pipeline, "vertical");
-}
 
 static void
-update_blur_uniforms (ShellBlurEffect *self,
-                      BlurData        *blur)
+update_brightness (ShellBlurEffect *self,
+                   uint8_t          paint_opacity)
 {
-  gboolean is_vertical = blur->type == VERTICAL;
-
-  if (blur->pixel_step_uniform > -1)
-    {
-      float pixel_step;
-
-      if (is_vertical)
-        pixel_step = 1.f / cogl_texture_get_height (blur->data.texture);
-      else
-        pixel_step = 1.f / cogl_texture_get_width (blur->data.texture);
-
-      cogl_pipeline_set_uniform_1f (blur->data.pipeline,
-                                    blur->pixel_step_uniform,
-                                    pixel_step);
-    }
-
-  if (blur->sigma_uniform > -1)
-    {
-      cogl_pipeline_set_uniform_1f (blur->data.pipeline,
-                                    blur->sigma_uniform,
-                                    self->sigma / self->downscale_factor);
-    }
-
-  if (blur->vertical_uniform > -1)
-    {
-      cogl_pipeline_set_uniform_1i (blur->data.pipeline,
-                                    blur->vertical_uniform,
-                                    is_vertical);
-    }
-}
+  cogl_pipeline_set_color4ub (self->brightness_fb.pipeline,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity);
 
-static void
-update_brightness_uniform (ShellBlurEffect *self)
-{
   if (self->brightness_uniform > -1)
     {
       cogl_pipeline_set_uniform_1f (self->brightness_fb.pipeline,
@@ -412,26 +249,6 @@ update_brightness_fbo (ShellBlurEffect *self,
                      downscale_factor);
 }
 
-static gboolean
-update_blur_fbo (ShellBlurEffect *self,
-                 BlurData        *blur,
-                 unsigned int     width,
-                 unsigned int     height,
-                 float            downscale_factor)
-{
-  if (self->tex_width == width &&
-      self->tex_height == height &&
-      self->downscale_factor == downscale_factor &&
-      blur->data.framebuffer)
-    {
-      return TRUE;
-    }
-
-  return update_fbo (&blur->data,
-                     width, height,
-                     downscale_factor);
-}
-
 static gboolean
 update_background_fbo (ShellBlurEffect *self,
                        unsigned int     width,
@@ -496,8 +313,6 @@ shell_blur_effect_set_actor (ClutterActorMeta *meta,
   clear_framebuffer_data (&self->actor_fb);
   clear_framebuffer_data (&self->background_fb);
   clear_framebuffer_data (&self->brightness_fb);
-  clear_framebuffer_data (&self->blur[VERTICAL].data);
-  clear_framebuffer_data (&self->blur[HORIZONTAL].data);
 
   /* we keep a back pointer here, to avoid going through the ActorMeta */
   self->actor = clutter_actor_meta_get_actor (meta);
@@ -543,7 +358,8 @@ update_actor_box (ShellBlurEffect     *self,
 
 static void
 add_blurred_pipeline (ShellBlurEffect  *self,
-                      ClutterPaintNode *node)
+                      ClutterPaintNode *node,
+                      uint8_t           paint_opacity)
 {
   g_autoptr (ClutterPaintNode) pipeline_node = NULL;
   float width, height;
@@ -553,7 +369,7 @@ add_blurred_pipeline (ShellBlurEffect  *self,
    */
   clutter_actor_get_size (self->actor, &width, &height);
 
-  update_brightness_uniform (self);
+  update_brightness (self, paint_opacity);
 
   pipeline_node = clutter_pipeline_node_new (self->brightness_fb.pipeline);
   clutter_paint_node_set_static_name (pipeline_node, "ShellBlurEffect (final)");
@@ -573,26 +389,13 @@ create_blur_nodes (ShellBlurEffect  *self,
                    uint8_t           paint_opacity)
 {
   g_autoptr (ClutterPaintNode) brightness_node = NULL;
-  g_autoptr (ClutterPaintNode) hblur_node = NULL;
-  g_autoptr (ClutterPaintNode) vblur_node = NULL;
-  BlurData *vblur;
-  BlurData *hblur;
+  g_autoptr (ClutterPaintNode) blur_node = NULL;
   float width;
   float height;
 
-  vblur = &self->blur[VERTICAL];
-  hblur = &self->blur[HORIZONTAL];
-
   clutter_actor_get_size (self->actor, &width, &height);
 
-  update_brightness_uniform (self);
-
-  cogl_pipeline_set_color4ub (self->brightness_fb.pipeline,
-                              paint_opacity,
-                              paint_opacity,
-                              paint_opacity,
-                              paint_opacity);
-
+  update_brightness (self, paint_opacity);
   brightness_node = clutter_layer_node_new_to_framebuffer (self->brightness_fb.framebuffer,
                                                            self->brightness_fb.pipeline);
   clutter_paint_node_set_static_name (brightness_node, "ShellBlurEffect (brightness)");
@@ -603,46 +406,21 @@ create_blur_nodes (ShellBlurEffect  *self,
                                       width, height,
                                     });
 
-  /* Horizontal pass:
-   *
-   * This layer node contains the vertically blurred image; draw the it using the
-   * horizontal blur pipeline.
-   */
-  update_blur_uniforms (self, hblur);
-
-  hblur_node = clutter_layer_node_new_to_framebuffer (hblur->data.framebuffer,
-                                                      hblur->data.pipeline);
-  clutter_paint_node_set_static_name (hblur_node, "ShellBlurEffect (horizontal pass)");
-  clutter_paint_node_add_child (brightness_node, hblur_node);
-  clutter_paint_node_add_rectangle (hblur_node,
+  blur_node = clutter_blur_node_new (self->tex_width / self->downscale_factor,
+                                     self->tex_height / self->downscale_factor,
+                                     self->sigma / self->downscale_factor);
+  clutter_paint_node_set_static_name (blur_node, "ShellBlurEffect (blur)");
+  clutter_paint_node_add_child (brightness_node, blur_node);
+  clutter_paint_node_add_rectangle (blur_node,
                                     &(ClutterActorBox) {
                                       0.f, 0.f,
                                       cogl_texture_get_width (self->brightness_fb.texture),
                                       cogl_texture_get_height (self->brightness_fb.texture),
                                     });
 
-  /* Vertical pass:
-   *
-   * Draw the actor contents into the vblur framebuffer using the vertical
-   * blur pipeline, which will output a vertically blurred image that will
-   * be used by the parent node (hblur_node).
-   */
-  update_blur_uniforms (self, vblur);
-
-  vblur_node = clutter_layer_node_new_to_framebuffer (vblur->data.framebuffer,
-                                                      vblur->data.pipeline);
-  clutter_paint_node_set_static_name (vblur_node, "ShellBlurEffect (vertical pass)");
-  clutter_paint_node_add_child (hblur_node, vblur_node);
-  clutter_paint_node_add_rectangle (vblur_node,
-                                    &(ClutterActorBox) {
-                                      0.f, 0.f,
-                                      cogl_texture_get_width (hblur->data.texture),
-                                      cogl_texture_get_height (hblur->data.texture)
-                                    });
-
   self->cache_flags |= BLUR_APPLIED;
 
-  return g_steal_pointer (&vblur_node);
+  return g_steal_pointer (&blur_node);
 }
 
 static void
@@ -706,11 +484,8 @@ update_framebuffers (ShellBlurEffect     *self,
 
   downscale_factor = calculate_downscale_factor (width, height, self->sigma);
 
-  updated =
-    update_actor_fbo (self, width, height, downscale_factor) &&
-    update_blur_fbo (self, &self->blur[VERTICAL], width, height, downscale_factor) &&
-    update_blur_fbo (self, &self->blur[HORIZONTAL], width, height, downscale_factor) &&
-    update_brightness_fbo (self, width, height, downscale_factor);
+  updated = update_actor_fbo (self, width, height, downscale_factor) &&
+            update_brightness_fbo (self, width, height, downscale_factor);
 
   if (self->mode == SHELL_BLUR_MODE_BACKGROUND)
     updated = updated && update_background_fbo (self, width, height);
@@ -831,6 +606,17 @@ shell_blur_effect_paint_node (ClutterEffect           *effect,
     {
       g_autoptr (ClutterPaintNode) blur_node = NULL;
 
+      switch (self->mode)
+        {
+        case SHELL_BLUR_MODE_ACTOR:
+          paint_opacity = clutter_actor_get_paint_opacity (self->actor);
+          break;
+
+        case SHELL_BLUR_MODE_BACKGROUND:
+          paint_opacity = 255;
+          break;
+        }
+
       if (needs_repaint (self, flags))
         {
           ClutterActorBox source_actor_box;
@@ -843,17 +629,15 @@ shell_blur_effect_paint_node (ClutterEffect           *effect,
           if (!update_framebuffers (self, paint_context, &source_actor_box))
             goto fail;
 
+          blur_node = create_blur_nodes (self, node, paint_opacity);
+
           switch (self->mode)
             {
             case SHELL_BLUR_MODE_ACTOR:
-              paint_opacity = clutter_actor_get_paint_opacity (self->actor);
-
-              blur_node = create_blur_nodes (self, node, paint_opacity);
               paint_actor_offscreen (self, blur_node, flags);
               break;
 
             case SHELL_BLUR_MODE_BACKGROUND:
-              blur_node = create_blur_nodes (self, node, 255);
               paint_background (self, blur_node, paint_context, &source_actor_box);
               break;
             }
@@ -861,7 +645,7 @@ shell_blur_effect_paint_node (ClutterEffect           *effect,
       else
         {
           /* Use the cached pipeline if no repaint is needed */
-          add_blurred_pipeline (self, node);
+          add_blurred_pipeline (self, node, paint_opacity);
         }
 
       /* Background blur needs to paint the actor after painting the blurred
@@ -895,14 +679,10 @@ shell_blur_effect_finalize (GObject *object)
   clear_framebuffer_data (&self->actor_fb);
   clear_framebuffer_data (&self->background_fb);
   clear_framebuffer_data (&self->brightness_fb);
-  clear_framebuffer_data (&self->blur[VERTICAL].data);
-  clear_framebuffer_data (&self->blur[HORIZONTAL].data);
 
   g_clear_pointer (&self->actor_fb.pipeline, cogl_object_unref);
   g_clear_pointer (&self->background_fb.pipeline, cogl_object_unref);
   g_clear_pointer (&self->brightness_fb.pipeline, cogl_object_unref);
-  g_clear_pointer (&self->blur[VERTICAL].data.pipeline, cogl_object_unref);
-  g_clear_pointer (&self->blur[HORIZONTAL].data.pipeline, cogl_object_unref);
 
   G_OBJECT_CLASS (shell_blur_effect_parent_class)->finalize (object);
 }
@@ -1013,9 +793,6 @@ shell_blur_effect_init (ShellBlurEffect *self)
   self->brightness_fb.pipeline = create_brightness_pipeline ();
   self->brightness_uniform =
     cogl_pipeline_get_uniform_location (self->brightness_fb.pipeline, "brightness");
-
-  setup_blur (&self->blur[VERTICAL], VERTICAL);
-  setup_blur (&self->blur[HORIZONTAL], HORIZONTAL);
 }
 
 ShellBlurEffect *


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