diff --git a/src/base/base_math.h b/src/base/base_math.h index a8fe3002..57d03e2e 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -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 diff --git a/src/ui/ui.c b/src/ui/ui.c index 2e810bcb..403472d2 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -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; } diff --git a/src/ui/ui.h b/src/ui/ui.h index b39acfc8..7bef02d0 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -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 diff --git a/src/ui/ui_draw.h b/src/ui/ui_draw.h index ab70bce6..bafb5ff1 100644 --- a/src/ui/ui_draw.h +++ b/src/ui/ui_draw.h @@ -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 \