blur-effect: Port to paint nodes

Port the blur effect to the new ClutterEffect.paint_node() vfunc.
Update the function names to match what they do, e.g. "apply_blur()"
now creates the blur subtree and thus was appropriately renamed to
"create_blur_nodes()".

There are 3 subtrees that can be generated by the blur effect:

 1. Actor mode (full subtree; no cache)

      Root
       |
       |
    Layer (brightness)
       |
    Layer (horizontal blur)
       |
    Layer (vertical blur)
       |
    Layer (actor)
       |
    Transform (downscale)
       |
     Actor

 2. Actor mode (partial subtree; cached contents)

      Root
       |
     Pipeline
  (final result)

 3. Background mode

      Root
       |-----------------------
       |                       |
    Layer (brightness)        Actor
       |
    Layer (horizontal blur)
       |
    Layer (vertical blur)
       |
    Layer (background)
       |
      Blit

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1339>
This commit is contained in:
Georges Basile Stavracas Neto 2020-06-29 15:35:02 -03:00 committed by Marge Bot
parent 78d6b13c29
commit 71807a4f10

View File

@ -163,7 +163,6 @@ struct _ShellBlurEffect
ClutterEffect parent_instance; ClutterEffect parent_instance;
ClutterActor *actor; ClutterActor *actor;
int old_opacity_override;
BlurData blur[2]; BlurData blur[2];
@ -483,21 +482,6 @@ calculate_downscale_factor (float width,
return downscale_factor; return downscale_factor;
} }
static void
clear_framebuffer (CoglFramebuffer *framebuffer)
{
static CoglColor transparent;
static gboolean initialized = FALSE;
if (!initialized)
{
cogl_color_init_from_4ub (&transparent, 0, 0, 0, 0);
initialized = TRUE;
}
cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &transparent);
}
static void static void
shell_blur_effect_set_actor (ClutterActorMeta *meta, shell_blur_effect_set_actor (ClutterActorMeta *meta,
ClutterActor *actor) ClutterActor *actor)
@ -558,100 +542,123 @@ update_actor_box (ShellBlurEffect *self,
} }
static void static void
paint_texture (ShellBlurEffect *self, add_blurred_pipeline (ShellBlurEffect *self,
ClutterPaintContext *paint_context) ClutterPaintNode *node)
{ {
CoglFramebuffer *framebuffer; g_autoptr (ClutterPaintNode) pipeline_node = NULL;
float width, height; float width, height;
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
/* Use the untransformed actor size here, since the framebuffer itself already /* Use the untransformed actor size here, since the framebuffer itself already
* has the actor transform matrix applied. * has the actor transform matrix applied.
*/ */
clutter_actor_get_size (self->actor, &width, &height); clutter_actor_get_size (self->actor, &width, &height);
update_brightness_uniform (self); update_brightness_uniform (self);
cogl_framebuffer_draw_rectangle (framebuffer,
self->brightness_fb.pipeline, pipeline_node = clutter_pipeline_node_new (self->brightness_fb.pipeline);
0, 0, clutter_paint_node_set_static_name (pipeline_node, "ShellBlurEffect (final)");
width, clutter_paint_node_add_child (node, pipeline_node);
height);
clutter_paint_node_add_rectangle (pipeline_node,
&(ClutterActorBox) {
0.f, 0.f,
width,
height,
});
} }
static void static ClutterPaintNode *
apply_blur (ShellBlurEffect *self, create_blur_nodes (ShellBlurEffect *self,
ClutterPaintContext *paint_context, ClutterPaintNode *node,
FramebufferData *from, uint8_t paint_opacity)
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 *vblur;
BlurData *hblur; BlurData *hblur;
float width;
float height;
vblur = &self->blur[VERTICAL]; vblur = &self->blur[VERTICAL];
hblur = &self->blur[HORIZONTAL]; hblur = &self->blur[HORIZONTAL];
/* Copy the actor contents into the vblur framebuffer */ clutter_actor_get_size (self->actor, &width, &height);
clear_framebuffer (vblur->data.framebuffer);
cogl_framebuffer_draw_rectangle (vblur->data.framebuffer,
from->pipeline,
0, 0,
cogl_texture_get_width (vblur->data.texture),
cogl_texture_get_height (vblur->data.texture));
/* Pass 1: update_brightness_uniform (self);
*
* Draw the actor contents (which is in the vblur framebuffer
* at this point) into the hblur framebuffer. This will run the
* vertical blur fragment shader, and will output a vertically
* blurred image.
*/
update_blur_uniforms (self, vblur);
clear_framebuffer (hblur->data.framebuffer); cogl_pipeline_set_color4ub (self->brightness_fb.pipeline,
cogl_framebuffer_draw_rectangle (hblur->data.framebuffer,
vblur->data.pipeline,
0, 0,
cogl_texture_get_width (hblur->data.texture),
cogl_texture_get_height (hblur->data.texture));
/* Pass 2:
*
* Now the opposite; draw the vertically blurred image using the
* horizontal blur pipeline into the brightness framebuffer.
*/
update_blur_uniforms (self, hblur);
cogl_pipeline_set_color4ub (hblur->data.pipeline,
paint_opacity, paint_opacity,
paint_opacity, paint_opacity,
paint_opacity, paint_opacity,
paint_opacity); paint_opacity);
clear_framebuffer (self->brightness_fb.framebuffer); brightness_node = clutter_layer_node_new_to_framebuffer (self->brightness_fb.framebuffer,
cogl_framebuffer_draw_rectangle (self->brightness_fb.framebuffer, self->brightness_fb.pipeline);
hblur->data.pipeline, clutter_paint_node_set_static_name (brightness_node, "ShellBlurEffect (brightness)");
0, 0, clutter_paint_node_add_child (node, brightness_node);
cogl_texture_get_width (self->brightness_fb.texture), clutter_paint_node_add_rectangle (brightness_node,
cogl_texture_get_height (self->brightness_fb.texture)); &(ClutterActorBox) {
0.f, 0.f,
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,
&(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; self->cache_flags |= BLUR_APPLIED;
return g_steal_pointer (&vblur_node);
} }
static gboolean static void
paint_background (ShellBlurEffect *self, paint_background (ShellBlurEffect *self,
ClutterPaintNode *node,
ClutterPaintContext *paint_context, ClutterPaintContext *paint_context,
ClutterActorBox *source_actor_box) ClutterActorBox *source_actor_box)
{ {
g_autoptr (GError) error = NULL; g_autoptr (ClutterPaintNode) background_node = NULL;
CoglFramebuffer *framebuffer; g_autoptr (ClutterPaintNode) blit_node = NULL;
CoglFramebuffer *src;
float transformed_x; float transformed_x;
float transformed_y; float transformed_y;
float transformed_width; float transformed_width;
float transformed_height; float transformed_height;
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
clutter_actor_box_get_origin (source_actor_box, clutter_actor_box_get_origin (source_actor_box,
&transformed_x, &transformed_x,
&transformed_y); &transformed_y);
@ -659,23 +666,30 @@ paint_background (ShellBlurEffect *self,
&transformed_width, &transformed_width,
&transformed_height); &transformed_height);
clear_framebuffer (self->background_fb.framebuffer); /* Background layer node */
cogl_blit_framebuffer (framebuffer, background_node =
self->background_fb.framebuffer, clutter_layer_node_new_to_framebuffer (self->background_fb.framebuffer,
transformed_x, self->background_fb.pipeline);
transformed_y, clutter_paint_node_set_static_name (background_node, "ShellBlurEffect (background)");
0, 0, clutter_paint_node_add_child (node, background_node);
transformed_width, clutter_paint_node_add_rectangle (background_node,
transformed_height, &(ClutterActorBox) {
&error); 0.f, 0.f,
self->tex_width / self->downscale_factor,
self->tex_height / self->downscale_factor,
});
if (error) /* Blit node */
{ src = clutter_paint_context_get_framebuffer (paint_context);
g_warning ("Error blitting overlay framebuffer: %s", error->message); blit_node = clutter_blit_node_new (src);
return FALSE; clutter_paint_node_set_static_name (blit_node, "ShellBlurEffect (blit)");
} clutter_paint_node_add_child (background_node, blit_node);
clutter_blit_node_add_blit_rectangle (CLUTTER_BLIT_NODE (blit_node),
return TRUE; transformed_x,
transformed_y,
0, 0,
transformed_width,
transformed_height);
} }
static gboolean static gboolean
@ -708,9 +722,20 @@ update_framebuffers (ShellBlurEffect *self,
return updated; return updated;
} }
static void
add_actor_node (ShellBlurEffect *self,
ClutterPaintNode *node,
int opacity)
{
g_autoptr (ClutterPaintNode) actor_node = NULL;
actor_node = clutter_actor_node_new (self->actor, opacity);
clutter_paint_node_add_child (node, actor_node);
}
static void static void
paint_actor_offscreen (ShellBlurEffect *self, paint_actor_offscreen (ShellBlurEffect *self,
ClutterPaintContext *paint_context, ClutterPaintNode *node,
ClutterEffectPaintFlags flags) ClutterEffectPaintFlags flags)
{ {
gboolean actor_dirty; gboolean actor_dirty;
@ -718,32 +743,53 @@ paint_actor_offscreen (ShellBlurEffect *self,
actor_dirty = (flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) != 0; actor_dirty = (flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) != 0;
/* The actor offscreen framebuffer is updated already */ /* The actor offscreen framebuffer is updated already */
if (!actor_dirty && (self->cache_flags & ACTOR_PAINTED)) if (actor_dirty || !(self->cache_flags & ACTOR_PAINTED))
return; {
g_autoptr (ClutterPaintNode) transform_node = NULL;
g_autoptr (ClutterPaintNode) layer_node = NULL;
graphene_matrix_t transform;
self->old_opacity_override = clutter_actor_get_opacity_override (self->actor); /* Layer node */
clutter_actor_set_opacity_override (self->actor, 0xff); layer_node = clutter_layer_node_new_to_framebuffer (self->actor_fb.framebuffer,
self->actor_fb.pipeline);
clutter_paint_node_set_static_name (layer_node, "ShellBlurEffect (actor offscreen)");
clutter_paint_node_add_child (node, layer_node);
clutter_paint_node_add_rectangle (layer_node,
&(ClutterActorBox) {
0.f, 0.f,
self->tex_width / self->downscale_factor,
self->tex_height / self->downscale_factor,
});
/* Draw the actor contents into the actor offscreen framebuffer */ /* Transform node */
clear_framebuffer (self->actor_fb.framebuffer); graphene_matrix_init_scale (&transform,
1.f / self->downscale_factor,
1.f / self->downscale_factor,
1.f);
transform_node = clutter_transform_node_new (&transform);
clutter_paint_node_set_static_name (transform_node, "ShellBlurEffect (downscale)");
clutter_paint_node_add_child (layer_node, transform_node);
cogl_framebuffer_push_matrix (self->actor_fb.framebuffer); /* Actor node */
cogl_framebuffer_scale (self->actor_fb.framebuffer, add_actor_node (self, transform_node, 255);
1.f / self->downscale_factor,
1.f / self->downscale_factor,
1.f);
clutter_paint_context_push_framebuffer (paint_context, self->cache_flags |= ACTOR_PAINTED;
self->actor_fb.framebuffer); }
else
{
g_autoptr (ClutterPaintNode) pipeline_node = NULL;
clutter_actor_continue_paint (self->actor, paint_context); pipeline_node = clutter_pipeline_node_new (self->actor_fb.pipeline);
clutter_paint_node_set_static_name (pipeline_node,
cogl_framebuffer_pop_matrix (self->actor_fb.framebuffer); "ShellBlurEffect (actor texture)");
clutter_paint_context_pop_framebuffer (paint_context); clutter_paint_node_add_child (node, pipeline_node);
clutter_paint_node_add_rectangle (pipeline_node,
clutter_actor_set_opacity_override (self->actor, self->old_opacity_override); &(ClutterActorBox) {
0.f, 0.f,
self->cache_flags |= ACTOR_PAINTED; self->tex_width / self->downscale_factor,
self->tex_height / self->downscale_factor,
});
}
} }
static gboolean static gboolean
@ -771,9 +817,10 @@ needs_repaint (ShellBlurEffect *self,
} }
static void static void
shell_blur_effect_paint (ClutterEffect *effect, shell_blur_effect_paint_node (ClutterEffect *effect,
ClutterPaintContext *paint_context, ClutterPaintNode *node,
ClutterEffectPaintFlags flags) ClutterPaintContext *paint_context,
ClutterEffectPaintFlags flags)
{ {
ShellBlurEffect *self = SHELL_BLUR_EFFECT (effect); ShellBlurEffect *self = SHELL_BLUR_EFFECT (effect);
uint8_t paint_opacity; uint8_t paint_opacity;
@ -782,6 +829,8 @@ shell_blur_effect_paint (ClutterEffect *effect,
if (self->sigma > 0) if (self->sigma > 0)
{ {
g_autoptr (ClutterPaintNode) blur_node = NULL;
if (needs_repaint (self, flags)) if (needs_repaint (self, flags))
{ {
ClutterActorBox source_actor_box; ClutterActorBox source_actor_box;
@ -799,20 +848,21 @@ shell_blur_effect_paint (ClutterEffect *effect,
case SHELL_BLUR_MODE_ACTOR: case SHELL_BLUR_MODE_ACTOR:
paint_opacity = clutter_actor_get_paint_opacity (self->actor); paint_opacity = clutter_actor_get_paint_opacity (self->actor);
paint_actor_offscreen (self, paint_context, flags); blur_node = create_blur_nodes (self, node, paint_opacity);
apply_blur (self, paint_context, &self->actor_fb, paint_opacity); paint_actor_offscreen (self, blur_node, flags);
break; break;
case SHELL_BLUR_MODE_BACKGROUND: case SHELL_BLUR_MODE_BACKGROUND:
if (!paint_background (self, paint_context, &source_actor_box)) blur_node = create_blur_nodes (self, node, 255);
goto fail; paint_background (self, blur_node, paint_context, &source_actor_box);
apply_blur (self, paint_context, &self->background_fb, 255);
break; break;
} }
} }
else
paint_texture (self, paint_context); {
/* Use the cached pipeline if no repaint is needed */
add_blurred_pipeline (self, node);
}
/* Background blur needs to paint the actor after painting the blurred /* Background blur needs to paint the actor after painting the blurred
* background. * background.
@ -823,7 +873,7 @@ shell_blur_effect_paint (ClutterEffect *effect,
break; break;
case SHELL_BLUR_MODE_BACKGROUND: case SHELL_BLUR_MODE_BACKGROUND:
clutter_actor_continue_paint (self->actor, paint_context); add_actor_node (self, node, -1);
break; break;
} }
@ -834,7 +884,7 @@ fail:
/* When no blur is applied, or the offscreen framebuffers /* When no blur is applied, or the offscreen framebuffers
* couldn't be created, fallback to simply painting the actor. * couldn't be created, fallback to simply painting the actor.
*/ */
clutter_actor_continue_paint (self->actor, paint_context); add_actor_node (self, node, -1);
} }
static void static void
@ -924,7 +974,7 @@ shell_blur_effect_class_init (ShellBlurEffectClass *klass)
meta_class->set_actor = shell_blur_effect_set_actor; meta_class->set_actor = shell_blur_effect_set_actor;
effect_class->paint = shell_blur_effect_paint; effect_class->paint_node = shell_blur_effect_paint_node;
properties[PROP_SIGMA] = properties[PROP_SIGMA] =
g_param_spec_int ("sigma", g_param_spec_int ("sigma",