ui checkpoints

This commit is contained in:
jacob 2025-10-21 19:11:01 -05:00
parent a5f10a2312
commit e64ae8e71c
3 changed files with 216 additions and 139 deletions

View File

@ -452,7 +452,7 @@ void UpdateUser(P_Window *window)
//- Begin UI //- Begin UI
UI_BeginBuild(); UI_BeginBuild();
UI_Box *pp_root_box = UI_BuildBox(UI_Flag_DrawImage, UI_NilKey); UI_Box *pp_root_box = UI_BuildBox(UI_Flag_DrawImage, UI_NilKey);
UI_PushParent(pp_root_box); UI_Push(Parent, pp_root_box);
//- Init render data buffers //- Init render data buffers
if (!g->material_instances_arena) if (!g->material_instances_arena)
@ -2006,10 +2006,12 @@ void UpdateUser(P_Window *window)
__profn("Draw debug info"); __profn("Draw debug info");
UI_Box *dbg_box = UI_BuildBox(0, UI_NilKey); UI_Box *dbg_box = UI_BuildBox(0, UI_NilKey);
UI_PushParent(dbg_box); UI_PushCheckpoint();
UI_PushSize(Axis_X, UI_SizeKind_Pixel, 5, 0);
UI_PushSize(Axis_Y, UI_SizeKind_Pixel, 5, 0);
{ {
UI_Push(Parent, dbg_box);
UI_Push(Width, UI_PixelSize(5, 0));
UI_Push(Height, UI_PixelSize(5, 0));
UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)); UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved));
UI_BuildSeparator(); UI_BuildSeparator();
@ -2105,9 +2107,7 @@ void UpdateUser(P_Window *window)
draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite)); draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite));
#endif #endif
} }
UI_PopSize(Axis_X); UI_PopCheckpoint();
UI_PopSize(Axis_Y);
UI_PopParent();
} }
#else #else
if (g->debug_draw) if (g->debug_draw)
@ -2545,7 +2545,6 @@ void UpdateUser(P_Window *window)
} }
/* Render UI */ /* Render UI */
UI_PopParent();
UI_SetDisplayImage(pp_root_box, g->ui_target); UI_SetDisplayImage(pp_root_box, g->ui_target);
GPU_Resource *ui_render = UI_EndBuild(ui_viewport); GPU_Resource *ui_render = UI_EndBuild(ui_viewport);

View File

@ -3,114 +3,124 @@ UI_SharedState UI_shared_state = ZI;
//////////////////////////////// ////////////////////////////////
//~ Key helpers //~ Key helpers
UI_Key UI_KeyFromString(u64 seed, String str) u64 UI_HashFromTop(void)
{
UI_Box *parent_box = UI_PeekTop(Parent);
u64 tag_hash = UI_PeekTop(Tag);
return RandU64FromSeeds(parent_box->key.hash, tag_hash);
}
u64 UI_HashFromString(u64 seed, String str)
{ {
/* TOIMPL */
if (seed == 0) if (seed == 0)
{ {
UI_SharedState *g = &UI_shared_state; seed = UI_HashFromTop();
seed = RandU64FromSeeds(g->top_parent->box->key.hash, g->top_tag->hash);
} }
return HashFnv64(seed, str);
}
UI_Key UI_KeyFromString(u64 seed, String str)
{
UI_Key result = ZI; UI_Key result = ZI;
result.hash = HashFnv64(seed, str); result.hash = UI_HashFromString(seed, str);
return result; return result;
} }
//////////////////////////////// ////////////////////////////////
//~ Stack helpers //~ Checkpoint helpers
//- Tag void UI_PushCheckpoint(void)
void UI_PushTagFromHash(u64 hash)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
if (g->top_tag) UI_Checkpoint *cp = g->first_free_checkpoint;
if (cp)
{ {
hash = RandU64FromSeeds(hash, g->top_tag->hash); g->first_free_checkpoint = cp->next;
ZeroStruct(cp);
} }
UI_TagNode *n = PushStruct(g->build_arena, UI_TagNode); else
StackPush(g->top_tag, n); {
n->hash = hash; cp = PushStruct(g->build_arena, UI_Checkpoint);
}
cp->v = g->top_checkpoint->v + 1;
g->top_checkpoint = cp;
} }
void UI_PushTagFromString(String str) void UI_PopCheckpoint(void)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
u64 hash = Fnv64Basis; UI_Checkpoint *cp = g->top_checkpoint;
if (g->top_tag) u64 v = cp->v;
for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind)
{ {
hash = g->top_tag->hash; UI_StyleNode *n = g->style_tops[kind];
while (n && n->checkpoint >= v)
{
UI_StyleNode *next = n->next;
g->style_tops[kind] = next;
n->next = g->first_free_style_node;
g->first_free_style_node = n;
n = next;
} }
hash = HashFnv64(hash, str); }
UI_TagNode *n = PushStruct(g->build_arena, UI_TagNode); cp->next = g->first_free_checkpoint;
n->hash = hash; g->first_free_checkpoint = cp;
StackPush(g->top_tag, n);
} }
void UI_PushTagF_(char *fmt_cstr, ...) ////////////////////////////////
{ //~ Style stack helpers
TempArena scratch = BeginScratchNoConflict();
String str = ZI;
{
va_list va;
va_start(va, fmt_cstr);
str = FormatStringV(scratch.arena, StringFromCstrNoLimit(fmt_cstr), va);
va_end(va);
}
UI_PushTagFromString(str);
EndScratch(scratch);
}
void UI_PopTag(void) UI_StyleNode *UI_PushStyleNode(UI_StyleKind kind, b32 pop_when_touched, UI_Style desc)
{
StackPop(UI_shared_state.top_tag);
}
//- Parent
void UI_PushParent(UI_Box *box)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_ParentNode *n = PushStruct(g->build_arena, UI_ParentNode); UI_StyleNode *n = g->first_free_style_node;
n->box = box; if (n)
StackPush(g->top_parent, n);
}
void UI_PopParent(void)
{ {
StackPop(UI_shared_state.top_parent); g->first_free_style_node = n->next;
ZeroStruct(n);
}
else
{
n = PushStruct(g->build_arena, UI_StyleNode);
}
n->pop_when_touched = pop_when_touched;
n->style = desc;
n->next = g->style_tops[kind];
n->checkpoint = g->top_checkpoint->v;
g->style_tops[kind] = n;
return n;
} }
//- Size UI_Style UI_PopStyleNode(UI_StyleKind kind)
void UI_PushSize(Axis axis, UI_SizeKind kind, f32 v, f32 strictness)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_SizeNode *n = PushStruct(g->build_arena, UI_SizeNode); UI_StyleNode *n = g->style_tops[kind];
n->size.kind = kind; UI_Style style = n->style;
n->size.v = v; g->style_tops[kind] = n->next;
n->size.strictness = strictness; n->next = g->first_free_style_node;
StackPush(g->top_size[axis], n); g->first_free_style_node = n;
return style;
} }
void UI_PopSize(Axis axis) UI_StyleNode *UI_PeekStyleNode(UI_StyleKind kind)
{
StackPop(UI_shared_state.top_size[axis]);
}
//- Layout dir
void UI_PushLayoutDir(Axis dir)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_AxisNode *n = PushStruct(g->build_arena, UI_AxisNode); UI_StyleNode *n = g->style_tops[kind];
n->axis = dir; return n;
StackPush(g->top_layout_dir, n);
} }
void UI_PopLayoutDir(void) UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 touch)
{ {
StackPop(UI_shared_state.top_layout_dir); UI_SharedState *g = &UI_shared_state;
UI_StyleNode *n = g->style_tops[kind];
UI_Style style = n->style;
if (touch && n->pop_when_touched)
{
g->style_tops[kind] = n->next;
n->next = g->first_free_style_node;
g->first_free_style_node = n;
}
return style;
} }
//////////////////////////////// ////////////////////////////////
@ -164,13 +174,13 @@ UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
box->tint = 0xFFFFFFFF; box->tint = 0xFFFFFFFF;
/* Pull from stack */ /* Pull from style stack */
box->parent = g->top_parent->box; box->parent = UI_TouchTop(Parent);
box->pref_size[Axis_X] = g->top_size[Axis_X]->size; box->pref_size[Axis_X] = UI_TouchTop(Width);
box->pref_size[Axis_Y] = g->top_size[Axis_Y]->size; box->pref_size[Axis_Y] = UI_TouchTop(Height);
box->layout_dir = g->top_layout_dir->axis; box->layout_axis = UI_TouchTop(LayoutAxis);
DllPushBack(g->top_parent->box->first, g->top_parent->box->last, box); DllPushBack(box->parent->first, box->parent->last, box);
++box->parent->count; ++box->parent->count;
return box; return box;
@ -212,6 +222,7 @@ void UI_BeginBuild(void)
} }
ResetArena(g->build_arena); ResetArena(g->build_arena);
g->boxes_count = 0; g->boxes_count = 0;
g->first_free_style_node = 0;
/* Init bins */ /* Init bins */
g->box_bins = PushStructs(g->build_arena, UI_BoxBin, UI_NumBoxLookupBins); g->box_bins = PushStructs(g->build_arena, UI_BoxBin, UI_NumBoxLookupBins);
@ -223,12 +234,28 @@ void UI_BeginBuild(void)
DllPushBackNP(bin->first, bin->last, g->root_box, next_in_bin, prev_in_bin); DllPushBackNP(bin->first, bin->last, g->root_box, next_in_bin, prev_in_bin);
++g->boxes_count; ++g->boxes_count;
/* Init stacks */ /* Init checkpoints */
UI_PushTagF("root"); g->top_checkpoint = PushStruct(g->build_arena, UI_Checkpoint);
UI_PushParent(g->root_box);
UI_PushSize(Axis_X, UI_SizeKind_Percent, 1, 0); /* Init styles */
UI_PushSize(Axis_Y, UI_SizeKind_Percent, 1, 0); {
UI_PushLayoutDir(Axis_X); UI_StyleNode *nodes = PushStructs(g->build_arena, UI_StyleNode, UI_StyleKind_Count);
for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind)
{
UI_StyleNode *n = &nodes[kind];
UI_Style *style = &n->style;
style->kind = kind;
g->style_tops[kind] = n;
switch(kind)
{
default: break;
case UI_StyleKind_Tag: { style->Tag = HashFnv64(Fnv64Basis, Lit("root")); } break;
case UI_StyleKind_Parent: { style->Parent = g->root_box; } break;
case UI_StyleKind_Width: { style->Width = UI_RatioSize(1, 0); } break;
case UI_StyleKind_Height: { style->Height = UI_RatioSize(1, 0); } break;
}
}
}
if (!g->back_build_arena) if (!g->back_build_arena)
{ {
@ -323,7 +350,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
for (Axis axis = 0; axis < Axis_CountXY; ++axis) for (Axis axis = 0; axis < Axis_CountXY; ++axis)
{ {
UI_Size pref_size = box->pref_size[axis]; UI_Size pref_size = box->pref_size[axis];
if (pref_size.kind == UI_SizeKind_Percent) if (pref_size.kind == UI_SizeKind_Ratio)
{ {
UI_Box *ancestor = box->parent; UI_Box *ancestor = box->parent;
f32 ancestor_size = 0; f32 ancestor_size = 0;
@ -349,7 +376,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
Vec2 final_size = VEC2(box->solved_dims[0], box->solved_dims[1]); Vec2 final_size = VEC2(box->solved_dims[0], box->solved_dims[1]);
if (parent) if (parent)
{ {
b32 is_layout_x = parent->layout_dir == Axis_X; b32 is_layout_x = parent->layout_axis == Axis_X;
f32 layout_cursor = parent->layout_cursor; f32 layout_cursor = parent->layout_cursor;
Vec2 offset = VEC2(layout_cursor * is_layout_x, layout_cursor * !is_layout_x); Vec2 offset = VEC2(layout_cursor * is_layout_x, layout_cursor * !is_layout_x);
parent->layout_cursor += final_size.x * is_layout_x + final_size.y * is_layout_x; parent->layout_cursor += final_size.x * is_layout_x + final_size.y * is_layout_x;

View File

@ -15,7 +15,7 @@ Struct(UI_Key)
Enum(UI_SizeKind) Enum(UI_SizeKind)
{ {
UI_SizeKind_Children, UI_SizeKind_Children,
UI_SizeKind_Percent, UI_SizeKind_Ratio,
UI_SizeKind_Pixel, UI_SizeKind_Pixel,
UI_SizeKind_Text, UI_SizeKind_Text,
}; };
@ -52,7 +52,7 @@ Struct(UI_Box)
String display_text; String display_text;
GPU_Resource *display_image; GPU_Resource *display_image;
UI_Size pref_size[Axis_CountXY]; UI_Size pref_size[Axis_CountXY];
Axis layout_dir; Axis layout_axis;
u32 tint; u32 tint;
//- Layout data //- Layout data
@ -71,12 +71,56 @@ Struct(UI_BoxBin)
}; };
//////////////////////////////// ////////////////////////////////
//~ Stack types //~ Style types
Struct(UI_TagNode) { UI_TagNode *next; u64 hash; }; //- Style data
Struct(UI_ParentNode) { UI_ParentNode *next; UI_Box *box; }; #define UI_StyleKindsXMacro(x) \
Struct(UI_SizeNode) { UI_SizeNode *next; UI_Size size; }; x(Tag, u64) \
Struct(UI_AxisNode) { UI_AxisNode *next; Axis axis; }; x(Parent, UI_Box *) \
x(LayoutAxis, Axis) \
x(Width, UI_Size) \
x(Height, UI_Size) \
x(BackgroundColor, u32) \
x(Opacity, f32) \
//- Style node
Enum(UI_StyleKind)
{
#define X(name, type) UI_StyleKind_##name,
UI_StyleKind_None,
UI_StyleKindsXMacro(X)
UI_StyleKind_Count,
#undef X
};
Struct(UI_Style)
{
UI_StyleKind kind;
/* Union of all style data types */
union
{
#define X(name, type) type name;
UI_StyleKindsXMacro(X)
#undef X
};
};
Struct(UI_StyleNode)
{
UI_StyleNode *next;
b32 pop_when_touched;
u64 checkpoint;
UI_Style style;
};
////////////////////////////////
//~ Checkpoint types
Struct(UI_Checkpoint)
{
UI_Checkpoint *next;
u64 v;
};
//////////////////////////////// ////////////////////////////////
//~ State types //~ State types
@ -98,17 +142,16 @@ Struct(UI_SharedState)
u64 boxes_count; u64 boxes_count;
u64 back_boxes_count; u64 back_boxes_count;
//- Stack state UI_Checkpoint *top_checkpoint;
UI_TagNode *top_tag; UI_Checkpoint *first_free_checkpoint;
UI_ParentNode *top_parent;
UI_SizeNode *top_size[Axis_CountXY]; UI_StyleNode *style_tops[UI_StyleKind_Count];
UI_AxisNode *top_layout_dir; UI_StyleNode *first_free_style_node;
//- Render state //- Render state
GPU_Resource *render_target; GPU_Resource *render_target;
i64 render_fence_target; i64 render_fence_target;
GPU_TransientBuffer draw_rects_tbuff; GPU_TransientBuffer draw_rects_tbuff;
Arena *draw_rects_arena; Arena *draw_rects_arena;
@ -117,29 +160,37 @@ Struct(UI_SharedState)
//////////////////////////////// ////////////////////////////////
//~ Key helpers //~ Key helpers
u64 UI_HashFromTop(void);
u64 UI_HashFromString(u64 seed, String str);
UI_Key UI_KeyFromString(u64 seed, String str); UI_Key UI_KeyFromString(u64 seed, String str);
//////////////////////////////// ////////////////////////////////
//~ Stack helpers //~ Checkpoint helpers
//- Tag void UI_PushCheckpoint(void);
void UI_PushTagFromHash(u64 hash); void UI_PopCheckpoint(void);
void UI_PushTagFromString(String str);
#define UI_PushTagF(fmt_cstr, ...) UI_PushTagF_(fmt_cstr, __VA_ARGS__, FmtEnd);
void UI_PushTagF_(char *fmt_cstr, ...);
void UI_PopTag(void);
//- Parent ////////////////////////////////
void UI_PushParent(UI_Box *box); //~ Style stack helpers
void UI_PopParent(void);
//- Size UI_StyleNode *UI_PushStyleNode(UI_StyleKind kind, b32 pop_when_touched, UI_Style desc);
void UI_PushSize(Axis axis, UI_SizeKind kind, f32 v, f32 strictness); UI_Style UI_PopStyleNode(UI_StyleKind kind);
void UI_PopSize(Axis axis); UI_StyleNode *UI_PeekTopStyleNode(UI_StyleKind kind);
UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 touch);
//- Layout dir #define UI_SetNext(name, ...) UI_PushStyleNode(UI_StyleKind_##name, 1, (UI_Style) { .name = __VA_ARGS__ })
void UI_PushLayoutDir(Axis dir); #define UI_Push(name, ...) UI_PushStyleNode(UI_StyleKind_##name, 0, (UI_Style) { .name = __VA_ARGS__ })
void UI_PopLayoutDir(void); #define UI_Pop(name, ...) UI_PopStyleNode(UI_StyleKind_##name).name
#define UI_TouchTop(name) UI_StyleFromTopNode(UI_StyleKind_##name, 1).name
#define UI_PeekTop(name) UI_StyleFromTopNode(UI_StyleKind_##name, 0).name
////////////////////////////////
//~ Size helpers
#define UI_ChildrenSize(_strictness) ((UI_Size) { .kind = UI_SizeKind_Children, .strictness = strictness_ })
#define UI_RatioSize(_ratio, _strictness) ((UI_Size) { .kind = UI_SizeKind_Ratio, .v = _ratio, .strictness = _strictness })
#define UI_PixelSize(_pixels, _strictness) ((UI_Size) { .kind = UI_SizeKind_Pixel, .v = _pixels, .strictness = _strictness })
#define UI_TextSize(_strictness) ((UI_Size) { .kind = UI_SizeKind_Text, .strictness = _strictness })
//////////////////////////////// ////////////////////////////////
//~ Box //~ Box