st: Move the theme node drawing state to StWidget

This ensures that two widgets sharing the same theme node won't trample
on each other's prerendered materials if the actors are of different
sizes. This also tries to be very careful to share as much as possible
during a transition.

This has the side effect that if a widget changes state a bunch of times,
we won't cache every state. Since we expect that state changes are
infrequent and that most cases we'll be able to use the texture cache
to do most of the heavy lifting, this cost is much more insignificant
than rendering a number of different actors with the same theme node
and different sizes.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
This commit is contained in:
Jasper St. Pierre 2013-04-04 20:29:59 -04:00
parent 72bc46d339
commit 49064ed56d
7 changed files with 124 additions and 120 deletions

View File

@ -1269,53 +1269,6 @@ st_theme_node_prerender_background (StThemeNode *node,
return texture; return texture;
} }
void
_st_theme_node_paint_state_free (StThemeNodePaintState *state)
{
int corner_id;
if (state->background_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (state->background_texture);
if (state->background_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->background_material);
if (state->background_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->background_shadow_material);
if (state->border_slices_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (state->border_slices_texture);
if (state->border_slices_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->border_slices_material);
if (state->prerendered_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (state->prerendered_texture);
if (state->prerendered_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->prerendered_material);
if (state->box_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->box_shadow_material);
for (corner_id = 0; corner_id < 4; corner_id++)
if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
cogl_handle_unref (state->corner_material[corner_id]);
_st_theme_node_paint_state_init (state);
}
void
_st_theme_node_paint_state_init (StThemeNodePaintState *state)
{
int corner_id;
state->background_texture = COGL_INVALID_HANDLE;
state->background_material = COGL_INVALID_HANDLE;
state->background_shadow_material = COGL_INVALID_HANDLE;
state->box_shadow_material = COGL_INVALID_HANDLE;
state->border_slices_texture = COGL_INVALID_HANDLE;
state->border_slices_material = COGL_INVALID_HANDLE;
state->prerendered_texture = COGL_INVALID_HANDLE;
state->prerendered_material = COGL_INVALID_HANDLE;
for (corner_id = 0; corner_id < 4; corner_id++)
state->corner_material[corner_id] = COGL_INVALID_HANDLE;
}
static void st_theme_node_paint_borders (StThemeNode *node, static void st_theme_node_paint_borders (StThemeNode *node,
StThemeNodePaintState *state, StThemeNodePaintState *state,
const ClutterActorBox *box, const ClutterActorBox *box,
@ -1345,7 +1298,7 @@ st_theme_node_render_resources (StThemeNode *node,
* geometry change versus things that can be cached regardless, such as * geometry change versus things that can be cached regardless, such as
* a background image. * a background image.
*/ */
_st_theme_node_paint_state_free (state); st_theme_node_paint_state_free (state);
state->alloc_width = width; state->alloc_width = width;
state->alloc_height = height; state->alloc_height = height;
@ -1939,12 +1892,12 @@ st_theme_node_paint_outline (StThemeNode *node,
void void
st_theme_node_paint (StThemeNode *node, st_theme_node_paint (StThemeNode *node,
StThemeNodePaintState *state,
const ClutterActorBox *box, const ClutterActorBox *box,
guint8 paint_opacity) guint8 paint_opacity)
{ {
float width, height; float width, height;
ClutterActorBox allocation; ClutterActorBox allocation;
StThemeNodePaintState *state = &node->state;
/* Some things take an ActorBox, some things just width/height */ /* Some things take an ActorBox, some things just width/height */
width = box->x2 - box->x1; width = box->x2 - box->x1;
@ -2064,7 +2017,54 @@ st_theme_node_paint (StThemeNode *node,
} }
} }
static void void
st_theme_node_paint_state_free (StThemeNodePaintState *state)
{
int corner_id;
if (state->background_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (state->background_texture);
if (state->background_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->background_material);
if (state->background_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->background_shadow_material);
if (state->border_slices_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (state->border_slices_texture);
if (state->border_slices_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->border_slices_material);
if (state->prerendered_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (state->prerendered_texture);
if (state->prerendered_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->prerendered_material);
if (state->box_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (state->box_shadow_material);
for (corner_id = 0; corner_id < 4; corner_id++)
if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
cogl_handle_unref (state->corner_material[corner_id]);
st_theme_node_paint_state_init (state);
}
void
st_theme_node_paint_state_init (StThemeNodePaintState *state)
{
int corner_id;
state->background_texture = COGL_INVALID_HANDLE;
state->background_material = COGL_INVALID_HANDLE;
state->background_shadow_material = COGL_INVALID_HANDLE;
state->box_shadow_material = COGL_INVALID_HANDLE;
state->border_slices_texture = COGL_INVALID_HANDLE;
state->border_slices_material = COGL_INVALID_HANDLE;
state->prerendered_texture = COGL_INVALID_HANDLE;
state->prerendered_material = COGL_INVALID_HANDLE;
for (corner_id = 0; corner_id < 4; corner_id++)
state->corner_material[corner_id] = COGL_INVALID_HANDLE;
}
void
st_theme_node_paint_state_copy (StThemeNodePaintState *state, st_theme_node_paint_state_copy (StThemeNodePaintState *state,
StThemeNodePaintState *other) StThemeNodePaintState *other)
{ {
@ -2073,10 +2073,7 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
if (state == other) if (state == other)
return; return;
/* Check omitted for speed: */ st_theme_node_paint_state_free (state);
/* g_return_if_fail (st_theme_node_paint_equal (node, other)); */
_st_theme_node_paint_state_free (state);
state->alloc_width = other->alloc_width; state->alloc_width = other->alloc_width;
state->alloc_height = other->alloc_height; state->alloc_height = other->alloc_height;
@ -2102,33 +2099,9 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
state->corner_material[corner_id] = cogl_handle_ref (other->corner_material[corner_id]); state->corner_material[corner_id] = cogl_handle_ref (other->corner_material[corner_id]);
} }
/**
* st_theme_node_copy_cached_paint_state:
* @node: a #StThemeNode
* @other: a different #StThemeNode
*
* Copy cached painting state from @other to @node. This function can be used to
* optimize redrawing cached background images when the style on an element changess
* in a way that doesn't affect background drawing. This function must only be called
* if st_theme_node_paint_equal (node, other) returns %TRUE.
*/
void void
st_theme_node_copy_cached_paint_state (StThemeNode *node,
StThemeNode *other)
{
st_theme_node_paint_state_copy (&node->state,
&other->state);
}
static void
st_theme_node_paint_state_invalidate (StThemeNodePaintState *state) st_theme_node_paint_state_invalidate (StThemeNodePaintState *state)
{ {
state->alloc_width = 0; state->alloc_width = 0;
state->alloc_height = 0; state->alloc_height = 0;
} }
void
st_theme_node_invalidate_paint_state (StThemeNode *node)
{
st_theme_node_paint_state_invalidate (&node->state);
}

View File

@ -29,23 +29,6 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct _StThemeNodePaintState StThemeNodePaintState;
struct _StThemeNodePaintState {
float alloc_width;
float alloc_height;
CoglHandle background_texture;
CoglHandle background_material;
CoglHandle border_slices_texture;
CoglHandle border_slices_material;
CoglHandle background_shadow_material;
CoglHandle box_shadow_material;
CoglHandle prerendered_texture;
CoglHandle prerendered_material;
CoglHandle corner_material[4];
};
struct _StThemeNode { struct _StThemeNode {
GObject parent; GObject parent;
@ -116,8 +99,6 @@ struct _StThemeNode {
guint background_image_shadow_computed : 1; guint background_image_shadow_computed : 1;
guint text_shadow_computed : 1; guint text_shadow_computed : 1;
guint link_type : 2; guint link_type : 2;
StThemeNodePaintState state;
}; };
struct _StThemeNodeClass { struct _StThemeNodeClass {
@ -128,9 +109,6 @@ struct _StThemeNodeClass {
void _st_theme_node_ensure_background (StThemeNode *node); void _st_theme_node_ensure_background (StThemeNode *node);
void _st_theme_node_ensure_geometry (StThemeNode *node); void _st_theme_node_ensure_geometry (StThemeNode *node);
void _st_theme_node_paint_state_init (StThemeNodePaintState *state);
void _st_theme_node_paint_state_free (StThemeNodePaintState *state);
G_END_DECLS G_END_DECLS
#endif /* __ST_THEME_NODE_PRIVATE_H__ */ #endif /* __ST_THEME_NODE_PRIVATE_H__ */

View File

@ -33,6 +33,9 @@ struct _StThemeNodeTransitionPrivate {
StThemeNode *old_theme_node; StThemeNode *old_theme_node;
StThemeNode *new_theme_node; StThemeNode *new_theme_node;
StThemeNodePaintState old_paint_state;
StThemeNodePaintState new_paint_state;
CoglHandle old_texture; CoglHandle old_texture;
CoglHandle new_texture; CoglHandle new_texture;
@ -75,6 +78,7 @@ on_timeline_new_frame (ClutterTimeline *timeline,
StThemeNodeTransition * StThemeNodeTransition *
st_theme_node_transition_new (StThemeNode *from_node, st_theme_node_transition_new (StThemeNode *from_node,
StThemeNode *to_node, StThemeNode *to_node,
StThemeNodePaintState *old_paint_state,
guint duration) guint duration)
{ {
StThemeNodeTransition *transition; StThemeNodeTransition *transition;
@ -90,6 +94,9 @@ st_theme_node_transition_new (StThemeNode *from_node,
transition->priv->old_theme_node = g_object_ref (from_node); transition->priv->old_theme_node = g_object_ref (from_node);
transition->priv->new_theme_node = g_object_ref (to_node); transition->priv->new_theme_node = g_object_ref (to_node);
st_theme_node_paint_state_copy (&transition->priv->old_paint_state,
old_paint_state);
transition->priv->timeline = clutter_timeline_new (duration); transition->priv->timeline = clutter_timeline_new (duration);
transition->priv->timeline_completed_id = transition->priv->timeline_completed_id =
@ -106,6 +113,12 @@ st_theme_node_transition_new (StThemeNode *from_node,
return transition; return transition;
} }
StThemeNodePaintState *
st_theme_node_transition_get_new_paint_state (StThemeNodeTransition *transition)
{
return &transition->priv->new_paint_state;
}
void void
st_theme_node_transition_update (StThemeNodeTransition *transition, st_theme_node_transition_update (StThemeNodeTransition *transition,
StThemeNode *new_node) StThemeNode *new_node)
@ -134,6 +147,12 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
*/ */
if (st_theme_node_equal (new_node, old_node)) if (st_theme_node_equal (new_node, old_node))
{ {
{
StThemeNodePaintState tmp = priv->old_paint_state;
priv->old_paint_state = priv->new_paint_state;
priv->new_paint_state = tmp;
}
if (clutter_timeline_get_elapsed_time (priv->timeline) > 0) if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
{ {
if (direction == CLUTTER_TIMELINE_FORWARD) if (direction == CLUTTER_TIMELINE_FORWARD)
@ -162,15 +181,10 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
clutter_timeline_set_duration (priv->timeline, new_duration); clutter_timeline_set_duration (priv->timeline, new_duration);
/* If the change doesn't affect painting, we don't need to redraw,
* but we still need to replace the node so that we properly share
* caching with the painting that happens after the transition finishes.
*/
if (!st_theme_node_paint_equal (priv->new_theme_node, new_node))
priv->needs_setup = TRUE;
g_object_unref (priv->new_theme_node); g_object_unref (priv->new_theme_node);
priv->new_theme_node = g_object_ref (new_node); priv->new_theme_node = g_object_ref (new_node);
st_theme_node_paint_state_invalidate (&priv->new_paint_state);
} }
} }
} }
@ -285,7 +299,7 @@ setup_framebuffers (StThemeNodeTransition *transition,
cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2, cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
priv->offscreen_box.y2, priv->offscreen_box.y1, priv->offscreen_box.y2, priv->offscreen_box.y1,
0.0, 1.0); 0.0, 1.0);
st_theme_node_paint (priv->old_theme_node, allocation, 255); st_theme_node_paint (priv->old_theme_node, &priv->old_paint_state, allocation, 255);
cogl_pop_framebuffer (); cogl_pop_framebuffer ();
cogl_push_framebuffer (priv->new_offscreen); cogl_push_framebuffer (priv->new_offscreen);
@ -293,7 +307,7 @@ setup_framebuffers (StThemeNodeTransition *transition,
cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2, cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
priv->offscreen_box.y2, priv->offscreen_box.y1, priv->offscreen_box.y2, priv->offscreen_box.y1,
0.0, 1.0); 0.0, 1.0);
st_theme_node_paint (priv->new_theme_node, allocation, 255); st_theme_node_paint (priv->new_theme_node, &priv->new_paint_state, allocation, 255);
cogl_pop_framebuffer (); cogl_pop_framebuffer ();
return TRUE; return TRUE;
@ -408,6 +422,9 @@ st_theme_node_transition_dispose (GObject *object)
priv->timeline_completed_id = 0; priv->timeline_completed_id = 0;
priv->timeline_new_frame_id = 0; priv->timeline_new_frame_id = 0;
st_theme_node_paint_state_free (&priv->old_paint_state);
st_theme_node_paint_state_free (&priv->new_paint_state);
G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object); G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object);
} }
@ -425,6 +442,9 @@ st_theme_node_transition_init (StThemeNodeTransition *transition)
transition->priv->old_offscreen = NULL; transition->priv->old_offscreen = NULL;
transition->priv->new_offscreen = NULL; transition->priv->new_offscreen = NULL;
st_theme_node_paint_state_init (&transition->priv->old_paint_state);
st_theme_node_paint_state_init (&transition->priv->new_paint_state);
transition->priv->needs_setup = TRUE; transition->priv->needs_setup = TRUE;
} }

View File

@ -56,6 +56,7 @@ GType st_theme_node_transition_get_type (void) G_GNUC_CONST;
StThemeNodeTransition *st_theme_node_transition_new (StThemeNode *from_node, StThemeNodeTransition *st_theme_node_transition_new (StThemeNode *from_node,
StThemeNode *to_node, StThemeNode *to_node,
StThemeNodePaintState *old_paint_state,
guint duration); guint duration);
void st_theme_node_transition_update (StThemeNodeTransition *transition, void st_theme_node_transition_update (StThemeNodeTransition *transition,
@ -69,6 +70,8 @@ void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition,
const ClutterActorBox *allocation, const ClutterActorBox *allocation,
ClutterActorBox *paint_box); ClutterActorBox *paint_box);
StThemeNodePaintState * st_theme_node_transition_get_new_paint_state (StThemeNodeTransition *transition);
G_END_DECLS G_END_DECLS
#endif #endif

View File

@ -49,7 +49,6 @@ static void
st_theme_node_init (StThemeNode *node) st_theme_node_init (StThemeNode *node)
{ {
node->transition_duration = -1; node->transition_duration = -1;
_st_theme_node_paint_state_init (&node->state);
} }
static void static void
@ -152,8 +151,6 @@ st_theme_node_finalize (GObject *object)
if (node->background_image) if (node->background_image)
g_free (node->background_image); g_free (node->background_image);
_st_theme_node_paint_state_free (&node->state);
G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object); G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
} }

View File

@ -94,6 +94,23 @@ typedef enum {
ST_GRADIENT_RADIAL ST_GRADIENT_RADIAL
} StGradientType; } StGradientType;
typedef struct _StThemeNodePaintState StThemeNodePaintState;
struct _StThemeNodePaintState {
float alloc_width;
float alloc_height;
CoglHandle background_texture;
CoglHandle background_material;
CoglHandle border_slices_texture;
CoglHandle border_slices_material;
CoglHandle background_shadow_material;
CoglHandle box_shadow_material;
CoglHandle prerendered_texture;
CoglHandle prerendered_material;
CoglHandle corner_material[4];
};
GType st_theme_node_get_type (void) G_GNUC_CONST; GType st_theme_node_get_type (void) G_GNUC_CONST;
StThemeNode *st_theme_node_new (StThemeContext *context, StThemeNode *st_theme_node_new (StThemeContext *context,
@ -250,12 +267,15 @@ gboolean st_theme_node_paint_equal (StThemeNode *node,
StThemeNode *other); StThemeNode *other);
void st_theme_node_paint (StThemeNode *node, void st_theme_node_paint (StThemeNode *node,
StThemeNodePaintState *state,
const ClutterActorBox *box, const ClutterActorBox *box,
guint8 paint_opacity); guint8 paint_opacity);
void st_theme_node_copy_cached_paint_state (StThemeNode *node, void st_theme_node_paint_state_init (StThemeNodePaintState *state);
StThemeNode *other); void st_theme_node_paint_state_free (StThemeNodePaintState *state);
void st_theme_node_invalidate_paint_state (StThemeNode *node); void st_theme_node_paint_state_copy (StThemeNodePaintState *state,
StThemeNodePaintState *other);
void st_theme_node_paint_state_invalidate (StThemeNodePaintState *node);
G_END_DECLS G_END_DECLS

View File

@ -79,6 +79,8 @@ struct _StWidgetPrivate
* that we can remove the pseudo classes on them. */ * that we can remove the pseudo classes on them. */
StWidget *prev_last_child; StWidget *prev_last_child;
StWidget *prev_first_child; StWidget *prev_first_child;
StThemeNodePaintState paint_state;
}; };
/** /**
@ -296,7 +298,7 @@ st_widget_texture_cache_changed (StTextureCache *cache,
if (!changed) if (!changed)
return; return;
st_theme_node_invalidate_paint_state (node); st_theme_node_paint_state_invalidate (&actor->priv->paint_state);
if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (actor))) if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (actor)))
clutter_actor_queue_redraw (CLUTTER_ACTOR (actor)); clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
@ -355,6 +357,8 @@ st_widget_finalize (GObject *gobject)
g_free (priv->accessible_name); g_free (priv->accessible_name);
g_free (priv->inline_style); g_free (priv->inline_style);
st_theme_node_paint_state_free (&priv->paint_state);
G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject); G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
} }
@ -441,7 +445,10 @@ st_widget_paint_background (StWidget *widget)
&allocation, &allocation,
opacity); opacity);
else else
st_theme_node_paint (theme_node, &allocation, opacity); st_theme_node_paint (theme_node,
&widget->priv->paint_state,
&allocation,
opacity);
} }
static void static void
@ -1517,12 +1524,17 @@ st_widget_init (StWidget *actor)
g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL); g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
g_signal_connect (st_texture_cache_get_default (), "texture-file-changed", g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
G_CALLBACK (st_widget_texture_cache_changed), actor); G_CALLBACK (st_widget_texture_cache_changed), actor);
st_theme_node_paint_state_init (&priv->paint_state);
} }
static void static void
on_transition_completed (StThemeNodeTransition *transition, on_transition_completed (StThemeNodeTransition *transition,
StWidget *widget) StWidget *widget)
{ {
st_theme_node_paint_state_copy (&widget->priv->paint_state,
st_theme_node_transition_get_new_paint_state (transition));
st_widget_remove_transition (widget); st_widget_remove_transition (widget);
} }
@ -1549,9 +1561,6 @@ st_widget_recompute_style (StWidget *widget,
paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node); paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node);
if (paint_equal)
st_theme_node_copy_cached_paint_state (new_theme_node, old_theme_node);
g_object_get (gtk_settings_get_default (), g_object_get (gtk_settings_get_default (),
"gtk-enable-animations", &animations_enabled, "gtk-enable-animations", &animations_enabled,
NULL); NULL);
@ -1574,6 +1583,7 @@ st_widget_recompute_style (StWidget *widget,
widget->priv->transition_animation = widget->priv->transition_animation =
st_theme_node_transition_new (old_theme_node, st_theme_node_transition_new (old_theme_node,
new_theme_node, new_theme_node,
&widget->priv->paint_state,
transition_duration); transition_duration);
g_signal_connect (widget->priv->transition_animation, "completed", g_signal_connect (widget->priv->transition_animation, "completed",
@ -1589,6 +1599,9 @@ st_widget_recompute_style (StWidget *widget,
st_widget_remove_transition (widget); st_widget_remove_transition (widget);
} }
if (!paint_equal)
st_theme_node_paint_state_invalidate (&widget->priv->paint_state);
g_signal_emit (widget, signals[STYLE_CHANGED], 0); g_signal_emit (widget, signals[STYLE_CHANGED], 0);
widget->priv->is_style_dirty = FALSE; widget->priv->is_style_dirty = FALSE;
} }