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
UI_BeginBuild();
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
if (!g->material_instances_arena)
@ -2006,10 +2006,12 @@ void UpdateUser(P_Window *window)
__profn("Draw debug info");
UI_Box *dbg_box = UI_BuildBox(0, UI_NilKey);
UI_PushParent(dbg_box);
UI_PushSize(Axis_X, UI_SizeKind_Pixel, 5, 0);
UI_PushSize(Axis_Y, UI_SizeKind_Pixel, 5, 0);
UI_PushCheckpoint();
{
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_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));
#endif
}
UI_PopSize(Axis_X);
UI_PopSize(Axis_Y);
UI_PopParent();
UI_PopCheckpoint();
}
#else
if (g->debug_draw)
@ -2545,7 +2545,6 @@ void UpdateUser(P_Window *window)
}
/* Render UI */
UI_PopParent();
UI_SetDisplayImage(pp_root_box, g->ui_target);
GPU_Resource *ui_render = UI_EndBuild(ui_viewport);

View File

@ -3,114 +3,124 @@ UI_SharedState UI_shared_state = ZI;
////////////////////////////////
//~ 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)
{
UI_SharedState *g = &UI_shared_state;
seed = RandU64FromSeeds(g->top_parent->box->key.hash, g->top_tag->hash);
seed = UI_HashFromTop();
}
return HashFnv64(seed, str);
}
UI_Key UI_KeyFromString(u64 seed, String str)
{
UI_Key result = ZI;
result.hash = HashFnv64(seed, str);
result.hash = UI_HashFromString(seed, str);
return result;
}
////////////////////////////////
//~ Stack helpers
//~ Checkpoint helpers
//- Tag
void UI_PushTagFromHash(u64 hash)
void UI_PushCheckpoint(void)
{
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);
StackPush(g->top_tag, n);
n->hash = hash;
else
{
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;
u64 hash = Fnv64Basis;
if (g->top_tag)
UI_Checkpoint *cp = g->top_checkpoint;
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);
n->hash = hash;
StackPush(g->top_tag, n);
}
cp->next = g->first_free_checkpoint;
g->first_free_checkpoint = cp;
}
void UI_PushTagF_(char *fmt_cstr, ...)
{
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);
}
////////////////////////////////
//~ Style stack helpers
void UI_PopTag(void)
{
StackPop(UI_shared_state.top_tag);
}
//- Parent
void UI_PushParent(UI_Box *box)
UI_StyleNode *UI_PushStyleNode(UI_StyleKind kind, b32 pop_when_touched, UI_Style desc)
{
UI_SharedState *g = &UI_shared_state;
UI_ParentNode *n = PushStruct(g->build_arena, UI_ParentNode);
n->box = box;
StackPush(g->top_parent, n);
}
void UI_PopParent(void)
UI_StyleNode *n = g->first_free_style_node;
if (n)
{
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
void UI_PushSize(Axis axis, UI_SizeKind kind, f32 v, f32 strictness)
UI_Style UI_PopStyleNode(UI_StyleKind kind)
{
UI_SharedState *g = &UI_shared_state;
UI_SizeNode *n = PushStruct(g->build_arena, UI_SizeNode);
n->size.kind = kind;
n->size.v = v;
n->size.strictness = strictness;
StackPush(g->top_size[axis], n);
UI_StyleNode *n = g->style_tops[kind];
UI_Style style = n->style;
g->style_tops[kind] = n->next;
n->next = g->first_free_style_node;
g->first_free_style_node = n;
return style;
}
void UI_PopSize(Axis axis)
{
StackPop(UI_shared_state.top_size[axis]);
}
//- Layout dir
void UI_PushLayoutDir(Axis dir)
UI_StyleNode *UI_PeekStyleNode(UI_StyleKind kind)
{
UI_SharedState *g = &UI_shared_state;
UI_AxisNode *n = PushStruct(g->build_arena, UI_AxisNode);
n->axis = dir;
StackPush(g->top_layout_dir, n);
UI_StyleNode *n = g->style_tops[kind];
return 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;
/* Pull from stack */
box->parent = g->top_parent->box;
box->pref_size[Axis_X] = g->top_size[Axis_X]->size;
box->pref_size[Axis_Y] = g->top_size[Axis_Y]->size;
box->layout_dir = g->top_layout_dir->axis;
/* Pull from style stack */
box->parent = UI_TouchTop(Parent);
box->pref_size[Axis_X] = UI_TouchTop(Width);
box->pref_size[Axis_Y] = UI_TouchTop(Height);
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;
return box;
@ -212,6 +222,7 @@ void UI_BeginBuild(void)
}
ResetArena(g->build_arena);
g->boxes_count = 0;
g->first_free_style_node = 0;
/* Init bins */
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);
++g->boxes_count;
/* Init stacks */
UI_PushTagF("root");
UI_PushParent(g->root_box);
UI_PushSize(Axis_X, UI_SizeKind_Percent, 1, 0);
UI_PushSize(Axis_Y, UI_SizeKind_Percent, 1, 0);
UI_PushLayoutDir(Axis_X);
/* Init checkpoints */
g->top_checkpoint = PushStruct(g->build_arena, UI_Checkpoint);
/* Init styles */
{
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)
{
@ -323,7 +350,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
for (Axis axis = 0; axis < Axis_CountXY; ++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;
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]);
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;
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;

View File

@ -15,7 +15,7 @@ Struct(UI_Key)
Enum(UI_SizeKind)
{
UI_SizeKind_Children,
UI_SizeKind_Percent,
UI_SizeKind_Ratio,
UI_SizeKind_Pixel,
UI_SizeKind_Text,
};
@ -52,7 +52,7 @@ Struct(UI_Box)
String display_text;
GPU_Resource *display_image;
UI_Size pref_size[Axis_CountXY];
Axis layout_dir;
Axis layout_axis;
u32 tint;
//- Layout data
@ -71,12 +71,56 @@ Struct(UI_BoxBin)
};
////////////////////////////////
//~ Stack types
//~ Style types
Struct(UI_TagNode) { UI_TagNode *next; u64 hash; };
Struct(UI_ParentNode) { UI_ParentNode *next; UI_Box *box; };
Struct(UI_SizeNode) { UI_SizeNode *next; UI_Size size; };
Struct(UI_AxisNode) { UI_AxisNode *next; Axis axis; };
//- Style data
#define UI_StyleKindsXMacro(x) \
x(Tag, u64) \
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
@ -98,17 +142,16 @@ Struct(UI_SharedState)
u64 boxes_count;
u64 back_boxes_count;
//- Stack state
UI_TagNode *top_tag;
UI_ParentNode *top_parent;
UI_SizeNode *top_size[Axis_CountXY];
UI_AxisNode *top_layout_dir;
UI_Checkpoint *top_checkpoint;
UI_Checkpoint *first_free_checkpoint;
UI_StyleNode *style_tops[UI_StyleKind_Count];
UI_StyleNode *first_free_style_node;
//- Render state
GPU_Resource *render_target;
i64 render_fence_target;
GPU_TransientBuffer draw_rects_tbuff;
Arena *draw_rects_arena;
@ -117,29 +160,37 @@ Struct(UI_SharedState)
////////////////////////////////
//~ Key helpers
u64 UI_HashFromTop(void);
u64 UI_HashFromString(u64 seed, String str);
UI_Key UI_KeyFromString(u64 seed, String str);
////////////////////////////////
//~ Stack helpers
//~ Checkpoint helpers
//- Tag
void UI_PushTagFromHash(u64 hash);
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);
void UI_PushCheckpoint(void);
void UI_PopCheckpoint(void);
//- Parent
void UI_PushParent(UI_Box *box);
void UI_PopParent(void);
////////////////////////////////
//~ Style stack helpers
//- Size
void UI_PushSize(Axis axis, UI_SizeKind kind, f32 v, f32 strictness);
void UI_PopSize(Axis axis);
UI_StyleNode *UI_PushStyleNode(UI_StyleKind kind, b32 pop_when_touched, UI_Style desc);
UI_Style UI_PopStyleNode(UI_StyleKind kind);
UI_StyleNode *UI_PeekTopStyleNode(UI_StyleKind kind);
UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 touch);
//- Layout dir
void UI_PushLayoutDir(Axis dir);
void UI_PopLayoutDir(void);
#define UI_SetNext(name, ...) UI_PushStyleNode(UI_StyleKind_##name, 1, (UI_Style) { .name = __VA_ARGS__ })
#define UI_Push(name, ...) UI_PushStyleNode(UI_StyleKind_##name, 0, (UI_Style) { .name = __VA_ARGS__ })
#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