From 1e2e11cd2b0abca503dffc31a5ca084278410207 Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Thu, 27 Apr 2023 00:39:31 +0300 Subject: [PATCH] overhaul and improve performance of computed extents --- Makefile | 4 ++-- src/box-layout-node.c | 47 ++++++++++++++++++------------------------- src/main.c | 6 ++---- src/node.c | 25 ++++++++++++++++++++--- src/node.h | 9 ++++++--- src/scrollable-node.c | 19 +++++------------ src/text-node.c | 30 ++++++++++----------------- src/text-node.h | 3 --- 8 files changed, 68 insertions(+), 75 deletions(-) diff --git a/Makefile b/Makefile index 7838424..9ad0e60 100644 --- a/Makefile +++ b/Makefile @@ -13,10 +13,10 @@ OBJS=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS)) DEPS=$(OBJS:%.o=%.d) -all: CFLAGS+=-Og -ggdb -DNDEBUG +all: CFLAGS+=-fsanitize=address -Og -ggdb all: $(BUILD) $(BIN) -release: CFLAGS+=-O2 -flto=auto -DNDEBUG +release: CFLAGS+=-O2 -flto=auto -DNDEBUG -ggdb release: clean $(BUILD) $(BIN) $(BIN): $(OBJS) diff --git a/src/box-layout-node.c b/src/box-layout-node.c index 220e93d..f2886e0 100644 --- a/src/box-layout-node.c +++ b/src/box-layout-node.c @@ -2,6 +2,8 @@ #include #include #include "box-layout-node.h" +#include "node.h" +#include "rect.h" int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) { @@ -27,38 +29,33 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) int growing_widgets = 0; int current_index = 0; - if (ev == UI_EVENT_RELAYOUT || ev == UI_EVENT_GET_HEIGHT || ev == UI_EVENT_GET_WIDTH) { + if (ev == UI_EVENT_RELAYOUT || ev == UI_EVENT_GET_EXTENTS) { for (int i = 0; i < node->nodes_count; i++) { UINode *current = node->nodes[i]; if (current->flags & UI_NODE_COMPONENT) continue; - double w = current->rect.w; - double h = current->rect.h; - if (current->width_policy == UI_SIZE_POLICY_DYNAMIC) { - node_dispatch(current, UI_EVENT_GET_WIDTH, 0, &w); - current->rect.w = w; - } - if (current->height_policy == UI_SIZE_POLICY_DYNAMIC) { - node_dispatch(current, UI_EVENT_GET_HEIGHT, 0, &h); - current->rect.h = h; - } + UIRect current_rect = node_get_computed_rect(current); + + current->rect.w = current_rect.w; + current->rect.h = current_rect.h; + if (is_horizontal) { - if (h > maximum_secondary_position) { - maximum_secondary_position = h; + if (current_rect.h > maximum_secondary_position) { + maximum_secondary_position = current_rect.h; } if (current->width_policy == UI_SIZE_POLICY_GROW) { growing_widgets++; } else { - size += w; + size += current_rect.w; } } else { - if (w > maximum_secondary_position) { - maximum_secondary_position = w; + if (current_rect.w > maximum_secondary_position) { + maximum_secondary_position = current_rect.w; } if (current->height_policy == UI_SIZE_POLICY_GROW) { growing_widgets++; } else { - size += h; + size += current_rect.h; } } if (current_index) { @@ -70,18 +67,14 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) maximum_secondary_position += is_horizontal ? margin_top + margin_bottom : margin_left + margin_right; size += is_vertical ? margin_top + margin_bottom : margin_left + margin_right; - if (ev == UI_EVENT_GET_WIDTH) { - *(double*)p = is_horizontal ? size : maximum_secondary_position; - return 1; + if (ev == UI_EVENT_GET_EXTENTS) { + ((UIRect*)p)->w = is_horizontal ? size : maximum_secondary_position; + ((UIRect*)p)->h = is_vertical ? size : maximum_secondary_position; + return UI_NODE_COMPUTED_EXTENTS_OK; } - if (ev == UI_EVENT_GET_HEIGHT) { - *(double*)p = is_vertical ? size : maximum_secondary_position; - return 1; - } - // we can distribute growing layout children only if we have a fixed sized policy in the layout primary direction - bool is_fixed_for_primary_direction = (is_horizontal && node->width_policy != UI_SIZE_POLICY_DYNAMIC) || (is_vertical && node->height_policy != UI_SIZE_POLICY_DYNAMIC); + bool is_fixed_for_primary_direction = (is_horizontal && node->width_policy != UI_SIZE_POLICY_COMPUTED) || (is_vertical && node->height_policy != UI_SIZE_POLICY_COMPUTED); double primary_direction = is_horizontal ? node->rect.w - margin_left - margin_right : node->rect.h - margin_top - margin_bottom; bool distributes_growing_widgets = growing_widgets && is_fixed_for_primary_direction; double size_per_growing_widget = 0; @@ -95,7 +88,7 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) } } - bool is_fixed_for_secondary_direction = (is_horizontal && node->height_policy != UI_SIZE_POLICY_DYNAMIC) || (is_vertical && node->width_policy != UI_SIZE_POLICY_DYNAMIC); + bool is_fixed_for_secondary_direction = (is_horizontal && node->height_policy != UI_SIZE_POLICY_COMPUTED) || (is_vertical && node->width_policy != UI_SIZE_POLICY_COMPUTED); double secondary_direction = is_horizontal ? node->rect.h - margin_top - margin_bottom : node->rect.w - margin_left - margin_right; // if our layout secondary direction is fixed, we set the maximum known secondary position to it if (is_fixed_for_secondary_direction) { diff --git a/src/main.c b/src/main.c index b6860af..5158102 100644 --- a/src/main.c +++ b/src/main.c @@ -125,20 +125,18 @@ int app_handle(struct UINode *node, void *data, int event_type, size_t d, void * UIScrollableNode *scroll = scrollable_new(scroll_container, NULL); UINode *buttons = node_new(scroll_container, "buttons"); buttons->width_policy = UI_SIZE_POLICY_GROW; - buttons->height_policy = UI_SIZE_POLICY_DYNAMIC; + buttons->height_policy = UI_SIZE_POLICY_COMPUTED; scroll->target = buttons; box_layout_new(buttons, UI_DIRECTION_VERTICAL); - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 3; i++) { UINode *button = node_new(buttons, "button"); state_background_node_new(button, UIPurple600, UIPurple700, UIPurple800, 6.0); text_node_new(button, state->font, UINeutral50, "button"); dispatcher_new(button, UI_EVENT_UNPRESSED, INCREMENT_COUNT, state, app_handle); button->width_policy = UI_SIZE_POLICY_GROW; } - - printf("%d\n", buttons->last_component_index); } break; diff --git a/src/node.c b/src/node.c index 2b6e68d..524f828 100644 --- a/src/node.c +++ b/src/node.c @@ -6,6 +6,24 @@ typedef struct UIWindow UIWindow; int window_invalidate_node(UIWindow *window, UINode *node); +UIRect node_get_computed_rect(UINode *node) { + UIRect rect = node->rect; + UIRect extents = node->rect; + + if (!node->cached_computed_extents && (node->width_policy == UI_SIZE_POLICY_COMPUTED || node->height_policy == UI_SIZE_POLICY_COMPUTED)) { + if (node_dispatch(node, UI_EVENT_GET_EXTENTS, 0, &extents) == UI_NODE_COMPUTED_EXTENTS_CACHED) { + // Whoever returned UI_NODE_COMPUTED_EXTENTS_CACHED has the responsibility to set + // cached_computed_extents back to false when the cache is no longer valid. + node->cached_computed_extents = true; + } + } + + rect.w = node->width_policy == UI_SIZE_POLICY_COMPUTED ? extents.w : rect.w; + rect.h = node->height_policy == UI_SIZE_POLICY_COMPUTED ? extents.h : rect.h; + + return rect; +} + int node_send(UINode *node, enum UIEvent ev, size_t d, void *p) { if (node->flags & UI_NODE_DISABLED) { @@ -36,7 +54,7 @@ int node_dispatch(UINode *node, enum UIEvent ev, size_t d, void *p) } if (node->nodes[i]->flags & UI_NODE_COMPONENT) { component_index = i; - result = node_dispatch(node->nodes[i], ev, d, p); + result = node_send(node->nodes[i], ev, d, p); if (result) break; } } @@ -78,11 +96,12 @@ void node_init(UINode *node) node->nodes_count = 0; node->nodes_capacity = 0; node->flags = 0; - node->width_policy = UI_SIZE_POLICY_DYNAMIC; - node->height_policy = UI_SIZE_POLICY_DYNAMIC; + node->width_policy = UI_SIZE_POLICY_COMPUTED; + node->height_policy = UI_SIZE_POLICY_COMPUTED; node->handle = NULL; node->handle_proto = NULL; node->last_component_index = UI_NODE_LAST_COMPONENT_INDEX_NONE; + node->cached_computed_extents = false; } void node_free(UINode *node) diff --git a/src/node.h b/src/node.h index aa8e0a7..a813ebd 100644 --- a/src/node.h +++ b/src/node.h @@ -6,11 +6,12 @@ #include "rect.h" #include +#define UI_NODE_COMPUTED_EXTENTS_OK (1) +#define UI_NODE_COMPUTED_EXTENTS_CACHED (2) enum UIEvent { UI_EVENT_REPAINT, UI_EVENT_RELAYOUT, - UI_EVENT_GET_WIDTH, - UI_EVENT_GET_HEIGHT, + UI_EVENT_GET_EXTENTS, UI_EVENT_HOVERED, UI_EVENT_UNHOVERED, UI_EVENT_PRESSED, @@ -26,7 +27,7 @@ enum UIEvent { }; enum UISizePolicy { - UI_SIZE_POLICY_DYNAMIC, + UI_SIZE_POLICY_COMPUTED, UI_SIZE_POLICY_GROW, UI_SIZE_POLICY_FIXED, UI_SIZE_POLICY_STATIC @@ -56,11 +57,13 @@ typedef struct UINode { enum UISizePolicy width_policy; enum UISizePolicy height_policy; int last_component_index; + bool cached_computed_extents; int (*handle)(struct UINode *node, enum UIEvent ev, size_t d, void *p); int (*handle_proto)(struct UINode *node, enum UIEvent ev, size_t d, void *p); } UINode; +UIRect node_get_computed_rect(UINode *node); int node_send(UINode *node, enum UIEvent ev, size_t d, void *p); int node_dispatch(UINode *node, enum UIEvent ev, size_t d, void *p); UINode *node_by_point(UINode *root, double x, double y); diff --git a/src/scrollable-node.c b/src/scrollable-node.c index b7c83fc..a579e1a 100644 --- a/src/scrollable-node.c +++ b/src/scrollable-node.c @@ -1,5 +1,6 @@ #include "scrollable-node.h" #include "node.h" +#include "rect.h" #include "window.h" #include #include @@ -49,20 +50,10 @@ int scrollable_handle(UINode *node, enum UIEvent ev, size_t d, void *p) break; } case UI_EVENT_RELAYOUT: { - double w = target->rect.w; - double h = target->rect.h; - if (target->width_policy == UI_SIZE_POLICY_DYNAMIC) { - node_dispatch(target, UI_EVENT_GET_WIDTH, 0, &w); - target->rect.w = w; - } else if (target->width_policy == UI_SIZE_POLICY_GROW) { - target->rect.w = node->parent->rect.w; - } - if (target->height_policy == UI_SIZE_POLICY_DYNAMIC) { - node_dispatch(target, UI_EVENT_GET_HEIGHT, 0, &h); - target->rect.h = h; - } else if (target->height_policy == UI_SIZE_POLICY_GROW) { - target->rect.h = node->parent->rect.h; - } + UIRect rect = {0}; + node_dispatch(target, UI_EVENT_GET_EXTENTS, 0, &rect); + target->rect.w = target->width_policy == UI_SIZE_POLICY_GROW ? node->parent->rect.w : rect.w; + target->rect.h = target->height_policy == UI_SIZE_POLICY_GROW ? node->parent->rect.h : rect.h; node_dispatch(target, UI_EVENT_RELAYOUT, 0, NULL); break; } diff --git a/src/text-node.c b/src/text-node.c index 1163734..b9093ba 100644 --- a/src/text-node.c +++ b/src/text-node.c @@ -4,6 +4,7 @@ #include "node.h" #include "pango/pango-layout.h" #include "pango/pango-types.h" +#include "rect.h" #include "text-node.h" UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA color, char *text) @@ -17,9 +18,6 @@ UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA col n->color = color; n->caret_index = -1; n->caret_node = NULL; - n->computed_dimensions_invalid = true; - n->computed_text_width = 0; - n->computed_text_height = 0; n->node.text = "text"; n->node.handle_proto = text_node_handle; node_attach(parent, (UINode*)n); @@ -50,7 +48,7 @@ int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p) n->layout = layout; n->pending_text = NULL; } - n->computed_dimensions_invalid = true; + node->parent->cached_computed_extents = false; } switch (ev) { @@ -63,22 +61,16 @@ int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p) cairo_fill(n->node.drw); break; } - case UI_EVENT_GET_WIDTH: /* through */ - case UI_EVENT_GET_HEIGHT: { - if (n->computed_dimensions_invalid) { - int w, h = 0; - pango_layout_get_size(n->layout, &w, &h); - n->computed_text_width = pango_units_to_double(w); - n->computed_text_height = pango_units_to_double(h); - n->computed_dimensions_invalid = false; + case UI_EVENT_GET_EXTENTS: { + if (!n->layout) { + break; } - if (ev == UI_EVENT_GET_WIDTH) { - *(double*)p = n->computed_text_width; - } - if (ev == UI_EVENT_GET_HEIGHT) { - *(double*)p = n->computed_text_height; - } - break; + int w, h = 0; + pango_layout_get_size(n->layout, &w, &h); + + ((UIRect*)p)->w = pango_units_to_double(w); + ((UIRect*)p)->h = pango_units_to_double(h); + return UI_NODE_COMPUTED_EXTENTS_CACHED; } case UI_EVENT_RELAYOUT: { if (!n->layout) { diff --git a/src/text-node.h b/src/text-node.h index ffacd9d..dc5b9de 100644 --- a/src/text-node.h +++ b/src/text-node.h @@ -15,9 +15,6 @@ typedef struct UITextNode { UIRGBA color; ssize_t caret_index; UINode *caret_node; - double computed_text_width; - double computed_text_height; - bool computed_dimensions_invalid; } UITextNode; int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p);