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 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

View File

@ -1,22 +1,5 @@
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
@ -25,8 +8,8 @@ UI_Key UI_KeyFromString(u64 seed, String str)
/* TOIMPL */
if (seed == 0)
{
UI_FiberState *f = UI_GetFiberState();
seed = RandU64FromSeeds(f->top_parent->box->key.hash, f->top_tag->hash);
UI_SharedState *g = &UI_shared_state;
seed = RandU64FromSeeds(g->top_parent->box->key.hash, g->top_tag->hash);
}
UI_Key result = ZI;
result.hash = HashFnv64(seed, str);
@ -40,27 +23,27 @@ UI_Key UI_KeyFromString(u64 seed, String str)
void UI_PushTagFromHash(u64 hash)
{
UI_FiberState *f = UI_GetFiberState();
if (f->top_tag)
UI_SharedState *g = &UI_shared_state;
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);
StackPush(f->top_tag, tag);
UI_Tag *tag = PushStruct(g->build_arena, UI_Tag);
StackPush(g->top_tag, tag);
tag->hash = hash;
}
void UI_PushTagFromString(String str)
{
UI_FiberState *f = UI_GetFiberState();
UI_SharedState *g = &UI_shared_state;
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);
UI_Tag *tag = PushStruct(f->build_arena, UI_Tag);
StackPush(f->top_tag, tag);
UI_Tag *tag = PushStruct(g->build_arena, UI_Tag);
StackPush(g->top_tag, tag);
tag->hash = hash;
}
@ -80,24 +63,24 @@ void UI_PushTagF_(char *fmt_cstr, ...)
void UI_PopTag(void)
{
UI_FiberState *f = UI_GetFiberState();
StackPop(f->top_tag);
UI_SharedState *g = &UI_shared_state;
StackPop(g->top_tag);
}
//- Parent
void UI_PushParent(UI_Box *box)
{
UI_FiberState *f = UI_GetFiberState();
UI_Parent *parent = PushStruct(f->build_arena, UI_Parent);
UI_SharedState *g = &UI_shared_state;
UI_Parent *parent = PushStruct(g->build_arena, UI_Parent);
parent->box = box;
StackPush(f->top_parent, parent);
StackPush(g->top_parent, parent);
}
void UI_PopParent(void)
{
UI_FiberState *f = UI_GetFiberState();
StackPop(f->top_parent);
UI_SharedState *g = &UI_shared_state;
StackPop(g->top_parent);
}
////////////////////////////////
@ -105,13 +88,24 @@ void UI_PopParent(void)
UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
{
UI_FiberState *f = UI_GetFiberState();
UI_BoxBin *bin = &f->box_bins[key.hash % UI_NumBoxLookupBins];
UI_BoxBin *back_bin = &f->back_box_bins[key.hash % UI_NumBoxLookupBins];
UI_SharedState *g = &UI_shared_state;
UI_BoxBin *bin = &g->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 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);
}
@ -133,11 +127,13 @@ UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
{
*box = *back_box;
}
++g->boxes_count;
box->key = key;
box->flags = flags;
box->parent = f->top_parent->box;
DllPushBack(f->top_parent->box->first, f->top_parent->box->last, box);
box->parent = g->top_parent->box;
DllPushBack(g->top_parent->box->first, g->top_parent->box->last, box);
++box->parent->count;
return box;
}
@ -147,8 +143,8 @@ UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
void UI_SetDisplayText(UI_Box *box, String str)
{
UI_FiberState *f = UI_GetFiberState();
String text = PushString(f->build_arena, str);
UI_SharedState *g = &UI_shared_state;
String text = PushString(g->build_arena, str);
box->display_text = text;
}
@ -165,38 +161,43 @@ void UI_SetDisplayImage(UI_Box *box, GPU_Resource *img)
void UI_BeginBuild(void)
{
UI_FiberState *f = UI_GetFiberState();
UI_SharedState *g = &UI_shared_state;
/* Swap front & back build states */
{
Arena *swp = f->build_arena;
f->build_arena = f->back_build_arena;
f->back_build_arena = swp;
Arena *swp = g->build_arena;
g->build_arena = g->back_build_arena;
g->back_build_arena = swp;
}
f->back_box_bins = f->box_bins;
f->back_root_box = f->root_box;
g->back_box_bins = g->box_bins;
g->back_root_box = g->root_box;
g->back_boxes_count = g->boxes_count;
/* 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 */
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 */
f->root_box = PushStruct(f->build_arena, UI_Box);
f->root_box->key = UI_RootKey;
UI_BoxBin *bin = &f->box_bins[f->root_box->key.hash % UI_NumBoxLookupBins];
DllPushBackNP(bin->first, bin->last, f->root_box, next_in_bin, prev_in_bin);
g->root_box = PushStruct(g->build_arena, UI_Box);
g->root_box->key = UI_RootKey;
g->root_box->pref_size[Axis_X].kind = UI_SizeKind_Absolute;
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 */
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 */
UI_BeginBuild();
@ -208,39 +209,199 @@ void UI_BeginBuild(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 */
////////////////////////////////
//- 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;
Fence *render_fence = GPU_FenceFromQueue(render_queue);
/* TODO: Real size */
Vec2I32 root_size = VEC2I32(500, 500);
/* Init render state */
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 */
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);
GPU_ReleaseResource(f->render_target, GPU_ReleaseFlag_None);
f->render_target = 0;
YieldOnFence(render_fence, g->render_fence_target);
GPU_ReleaseResource(g->render_target, GPU_ReleaseFlag_None);
g->render_target = 0;
}
if (!f->render_target)
if (!g->render_target)
{
GPU_ResourceDesc desc = ZI;
desc.kind = GPU_ResourceKind_Texture2D;
desc.flags = GPU_ResourceFlag_Renderable;
desc.texture.format = GPU_Format_R8G8B8A8_Unorm;
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 */
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;
};
////////////////////////////////
//~ 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
@ -21,20 +38,33 @@ Enum(UI_Flag)
Struct(UI_Box)
{
//- Hash list
UI_Box *next_in_bin;
UI_Box *prev_in_bin;
//- Tree
UI_Box *parent;
UI_Box *first;
UI_Box *last;
UI_Box *next;
UI_Box *prev;
u64 count;
//- Persistent data
UI_Key key;
UI_Flag flags;
//- Per-build data
UI_Flag flags;
String display_text;
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)
@ -54,7 +84,7 @@ Struct(UI_Parent) { UI_Parent *next; UI_Box *box; };
#define UI_NumBoxLookupBins 16384
Struct(UI_FiberState)
Struct(UI_SharedState)
{
//- Build state
Arena *build_arena;
@ -66,6 +96,9 @@ Struct(UI_FiberState)
UI_Box *root_box;
UI_Box *back_root_box;
u64 boxes_count;
u64 back_boxes_count;
//- Stack state
UI_Tag *top_tag;
UI_Parent *top_parent;
@ -73,18 +106,9 @@ Struct(UI_FiberState)
//- Render state
GPU_Resource *render_target;
i64 render_fence_target;
};
Struct(UI_SharedState)
{
UI_FiberState *fiber_states[MaxFibers];
Arena *draw_rects_arena;
} extern UI_shared_state;
////////////////////////////////
//~ State helpers
UI_FiberState *UI_GetFiberState(void);
////////////////////////////////
//~ Key helpers

View File

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