diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-08-26 17:52:57 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-08-26 17:52:57 +0000 |
commit | 372db8d239844ade457c62e33fa83ff2f806b06a (patch) | |
tree | ef7c93ca6dd33601e4b2517999d6d31045051b9f | |
parent | 3b1300632d3869bd7d5ca729a2a4d8e73833c9fe (diff) | |
parent | d57e6b754f00bf9ec6e49812f57195c66a8a6f9e (diff) | |
download | gtk+-372db8d239844ade457c62e33fa83ff2f806b06a.tar.gz |
Merge branch 'broadway-prune' into 'master'
broadway: Prune fully clipped render nodes
Closes #3086
See merge request GNOME/gtk!2487
-rw-r--r-- | gdk/broadway/broadway-protocol.h | 2 | ||||
-rw-r--r-- | gsk/broadway/gskbroadwayrenderer.c | 185 |
2 files changed, 147 insertions, 40 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 df5028169e..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); } @@ -299,9 +315,45 @@ 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); @@ -467,14 +533,21 @@ get_colorized_texture (GdkTexture *texture, /* Note: This tracks the offset so that we can convert - the absolute coordinates of the GskRenderNodes to - parent-relative which is what the dom uses, and - which is good for re-using subtrees. */ + * the absolute coordinates of the GskRenderNodes to + * parent-relative which is what the dom uses, and + * which is good for re-using subtrees. + * + * We also track the clip bounds which is a best-effort + * clip region tracking (i.e. can be unset or larger + * than real clip, but not smaller). This can be used + * to avoid sending completely clipped nodes. + */ static void gsk_broadway_renderer_add_node (GskRenderer *renderer, GskRenderNode *node, float offset_x, - float offset_y) + float offset_y, + graphene_rect_t *clip_bounds) { GdkDisplay *display = gdk_surface_get_display (gsk_renderer_get_surface (renderer)); GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display); @@ -490,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; @@ -505,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; @@ -541,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)); @@ -549,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); @@ -561,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)); @@ -573,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)); @@ -585,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; @@ -602,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); @@ -617,43 +690,53 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, } gsk_broadway_renderer_add_node (renderer, gsk_shadow_node_get_child (node), - offset_x, offset_y); + offset_x, offset_y, clip_bounds); } 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, gsk_opacity_node_get_child (node), - offset_x, offset_y); + offset_x, offset_y, clip_bounds); } 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; + + if (clip_bounds) + graphene_rect_intersection (&child_bounds, clip_bounds, &child_bounds); add_rounded_rect (nodes, rclip, offset_x, offset_y); gsk_broadway_renderer_add_node (renderer, gsk_rounded_clip_node_get_child (node), rclip->bounds.origin.x, - rclip->bounds.origin.y); + rclip->bounds.origin.y, + &child_bounds); } 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; + + if (clip_bounds) + graphene_rect_intersection (&child_bounds, clip_bounds, &child_bounds); add_rect (nodes, clip, offset_x, offset_y); gsk_broadway_renderer_add_node (renderer, gsk_clip_node_get_child (node), clip->origin.x, - clip->origin.y); + clip->origin.y, + &child_bounds); } return; @@ -662,17 +745,26 @@ 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; - gsk_transform_to_translate (transform, &dx, &dy); + graphene_rect_t child_bounds; + graphene_rect_t *child_bounds_p = NULL; + gsk_transform_to_translate (transform, &dx, &dy); add_uint32 (nodes, 0); // Translate add_xy (nodes, dx, dy, 0, 0); + + if (clip_bounds) + { + graphene_rect_offset_r (clip_bounds, -dx, -dy, &child_bounds); + child_bounds_p = &child_bounds; + } + gsk_broadway_renderer_add_node (renderer, gsk_transform_node_get_child (node), - 0, 0); + 0, 0, child_bounds_p); } else { @@ -681,35 +773,50 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, gsk_transform_to_matrix (transform, &matrix); add_uint32 (nodes, 1); // General transform add_matrix (nodes, &matrix); + // We just drop the clip bounds here to make things simpler gsk_broadway_renderer_add_node (renderer, gsk_transform_node_get_child (node), - 0, 0); + 0, 0, NULL); } } } 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); gsk_broadway_renderer_add_node (renderer, - gsk_debug_node_get_child (node), offset_x, offset_y); + gsk_debug_node_get_child (node), offset_x, offset_y, clip_bounds); } return; /* 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); + { + /* 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; @@ -722,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); @@ -744,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; @@ -798,7 +905,7 @@ gsk_broadway_renderer_render (GskRenderer *renderer, self->nodes = self->draw_context->nodes; self->node_textures = self->draw_context->node_textures; - gsk_broadway_renderer_add_node (renderer, root, 0, 0); + gsk_broadway_renderer_add_node (renderer, root, 0, 0, NULL); self->nodes = NULL; self->node_textures = NULL; |