per-box tags -> per-box tags key; cache (key -> tags_list), and (key * string -> theme_pattern) separately; accelerate all theme lookups

This commit is contained in:
Ryan Fleury
2025-02-19 09:35:45 -08:00
parent 9805f8cd20
commit f87dd1be82
5 changed files with 274 additions and 78 deletions
+11 -15
View File
@@ -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);
+2 -4
View File
@@ -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,
+2 -2
View File
@@ -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;
+204 -53
View File
@@ -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
+55 -4
View File
@@ -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);