From f87dd1be8285df5a9cce082772242c649a628104 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Wed, 19 Feb 2025 09:35:45 -0800 Subject: [PATCH] per-box tags -> per-box tags key; cache (key -> tags_list), and (key * string -> theme_pattern) separately; accelerate all theme lookups --- src/raddbg/raddbg_core.c | 26 ++-- src/raddbg/raddbg_widgets.c | 6 +- src/ui/ui_basic_widgets.c | 4 +- src/ui/ui_core.c | 257 ++++++++++++++++++++++++++++-------- src/ui/ui_core.h | 59 ++++++++- 5 files changed, 274 insertions(+), 78 deletions(-) diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 32dda593..8f358979 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -8119,7 +8119,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The Vec4F32 box_background_color = {0}; if(box->flags & UI_BoxFlag_DrawBackground) { - box_background_color = ui_color_from_tags_name(box->tags, str8_lit("background")); + box_background_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("background")); } // rjf: draw background @@ -8141,7 +8141,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The // rjf: hot effect extension if(box->flags & UI_BoxFlag_DrawHotEffects) { - Vec4F32 hover_color = ui_color_from_tags_name(box->tags, str8_lit("hover")); + Vec4F32 hover_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("hover")); // rjf: brighten { @@ -8239,11 +8239,9 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The ellipses_run = fnt_push_run_from_string(scratch.arena, ellipses_font, ellipses_size, 0, box->tab_size, ellipses_raster_flags, str8_lit("...")); } dr_truncated_fancy_run_list(text_position, &box->display_fruns, max_x, ellipses_run); - if(box->flags & UI_BoxFlag_HasFuzzyMatchRanges) + if(box->flags & UI_BoxFlag_HasFuzzyMatchRanges) UI_TagF("pop") { - String8 tags[] = {str8_lit("pop")}; - String8Array tags_array = {tags, ArrayCount(tags)}; - Vec4F32 match_color = ui_color_from_tags_name(tags_array, str8_lit("background")); + Vec4F32 match_color = ui_color_from_tags_key_name(ui_top_tags_key(), str8_lit("background")); dr_truncated_fancy_run_fuzzy_matches(text_position, &box->display_fruns, max_x, &box->fuzzy_match_ranges, match_color); } } @@ -8333,7 +8331,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The // rjf: draw border if(b->flags & UI_BoxFlag_DrawBorder) { - Vec4F32 border_color = ui_color_from_tags_name(box->tags, str8_lit("border")); + Vec4F32 border_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("border")); Rng2F32 b_border_rect = pad_2f32(b->rect, 1.f); R_Rect2DInst *inst = dr_rect(b_border_rect, border_color, 0, 1.f, 1.f); MemoryCopyArray(inst->corner_radii, b->corner_radii); @@ -8341,7 +8339,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The // rjf: hover effect if(b->flags & UI_BoxFlag_DrawHotEffects) { - Vec4F32 color = ui_color_from_tags_name(box->tags, str8_lit("hover")); + Vec4F32 color = ui_color_from_tags_key_name(box->tags_key, str8_lit("hover")); color.w *= b->hot_t; R_Rect2DInst *inst = dr_rect(b_border_rect, color, 0, 1.f, 1.f); inst->colors[Corner_01].w *= 0.2f; @@ -8360,7 +8358,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The // rjf: draw sides if(b->flags & UI_BoxFlag_DrawSideTop|UI_BoxFlag_DrawSideBottom|UI_BoxFlag_DrawSideLeft|UI_BoxFlag_DrawSideRight) { - Vec4F32 border_color = ui_color_from_tags_name(box->tags, str8_lit("border")); + Vec4F32 border_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("border")); Rng2F32 r = b->rect; F32 half_thickness = 1.f; F32 softness = 0.f; @@ -8385,7 +8383,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The // rjf: draw focus overlay if(b->flags & UI_BoxFlag_Clickable && !(b->flags & UI_BoxFlag_DisableFocusOverlay) && b->focus_hot_t > 0.01f) { - Vec4F32 color = ui_color_from_tags_name(box->tags, str8_lit("focus")); + Vec4F32 color = ui_color_from_tags_key_name(box->tags_key, str8_lit("focus")); color.w *= 0.09f*b->focus_hot_t; R_Rect2DInst *inst = dr_rect(b->rect, color, 0, 0, 0.f); MemoryCopyArray(inst->corner_radii, b->corner_radii); @@ -8400,7 +8398,7 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The rect = pad_2f32(rect, 1.f); rect = intersect_2f32(window_rect, rect); } - Vec4F32 color = ui_color_from_tags_name(box->tags, str8_lit("focus")); + Vec4F32 color = ui_color_from_tags_key_name(box->tags_key, str8_lit("focus")); color.w *= b->focus_active_t; R_Rect2DInst *inst = dr_rect(rect, color, 0, 1.f, 1.f); MemoryCopyArray(inst->corner_radii, b->corner_radii); @@ -8453,11 +8451,9 @@ ws->cfg_palettes[RD_PaletteCode_##name].selection = current->colors[RD_The } //- rjf: draw border/overlay color to signify error - if(ws->error_t > 0.01f) + if(ws->error_t > 0.01f) UI_TagF("bad") { - String8 tags[] = {str8_lit("bad")}; - String8Array tags_array = {tags, ArrayCount(tags)}; - Vec4F32 color = ui_color_from_tags_name(tags_array, str8_lit("text")); + Vec4F32 color = ui_color_from_name(str8_lit("text")); color.w *= ws->error_t; Rng2F32 rect = os_client_rect_from_window(ws->os); dr_rect(pad_2f32(rect, 24.f), color, 0, 16.f, 12.f); diff --git a/src/raddbg/raddbg_widgets.c b/src/raddbg/raddbg_widgets.c index 0088425b..1b83745d 100644 --- a/src/raddbg/raddbg_widgets.c +++ b/src/raddbg/raddbg_widgets.c @@ -1046,12 +1046,10 @@ internal UI_BOX_CUSTOM_DRAW(rd_thread_box_draw_extensions) } // rjf: locked icon on frozen threads - if(u->is_frozen) + if(u->is_frozen) UI_TagF("bad") { F32 lock_icon_off = ui_top_font_size()*0.2f; - String8 tags[] = {str8_lit("bad")}; - String8Array tags_array = {tags, ArrayCount(tags)}; - Vec4F32 color = ui_color_from_tags_name(tags_array, str8_lit("text")); + Vec4F32 color = ui_color_from_name(str8_lit("text")); dr_text(rd_font_from_slot(RD_FontSlot_Icons), box->font_size, 0, 0, FNT_RasterFlag_Smooth, v2f32((box->rect.x0 + box->rect.x1)/2 + lock_icon_off/2, diff --git a/src/ui/ui_basic_widgets.c b/src/ui/ui_basic_widgets.c index 9ac55cf7..2917ed6a 100644 --- a/src/ui/ui_basic_widgets.c +++ b/src/ui/ui_basic_widgets.c @@ -134,9 +134,9 @@ internal UI_BOX_CUSTOM_DRAW(ui_line_edit_draw) FNT_Tag font = box->font; F32 font_size = box->font_size; F32 tab_size = box->tab_size; - Vec4F32 cursor_color = ui_color_from_tags_name(box->tags, str8_lit("cursor")); + Vec4F32 cursor_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("cursor")); cursor_color.w *= box->parent->parent->focus_active_t; - Vec4F32 select_color = ui_color_from_tags_name(box->tags, str8_lit("selection")); + Vec4F32 select_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("selection")); select_color.w *= 0.1f*(box->parent->parent->focus_active_t*0.2f + 0.8f); Vec2F32 text_position = ui_box_text_position(box); String8 edited_string = draw_data->edited_string; diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 0240d53c..074c0439 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -797,6 +797,11 @@ ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, U ui_state->ctx_menu_changed = 0; ui_state->default_animation_rate = 1 - pow_f32(2, (-80.f * ui_state->animation_dt)); ui_state->tooltip_can_overflow_window = 0; + ui_state->tags_key_stack_top = ui_state->tags_key_stack_free = 0; + ui_state->tags_cache_slots_count = 512; + ui_state->tags_cache_slots = push_array(ui_build_arena(), UI_TagsCacheSlot, ui_state->tags_cache_slots_count); + ui_state->theme_pattern_cache_slots_count = 512; + ui_state->theme_pattern_cache_slots = push_array(ui_build_arena(), UI_ThemePatternCacheSlot, ui_state->theme_pattern_cache_slots_count); } //- rjf: prune unused animation nodes @@ -2023,7 +2028,7 @@ ui_begin_ctx_menu(UI_Key key) ui_state->ctx_menu_root->flags |= UI_BoxFlag_Clip; ui_state->ctx_menu_root->flags |= UI_BoxFlag_Clickable; ui_state->ctx_menu_root->corner_radii[Corner_00] = ui_state->ctx_menu_root->corner_radii[Corner_01] = ui_state->ctx_menu_root->corner_radii[Corner_10] = ui_state->ctx_menu_root->corner_radii[Corner_11] = ui_top_font_size()*0.25f; - ui_state->ctx_menu_root->tags = ui_top_tags(); + ui_state->ctx_menu_root->tags_key = ui_top_tags_key(); ui_state->ctx_menu_root->palette = ui_top_palette(); ui_state->ctx_menu_root->blur_size = ui_top_blur_size(); ui_spacer(ui_em(1.f, 1.f)); @@ -2198,31 +2203,17 @@ ui_build_palette_(UI_Palette *base, UI_Palette *overrides) return palette; } -//- rjf: tag gathering +//- rjf: current style tags key -internal String8Array -ui_top_tags(void) +internal UI_Key +ui_top_tags_key(void) { - if(ui_state->current_gen_tags_gen != ui_state->tag_stack.gen) + UI_Key key = ui_key_zero(); + if(ui_state->tags_key_stack_top != 0) { - ui_state->current_gen_tags_gen = ui_state->tag_stack.gen; - Temp scratch = scratch_begin(0, 0); - String8List tags = {0}; - for(UI_TagNode *n = ui_state->tag_stack.top; n != 0; n = n->next) - { - if(n->v.size == 1 && n->v.str[0] == '.') - { - break; - } - if(n->v.size != 0) - { - str8_list_push(ui_build_arena(), &tags, push_str8_copy(ui_build_arena(), n->v)); - } - } - ui_state->current_gen_tags = str8_array_from_list(ui_build_arena(), &tags); - scratch_end(scratch); + key = ui_state->tags_key_stack_top->key; } - return ui_state->current_gen_tags; + return key; } //- rjf: theme color lookups @@ -2230,49 +2221,94 @@ ui_top_tags(void) internal Vec4F32 ui_color_from_name(String8 name) { - Vec4F32 result = ui_color_from_tags_name(ui_top_tags(), name); + Vec4F32 result = ui_color_from_tags_key_name(ui_top_tags_key(), name); return result; } internal Vec4F32 -ui_color_from_tags_name(String8Array tags, String8 name) +ui_color_from_tags_key_name(UI_Key key, String8 name) { Vec4F32 result = {0}; { - UI_Theme *theme = ui_state->theme; - UI_ThemePattern *pattern = 0; - U64 best_match_count = 0; - for(U64 idx = 0; idx < theme->patterns_count; idx += 1) + //- rjf: compute final key, mixing (tags_key, name) + UI_Key final_key = ui_key_from_string(key, name); + + //- rjf: map to existing node + U64 slot_idx = final_key.u64[0]%ui_state->theme_pattern_cache_slots_count; + UI_ThemePatternCacheSlot *slot = &ui_state->theme_pattern_cache_slots[slot_idx]; + UI_ThemePatternCacheNode *node = 0; + for(UI_ThemePatternCacheNode *n = slot->first; + n != 0; + n = n->next) { - UI_ThemePattern *p = &theme->patterns[idx]; - U64 match_count = 0; - B32 name_matches = 0; - for EachIndex(key_tags_idx, tags.count+1) + if(ui_key_match(n->key, final_key)) { - String8 key_string = key_tags_idx < tags.count ? tags.v[key_tags_idx] : name; - for EachIndex(p_tags_idx, p->tags.count) + node = n; + } + } + + //- rjf: no node? create + if(node == 0) + { + // rjf: map tags_key (without name) -> full list of tags + String8Array tags = {0}; + { + U64 tags_cache_slot_idx = key.u64[0]%ui_state->tags_cache_slots_count; + UI_TagsCacheSlot *tags_cache_slot = &ui_state->tags_cache_slots[tags_cache_slot_idx]; + for(UI_TagsCacheNode *n = tags_cache_slot->first; n != 0; n = n->next) { - if(str8_match(p->tags.v[p_tags_idx], key_string, 0)) + if(ui_key_match(n->key, key)) { - name_matches = (key_tags_idx == tags.count); - match_count += 1; + tags = n->tags; break; } } } - if(name_matches && match_count > best_match_count) + + // rjf: map tags to theme pattern + UI_Theme *theme = ui_state->theme; + UI_ThemePattern *pattern = 0; + U64 best_match_count = 0; + for(U64 idx = 0; idx < theme->patterns_count; idx += 1) { - pattern = p; - best_match_count = match_count; - } - if(match_count == tags.count+1) - { - break; + UI_ThemePattern *p = &theme->patterns[idx]; + U64 match_count = 0; + B32 name_matches = 0; + for EachIndex(key_tags_idx, tags.count+1) + { + String8 key_string = key_tags_idx < tags.count ? tags.v[key_tags_idx] : name; + for EachIndex(p_tags_idx, p->tags.count) + { + if(str8_match(p->tags.v[p_tags_idx], key_string, 0)) + { + name_matches = (key_tags_idx == tags.count); + match_count += 1; + break; + } + } + } + if(name_matches && match_count > best_match_count) + { + pattern = p; + best_match_count = match_count; + } + if(match_count == tags.count+1) + { + break; + } } + + // rjf: store in (key, name) -> (pattern) cache + node = push_array(ui_build_arena(), UI_ThemePatternCacheNode, 1); + SLLQueuePush(slot->first, slot->last, node); + node->key = final_key; + node->pattern = pattern; } - if(pattern != 0) + + //- rjf: grab resultant color + if(node != 0 && node->pattern != 0) { - result = pattern->linear; + result = node->pattern->linear; } } return result; @@ -2438,7 +2474,11 @@ ui_build_box_from_key(UI_BoxFlags flags, UI_Key key) box->text_padding = ui_state->text_padding_stack.top->v; box->hover_cursor = ui_state->hover_cursor_stack.top->v; box->custom_draw = 0; - box->tags = ui_top_tags(); + box->tags_key = ui_key_zero(); + if(ui_state->tags_key_stack_top != 0) + { + box->tags_key = ui_state->tags_key_stack_top->key; + } } //- rjf: auto-pop all stacks @@ -3145,7 +3185,7 @@ node->v = new_value;\ SLLStackPush(state->name_lower##_stack.top, node);\ if(node->next == &state->name_lower##_nil_stack_top)\ {\ -state->name_lower##_stack.bottom_val = (new_value);\ +state->name_lower##_stack.bottom_val = (node->v);\ }\ state->name_lower##_stack.auto_pop = 0;\ state->name_lower##_stack.gen += 1;\ @@ -3173,13 +3213,124 @@ state->name_lower##_stack.auto_pop = 1;\ state->name_lower##_stack.gen += 1;\ return old_value; +internal void +ui__push_tags_key_from_appended_string(String8 string) +{ + // rjf: generate new key, by combining hash of this new string with the top + // of the tags key stack + UI_Key seed_key = {0}; + if(ui_state->tags_key_stack_top != 0) + { + seed_key = ui_state->tags_key_stack_top->key; + } + UI_Key key = seed_key; + if(!str8_match(str8_lit("."), string, 0) && string.size > 0) + { + key = ui_key_from_string(seed_key, string); + } + + // rjf: push this new key onto the stack + { + UI_TagsKeyStackNode *node = ui_state->tags_key_stack_free; + if(node != 0) + { + SLLStackPop(ui_state->tags_key_stack_free); + } + else + { + node = push_array(ui_build_arena(), UI_TagsKeyStackNode, 1); + } + SLLStackPush(ui_state->tags_key_stack_top, node); + node->key = key; + } + + // rjf: store in tags cache + U64 slot_idx = key.u64[0] % ui_state->tags_cache_slots_count; + UI_TagsCacheSlot *slot = &ui_state->tags_cache_slots[slot_idx]; + UI_TagsCacheNode *node = 0; + for(UI_TagsCacheNode *n = slot->first; n != 0; n = n->next) + { + if(ui_key_match(n->key, key)) + { + node = n; + break; + } + } + if(node == 0) + { + Temp scratch = scratch_begin(0, 0); + String8List tags = {0}; + if(!str8_match(string, str8_lit("."), 0)) + { + if(string.size != 0) + { + str8_list_push(scratch.arena, &tags, push_str8_copy(ui_build_arena(), string)); + } + for(UI_TagNode *n = ui_state->tag_stack.top; n != 0; n = n->next) + { + if(n->v.size == 1 && n->v.str[0] == '.') + { + break; + } + if(n->v.size != 0) + { + str8_list_push(scratch.arena, &tags, push_str8_copy(ui_build_arena(), n->v)); + } + } + } + node = push_array(ui_build_arena(), UI_TagsCacheNode, 1); + SLLQueuePush(slot->first, slot->last, node); + node->key = key; + node->tags = str8_array_from_list(ui_build_arena(), &tags); + scratch_end(scratch); + } +} + +internal void +ui__pop_tags_key(void) +{ + if(ui_state->tags_key_stack_top != 0) + { + UI_TagsKeyStackNode *popped = ui_state->tags_key_stack_top; + SLLStackPop(ui_state->tags_key_stack_top); + SLLStackPush(ui_state->tags_key_stack_free, popped); + } +} + //- rjf: manual implementations -internal String8 ui_top_tag(void) { UI_StackTopImpl(ui_state, Tag, tag) } -internal String8 ui_bottom_tag(void) { UI_StackBottomImpl(ui_state, Tag, tag) } -internal String8 ui_push_tag(String8 v) { UI_StackPushImpl(ui_state, Tag, tag, String8, push_str8_copy(ui_build_arena(), v)) } -internal String8 ui_pop_tag(void) { UI_StackPopImpl(ui_state, Tag, tag) } -internal String8 ui_set_next_tag(String8 v) { UI_StackSetNextImpl(ui_state, Tag, tag, String8, push_str8_copy(ui_build_arena(), v)) } +internal String8 +ui_top_tag(void) +{ + UI_StackTopImpl(ui_state, Tag, tag) +} + +internal String8 +ui_bottom_tag(void) +{ + UI_StackBottomImpl(ui_state, Tag, tag) +} + +internal String8 +ui_push_tag(String8 v) +{ + ui__push_tags_key_from_appended_string(v); + UI_StackPushImpl(ui_state, Tag, tag, String8, push_str8_copy(ui_build_arena(), v)) +} + +internal String8 +ui_pop_tag(void) +{ + ui__pop_tags_key(); + UI_StackPopImpl(ui_state, Tag, tag) +} + +internal String8 +ui_set_next_tag(String8 v) +{ + ui__push_tags_key_from_appended_string(v); + UI_StackSetNextImpl(ui_state, Tag, tag, String8, push_str8_copy(ui_build_arena(), v)) +} //- rjf: helpers diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index a020f576..ade38b1c 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -440,7 +440,7 @@ struct UI_Box //- rjf: per-build equipment UI_Key key; UI_BoxFlags flags; - String8Array tags; + UI_Key tags_key; String8 string; UI_TextAlign text_align; Vec2F32 fixed_position; @@ -646,6 +646,49 @@ struct UI_AnimSlot //////////////////////////////// //~ rjf: State Types +//- rjf: cache for mapping 64-bit key -> array of tags + +typedef struct UI_TagsCacheNode UI_TagsCacheNode; +struct UI_TagsCacheNode +{ + UI_TagsCacheNode *next; + UI_Key key; + String8Array tags; +}; + +typedef struct UI_TagsCacheSlot UI_TagsCacheSlot; +struct UI_TagsCacheSlot +{ + UI_TagsCacheNode *first; + UI_TagsCacheNode *last; +}; + +typedef struct UI_TagsKeyStackNode UI_TagsKeyStackNode; +struct UI_TagsKeyStackNode +{ + UI_TagsKeyStackNode *next; + UI_Key key; +}; + +//- rjf: cache for mapping 64-bit key * string -> theme pattern + +typedef struct UI_ThemePatternCacheNode UI_ThemePatternCacheNode; +struct UI_ThemePatternCacheNode +{ + UI_ThemePatternCacheNode *next; + UI_Key key; + UI_ThemePattern *pattern; +}; + +typedef struct UI_ThemePatternCacheSlot UI_ThemePatternCacheSlot; +struct UI_ThemePatternCacheSlot +{ + UI_ThemePatternCacheNode *first; + UI_ThemePatternCacheNode *last; +}; + +//- rjf: cache for mapping 64-bit key -> box + typedef struct UI_BoxHashSlot UI_BoxHashSlot; struct UI_BoxHashSlot { @@ -653,6 +696,8 @@ struct UI_BoxHashSlot UI_Box *hash_last; }; +//- rjf: main state bundle + typedef struct UI_State UI_State; struct UI_State { @@ -683,6 +728,12 @@ struct UI_State B32 tooltip_can_overflow_window; String8Array current_gen_tags; U64 current_gen_tags_gen; + UI_TagsKeyStackNode *tags_key_stack_top; + UI_TagsKeyStackNode *tags_key_stack_free; + U64 tags_cache_slots_count; + UI_TagsCacheSlot *tags_cache_slots; + U64 theme_pattern_cache_slots_count; + UI_ThemePatternCacheSlot *theme_pattern_cache_slots; //- rjf: build phase output UI_Box *root; @@ -911,12 +962,12 @@ internal void ui_set_auto_focus_hot_key(UI_Key key); internal UI_Palette * ui_build_palette_(UI_Palette *base, UI_Palette *overrides); #define ui_build_palette(base, ...) ui_build_palette_((base), &(UI_Palette){.text = v4f32(0, 0, 0, 0), __VA_ARGS__}) -//- rjf: tag gathering -internal String8Array ui_top_tags(void); +//- rjf: current style tags key +internal UI_Key ui_top_tags_key(void); //- rjf: theme color lookups internal Vec4F32 ui_color_from_name(String8 name); -internal Vec4F32 ui_color_from_tags_name(String8Array tags, String8 name); +internal Vec4F32 ui_color_from_tags_key_name(UI_Key key, String8 name); //- rjf: box node construction internal UI_Box * ui_build_box_from_key(UI_BoxFlags flags, UI_Key key);