ui layer testing

This commit is contained in:
jacob 2025-10-20 18:38:00 -05:00
parent b74927602c
commit fca8ba5a8d
4 changed files with 285 additions and 85 deletions

View File

@ -4,6 +4,19 @@
#define Tau ((f32)6.28318530717958647693) #define Tau ((f32)6.28318530717958647693)
#define GoldenRatio ((f32)1.61803398874989484820) #define GoldenRatio ((f32)1.61803398874989484820)
////////////////////////////////
//~ Axis types
Enum(Axis)
{
Axis_X = 0,
Axis_Y = 1,
Axis_Z = 2,
Axis_CountXY = 2,
Axis_CountXYZ = 3,
};
//////////////////////////////// ////////////////////////////////
//~ Vector types //~ Vector types

View File

@ -1,22 +1,5 @@
UI_SharedState UI_shared_state = ZI; UI_SharedState UI_shared_state = ZI;
////////////////////////////////
//~ State helpers
UI_FiberState *UI_GetFiberState(void)
{
UI_FiberState *f = UI_shared_state.fiber_states[FiberId()];
if (!f)
{
Arena *perm = PermArena();
PushAlign(perm, CachelineSize);
f = PushStruct(perm, UI_FiberState);
UI_shared_state.fiber_states[FiberId()] = f;
PushAlign(perm, CachelineSize);
}
return f;
}
//////////////////////////////// ////////////////////////////////
//~ Key helpers //~ Key helpers
@ -25,8 +8,8 @@ UI_Key UI_KeyFromString(u64 seed, String str)
/* TOIMPL */ /* TOIMPL */
if (seed == 0) if (seed == 0)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
seed = RandU64FromSeeds(f->top_parent->box->key.hash, f->top_tag->hash); seed = RandU64FromSeeds(g->top_parent->box->key.hash, g->top_tag->hash);
} }
UI_Key result = ZI; UI_Key result = ZI;
result.hash = HashFnv64(seed, str); result.hash = HashFnv64(seed, str);
@ -40,27 +23,27 @@ UI_Key UI_KeyFromString(u64 seed, String str)
void UI_PushTagFromHash(u64 hash) void UI_PushTagFromHash(u64 hash)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
if (f->top_tag) if (g->top_tag)
{ {
hash = RandU64FromSeeds(hash, f->top_tag->hash); hash = RandU64FromSeeds(hash, g->top_tag->hash);
} }
UI_Tag *tag = PushStruct(f->build_arena, UI_Tag); UI_Tag *tag = PushStruct(g->build_arena, UI_Tag);
StackPush(f->top_tag, tag); StackPush(g->top_tag, tag);
tag->hash = hash; tag->hash = hash;
} }
void UI_PushTagFromString(String str) void UI_PushTagFromString(String str)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
u64 hash = Fnv64Basis; u64 hash = Fnv64Basis;
if (f->top_tag) if (g->top_tag)
{ {
hash = f->top_tag->hash; hash = g->top_tag->hash;
} }
hash = HashFnv64(hash, str); hash = HashFnv64(hash, str);
UI_Tag *tag = PushStruct(f->build_arena, UI_Tag); UI_Tag *tag = PushStruct(g->build_arena, UI_Tag);
StackPush(f->top_tag, tag); StackPush(g->top_tag, tag);
tag->hash = hash; tag->hash = hash;
} }
@ -80,24 +63,24 @@ void UI_PushTagF_(char *fmt_cstr, ...)
void UI_PopTag(void) void UI_PopTag(void)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
StackPop(f->top_tag); StackPop(g->top_tag);
} }
//- Parent //- Parent
void UI_PushParent(UI_Box *box) void UI_PushParent(UI_Box *box)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
UI_Parent *parent = PushStruct(f->build_arena, UI_Parent); UI_Parent *parent = PushStruct(g->build_arena, UI_Parent);
parent->box = box; parent->box = box;
StackPush(f->top_parent, parent); StackPush(g->top_parent, parent);
} }
void UI_PopParent(void) void UI_PopParent(void)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
StackPop(f->top_parent); StackPop(g->top_parent);
} }
//////////////////////////////// ////////////////////////////////
@ -105,13 +88,24 @@ void UI_PopParent(void)
UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key) UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
UI_BoxBin *bin = &f->box_bins[key.hash % UI_NumBoxLookupBins]; UI_BoxBin *bin = &g->box_bins[key.hash % UI_NumBoxLookupBins];
UI_BoxBin *back_bin = &f->back_box_bins[key.hash % UI_NumBoxLookupBins]; UI_BoxBin *back_bin = &g->back_box_bins[key.hash % UI_NumBoxLookupBins];
UI_Box *box = PushStruct(f->build_arena, UI_Box); UI_Box *box = PushStruct(g->build_arena, UI_Box);
if (key.hash != 0) if (key.hash != 0)
{ {
#if RTC
/* Validate box not already built */
for (UI_Box *tmp = bin->first; tmp; tmp = tmp->next_in_bin)
{
if (tmp->key.hash == key.hash)
{
Assert(0); /* Box with matching key already built */
break;
}
}
#endif
DllPushBackNP(bin->first, bin->last, box, next_in_bin, prev_in_bin); DllPushBackNP(bin->first, bin->last, box, next_in_bin, prev_in_bin);
} }
@ -133,11 +127,13 @@ UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
{ {
*box = *back_box; *box = *back_box;
} }
++g->boxes_count;
box->key = key; box->key = key;
box->flags = flags; box->flags = flags;
box->parent = f->top_parent->box; box->parent = g->top_parent->box;
DllPushBack(f->top_parent->box->first, f->top_parent->box->last, box); DllPushBack(g->top_parent->box->first, g->top_parent->box->last, box);
++box->parent->count;
return box; return box;
} }
@ -147,8 +143,8 @@ UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
void UI_SetDisplayText(UI_Box *box, String str) void UI_SetDisplayText(UI_Box *box, String str)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
String text = PushString(f->build_arena, str); String text = PushString(g->build_arena, str);
box->display_text = text; box->display_text = text;
} }
@ -165,38 +161,43 @@ void UI_SetDisplayImage(UI_Box *box, GPU_Resource *img)
void UI_BeginBuild(void) void UI_BeginBuild(void)
{ {
UI_FiberState *f = UI_GetFiberState(); UI_SharedState *g = &UI_shared_state;
/* Swap front & back build states */ /* Swap front & back build states */
{ {
Arena *swp = f->build_arena; Arena *swp = g->build_arena;
f->build_arena = f->back_build_arena; g->build_arena = g->back_build_arena;
f->back_build_arena = swp; g->back_build_arena = swp;
} }
f->back_box_bins = f->box_bins; g->back_box_bins = g->box_bins;
f->back_root_box = f->root_box; g->back_root_box = g->root_box;
g->back_boxes_count = g->boxes_count;
/* Reset front build state */ /* Reset front build state */
if (!f->build_arena) if (!g->build_arena)
{ {
f->build_arena = AcquireArena(Gibi(64)); g->build_arena = AcquireArena(Gibi(64));
} }
ResetArena(f->build_arena); ResetArena(g->build_arena);
g->boxes_count = 0;
/* Init bins */ /* Init bins */
f->box_bins = PushStructs(f->build_arena, UI_BoxBin, UI_NumBoxLookupBins); g->box_bins = PushStructs(g->build_arena, UI_BoxBin, UI_NumBoxLookupBins);
/* Init root box */ /* Init root box */
f->root_box = PushStruct(f->build_arena, UI_Box); g->root_box = PushStruct(g->build_arena, UI_Box);
f->root_box->key = UI_RootKey; g->root_box->key = UI_RootKey;
UI_BoxBin *bin = &f->box_bins[f->root_box->key.hash % UI_NumBoxLookupBins]; g->root_box->pref_size[Axis_X].kind = UI_SizeKind_Absolute;
DllPushBackNP(bin->first, bin->last, f->root_box, next_in_bin, prev_in_bin); g->root_box->pref_size[Axis_Y].kind = UI_SizeKind_Absolute;
UI_BoxBin *bin = &g->box_bins[g->root_box->key.hash % UI_NumBoxLookupBins];
DllPushBackNP(bin->first, bin->last, g->root_box, next_in_bin, prev_in_bin);
++g->boxes_count;
/* Init stacks */ /* Init stacks */
UI_PushTagF("root"); UI_PushTagF("root");
UI_PushParent(f->root_box); UI_PushParent(g->root_box);
if (!f->back_build_arena) if (!g->back_build_arena)
{ {
/* Back buffer not initialized, swap again */ /* Back buffer not initialized, swap again */
UI_BeginBuild(); UI_BeginBuild();
@ -208,39 +209,199 @@ void UI_BeginBuild(void)
GPU_Resource *UI_EndBuild(void) GPU_Resource *UI_EndBuild(void)
{ {
UI_FiberState *f = UI_GetFiberState(); TempArena scratch = BeginScratchNoConflict();
UI_SharedState *g = &UI_shared_state;
/* TODO: Ensure root is parent */ /* TODO: Ensure root is parent */
////////////////////////////////
//- Layout
/* Init root size */
Vec2I32 root_size = VEC2I32(500, 500);
g->root_box->p0 = VEC2(0, 0);
g->root_box->p1 = VEC2(root_size.x, root_size.y);
/* Build pre-order & post-order box arrays */
u64 boxes_count = g->boxes_count;
UI_Box **boxes_pre = PushStructsNoZero(scratch.arena, UI_Box *, boxes_count);
UI_Box **boxes_post = PushStructsNoZero(scratch.arena, UI_Box *, boxes_count);
{
Struct(BoxNode) { BoxNode *next; b32 visited; UI_Box *box; };
BoxNode *first_dfs = 0;
u64 pre_index = 0;
u64 post_index = 0;
{
BoxNode *n = PushStruct(scratch.arena, BoxNode);
n->box = g->root_box;
StackPush(first_dfs, n);
}
while (first_dfs)
{
BoxNode *n = first_dfs;
UI_Box *box = n->box;
if (!n->visited)
{
/* Push children to dfs stack */
for (UI_Box *child = box->last; child; child = child->prev)
{
BoxNode *child_n = PushStruct(scratch.arena, BoxNode);
child_n->box = child;
StackPush(first_dfs, child_n);
}
boxes_pre[pre_index++] = box;
n->visited = 1;
}
else
{
StackPop(first_dfs);
boxes_post[post_index++] = box;
}
}
Assert(pre_index == boxes_count);
Assert(post_index == boxes_count);
}
/* Calculate independent sizes */
for (u64 box_index = 1; box_index < boxes_count; ++box_index)
{
UI_Box *box = boxes_pre[box_index];
for (Axis axis = 0; axis < Axis_CountXY; ++axis)
{
UI_Size pref_size = box->pref_size[axis];
if (pref_size.kind == UI_SizeKind_Absolute)
{
box->solved_size[axis].v = pref_size.v;
}
else if (pref_size.kind == UI_SizeKind_Text)
{
/* FIXME: Calculate actual text size here */
f32 text_size = 10;
box->solved_size[axis].v = text_size;
}
}
}
/* Calculate upwards-dependent sizes */
for (u64 box_index = 0; box_index < boxes_count; ++box_index)
{
UI_Box *box = boxes_pre[box_index];
for (Axis axis = 0; axis < Axis_CountXY; ++axis)
{
UI_Size pref_size = box->pref_size[axis];
if (pref_size.kind == UI_SizeKind_Percent)
{
UI_Box *ancestor = box->parent;
UI_Size ancestor_size = ZI;
for (; ancestor; ancestor = ancestor->parent)
{
UI_Size tmp = ancestor->solved_size[axis];
if (tmp.kind == UI_SizeKind_Absolute || tmp.kind == UI_SizeKind_Text)
{
ancestor_size = tmp;
break;
}
}
// box->solved_size =
}
}
}
/* TODO: Remove this */
#if 0
{
Struct(BoxNode) { BoxNode *next; UI_Box *box; };
BoxNode *first_dfs = 0;
BoxNode *first_dfs_reverse = 0;
/* Constrain layout downwards */
UI_Box *box = g->root_box;
while (box)
{
/* Push to reverse stack */
{
BoxNode *n = PushStruct(scratch.arena, BoxNode);
n->box = box;
StackPush(first_dfs_reverse, n);
}
{
}
/* Push children to dfs stack (in reverse) */
for (UI_Box *child = box->last; child; child = child->prev)
{
BoxNode *n = PushStruct(scratch.arena, BoxNode);
n->box = child;
StackPush(first_dfs, n);
}
/* Pop dfs stack */
if (first_dfs)
{
box = first_dfs->box;
StackPop(first_dfs);
}
else
{
box = 0;
}
}
}
#endif
////////////////////////////////
//- Build instances
GPU_QueueKind render_queue = GPU_QueueKind_Direct; GPU_QueueKind render_queue = GPU_QueueKind_Direct;
Fence *render_fence = GPU_FenceFromQueue(render_queue); Fence *render_fence = GPU_FenceFromQueue(render_queue);
/* TODO: Real size */ /* Init render state */
Vec2I32 root_size = VEC2I32(500, 500); if (!g->draw_rects_arena)
{
g->draw_rects_arena = AcquireArena(Gibi(64));
}
/* Build rect instance data */
for (u64 box_index = 0; box_index < boxes_count; ++box_index)
{
UI_Box *box = boxes_pre[box_index];
UI_RectInstance *rect = PushStruct(g->draw_rects_arena, UI_RectInstance);
rect->p0 = box->p0;
rect->p1 = box->p1;
rect->tint_srgb = 0xFFFFFFFF;
}
////////////////////////////////
//- Render
/* Acquire render target */ /* Acquire render target */
if (f->render_target && !EqVec2I32(root_size, GPU_GetTextureSize2D(f->render_target))) if (g->render_target && !EqVec2I32(root_size, GPU_GetTextureSize2D(g->render_target)))
{ {
YieldOnFence(render_fence, f->render_fence_target); YieldOnFence(render_fence, g->render_fence_target);
GPU_ReleaseResource(f->render_target, GPU_ReleaseFlag_None); GPU_ReleaseResource(g->render_target, GPU_ReleaseFlag_None);
f->render_target = 0; g->render_target = 0;
} }
if (!f->render_target) if (!g->render_target)
{ {
GPU_ResourceDesc desc = ZI; GPU_ResourceDesc desc = ZI;
desc.kind = GPU_ResourceKind_Texture2D; desc.kind = GPU_ResourceKind_Texture2D;
desc.flags = GPU_ResourceFlag_Renderable; desc.flags = GPU_ResourceFlag_Renderable;
desc.texture.format = GPU_Format_R8G8B8A8_Unorm; desc.texture.format = GPU_Format_R8G8B8A8_Unorm;
desc.texture.size = VEC3I32(root_size.x, root_size.y, 1); desc.texture.size = VEC3I32(root_size.x, root_size.y, 1);
f->render_target = GPU_AcquireResource(desc); g->render_target = GPU_AcquireResource(desc);
} }
/* Build command list */ /* Build command list */
GPU_CommandList *cl = GPU_BeginCommandList(render_queue); GPU_CommandList *cl = GPU_BeginCommandList(render_queue);
{ {
} }
f->render_fence_target = GPU_EndCommandList(cl); g->render_fence_target = GPU_EndCommandList(cl);
return f->render_target; /* Reset render state */
ResetArena(g->draw_rects_arena);
EndScratch(scratch);
return g->render_target;
} }

View File

@ -9,6 +9,23 @@ Struct(UI_Key)
u64 hash; u64 hash;
}; };
////////////////////////////////
//~ Size types
Enum(UI_SizeKind)
{
UI_SizeKind_Children,
UI_SizeKind_Percent,
UI_SizeKind_Absolute,
UI_SizeKind_Text,
};
Struct(UI_Size)
{
UI_SizeKind kind;
f32 v;
};
//////////////////////////////// ////////////////////////////////
//~ Box types //~ Box types
@ -21,20 +38,33 @@ Enum(UI_Flag)
Struct(UI_Box) Struct(UI_Box)
{ {
//- Hash list
UI_Box *next_in_bin; UI_Box *next_in_bin;
UI_Box *prev_in_bin; UI_Box *prev_in_bin;
//- Tree
UI_Box *parent; UI_Box *parent;
UI_Box *first; UI_Box *first;
UI_Box *last; UI_Box *last;
UI_Box *next; UI_Box *next;
UI_Box *prev; UI_Box *prev;
u64 count;
//- Persistent data
UI_Key key; UI_Key key;
UI_Flag flags;
//- Per-build data
UI_Flag flags;
String display_text; String display_text;
GPU_Resource *display_image; GPU_Resource *display_image;
UI_Size pref_size[Axis_CountXY];
//- Solver data
UI_Size solved_size[Axis_CountXY];
//- Post-solve data
Vec2 p0;
Vec2 p1;
}; };
Struct(UI_BoxBin) Struct(UI_BoxBin)
@ -54,7 +84,7 @@ Struct(UI_Parent) { UI_Parent *next; UI_Box *box; };
#define UI_NumBoxLookupBins 16384 #define UI_NumBoxLookupBins 16384
Struct(UI_FiberState) Struct(UI_SharedState)
{ {
//- Build state //- Build state
Arena *build_arena; Arena *build_arena;
@ -66,6 +96,9 @@ Struct(UI_FiberState)
UI_Box *root_box; UI_Box *root_box;
UI_Box *back_root_box; UI_Box *back_root_box;
u64 boxes_count;
u64 back_boxes_count;
//- Stack state //- Stack state
UI_Tag *top_tag; UI_Tag *top_tag;
UI_Parent *top_parent; UI_Parent *top_parent;
@ -73,18 +106,9 @@ Struct(UI_FiberState)
//- Render state //- Render state
GPU_Resource *render_target; GPU_Resource *render_target;
i64 render_fence_target; i64 render_fence_target;
}; Arena *draw_rects_arena;
Struct(UI_SharedState)
{
UI_FiberState *fiber_states[MaxFibers];
} extern UI_shared_state; } extern UI_shared_state;
////////////////////////////////
//~ State helpers
UI_FiberState *UI_GetFiberState(void);
//////////////////////////////// ////////////////////////////////
//~ Key helpers //~ Key helpers

View File

@ -16,7 +16,9 @@ AssertRootConst(UI_RectSig, 20);
Struct(UI_RectInstance) Struct(UI_RectInstance)
{ {
i32 _; Vec2 p0;
Vec2 p1;
u32 tint_srgb;
}; };
#define UI_DefaultRectInstance (UI_RectInstance) { \ #define UI_DefaultRectInstance (UI_RectInstance) { \
0 \ 0 \