From d57e6b754f00bf9ec6e49812f57195c66a8a6f9e Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 26 Aug 2020 18:08:00 +0200 Subject: [PATCH] broadway: Prune fully clipped render nodes If some node is fully outside the clip region we don't send it to the daemon. This helps a lot in how much data we send for scrolling viewports. However, sending partial trees makes node reuse a bit more tricky. We can't save for reuse any node that could possibly clip different depending on the clip region, as that could be different next frame. So, unless the node is fully contained in the current clip (and we thus know it is not parial) we don't allow reusing that next frame. This fixes #3086 --- gdk/broadway/broadway-protocol.h | 2 +- gsk/broadway/gskbroadwayrenderer.c | 132 +++++++++++++++++++++++------ 2 files changed, 107 insertions(+), 27 deletions(-) diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h index 35fb02cab2..9b56dff6df 100644 --- a/gdk/broadway/broadway-protocol.h +++ b/gdk/broadway/broadway-protocol.h @@ -8,7 +8,7 @@ typedef struct { gint32 width, height; } BroadwayRect; -typedef enum { /* Sync changes with broadway.js */ +typedef enum { /* Sync changes with broadway.js and node_type_is_container() */ BROADWAY_NODE_TEXTURE = 0, BROADWAY_NODE_CONTAINER = 1, BROADWAY_NODE_COLOR = 2, diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c index b56a095f73..e18b03f826 100644 --- a/gsk/broadway/gskbroadwayrenderer.c +++ b/gsk/broadway/gskbroadwayrenderer.c @@ -90,6 +90,23 @@ add_uint32 (GArray *nodes, guint32 v) g_array_append_val (nodes, v); } +static guint +add_uint32_placeholder (GArray *nodes) +{ + guint pos = nodes->len; + guint32 v = 0; + + g_array_append_val (nodes, v); + return pos; +} + +static void +set_uint32_at (GArray *nodes, guint index, guint32 v) +{ + g_array_index (nodes, guint32, index) = v; +} + + static void add_float (GArray *nodes, float f) { @@ -212,10 +229,9 @@ collect_reused_node (GskRenderer *renderer, if (self->last_node_lookup && (old_id = GPOINTER_TO_INT(g_hash_table_lookup (self->last_node_lookup, node))) != 0) - { - g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER (old_id)); - collect_reused_child_nodes (renderer, node); - } + g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER (old_id)); + + collect_reused_child_nodes (renderer, node); } @@ -298,10 +314,46 @@ collect_reused_child_nodes (GskRenderer *renderer, } } +static gboolean +node_is_visible (GskRenderNode *node, + graphene_rect_t *clip_bounds) +{ + if (clip_bounds == NULL || + graphene_rect_intersection (clip_bounds, &node->bounds, NULL)) + return TRUE; + + return FALSE; +} + +static gboolean +node_is_fully_visible (GskRenderNode *node, + graphene_rect_t *clip_bounds) +{ + if (clip_bounds == NULL || + graphene_rect_contains_rect (clip_bounds, &node->bounds)) + return TRUE; + + return FALSE; +} + +static gboolean +node_type_is_container (BroadwayNodeType type) +{ + return + type == BROADWAY_NODE_SHADOW || + type == BROADWAY_NODE_OPACITY || + type == BROADWAY_NODE_ROUNDED_CLIP || + type == BROADWAY_NODE_CLIP || + type == BROADWAY_NODE_TRANSFORM || + type == BROADWAY_NODE_DEBUG || + type == BROADWAY_NODE_CONTAINER; +} + static gboolean add_new_node (GskRenderer *renderer, GskRenderNode *node, - BroadwayNodeType type) + BroadwayNodeType type, + graphene_rect_t *clip_bounds) { GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer); guint32 id, old_id; @@ -319,7 +371,21 @@ add_new_node (GskRenderer *renderer, } id = ++self->next_node_id; - g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(id)); + + /* Never try to reuse partially visible container types the next + * frame, as they could be be partial due to pruning against clip_bounds, + * and the clip_bounds may be different the next frame. However, anything + * that is fully visible will not be pruned, so is ok to reuse. + * + * Note: its quite possible that the node is fully visible, but contains + * a clip node which means the tree under that partial. That is fine and we can + * still reuse *this* node next frame, but we can't use the child that is + * partial, for example in a different place, because then it might see + * the partial region of the tree. + */ + if (!node_type_is_container (type) || + node_is_fully_visible (node, clip_bounds)) + g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(id)); add_uint32 (self->nodes, type); add_uint32 (self->nodes, id); @@ -497,7 +563,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, /* Leaf nodes */ case GSK_TEXTURE_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE)) + if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE, clip_bounds)) { GdkTexture *texture = gsk_texture_node_get_texture (node); guint32 texture_id; @@ -512,7 +578,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_CAIRO_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE)) + if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE, clip_bounds)) { cairo_surface_t *surface = gsk_cairo_node_peek_surface (node); cairo_surface_t *image_surface = NULL; @@ -548,7 +614,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_COLOR_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_COLOR)) + if (add_new_node (renderer, node, BROADWAY_NODE_COLOR, clip_bounds)) { add_rect (nodes, &node->bounds, offset_x, offset_y); add_rgba (nodes, gsk_color_node_peek_color (node)); @@ -556,7 +622,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_BORDER_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_BORDER)) + if (add_new_node (renderer, node, BROADWAY_NODE_BORDER, clip_bounds)) { int i; add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y); @@ -568,7 +634,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_OUTSET_SHADOW_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_OUTSET_SHADOW)) + if (add_new_node (renderer, node, BROADWAY_NODE_OUTSET_SHADOW, clip_bounds)) { add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y); add_rgba (nodes, gsk_outset_shadow_node_peek_color (node)); @@ -580,7 +646,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_INSET_SHADOW_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_INSET_SHADOW)) + if (add_new_node (renderer, node, BROADWAY_NODE_INSET_SHADOW, clip_bounds)) { add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y); add_rgba (nodes, gsk_inset_shadow_node_peek_color (node)); @@ -592,7 +658,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_LINEAR_GRADIENT_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_LINEAR_GRADIENT)) + if (add_new_node (renderer, node, BROADWAY_NODE_LINEAR_GRADIENT, clip_bounds)) { guint i, n; @@ -609,7 +675,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, /* Bin nodes */ case GSK_SHADOW_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_SHADOW)) + if (add_new_node (renderer, node, BROADWAY_NODE_SHADOW, clip_bounds)) { gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node); @@ -629,7 +695,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_OPACITY_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_OPACITY)) + if (add_new_node (renderer, node, BROADWAY_NODE_OPACITY, clip_bounds)) { add_float (nodes, gsk_opacity_node_get_opacity (node)); gsk_broadway_renderer_add_node (renderer, @@ -639,7 +705,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_ROUNDED_CLIP_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_ROUNDED_CLIP)) + if (add_new_node (renderer, node, BROADWAY_NODE_ROUNDED_CLIP, clip_bounds)) { const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node); graphene_rect_t child_bounds = rclip->bounds; @@ -657,7 +723,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_CLIP_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_CLIP)) + if (add_new_node (renderer, node, BROADWAY_NODE_CLIP, clip_bounds)) { const graphene_rect_t *clip = gsk_clip_node_peek_clip (node); graphene_rect_t child_bounds = *clip; @@ -679,7 +745,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, GskTransform *transform = gsk_transform_node_get_transform (node); GskTransformCategory category = gsk_transform_get_category (transform); - if (add_new_node (renderer, node, BROADWAY_NODE_TRANSFORM)) { + if (add_new_node (renderer, node, BROADWAY_NODE_TRANSFORM, clip_bounds)) { if (category >= GSK_TRANSFORM_CATEGORY_2D_TRANSLATE) { float dx, dy; @@ -717,7 +783,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_DEBUG_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_DEBUG)) + if (add_new_node (renderer, node, BROADWAY_NODE_DEBUG, clip_bounds)) { const char *message = gsk_debug_node_get_message (node); add_string (nodes, message); @@ -729,14 +795,28 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, /* Generic nodes */ case GSK_CONTAINER_NODE: - if (add_new_node (renderer, node, BROADWAY_NODE_CONTAINER)) + if (add_new_node (renderer, node, BROADWAY_NODE_CONTAINER, clip_bounds)) { - guint i; + guint i, placeholder; + guint32 n_children = 0; - add_uint32 (nodes, gsk_container_node_get_n_children (node)); + placeholder = add_uint32_placeholder (nodes); for (i = 0; i < gsk_container_node_get_n_children (node); i++) - gsk_broadway_renderer_add_node (renderer, - gsk_container_node_get_child (node, i), offset_x, offset_y, clip_bounds); + { + /* We prune fully clipped children, but we only do this for container_node, as + * we don't have a way for any other nodes to say there are children missing (i.e. + * bins always assume there is a child). + * Pruning is really only useful for large sets of children anyway, so thats + * probably fine. */ + GskRenderNode *child = gsk_container_node_get_child (node, i); + if (node_is_visible (child, clip_bounds)) + { + n_children++; + gsk_broadway_renderer_add_node (renderer, + child, offset_x, offset_y, clip_bounds); + } + } + set_uint32_at (nodes, placeholder, n_children); } return; @@ -749,7 +829,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, const graphene_vec4_t *color_offset = gsk_color_matrix_node_peek_color_offset (node); GdkTexture *texture = gsk_texture_node_get_texture (child); GdkTexture *colorized_texture = get_colorized_texture (texture, color_matrix, color_offset); - if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE)) + if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE, clip_bounds)) { guint32 texture_id = gdk_broadway_display_ensure_texture (display, colorized_texture); add_rect (nodes, &child->bounds, offset_x, offset_y); @@ -771,7 +851,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, break; /* Fallback */ } - if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE)) + if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE, clip_bounds)) { GdkTexture *texture; cairo_surface_t *surface;