From 0eba21b2b58593599829732c5556f0a15ed75a06 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Apr 2022 19:28:53 -0400 Subject: [PATCH 1/3] gsk: Track disjointness of container nodes This can be used to optimize some things in the GL renderer. --- gsk/gskrendernodeimpl.c | 21 +++++++++++++++++++++ gsk/gskrendernodeprivate.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 384de83bad..53b7d35f9d 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -2576,6 +2576,7 @@ struct _GskContainerNode { GskRenderNode render_node; + gboolean disjoint; guint n_children; GskRenderNode **children; }; @@ -2724,6 +2725,7 @@ gsk_container_node_new (GskRenderNode **children, self = gsk_render_node_alloc (GSK_CONTAINER_NODE); node = (GskRenderNode *) self; + self->disjoint = TRUE; self->n_children = n_children; if (n_children == 0) @@ -2743,6 +2745,7 @@ gsk_container_node_new (GskRenderNode **children, for (guint i = 1; i < n_children; i++) { self->children[i] = gsk_render_node_ref (children[i]); + self->disjoint &= !graphene_rect_intersection (&bounds, &(children[i]->bounds), NULL); graphene_rect_union (&bounds, &(children[i]->bounds), &bounds); node->prefers_high_depth |= gsk_render_node_prefers_high_depth (children[i]); } @@ -2801,6 +2804,24 @@ gsk_container_node_get_children (const GskRenderNode *node, return self->children; } +/*< private> + * gsk_container_node_is_disjoint: + * @node: a container `GskRenderNode` + * + * Returns `TRUE` if it is known that the child nodes are not + * overlapping. There is no guarantee that they do overlap + * if this function return FALSE. + * + * Returns: `TRUE` if children don't overlap + */ +gboolean +gsk_container_node_is_disjoint (const GskRenderNode *node) +{ + const GskContainerNode *self = (const GskContainerNode *) node; + + return self->disjoint; +} + /*** GSK_TRANSFORM_NODE ***/ /** diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index cdb75afd2f..637e6cb4fd 100644 --- a/gsk/gskrendernodeprivate.h +++ b/gsk/gskrendernodeprivate.h @@ -113,6 +113,8 @@ void gsk_transform_node_get_translate (const GskRenderNode *no float *dy); gboolean gsk_render_node_prefers_high_depth (const GskRenderNode *node); +gboolean gsk_container_node_is_disjoint (const GskRenderNode *node); + G_END_DECLS From 38eb182947894e956e575fde36ed41275b49dc05 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 5 Apr 2022 14:55:36 -0400 Subject: [PATCH 2/3] gsk: Collect opacity information Collect information about whether to use offscreens for opacity during node construction, so we don't need to walk the tree repeatedly, later. --- gsk/gskrendernode.c | 19 ++++++++++++++++--- gsk/gskrendernodeimpl.c | 26 ++++++++++++++++++++++++++ gsk/gskrendernodeprivate.h | 3 +++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c index 295ddf698e..14a29c358c 100644 --- a/gsk/gskrendernode.c +++ b/gsk/gskrendernode.c @@ -58,14 +58,14 @@ value_render_node_init (GValue *value) { value->data[0].v_pointer = NULL; } - + static void value_render_node_free_value (GValue *value) { if (value->data[0].v_pointer != NULL) gsk_render_node_unref (value->data[0].v_pointer); } - + static void value_render_node_copy_value (const GValue *src, GValue *dst) @@ -75,7 +75,7 @@ value_render_node_copy_value (const GValue *src, else dst->data[0].v_pointer = NULL; } - + static gpointer value_render_node_peek_pointer (const GValue *value) { @@ -738,3 +738,16 @@ gsk_render_node_prefers_high_depth (const GskRenderNode *node) { return node->prefers_high_depth; } + +/* Whether we need an offscreen to handle opacity correctly for this node. + * We don't if there is only one drawing node inside (could be child + * node, or grandchild, or...). + * + * For containers with multiple children, we can avoid the offscreen if + * the children are known not to overlap. + */ +gboolean +gsk_render_node_use_offscreen_for_opacity (const GskRenderNode *node) +{ + return node->offscreen_for_opacity; +} diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 53b7d35f9d..175c458848 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -139,6 +139,7 @@ gsk_color_node_new (const GdkRGBA *rgba, self = gsk_render_node_alloc (GSK_COLOR_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; self->color = *rgba; graphene_rect_init_from_rect (&node->bounds, bounds); @@ -284,6 +285,7 @@ gsk_linear_gradient_node_new (const graphene_rect_t *bounds, self = gsk_render_node_alloc (GSK_LINEAR_GRADIENT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; graphene_rect_init_from_rect (&node->bounds, bounds); graphene_point_init_from_point (&self->start, start); @@ -336,6 +338,7 @@ gsk_repeating_linear_gradient_node_new (const graphene_rect_t *bounds, self = gsk_render_node_alloc (GSK_REPEATING_LINEAR_GRADIENT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; graphene_rect_init_from_rect (&node->bounds, bounds); graphene_point_init_from_point (&self->start, start); @@ -584,6 +587,7 @@ gsk_radial_gradient_node_new (const graphene_rect_t *bounds, self = gsk_render_node_alloc (GSK_RADIAL_GRADIENT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; graphene_rect_init_from_rect (&node->bounds, bounds); graphene_point_init_from_point (&self->center, center); @@ -652,6 +656,7 @@ gsk_repeating_radial_gradient_node_new (const graphene_rect_t *bounds, self = gsk_render_node_alloc (GSK_REPEATING_RADIAL_GRADIENT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; graphene_rect_init_from_rect (&node->bounds, bounds); graphene_point_init_from_point (&self->center, center); @@ -1030,6 +1035,7 @@ gsk_conic_gradient_node_new (const graphene_rect_t *bounds, self = gsk_render_node_alloc (GSK_CONIC_GRADIENT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; graphene_rect_init_from_rect (&node->bounds, bounds); graphene_point_init_from_point (&self->center, center); @@ -1413,6 +1419,7 @@ gsk_border_node_new (const GskRoundedRect *outline, self = gsk_render_node_alloc (GSK_BORDER_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; gsk_rounded_rect_init_copy (&self->outline, outline); memcpy (self->border_width, border_width, sizeof (self->border_width)); @@ -1559,6 +1566,7 @@ gsk_texture_node_new (GdkTexture *texture, self = gsk_render_node_alloc (GSK_TEXTURE_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; self->texture = g_object_ref (texture); graphene_rect_init_from_rect (&node->bounds, bounds); @@ -2014,6 +2022,7 @@ gsk_inset_shadow_node_new (const GskRoundedRect *outline, self = gsk_render_node_alloc (GSK_INSET_SHADOW_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; gsk_rounded_rect_init_copy (&self->outline, outline); self->color = *color; @@ -2313,6 +2322,7 @@ gsk_outset_shadow_node_new (const GskRoundedRect *outline, self = gsk_render_node_alloc (GSK_OUTSET_SHADOW_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; gsk_rounded_rect_init_copy (&self->outline, outline); self->color = *color; @@ -2506,6 +2516,7 @@ gsk_cairo_node_new (const graphene_rect_t *bounds) self = gsk_render_node_alloc (GSK_CAIRO_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; graphene_rect_init_from_rect (&node->bounds, bounds); @@ -2748,9 +2759,11 @@ gsk_container_node_new (GskRenderNode **children, self->disjoint &= !graphene_rect_intersection (&bounds, &(children[i]->bounds), NULL); graphene_rect_union (&bounds, &(children[i]->bounds), &bounds); node->prefers_high_depth |= gsk_render_node_prefers_high_depth (children[i]); + node->offscreen_for_opacity |= children[i]->offscreen_for_opacity; } graphene_rect_init_from_rect (&node->bounds, &bounds); + node->offscreen_for_opacity |= !self->disjoint; } return node; @@ -2983,6 +2996,7 @@ gsk_transform_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_TRANSFORM_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); self->transform = gsk_transform_ref (transform); @@ -3127,6 +3141,7 @@ gsk_opacity_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_OPACITY_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); self->opacity = CLAMP (opacity, 0.0, 1.0); @@ -3330,6 +3345,7 @@ gsk_color_matrix_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_COLOR_MATRIX_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); graphene_matrix_init_from_matrix (&self->color_matrix, color_matrix); @@ -3478,6 +3494,7 @@ gsk_repeat_node_new (const graphene_rect_t *bounds, self = gsk_render_node_alloc (GSK_REPEAT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = TRUE; graphene_rect_init_from_rect (&node->bounds, bounds); @@ -3615,6 +3632,7 @@ gsk_clip_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_CLIP_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); graphene_rect_normalize_r (clip, &self->clip); @@ -3748,6 +3766,7 @@ gsk_rounded_clip_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_ROUNDED_CLIP_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); gsk_rounded_rect_init_copy (&self->clip, clip); @@ -3967,6 +3986,7 @@ gsk_shadow_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_SHADOW_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); self->n_shadows = n_shadows; @@ -4163,6 +4183,7 @@ gsk_blend_node_new (GskRenderNode *bottom, self = gsk_render_node_alloc (GSK_BLEND_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = TRUE; self->bottom = gsk_render_node_ref (bottom); self->top = gsk_render_node_ref (top); @@ -4313,6 +4334,7 @@ gsk_cross_fade_node_new (GskRenderNode *start, self = gsk_render_node_alloc (GSK_CROSS_FADE_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = TRUE; self->start = gsk_render_node_ref (start); self->end = gsk_render_node_ref (end); @@ -4499,6 +4521,7 @@ gsk_text_node_new (PangoFont *font, self = gsk_render_node_alloc (GSK_TEXT_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = FALSE; self->font = g_object_ref (font); self->color = *color; @@ -4905,6 +4928,7 @@ gsk_blur_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_BLUR_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); self->radius = radius; @@ -5034,6 +5058,7 @@ gsk_debug_node_new (GskRenderNode *child, self = gsk_render_node_alloc (GSK_DEBUG_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = child->offscreen_for_opacity; self->child = gsk_render_node_ref (child); self->message = message; @@ -5195,6 +5220,7 @@ gsk_gl_shader_node_new (GskGLShader *shader, self = gsk_render_node_alloc (GSK_GL_SHADER_NODE); node = (GskRenderNode *) self; + node->offscreen_for_opacity = TRUE; graphene_rect_init_from_rect (&node->bounds, bounds); self->shader = g_object_ref (shader); diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index 637e6cb4fd..02bd846979 100644 --- a/gsk/gskrendernodeprivate.h +++ b/gsk/gskrendernodeprivate.h @@ -29,6 +29,7 @@ struct _GskRenderNode graphene_rect_t bounds; guint prefers_high_depth : 1; + guint offscreen_for_opacity : 1; }; struct _GskRenderNodeClass @@ -115,6 +116,8 @@ gboolean gsk_render_node_prefers_high_depth (const GskRenderNode *no gboolean gsk_container_node_is_disjoint (const GskRenderNode *node); +gboolean gsk_render_node_use_offscreen_for_opacity (const GskRenderNode *node); + G_END_DECLS From d72ed045df97104fee7cb8917db74be1d117b62f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Apr 2022 19:37:42 -0400 Subject: [PATCH 3/3] gsk/gl: Use pre-collected opacity information We now collect this information during node construction, so use it here. The concrete change here is that we now avoid offscreens for container nodes with multiple children, as long as they don't overlap. In particular, this avoid offscreens for ellipsized dim labels. --- gsk/gl/gskglrenderjob.c | 58 +---------------------------------------- 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index 30c6cd7a41..f22067cd4d 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -2860,58 +2860,6 @@ gsk_gl_render_job_visit_cross_fade_node (GskGLRenderJob *job, gsk_gl_render_job_end_draw (job); } -static gboolean -is_non_branching (const GskRenderNode *node) -{ - switch ((int)gsk_render_node_get_node_type (node)) - { - case GSK_COLOR_NODE: - case GSK_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - case GSK_RADIAL_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: - case GSK_CONIC_GRADIENT_NODE: - case GSK_BORDER_NODE: - case GSK_TEXTURE_NODE: - case GSK_INSET_SHADOW_NODE: - case GSK_OUTSET_SHADOW_NODE: - case GSK_TEXT_NODE: - case GSK_CAIRO_NODE: - return TRUE; - - case GSK_TRANSFORM_NODE: - return is_non_branching (gsk_transform_node_get_child (node)); - - case GSK_OPACITY_NODE: - return is_non_branching (gsk_opacity_node_get_child (node)); - - case GSK_COLOR_MATRIX_NODE: - return is_non_branching (gsk_color_matrix_node_get_child (node)); - - case GSK_CLIP_NODE: - return is_non_branching (gsk_clip_node_get_child (node)); - - case GSK_ROUNDED_CLIP_NODE: - return is_non_branching (gsk_rounded_clip_node_get_child (node)); - - case GSK_SHADOW_NODE: - return is_non_branching (gsk_shadow_node_get_child (node)); - - case GSK_BLUR_NODE: - return is_non_branching (gsk_shadow_node_get_child (node)); - - case GSK_DEBUG_NODE: - return is_non_branching (gsk_debug_node_get_child (node)); - - case GSK_CONTAINER_NODE: - return gsk_container_node_get_n_children (node) == 1 && - is_non_branching (gsk_container_node_get_child (node, 0)); - - default: - return FALSE; - } -} - static inline void gsk_gl_render_job_visit_opacity_node (GskGLRenderJob *job, const GskRenderNode *node) @@ -2924,11 +2872,7 @@ gsk_gl_render_job_visit_opacity_node (GskGLRenderJob *job, { float prev_alpha = gsk_gl_render_job_set_alpha (job, new_alpha); - /* Handle a few easy cases without offscreen. We bail out - * as soon as we see nodes with multiple children - in theory, - * we would only need offscreens for overlapping children. - */ - if (is_non_branching (child)) + if (!gsk_render_node_use_offscreen_for_opacity (child)) { gsk_gl_render_job_visit_node (job, child); gsk_gl_render_job_set_alpha (job, prev_alpha);