diff --git a/src/base/base_util.h b/src/base/base_util.h index 8e4fa106..2523abe7 100644 --- a/src/base/base_util.h +++ b/src/base/base_util.h @@ -277,7 +277,7 @@ Inline void RemoveDictEntry(Dict *dict, DictEntry *entry) dict->last = prev; } } - /* Insert into free list */ + /* Add to free list */ { entry->next = dict->first_free; dict->first_free = entry; diff --git a/src/glyph_cache/glyph_cache.c b/src/glyph_cache/glyph_cache.c index 497c49f8..145c00a5 100644 --- a/src/glyph_cache/glyph_cache.c +++ b/src/glyph_cache/glyph_cache.c @@ -31,42 +31,88 @@ u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc) GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size) { GC_Run result = Zi; - TempArena scratch = BeginScratch(arena); - Arena *perm = PermArena(); - - u64 codepoints_count = 0; - u32 *codepoints = 0; + if (str.len > 0) { - String32 str32 = String32FromString(scratch.arena, str); - codepoints_count = str32.len; - codepoints = str32.text; - } + TempArena scratch = BeginScratch(arena); + Arena *perm = PermArena(); - ////////////////////////////// - //- Grab glyphs from cache - - u64 ready_glyphs_count = 0; - GC_Glyph **ready_glyphs = PushStructsNoZero(scratch.arena, GC_Glyph *, codepoints_count); - - u32 *uncached_codepoints = PushStructsNoZero(scratch.arena, u32, codepoints_count); - u64 uncached_codepoints_count = 0; - - /* TODO: Include advances for glyphs in run that have rasterized but not finished uploading to atlas */ - u64 pending_glyphs_count = 0; - { - if (codepoints_count > 0) + u64 codepoints_count = 0; + u32 *codepoints = 0; { - Lock lock = LockS(&GC.glyphs_mutex); - { - i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy); - for (u64 codepoint_idx = 0; codepoint_idx < codepoints_count; ++codepoint_idx) - { - u32 codepoint = codepoints[codepoint_idx]; + String32 str32 = String32FromString(scratch.arena, str); + codepoints_count = str32.len; + codepoints = str32.text; + } + ////////////////////////////// + //- Grab glyphs from cache + + u64 ready_glyphs_count = 0; + GC_Glyph **ready_glyphs = PushStructsNoZero(scratch.arena, GC_Glyph *, codepoints_count); + + u32 *uncached_codepoints = PushStructsNoZero(scratch.arena, u32, codepoints_count); + u64 uncached_codepoints_count = 0; + + /* TODO: Include advances for glyphs in run that have rasterized but not finished uploading to atlas */ + u64 pending_glyphs_count = 0; + { + if (codepoints_count > 0) + { + Lock lock = LockS(&GC.glyphs_mutex); + { + i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy); + for (u64 codepoint_idx = 0; codepoint_idx < codepoints_count; ++codepoint_idx) + { + u32 codepoint = codepoints[codepoint_idx]; + + GC_GlyphDesc desc = Zi; + desc.font = font; + desc.font_size = font_size; + desc.codepoint = codepoint; + + u64 hash = GC_HashFromGlyphDesc(desc); + GC_GlyphBin *bin = &GC.glyph_bins[hash % countof(GC.glyph_bins)]; + GC_Glyph *glyph = bin->first; + for (; glyph; glyph = glyph->next) + { + if (glyph->hash == hash) break; + } + + if (glyph == 0) + { + uncached_codepoints[uncached_codepoints_count] = codepoint; + uncached_codepoints_count += 1; + } + else if (completion < Atomic64Fetch(&glyph->async_copy_completion_target)) + { + pending_glyphs_count += 1; + } + else + { + ready_glyphs[ready_glyphs_count] = glyph; + ready_glyphs_count += 1; + } + } + } + Unlock(&lock); + } + } + + ////////////////////////////// + //- Create cache entries + + u64 submit_cmds_count = 0; + GC_Cmd *submit_cmds = PushStructsNoZero(scratch.arena, GC_Cmd, uncached_codepoints_count); + if (uncached_codepoints_count > 0) + { + Lock lock = LockE(&GC.glyphs_mutex); + { + for (u64 uncached_codepoint_idx = 0; uncached_codepoint_idx < uncached_codepoints_count; ++uncached_codepoint_idx) + { GC_GlyphDesc desc = Zi; desc.font = font; desc.font_size = font_size; - desc.codepoint = codepoint; + desc.codepoint = uncached_codepoints[uncached_codepoint_idx]; u64 hash = GC_HashFromGlyphDesc(desc); GC_GlyphBin *bin = &GC.glyph_bins[hash % countof(GC.glyph_bins)]; @@ -78,140 +124,98 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size if (glyph == 0) { - uncached_codepoints[uncached_codepoints_count] = codepoint; - uncached_codepoints_count += 1; - } - else if (completion < Atomic64Fetch(&glyph->async_copy_completion_target)) - { - pending_glyphs_count += 1; - } - else - { - ready_glyphs[ready_glyphs_count] = glyph; - ready_glyphs_count += 1; + glyph = PushStruct(perm, GC_Glyph); + glyph->desc = desc; + glyph->hash = hash; + Atomic64FetchSet(&glyph->async_copy_completion_target, I64Max); + SllStackPush(bin->first, glyph); + /* Create cmd */ + { + GC_Cmd *cmd = &submit_cmds[submit_cmds_count]; + cmd->glyph = glyph; + ++submit_cmds_count; + } } } } Unlock(&lock); } - } - ////////////////////////////// - //- Create cache entries + ////////////////////////////// + //- Submit cmds - u64 submit_cmds_count = 0; - GC_Cmd *submit_cmds = PushStructsNoZero(scratch.arena, GC_Cmd, uncached_codepoints_count); - if (uncached_codepoints_count > 0) - { - Lock lock = LockE(&GC.glyphs_mutex); + if (submit_cmds_count > 0) { - for (u64 uncached_codepoint_idx = 0; uncached_codepoint_idx < uncached_codepoints_count; ++uncached_codepoint_idx) + Lock lock = LockE(&GC.submit.mutex); + for (u64 cmd_idx = 0; cmd_idx < submit_cmds_count; ++cmd_idx) { - GC_GlyphDesc desc = Zi; - desc.font = font; - desc.font_size = font_size; - desc.codepoint = uncached_codepoints[uncached_codepoint_idx]; - - u64 hash = GC_HashFromGlyphDesc(desc); - GC_GlyphBin *bin = &GC.glyph_bins[hash % countof(GC.glyph_bins)]; - GC_Glyph *glyph = bin->first; - for (; glyph; glyph = glyph->next) + GC_Cmd *src = &submit_cmds[cmd_idx]; + GC_CmdNode *n = GC.submit.first_free; + if (n) { - if (glyph->hash == hash) break; + SllStackPop(GC.submit.first_free); + ZeroStruct(n); } - - if (glyph == 0) + else { - glyph = PushStruct(perm, GC_Glyph); - glyph->desc = desc; - glyph->hash = hash; - Atomic64FetchSet(&glyph->async_copy_completion_target, I64Max); - SllStackPush(bin->first, glyph); - /* Create cmd */ - { - GC_Cmd *cmd = &submit_cmds[submit_cmds_count]; - cmd->glyph = glyph; - ++submit_cmds_count; - } + n = PushStruct(perm, GC_CmdNode); } + n->cmd = *src; + GC.submit.count += 1; + SllQueuePush(GC.submit.first, GC.submit.last, n); } + Unlock(&lock); + SignalAsyncTick(); } - Unlock(&lock); - } - ////////////////////////////// - //- Submit cmds + ////////////////////////////// + //- Create run from glyphs - if (submit_cmds_count > 0) - { - Lock lock = LockE(&GC.submit.mutex); - for (u64 cmd_idx = 0; cmd_idx < submit_cmds_count; ++cmd_idx) + f32 baseline_pos = 0; + result.rects = PushStructs(arena, GC_RunRect, ready_glyphs_count); + result.rects_count = ready_glyphs_count; + for (u64 glyph_idx = 0; glyph_idx < ready_glyphs_count; ++glyph_idx) { - GC_Cmd *src = &submit_cmds[cmd_idx]; - GC_CmdNode *n = GC.submit.first_free; - if (n) + GC_Glyph *glyph = ready_glyphs[glyph_idx]; + GC_RunRect *rect = &result.rects[glyph_idx]; + + rect->tex = glyph->atlas->tex_ref; + rect->tex_slice = glyph->tex_slice; + rect->tex_slice_uv = glyph->tex_slice_uv; + + rect->baseline_pos = baseline_pos; + rect->advance = glyph->advance; + + rect->bounds = glyph->bounds; + + if (glyph_idx == 0) { - SllStackPop(GC.submit.first_free); - ZeroStruct(n); + result.bounds = rect->bounds; } else { - n = PushStruct(perm, GC_CmdNode); + result.bounds = UnionRng2(result.bounds, rect->bounds); } - n->cmd = *src; - GC.submit.count += 1; - SllQueuePush(GC.submit.first, GC.submit.last, n); + + baseline_pos += rect->advance; + result.baseline_length = MaxF32(result.baseline_length, baseline_pos); } - Unlock(&lock); - SignalAsyncTick(); - } - ////////////////////////////// - //- Create run from glyphs - - f32 baseline_pos = 0; - result.rects = PushStructs(arena, GC_RunRect, ready_glyphs_count); - result.rects_count = ready_glyphs_count; - for (u64 glyph_idx = 0; glyph_idx < ready_glyphs_count; ++glyph_idx) - { - GC_Glyph *glyph = ready_glyphs[glyph_idx]; - GC_RunRect *rect = &result.rects[glyph_idx]; - - rect->tex = glyph->atlas->tex_ref; - rect->tex_slice = glyph->tex_slice; - rect->tex_slice_uv = glyph->tex_slice_uv; - - rect->baseline_pos = baseline_pos; - rect->advance = glyph->advance; - - rect->bounds = glyph->bounds; - - if (glyph_idx == 0) + if (ready_glyphs_count > 0) { - result.bounds = rect->bounds; - } - else - { - result.bounds = UnionRng2(result.bounds, rect->bounds); + GC_Glyph *glyph = ready_glyphs[0]; + result.font_size = glyph->font_size; + result.font_ascent = glyph->font_ascent; + result.font_descent = glyph->font_descent; + result.font_cap = glyph->font_cap; } - baseline_pos += rect->advance; - result.baseline_length = MaxF32(result.baseline_length, baseline_pos); - } - - if (ready_glyphs_count > 0) - { - GC_Glyph *glyph = ready_glyphs[0]; - result.font_size = glyph->font_size; - result.font_ascent = glyph->font_ascent; - result.font_descent = glyph->font_descent; - result.font_cap = glyph->font_cap; + EndScratch(scratch); } // result.ready = uncached_codepoints_count == 0 && pending_glyphs_count == 0; result.ready = 1; - EndScratch(scratch); return result; } diff --git a/src/net/net.c b/src/net/net.c index db33bf05..be47badd 100644 --- a/src/net/net.c +++ b/src/net/net.c @@ -286,7 +286,7 @@ N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_ /* FIXME: Ensure chunk_count > 0 */ ma->is_reliable = is_reliable; - /* Insert into channel list */ + /* Add to channel list */ ma->touched_ns = now_ns; if (channel->most_recent_msg_assembler) { @@ -299,7 +299,7 @@ N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_ } channel->most_recent_msg_assembler = ma; - /* Insert into lookup table */ + /* Add to lookup table */ u64 hash = N_HashFromMsg(channel->id, msg_id); ma->hash = hash; N_MsgAssemblerLookupBin *bin = &host->msg_assembler_lookup_bins[hash % host->num_msg_assembler_lookup_bins]; diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 526d946e..d4349af0 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -19,7 +19,12 @@ void V_Shutdown(void) V_Frame *V_CurrentFrame(void) { - return &V.frames[V.current_frame_idx]; + return &V.frames[V.current_frame_tick % countof(V.frames)]; +} + +V_Frame *V_LastFrame(void) +{ + return &V.frames[(V.current_frame_tick - 1) % countof(V.frames)]; } S_Cmd *V_PushSimCmd(S_CmdKind kind) @@ -421,19 +426,13 @@ void V_TickForever(WaveLaneCtx *lane) b32 shutdown = 0; while (!shutdown) { - u64 last_frame_idx = V.current_frame_idx; - u64 frame_idx = last_frame_idx + 1; - if (frame_idx >= countof(V.frames)) - { - frame_idx = 0; - } - V.current_frame_idx = frame_idx; - V_Frame *last_frame = &V.frames[last_frame_idx]; - V_Frame *frame = &V.frames[frame_idx]; - ////////////////////////////// //- Begin frame + V.current_frame_tick += 1; + V_Frame *last_frame = V_LastFrame(); + V_Frame *frame = V_CurrentFrame(); + { Arena *old_arena = frame->arena; Arena *old_dverts_arena = frame->dverts_arena; @@ -462,8 +461,8 @@ void V_TickForever(WaveLaneCtx *lane) frame->edit_camera_zoom = last_frame->edit_camera_zoom; frame->look = last_frame->look; + frame->tick = V.current_frame_tick; frame->time_ns = TimeNs(); - frame->tick = last_frame->tick + 1; frame->dt_ns = frame->time_ns - last_frame->time_ns; frame->dt = SecondsFromNs(frame->dt_ns); @@ -790,20 +789,29 @@ void V_TickForever(WaveLaneCtx *lane) /* TODO: Remove this (testing) */ //- Init test layout - if (!V.root_space) + if (!V.root_panel) { - V_Space *space = PushStruct(perm, V_Space); - space->axis = Axis_X; - V.root_space = space; - - space->pref_size[Axis_X] = UI_GROW(1, 0); - space->pref_size[Axis_Y] = UI_GROW(1, 0); + { + V_Panel *panel = PushStruct(perm, V_Panel); + panel->axis = Axis_X; + // panel->pref_size[Axis_X] = UI_PIX(frame->ui_dims.x, 0); + // panel->pref_size[Axis_Y] = UI_PIX(frame->ui_dims.y, 0); + panel->key = UI_TransKey(); /* TODO: Don't use transient keys for panels */ + panel->pref_size[Axis_X] = UI_GROW(1, 0); + panel->pref_size[Axis_Y] = UI_GROW(1, 0); + panel->is_organizing_panel = 1; + V.root_panel = panel; + } { V_Panel *panel = PushStruct(perm, V_Panel); - panel->space = space; - DllQueuePushNP(space->first_panel, space->last_panel, panel, next_in_space, prev_in_space); - ++space->panels_count; + panel->parent = V.root_panel; + panel->axis = !panel->parent->axis; + panel->key = UI_TransKey(); /* TODO: Don't use transient keys for panels */ + panel->pref_size[Axis_X] = UI_GROW(1, 0); + panel->pref_size[Axis_Y] = UI_GROW(1, 0); + DllQueuePush(panel->parent->first, panel->parent->last, panel); + ++panel->parent->count; { V_Window *window = PushStruct(perm, V_Window); @@ -824,9 +832,13 @@ void V_TickForever(WaveLaneCtx *lane) { V_Panel *panel = PushStruct(perm, V_Panel); - panel->space = space; - DllQueuePushNP(space->first_panel, space->last_panel, panel, next_in_space, prev_in_space); - ++space->panels_count; + panel->parent = V.root_panel; + panel->axis = !panel->parent->axis; + panel->key = UI_TransKey(); /* TODO: Don't use transient keys for panels */ + panel->pref_size[Axis_X] = UI_GROW(1, 0); + panel->pref_size[Axis_Y] = UI_GROW(1, 0); + DllQueuePush(panel->parent->first, panel->parent->last, panel); + ++panel->parent->count; { V_Window *window = PushStruct(perm, V_Window); @@ -837,6 +849,29 @@ void V_TickForever(WaveLaneCtx *lane) } } + // { + // V_Space *space = PushStruct(perm, V_Space); + // space->parent = V.root_space; + // space->axis = !space->parent->axis; + // space->pref_size[Axis_X] = UI_GROW(1, 0); + // space->pref_size[Axis_Y] = UI_GROW(1, 0); + // DllQueuePush(space->parent->first, space->parent->last, space); + // ++space->parent->count; + + // V_Panel *panel = PushStruct(perm, V_Panel); + // panel->space = space; + // DllQueuePushNP(space->first_panel, space->last_panel, panel, next_in_space, prev_in_space); + // ++space->panels_count; + + // { + // V_Window *window = PushStruct(perm, V_Window); + // window->panel = panel; + // window->is_viewport_window = 1; + // DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); + // ++panel->windows_count; + // } + // } + // { // V_Panel *panel = PushStruct(perm, V_Panel); // panel->space = space; @@ -855,55 +890,53 @@ void V_TickForever(WaveLaneCtx *lane) if (frame->is_editing) { - Struct(SpaceDfsNode) { SpaceDfsNode *next; b32 visited; V_Space *space; UI_Checkpoint cp; }; - SpaceDfsNode *first_space_dfs = PushStruct(frame->arena, SpaceDfsNode); - first_space_dfs->space = V.root_space; + Struct(PanelDfsNode) { PanelDfsNode *next; b32 visited; V_Panel *panel; UI_Checkpoint cp; }; + PanelDfsNode *first_panel_dfs = PushStruct(frame->arena, PanelDfsNode); + first_panel_dfs->panel = V.root_panel; - //- Iterate spaces - while (first_space_dfs) + //- Iterate panels + while (first_panel_dfs) { - SpaceDfsNode *space_dfs = first_space_dfs; - V_Space *space = space_dfs->space; - if (!space_dfs->visited) + PanelDfsNode *panel_dfs = first_panel_dfs; + V_Panel *panel = panel_dfs->panel; + if (!panel_dfs->visited) { - space_dfs->visited = 1; - for (V_Space *child = space->last; child; child = child->prev) + panel_dfs->visited = 1; + for (V_Panel *child = panel->last; child; child = child->prev) { - SpaceDfsNode *n = PushStruct(frame->arena, SpaceDfsNode); - n->space = child; - SllStackPush(first_space_dfs, n); + PanelDfsNode *n = PushStruct(frame->arena, PanelDfsNode); + n->panel = child; + SllStackPush(first_panel_dfs, n); } - UI_Key space_key = UI_TransKey(); - UI_SetNext(Width, space->pref_size[Axis_X]); - UI_SetNext(Width, space->pref_size[Axis_Y]); - if (space->axis == Axis_X) + UI_SetNext(Width, panel->pref_size[Axis_X]); + UI_SetNext(Height, panel->pref_size[Axis_Y]); + if (panel->axis == Axis_X) { - UI_BuildRowEx(space_key); + UI_BuildRowEx(panel->key); } else { - UI_BuildColumnEx(space_key); + UI_BuildColumnEx(panel->key); } - space_dfs->cp = UI_PushCP(space_key); + panel_dfs->cp = UI_PushCP(panel->key); - //- Build space panels - for (V_Panel *panel = space->first_panel; panel; panel = panel->next_in_space) + if (!panel->is_organizing_panel) { // UI_SetNext(Width, UI_SHRINK(0, 1)); - UI_SetNext(Width, UI_GROW(1, 0)); - UI_SetNext(Height, UI_GROW(1, 0)); + // UI_SetNext(Width, UI_GROW(1, 0)); + // UI_SetNext(Height, UI_GROW(1, 0)); UI_PushCP(UI_BuildColumn()); { i64 active_window_idx = ClampI64(panel->active_window_idx, 0, MaxI64(panel->windows_count - 1, 0)); - //- Build panel window tabs + //- Build window tabs f32 tab_spacing = 10; V_Window *active_window = 0; { - UI_SetNext(Width, UI_SHRINK(0, 0)); - UI_SetNext(Height, UI_SHRINK(0, 0)); - UI_PushCP(UI_BuildRow()); i64 window_idx = 0; + UI_SetNext(Width, UI_SHRINK(0, 1)); + UI_SetNext(Height, UI_SHRINK(0, 1)); + UI_PushCP(UI_BuildRow()); for (V_Window *window = panel->first_window; window; window = window->next_in_panel) { if (!window->is_viewport_window) @@ -920,8 +953,8 @@ void V_TickForever(WaveLaneCtx *lane) } UI_SetNext(BackgroundColor, VEC4(0.2, 0.2, 0.2, 1)); UI_SetNext(Border, 1); - UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 0)); - UI_SetNext(Height, UI_SHRINK(theme.text_padding_y, 0)); + UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 1)); + UI_SetNext(Height, UI_SHRINK(theme.text_padding_y, 1)); UI_SetNext(ChildAlignment, UI_Alignment_Center); UI_PushCP(UI_BuildRow()); { @@ -940,7 +973,7 @@ void V_TickForever(WaveLaneCtx *lane) } UI_PopCP(UI_TopCP()); } - //- Build active panel active_window + //- Build active window if (active_window && !active_window->is_viewport_window) { V_Window *window = active_window; @@ -968,13 +1001,24 @@ void V_TickForever(WaveLaneCtx *lane) } } UI_PopCP(UI_TopCP()); - } } else { - UI_PopCP(space_dfs->cp); - SllStackPop(first_space_dfs); + UI_PopCP(panel_dfs->cp); + SllStackPop(first_panel_dfs); + + //- Build divider + if (panel->next != 0) + { + UI_Key divider_key = UI_TransKey(); + UI_SetNext(BackgroundColor, Color_Cyan); + // UI_SetNext(Border, 2); + UI_SetNext(AxisSize, UI_GROW(1, 1), .axis = panel->axis); + UI_SetNext(AxisSize, UI_PIX(5, 1), .axis = !panel->axis); + /* FIXME: Non-transient key */ + UI_BuildBoxEx(divider_key); + } } } } diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 2ab146c2..34e19790 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -149,28 +149,21 @@ Struct(V_CommandsWidget) /* TODO: Move boolean fields into bitwise property flags */ -Struct(V_Space) -{ - V_Space *parent; - V_Space *next; - V_Space *prev; - V_Space *first; - V_Space *last; - - i64 panels_count; - struct V_Panel *first_panel; - struct V_Panel *last_panel; - - UI_Size pref_size[Axis_CountXY]; - - Axis axis; -}; - Struct(V_Panel) { - V_Space *space; - V_Panel *next_in_space; - V_Panel *prev_in_space; + V_Panel *parent; + V_Panel *next; + V_Panel *prev; + V_Panel *first; + V_Panel *last; + i64 count; + + UI_Key key; + + UI_Size pref_size[Axis_CountXY]; + Axis axis; + + b32 is_organizing_panel; i64 active_window_idx; i64 windows_count; @@ -277,12 +270,12 @@ Struct(V_Ctx) S_Lookup lookup; S_Key player_key; - V_Space *root_space; + V_Panel *root_panel; Atomic32 shutdown; Fence shutdown_complete; - u64 current_frame_idx; + i64 current_frame_tick; V_Frame frames[2]; }; @@ -298,6 +291,7 @@ void V_Shutdown(void); //~ Helpers V_Frame *V_CurrentFrame(void); +V_Frame *V_LastFrame(void); S_Cmd *V_PushSimCmd(S_CmdKind kind); String V_StringFromHotkey(Arena *arena, V_Hotkey hotkey); diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 2b1da1c1..adbf7fa5 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -1,4 +1,4 @@ -UI_State UI_state = Zi; +UI_Ctx UI = Zi; //////////////////////////////////////////////////////////// //~ Bootstrap @@ -18,6 +18,11 @@ GC_FontKey UI_GetDefaultFont(void) //////////////////////////////////////////////////////////// //~ Key helpers +b32 UI_MatchKey(UI_Key a, UI_Key b) +{ + return a.hash == b.hash; +} + UI_Key UI_KeyFromString(String str) { u64 top_tag = UI_UseTop(Tag); @@ -43,8 +48,7 @@ UI_Key UI_KeyF_(String fmt, ...) UI_Key UI_TransKey(void) { - UI_Frame *frame = UI_CurrentFrame(); - u64 seed = ++frame->transient_key_seed; + u64 seed = ++UI.transient_key_seed; UI_Key key = Zi; key.hash = RandU64FromSeed(seed); return key; @@ -52,16 +56,14 @@ UI_Key UI_TransKey(void) UI_Box *UI_BoxFromKey(UI_Key key) { - UI_State *g = &UI_state; UI_Box *box = 0; if (key.hash != 0) { - UI_BoxBin *bin = &g->box_bins[key.hash % UI_NumBoxLookupBins]; - for (UI_Box *tmp = bin->first; tmp; tmp = tmp->next_in_bin) + UI_BoxBin *bin = &UI.box_bins[key.hash % countof(UI.box_bins)]; + for (box = bin->first; box; box = box->next_in_bin) { - if (tmp->key.hash == key.hash) + if (UI_MatchKey(box->key, key)) { - box = tmp; break; } } @@ -69,6 +71,105 @@ UI_Box *UI_BoxFromKey(UI_Key key) return box; } +//////////////////////////////////////////////////////////// +//~ Iteration helpers + +UI_BoxIterResult UI_FirstBox(Arena *arena, UI_BoxIter *iter, UI_Key start_key) +{ + /* Free old stack */ + if (iter->first) + { + if (iter->last_free) + { + iter->last_free->next = iter->first; + } + else + { + iter->first_free = iter->first; + } + iter->last_free = iter->first; + iter->first = 0; + } + /* Create root dfs node */ + UI_BoxIterDfsNode *dfs = iter->first_free; + if (dfs) + { + SllQueuePop(iter->first_free, iter->last_free); + ZeroStruct(dfs); + } + else + { + dfs = PushStruct(arena, UI_BoxIterDfsNode); + } + dfs->box = UI_BoxFromKey(start_key); + iter->first = dfs; + return UI_NextBox(arena, iter); +} + +UI_BoxIterResult UI_NextBox(Arena *arena, UI_BoxIter *iter) +{ + UI_BoxIterResult result = Zi; + if (iter->first) + { + UI_BoxIterDfsNode *dfs = iter->first; + UI_Box *box = dfs->box; + if (!dfs->visited) + { + /* Pre order */ + dfs->visited = 1; + result.box = box; + result.pre = 1; + /* Push floating children to dfs stack */ + for (UI_Box *child = box->last; child; child = child->prev) + { + if (AnyBit(child->desc.flags, UI_BoxFlag_Floating)) + { + UI_BoxIterDfsNode *child_dfs = iter->first_free; + if (child_dfs) + { + SllQueuePop(iter->first_free, iter->last_free); + ZeroStruct(child_dfs); + } + else + { + child_dfs = PushStruct(arena, UI_BoxIterDfsNode); + } + child_dfs->box = child; + SllStackPush(iter->first, child_dfs); + } + } + /* Push non-floating children to dfs stack */ + for (UI_Box *child = box->last; child; child = child->prev) + { + if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) + { + UI_BoxIterDfsNode *child_dfs = iter->first_free; + if (child_dfs) + { + SllQueuePop(iter->first_free, iter->last_free); + ZeroStruct(child_dfs); + } + else + { + child_dfs = PushStruct(arena, UI_BoxIterDfsNode); + } + child_dfs->box = child; + SllStackPush(iter->first, child_dfs); + } + } + } + else + { + /* Post order */ + result.box = box; + result.pre = 0; + SllStackPop(iter->first); + SllQueuePush(iter->first_free, iter->last_free, dfs); + } + } + return result; +} + //////////////////////////////////////////////////////////// //~ String helpers @@ -410,7 +511,6 @@ UI_Key UI_BuildBoxEx(UI_Key key) return key; } - void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv) { UI_Frame *frame = UI_CurrentFrame(); @@ -430,7 +530,6 @@ void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv) UI_Report UI_ReportFromKey(UI_Key key) { - UI_State *g = &UI_state; UI_Report result = Zi; UI_Box *box = UI_BoxFromKey(key); @@ -447,38 +546,39 @@ UI_Report UI_ReportFromKey(UI_Key key) UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) { - UI_State *g = &UI_state; ////////////////////////////// //- Init persistent state - if (!g->box_arena) + if (!UI.box_arena) { - g->box_arena = AcquireArena(Gibi(64)); - g->box_bins = PushStructs(g->box_arena, UI_BoxBin, UI_NumBoxLookupBins); - - for (u64 i = 0; i < countof(g->frames); ++i) + UI.box_arena = AcquireArena(Gibi(64)); + /* Init frames */ + for (u64 i = 0; i < countof(UI.frames); ++i) { - UI_Frame *frame = &g->frames[i]; + UI_Frame *frame = &UI.frames[i]; frame->arena = AcquireArena(Gibi(64)); frame->rects_arena = AcquireArena(Gibi(64)); frame->gpu_arena = G_AcquireArena(); } + /* Init root box */ + { + UI_Box *box = PushStruct(UI.box_arena, UI_Box); + box->key = UI_RootKey; + UI.boxes_count += 1; + UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)]; + bin->first = box; + bin->last = box; + UI.root_box = box; + } } ////////////////////////////// //- Begin frame - u64 last_frame_idx = g->current_frame_idx; - u64 frame_idx = last_frame_idx + 1; - if (frame_idx >= countof(g->frames)) - { - frame_idx = 0; - } - g->current_frame_idx = frame_idx; - - UI_Frame *last_frame = &g->frames[last_frame_idx]; - UI_Frame *frame = &g->frames[frame_idx]; + UI.current_frame_tick += 1; + UI_Frame *last_frame = UI_LastFrame(); + UI_Frame *frame = UI_CurrentFrame(); { Arena *old_arena = frame->arena; @@ -500,7 +600,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) i64 dt_ns = now_ns - last_frame->time_ns; frame->time_ns = now_ns; frame->dt_ns = dt_ns; - frame->tick = last_frame->tick + 1; + frame->tick = UI.current_frame_tick; } /* Init style stack */ @@ -538,7 +638,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) } /* Init box reports */ - for (u64 pre_index = g->boxes_count; pre_index-- > 0;) + for (u64 pre_index = UI.boxes_count; pre_index-- > 0;) { UI_Box *box = last_frame->boxes_pre[pre_index]; if (hovered_box == 0 && box->desc.flags & UI_BoxFlag_Interactable) @@ -627,7 +727,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) } /* Update box hot & active states */ - for (u64 pre_index = 0; pre_index < g->boxes_count; ++pre_index) + for (u64 pre_index = 0; pre_index < UI.boxes_count; ++pre_index) { UI_Box *box = last_frame->boxes_pre[pre_index]; UI_Report *report = &box->report; @@ -666,8 +766,12 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) UI_Frame *UI_CurrentFrame(void) { - UI_State *g = &UI_state; - return &g->frames[g->current_frame_idx]; + return &UI.frames[UI.current_frame_tick % countof(UI.frames)]; +}; + +UI_Frame *UI_LastFrame(void) +{ + return &UI.frames[(UI.current_frame_tick - 1) % countof(UI.frames)]; }; Arena *UI_FrameArena(void) @@ -686,7 +790,7 @@ Vec2 UI_CursorPos(void) void UI_EndFrame(UI_Frame *frame) { TempArena scratch = BeginScratchNoConflict(); - UI_State *g = &UI_state; + UI_BoxIter box_iter = Zi; Vec2I32 monitor_size = frame->window_frame.monitor_size; Rng3 monitor_viewport = RNG3(VEC3(0, 0, 0), VEC3(monitor_size.x, monitor_size.y, 1)); @@ -699,184 +803,197 @@ void UI_EndFrame(UI_Frame *frame) ////////////////////////////// //- Process commands - g->boxes_count = 0; - - for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - UI_Cmd cmd = cmd_node->cmd; + ////////////////////////////// + //- Create boxes from build cmds - switch (cmd.kind) + for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - case UI_CmdKind_BuildBox: + UI_Cmd cmd = cmd_node->cmd; + if (cmd.kind == UI_CmdKind_BuildBox) { UI_Key key = cmd.box.key; - if (key.hash == 0) - { - key = UI_TransKey(); - } - - b32 is_root = frame->root_box == 0; - UI_Box *parent = 0; - if (!is_root) - { - parent = UI_BoxFromKey(cmd.box.parent); - if (!parent) - { - parent = frame->root_box; - } - } - - /* Allocate box */ UI_Box *box = 0; { - UI_BoxBin *bin = &g->box_bins[key.hash % UI_NumBoxLookupBins]; - for (UI_Box *tmp = bin->first; tmp && !box; tmp = tmp->next_in_bin) + UI_BoxBin *bin = &UI.box_bins[key.hash % countof(UI.box_bins)]; + for (box = bin->first; box; box = box->next_in_bin) { - if (tmp->key.hash == key.hash) + if (UI_MatchKey(box->key, key)) { - box = tmp; + break; } } - if (box) + /* Allocate new box */ + if (box == 0) { - /* Remove box from old parent */ - if (box->parent) - { - DllQueueRemove(box->parent->first, box->parent->last, box); - --box->parent->count; - } - } - else - { - box = g->first_free_box; + /* Allocate new box */ + box = UI.first_free_box; if (box) { - SllStackPop(g->first_free_box); + SllStackPop(UI.first_free_box); ZeroStruct(box); } else { - box = PushStruct(g->box_arena, UI_Box); + box = PushStruct(UI.box_arena, UI_Box); } + box->key = key; DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin); + ++UI.boxes_count; } } - ++g->boxes_count; - - /* Reset box */ - UI_Box old_box = *box; - { - ZeroStruct(box); - box->next_in_bin = old_box.next_in_bin; - box->prev_in_bin = old_box.prev_in_bin; - box->report = old_box.report; - } - box->key = key; - box->last_updated_tick = frame->tick; - - /* Update box */ - { - box->desc = cmd.box; - - /* Insert box into parent */ - if (parent) - { - DllQueuePush(parent->first, parent->last, box); - box->parent = parent; - ++parent->count; - } - - /* Fetch run */ - if (box->desc.text.len > 0) - { - box->glyph_run = GC_RunFromString(frame->arena, box->desc.text, box->desc.font, box->desc.font_size); - } - } - - if (is_root) - { - frame->root_box = box; - } - } break; - - case UI_CmdKind_SetRawTexture: - { - UI_Key key = cmd.set_raw_texture.key; - UI_Box *box = UI_BoxFromKey(key); - if (box) - { - box->raw_texture = cmd.set_raw_texture.tex; - box->raw_texture_slice_uv = cmd.set_raw_texture.slice_uv; - } - } break; + box->last_build_tick = frame->tick; + } } + ////////////////////////////// + //- Update boxes from cmds + + for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + { + UI_Cmd cmd = cmd_node->cmd; + + switch (cmd.kind) + { + case UI_CmdKind_BuildBox: + { + UI_Key key = cmd.box.key; + if (UI_MatchKey(key, UI_NilKey)) + { + key = UI_TransKey(); + } + UI_Box *box = UI_BoxFromKey(key); + + UI_Box *parent = 0; + if (box != UI.root_box) + { + parent = UI_BoxFromKey(cmd.box.parent); + if (!parent) + { + parent = UI.root_box; + } + } + + /* Update parent */ + if (box->parent != parent) + { + if (box->parent) + { + /* Remove from old parent */ + DllQueueRemove(box->parent->first, box->parent->last, box); + --box->parent->count; + } + if (parent) + { + /* Add to new parent */ + DllQueuePush(parent->first, parent->last, box); + ++parent->count; + } + box->parent = parent; + } + + /* Update box */ + { + box->desc = cmd.box; + box->glyph_run = GC_RunFromString(frame->arena, box->desc.text, box->desc.font, box->desc.font_size); + } + } break; + + case UI_CmdKind_SetRawTexture: + { + UI_Key key = cmd.set_raw_texture.key; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + box->raw_texture = cmd.set_raw_texture.tex; + box->raw_texture_slice_uv = cmd.set_raw_texture.slice_uv; + } + } break; + } + } } ////////////////////////////// - //- Prune boxes + //- Prune cached boxes - // { - // u64 cur_tick = frame->tick; - // UI_BoxIter it = UI_ITER(g->root_box); - // UI_Box *box = UI_NextBox(&it); - // while (box != 0) - // { - // UI_Box *next = UI_NextBox(&it); - // box = next; - // } - // } + { + u64 prunes_count = 0; + UI_Box **prunes = PushStructsNoZero(scratch.arena, UI_Box *, UI.boxes_count); + for (UI_BoxIterResult ir = UI_FirstBox(scratch.arena, &box_iter, UI_RootKey); ir.box; ir = UI_NextBox(scratch.arena, &box_iter)) + { + if (ir.pre) + { + UI_Box *box = ir.box; + if (box->last_build_tick < frame->tick) + { + prunes[prunes_count++] = box; + } + } + } + for (u64 prune_idx = 0; prune_idx < prunes_count; ++prune_idx) + { + UI_Box *box = prunes[prune_idx]; + UI_Box *parent = box->parent; + UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)]; + /* Re-parent children */ + if (box->first) + { + for (UI_Box *child = box->first; child; child = child->next) + { + child->parent = parent; + } + if (parent->last) + { + parent->last->next = box->first; + box->first->prev = parent->last; + } + else + { + parent->first = box->first; + } + parent->last = box->last; + parent->count += box->count; + } + /* Remove from parent */ + DllQueueRemove(parent->first, parent->last, box); + --parent->count; + /* Remove from lookup table */ + DllQueueRemoveNP(bin->first, bin->last, box, next_in_bin, prev_in_bin); + /* Add to free list */ + SllStackPush(UI.first_free_box, box); + --UI.boxes_count; + } + } ////////////////////////////// //- Layout - /* Build pre-order & post-order box arrays */ - u64 boxes_count = g->boxes_count; + /* Prepare layout data */ + u64 boxes_count = UI.boxes_count; UI_Box **boxes_pre = PushStructsNoZero(frame->arena, UI_Box *, boxes_count); UI_Box **boxes_post = PushStructsNoZero(frame->arena, UI_Box *, boxes_count); frame->boxes_pre = boxes_pre; frame->boxes_post = boxes_post; { - Struct(BoxNode) { BoxNode *next; b32 visited; UI_Box *box; }; - BoxNode *first_dfs = PushStruct(scratch.arena, BoxNode); u64 pre_index = 0; u64 post_index = 0; - first_dfs->box = frame->root_box; - while (first_dfs) + for (UI_BoxIterResult ir = UI_FirstBox(scratch.arena, &box_iter, UI_RootKey); ir.box; ir = UI_NextBox(scratch.arena, &box_iter)) { - BoxNode *n = first_dfs; - UI_Box *box = n->box; - if (!n->visited) + UI_Box *box = ir.box; + if (ir.pre) { - /* Push floating children to dfs stack */ - for (UI_Box *child = box->last; child; child = child->prev) - { - if (AnyBit(child->desc.flags, UI_BoxFlag_Floating)) - { - BoxNode *child_n = PushStruct(scratch.arena, BoxNode); - child_n->box = child; - SllStackPush(first_dfs, child_n); - } - } - /* Push non-floating children to dfs stack */ - for (UI_Box *child = box->last; child; child = child->prev) - { - if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) - { - BoxNode *child_n = PushStruct(scratch.arena, BoxNode); - child_n->box = child; - SllStackPush(first_dfs, child_n); - } - } - box->pre_index = pre_index++; - boxes_pre[box->pre_index] = box; - n->visited = 1; + box->pre_index = pre_index; + boxes_pre[pre_index] = box; + pre_index += 1; + + /* Reset layout data */ + ZeroStruct(&box->layout); } else { - SllStackPop(first_dfs); - box->post_index = post_index++; - boxes_post[box->post_index] = box; + box->post_index = post_index; + boxes_post[post_index] = box; + post_index += 1; } } Assert(pre_index == boxes_count); @@ -892,7 +1009,7 @@ void UI_EndFrame(UI_Frame *frame) UI_Size pref_size = box->desc.pref_size[axis]; if (pref_size.kind == UI_SizeKind_Pixel) { - box->solved_dims[axis] = pref_size.v; + box->layout.solved_dims[axis] = pref_size.v; } else if (pref_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) { @@ -906,7 +1023,7 @@ void UI_EndFrame(UI_Frame *frame) { text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent; } - box->solved_dims[axis] = text_size + (pref_size.v * 2); + box->layout.solved_dims[axis] = text_size + (pref_size.v * 2); } } } @@ -929,11 +1046,11 @@ void UI_EndFrame(UI_Frame *frame) if (ancestor_size.kind == UI_SizeKind_Pixel || (ancestor_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText))) { /* Match independent ancestor */ - match_size = ancestor->solved_dims[axis]; + match_size = ancestor->layout.solved_dims[axis]; found_match = 1; } } - box->solved_dims[axis] = match_size * pref_size.v; + box->layout.solved_dims[axis] = match_size * pref_size.v; } } } @@ -954,15 +1071,15 @@ void UI_EndFrame(UI_Frame *frame) { if (axis == box->desc.child_layout_axis) { - accum += child->solved_dims[axis]; + accum += child->layout.solved_dims[axis]; } else { - accum = MaxF32(child->solved_dims[axis], accum); + accum = MaxF32(child->layout.solved_dims[axis], accum); } } } - box->solved_dims[axis] = accum + (pref_size.v * 2); + box->layout.solved_dims[axis] = accum + (pref_size.v * 2); } } } @@ -977,7 +1094,7 @@ void UI_EndFrame(UI_Frame *frame) UI_Size pref_size = box->desc.pref_size[axis]; if (pref_size.kind == UI_SizeKind_Grow) { - box->solved_dims[axis] = box->parent->solved_dims[axis] * pref_size.v; + box->layout.solved_dims[axis] = box->parent->layout.solved_dims[axis] * pref_size.v; } } } @@ -988,7 +1105,7 @@ void UI_EndFrame(UI_Frame *frame) UI_Box *box = boxes_pre[pre_index]; for (Axis axis = 0; axis < Axis_CountXY; ++axis) { - f32 box_size = box->solved_dims[axis]; + f32 box_size = box->layout.solved_dims[axis]; /* Solve non-floating violations */ { f32 size_accum = 0; @@ -997,7 +1114,7 @@ void UI_EndFrame(UI_Frame *frame) { if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) { - f32 size = child->solved_dims[axis]; + f32 size = child->layout.solved_dims[axis]; f32 strictness = child->desc.pref_size[axis].strictness; f32 flex = size * (1.0 - strictness); if (axis == box->desc.child_layout_axis) @@ -1020,7 +1137,7 @@ void UI_EndFrame(UI_Frame *frame) { if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) { - f32 size = child->solved_dims[axis]; + f32 size = child->layout.solved_dims[axis]; f32 strictness = child->desc.pref_size[axis].strictness; f32 flex = size * (1.0 - strictness); f32 new_size = size; @@ -1037,24 +1154,24 @@ void UI_EndFrame(UI_Frame *frame) } } adjusted_size_accum += new_size; - child->solved_dims[axis] = new_size; + child->layout.solved_dims[axis] = new_size; } } size_accum = adjusted_size_accum; } - box->final_children_size_accum[axis] = size_accum; + box->layout.final_children_size_accum[axis] = size_accum; } /* Solve floating violations */ for (UI_Box *child = box->first; child; child = child->next) { if (AnyBit(child->desc.flags, UI_BoxFlag_Floating) && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp)) { - f32 size = child->solved_dims[axis]; + f32 size = child->layout.solved_dims[axis]; if (size > box_size) { f32 strictness = child->desc.pref_size[axis].strictness; f32 flex = size * (1.0 - strictness); - child->solved_dims[axis] = MaxF32(size - flex, box_size); + child->layout.solved_dims[axis] = MaxF32(size - flex, box_size); } } } @@ -1071,25 +1188,25 @@ void UI_EndFrame(UI_Frame *frame) { Axis axis = box->desc.child_layout_axis; UI_AxisAlignment alignment = box->desc.child_alignment[axis]; - f32 box_size = box->solved_dims[axis]; - f32 size_accum = box->final_children_size_accum[axis]; + f32 box_size = box->layout.solved_dims[axis]; + f32 size_accum = box->layout.final_children_size_accum[axis]; switch(alignment) { default: break; case UI_AxisAlignment_Center: { - box->layout_cursor = box_size / 2 - size_accum / 2; + box->layout.cursor = box_size / 2 - size_accum / 2; } break; case UI_AxisAlignment_End: { - box->layout_cursor = box_size - size_accum; + box->layout.cursor = box_size - size_accum; } break; } } /* Position */ { - f32 *dims_arr = box->solved_dims; + f32 *dims_arr = box->layout.solved_dims; Vec2 dims_vec = VEC2(dims_arr[0], dims_arr[1]); Vec2 final_pos = Zi; @@ -1113,7 +1230,7 @@ void UI_EndFrame(UI_Frame *frame) /* Non-floating box position */ else if (parent) { - f32 layout_cursor = parent->layout_cursor; + f32 layout_cursor = parent->layout.cursor; f32 offset[2] = Zi; /* Compute offset in layout direction */ { @@ -1129,13 +1246,13 @@ void UI_EndFrame(UI_Frame *frame) default: break; case UI_AxisAlignment_Center: { - f32 parent_size = parent->solved_dims[axis]; + f32 parent_size = parent->layout.solved_dims[axis]; f32 box_size = dims_arr[axis]; offset[axis] = parent_size / 2 - box_size / 2; } break; case UI_AxisAlignment_End: { - f32 parent_size = parent->solved_dims[axis]; + f32 parent_size = parent->layout.solved_dims[axis]; f32 box_size = dims_arr[axis]; offset[axis] = parent_size - box_size; } break; @@ -1143,7 +1260,7 @@ void UI_EndFrame(UI_Frame *frame) } final_pos.x = parent->rect.p0.x + offset[0]; final_pos.y = parent->rect.p0.y + offset[1]; - parent->layout_cursor += dims_arr[parent->desc.child_layout_axis]; + parent->layout.cursor += dims_arr[parent->desc.child_layout_axis]; } /* Submit position */ diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 5450de84..f85e64c1 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -2,7 +2,7 @@ //~ Key types #define UI_NilKey ((UI_Key) { 0 }) -#define UI_RootKey ((UI_Key) { 0xa3deb3749ef35a7aUll }) +#define UI_RootKey ((UI_Key) { 0xaaaaaaaaaaaaaaaaUll }) Struct(UI_Key) { @@ -253,7 +253,7 @@ Struct(UI_Box) UI_Box *next_in_bin; UI_Box *prev_in_bin; UI_Report report; - u64 last_updated_tick; + i64 last_build_tick; //- Tree links UI_Box *parent; @@ -267,16 +267,19 @@ Struct(UI_Box) UI_BoxDesc desc; G_Texture2DRef raw_texture; Rng2 raw_texture_slice_uv; + GC_Run glyph_run; //- Pre-layout data u64 pre_index; u64 post_index; //- Layout data - GC_Run glyph_run; - f32 layout_cursor; - f32 solved_dims[Axis_CountXY]; - f32 final_children_size_accum[Axis_CountXY]; + struct + { + f32 cursor; + f32 solved_dims[Axis_CountXY]; + f32 final_children_size_accum[Axis_CountXY]; + } layout; //- Layout results Rng2 rect; @@ -293,9 +296,30 @@ Struct(UI_BoxBin) }; //////////////////////////////////////////////////////////// -//~ State types +//~ Iterator types -#define UI_NumBoxLookupBins 16384 +Struct(UI_BoxIterDfsNode) +{ + UI_BoxIterDfsNode *next; + UI_Box *box; + b32 visited; +}; + +Struct(UI_BoxIter) +{ + UI_BoxIterDfsNode *first; + UI_BoxIterDfsNode *first_free; + UI_BoxIterDfsNode *last_free; +}; + +Struct(UI_BoxIterResult) +{ + UI_Box *box; + b32 pre; +}; + +//////////////////////////////////////////////////////////// +//~ Context types Enum(UI_FrameFlag) { @@ -314,10 +338,8 @@ Struct(UI_Frame) G_ResourceHandle backbuffer; G_CommandListHandle cl; - u64 transient_key_seed; - /* Time */ - u64 tick; + i64 tick; i64 time_ns; i64 dt_ns; @@ -338,21 +360,25 @@ Struct(UI_Frame) UI_StyleNode *first_free_style_node; /* Layout */ - UI_Box *root_box; UI_Box **boxes_pre; UI_Box **boxes_post; }; -Struct(UI_State) +Struct(UI_Ctx) { - Arena *box_arena; - UI_BoxBin *box_bins; u64 boxes_count; + Arena *box_arena; + UI_Box *root_box; + UI_BoxBin box_bins[Kibi(256)]; UI_Box *first_free_box; - u64 current_frame_idx; + u64 transient_key_seed; + + i64 current_frame_tick; UI_Frame frames[2]; -} extern UI_state; +}; + +extern UI_Ctx UI; //////////////////////////////////////////////////////////// //~ Bootstrap @@ -367,14 +393,19 @@ GC_FontKey UI_GetDefaultFont(void); //////////////////////////////////////////////////////////// //~ Key helpers +b32 UI_MatchKey(UI_Key a, UI_Key b); UI_Key UI_KeyFromString(String str); UI_Key UI_KeyF_(String fmt, ...); #define UI_KeyF(fmt_cstr, ...) UI_KeyF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd) - UI_Key UI_TransKey(void); - UI_Box *UI_BoxFromKey(UI_Key key); +//////////////////////////////////////////////////////////// +//~ Iteration helpers + +UI_BoxIterResult UI_FirstBox(Arena *arena, UI_BoxIter *iter, UI_Key start_key); +UI_BoxIterResult UI_NextBox(Arena *arena, UI_BoxIter *iter); + //////////////////////////////////////////////////////////// //~ String helpers @@ -454,6 +485,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color); //~ Frame helpers UI_Frame *UI_CurrentFrame(void); +UI_Frame *UI_LastFrame(void); Arena *UI_FrameArena(void); Vec2 UI_CursorPos(void);