From bed51e195ea074cb5ef351c52e92dbf3433706c6 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 30 Dec 2025 00:06:00 -0600 Subject: [PATCH] immediate mode tweak vars --- src/base/base.cgh | 35 ++++++++++ src/base/base_rand.c | 23 +------ src/base/base_rand.h | 6 -- src/base/base_state.h | 2 +- src/base/base_tweak.c | 115 ++++++++++++++++--------------- src/base/base_tweak.h | 76 +++++++++++--------- src/base/base_uid.c | 4 +- src/base/base_util.h | 13 ---- src/config.h | 10 +-- src/glyph_cache/glyph_cache.c | 4 +- src/gpu/gpu_dx12/gpu_dx12_core.c | 2 +- src/meta/meta.c | 2 +- src/pp/pp_vis/pp_vis_core.c | 45 +++++------- src/ttf/ttf_dwrite/ttf_dwrite.c | 2 +- src/ui/ui_core.c | 7 +- src/ui/ui_core.h | 1 + 16 files changed, 169 insertions(+), 178 deletions(-) diff --git a/src/base/base.cgh b/src/base/base.cgh index 296e30b6..f7b1ba60 100644 --- a/src/base/base.cgh +++ b/src/base/base.cgh @@ -734,6 +734,41 @@ typedef ExitFuncDef(ExitFunc); #endif +//////////////////////////////////////////////////////////// +//~ Basic mixing/hashing + +#if IsLanguageC + // Based on Jon Maiga's "mx3" + // https://jonkagstrom.com/mx3/mx3_rev2.html + u64 MixU64(u64 seed) + { + seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; + seed = (seed ^ (seed >> 29)) * 0xbea225f9eb34556d; + seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; + seed = (seed ^ (seed >> 29)); + return seed; + } + + u64 MixU64s(u64 seed_a, u64 seed_b) + { + return MixU64((seed_a * 3) + seed_b); + } + + // FNV-1a parameters for different hash sizes: + // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters + Inline u64 HashFnv64(u64 seed, String s) + { + u64 hash = seed; + for (u64 i = 0; i < s.len; ++i) + { + hash ^= (u8)s.text[i]; + hash *= 0x100000001B3; + } + return hash; + } +#endif + + //////////////////////////////////////////////////////////// //~ @hookdecl Core api diff --git a/src/base/base_rand.c b/src/base/base_rand.c index 4d084cf7..f9d9b461 100644 --- a/src/base/base_rand.c +++ b/src/base/base_rand.c @@ -6,31 +6,10 @@ u64 RandU64FromState(RandState *state) TrueRand(StringFromStruct(&seed)); state->seed = seed; } - return seed ^ RandU64FromSeed(++state->counter); + return seed ^ MixU64(++state->counter); } f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end) { return range_start + (range_end - range_start) * ((f64)(RandU64FromState(state) % RandMaxF64) / (f64)RandMaxF64); } - -// Based on Jon Maiga's "mx3" -// https://jonkagstrom.com/mx3/mx3_rev2.html -u64 RandU64FromSeed(u64 seed) -{ - seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; - seed = (seed ^ (seed >> 29)) * 0xbea225f9eb34556d; - seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; - seed = (seed ^ (seed >> 29)); - return seed; -} - -u64 RandU64FromSeeds(u64 seed_a, u64 seed_b) -{ - return RandU64FromSeed((seed_a * 3) + seed_b); -} - -f64 RandF64FromSeed(u64 seed, f64 range_start, f64 range_end) -{ - return range_start + (range_end - range_start) * ((f64)(RandU64FromSeed(seed) % RandMaxF64) / (f64)RandMaxF64); -} diff --git a/src/base/base_rand.h b/src/base/base_rand.h index e42d97e0..18dd5958 100644 --- a/src/base/base_rand.h +++ b/src/base/base_rand.h @@ -13,11 +13,5 @@ Struct(RandState) //////////////////////////////////////////////////////////// //~ Rand ops -//- Stateful u64 RandU64FromState(RandState *state); f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end); - -//- Seeded -u64 RandU64FromSeed(u64 seed); -u64 RandU64FromSeeds(u64 seed_a, u64 seed_b); -f64 RandF64FromSeed(u64 seed, f64 range_start, f64 range_end); diff --git a/src/base/base_state.h b/src/base/base_state.h index d77d3a2b..0f29ab7e 100644 --- a/src/base/base_state.h +++ b/src/base/base_state.h @@ -3,7 +3,7 @@ Struct(BaseCtx) { - TweakVarsCtx tweak; + TweakCtx tweak; CmdLineCtx cmdline; ResourceCtx resource; GstatCtx gstat; diff --git a/src/base/base_tweak.c b/src/base/base_tweak.c index 20b332b6..2bb5de80 100644 --- a/src/base/base_tweak.c +++ b/src/base/base_tweak.c @@ -4,78 +4,79 @@ void BootstrapTweakVars(void) { // TODO: Swap in/out for persistence - #define X(name, type, default_value) Base.tweak.shared_vars.##name = default_value; - TweakVarsXMacro(X); - #undef X } //////////////////////////////////////////////////////////// -//~ Tweak var get/set +//~ Tweak var operations -//- Auto-generated functions - -#define X(name, type, default_value) \ - type GetGlobalTweakVar_TweakVarKind_##name(void) \ - { \ - type result = Zi; \ - LockTicketMutex(&Base.tweak.tm); \ - { \ - result = Base.tweak.shared_vars.##name; \ - } \ - UnlockTicketMutex(&Base.tweak.tm); \ - return result; \ - } \ - void SetGlobalTweakVar_TweakVarKind_##name(type v) \ - { \ - LockTicketMutex(&Base.tweak.tm); \ - { \ - Base.tweak.shared_vars.name = v; \ - } \ - UnlockTicketMutex(&Base.tweak.tm); \ - } - TweakVarsXMacro(X); -#undef X - -//- Helpers - -TweakVarDesc TweakVarDescFromKind(TweakVarKind kind) +TweakVar TweakEx(String name, TweakKind kind, TweakValue value, b32 set_new_value) { - Readonly PERSIST TweakVarDesc descs[TweakVarKind_COUNT] = { -#define X(_name, _type, _default_value) \ - { \ - .name = CompLit(#_name), \ - .type = TweakVarType_##_type, \ - .offset = offsetof(TweakVars, _name), \ - .default_##_type = (_default_value), \ - }, - TweakVarsXMacro(X) -#undef X - }; - - TweakVarDesc result = Zi; - if (kind >= 0 && kind < countof(descs)) - { - result = descs[kind]; - } - return result; -} - -TweakVars GetAllGlobalTweakVars(void) -{ - TweakVars result; + TweakVar result = Zi; + u64 hash = MixU64(HashFnv64(Fnv64Basis, name)); + TweakVarEntryBin *bin = &Base.tweak.entry_bins[hash % countof(Base.tweak.entry_bins)]; LockTicketMutex(&Base.tweak.tm); { - result = Base.tweak.shared_vars; + TweakVarEntry *e = bin->first; + for (; e; e = e->next_in_bin) + { + if (e->hash == hash) + { + break; + } + } + if (e) + { + if (set_new_value) + { + e->v.value = value; + } + result = e->v; + } + else + { + Arena *perm = PermArena(); + PushAlign(perm, CachelineSize); + e = PushStruct(perm, TweakVarEntry); + e->hash = hash; + { + TweakVar *v = &e->v; + v->name = PushString(perm, name); + v->kind = kind; + v->value = value; + v->default_value = value; + result = *v; + } + PushAlign(perm, CachelineSize); + SllQueuePushN(bin->first, bin->last, e, next_in_bin); + SllQueuePushN(Base.tweak.first_entry, Base.tweak.last_entry, e, next_in_list); + Base.tweak.entries_count += 1; + } } UnlockTicketMutex(&Base.tweak.tm); return result; } -void SetAllGlobalTweakVars(TweakVars *v) +TweakVarArray GetAllTweakVars(Arena *arena) { + TweakVarArray result = Zi; + LockTicketMutex(&Base.tweak.tm); { - CopyStruct(&Base.tweak.shared_vars, v); + result.count = Base.tweak.entries_count; + result.v = PushStructsNoZero(arena, TweakVar, result.count); + i64 var_idx = 0; + for (TweakVarEntry *e = Base.tweak.first_entry; e; e = e->next_in_list) + { + result.v[var_idx] = e->v; + var_idx += 1; + } } UnlockTicketMutex(&Base.tweak.tm); + + return result; +} + +b32 MatchTweakValue(TweakValue a, TweakValue b) +{ + return MatchStruct(&a, &b); } diff --git a/src/base/base_tweak.h b/src/base/base_tweak.h index dd40d2ff..96f87c50 100644 --- a/src/base/base_tweak.h +++ b/src/base/base_tweak.h @@ -1,45 +1,59 @@ //////////////////////////////////////////////////////////// //~ Tweak var types -Enum(TweakVarType) +Enum(TweakKind) { - TweakVarType_b32, + TweakKind_Bool, }; -Enum(TweakVarKind) +Struct(TweakValue) { -#define X(name, ...) TweakVarKind_##name, - TweakVarsXMacro(X) -#undef X - TweakVarKind_COUNT + f64 x, y, z, w; }; -Struct(TweakVars) -{ -#define X(name, type, ...) type name; - TweakVarsXMacro(X) -#undef X - i32 __; // Prevent empty struct -}; - -Struct(TweakVarDesc) +Struct(TweakVar) { String name; - TweakVarType type; - i32 offset; - union - { - b32 default_b32; - }; + TweakKind kind; + TweakValue value; + TweakValue default_value; +}; + +Struct(TweakVarArray) +{ + i64 count; + TweakVar *v; +}; + +//////////////////////////////////////////////////////////// +//~ Cache types + +Struct(TweakVarEntry) +{ + TweakVarEntry *next_in_bin; + TweakVarEntry *next_in_list; + u64 hash; + TweakVar v; +}; + +Struct(TweakVarEntryBin) +{ + TweakVarEntry *first; + TweakVarEntry *last; }; //////////////////////////////////////////////////////////// //~ State types -Struct(TweakVarsCtx) +Struct(TweakCtx) { TicketMutex tm; - TweakVars shared_vars; + + TweakVarEntryBin entry_bins[8192]; + + i64 entries_count; + TweakVarEntry *first_entry; + TweakVarEntry *last_entry; }; //////////////////////////////////////////////////////////// @@ -50,15 +64,9 @@ void BootstrapTweakVars(void); //////////////////////////////////////////////////////////// //~ Tweak var operations -//- Auto-generated functions -#define X(name, type, ...) \ - type GetGlobalTweakVar_##name(void); \ - void SetGlobalTweakVar_##name(type v); -#undef X +TweakVar TweakEx(String name, TweakKind kind, TweakValue value, b32 set_new_value); +TweakVarArray GetAllTweakVars(Arena *arena); //- Helpers -TweakVarDesc TweakVarDescFromKind(TweakVarKind kind); -TweakVars GetAllGlobalTweakVars(void); -void SetAllGlobalTweakVars(TweakVars *v); -#define GetGlobalTweakVar(name) GetGlobalTweakVar_TweakVarKind_##name() -#define SetGlobalTweakVar(name, v) SetGlobalTweakVar_TweakVarKind_##name((v)) +b32 MatchTweakValue(TweakValue a, TweakValue b); +#define TweakB32(_name, _default_value) (TweakEx(Lit(_name), TweakKind_Bool, (TweakValue) { .x = !!(_default_value) }, 0).value.x >= 0.5) diff --git a/src/base/base_uid.c b/src/base/base_uid.c index e2539bce..06dd207b 100644 --- a/src/base/base_uid.c +++ b/src/base/base_uid.c @@ -14,8 +14,8 @@ Uid CombineUid(Uid a, Uid b) result.lo = (a.lo * 3) + b.lo; result.hi += result.lo; result.lo += result.hi; - result.hi = RandU64FromSeed(result.hi); - result.lo = RandU64FromSeed(result.lo); + result.hi = MixU64(result.hi); + result.lo = MixU64(result.lo); result.hi += result.lo; result.lo += result.hi; return result; diff --git a/src/base/base_util.h b/src/base/base_util.h index df62ce1a..7b8c074a 100644 --- a/src/base/base_util.h +++ b/src/base/base_util.h @@ -44,19 +44,6 @@ Struct(Dict) //////////////////////////////////////////////////////////// //~ Hash utils -// FNV-1a parameters for different hash sizes: -// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters -Inline u64 HashFnv64(u64 seed, String s) -{ - u64 hash = seed; - for (u64 i = 0; i < s.len; ++i) - { - hash ^= (u8)s.text[i]; - hash *= 0x100000001B3; - } - return hash; -} - #define HashF(fmt_cstr, ...) HashF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd) Inline u64 HashF_(String fmt, ...) { diff --git a/src/config.h b/src/config.h index 9e77fc1c..65090c47 100644 --- a/src/config.h +++ b/src/config.h @@ -1,12 +1,4 @@ -//////////////////////////////////////////////////////////// -//~ Debug tweak vars - -#define TweakVarsXMacro(X) \ - X(CeilGlyphAdvances, b32, 0) \ -/* --------------------------------- */ - -//////////////////////////////////////////////////////////// -//~ Project-wide configurable constants +// Project-wide configurable constants #define WRITE_DIR "power_play" diff --git a/src/glyph_cache/glyph_cache.c b/src/glyph_cache/glyph_cache.c index cdda1130..900c929c 100644 --- a/src/glyph_cache/glyph_cache.c +++ b/src/glyph_cache/glyph_cache.c @@ -21,7 +21,7 @@ GC_FontKey GC_FontKeyFromResource(ResourceKey resource) u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc) { // TODO: Lower font-size precision to prevent unique hashes for slightly-different font sizes - return RandU64FromSeeds(desc.font.r.v, ((u64)desc.codepoint << 32) | *(u32 *)&desc.font_size); + return MixU64s(desc.font.r.v, ((u64)desc.codepoint << 32) | *(u32 *)&desc.font_size); } //////////////////////////////////////////////////////////// @@ -180,7 +180,7 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size GC_RunRect *rect = &result.rects[glyph_idx]; f32 advance = 0; - if (GetGlobalTweakVar(CeilGlyphAdvances)) + if (TweakB32("Ceil glyph advances", 1)) { advance = CeilF32(glyph->advance * scale); } diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index d536b882..a62ce0bd 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -446,7 +446,7 @@ D3D12_BARRIER_LAYOUT G_D12_BarrierLayoutFromLayout(G_Layout layout) G_D12_Pipeline *G_D12_PipelineFromDesc(G_D12_PipelineDesc desc) { - u64 hash = RandU64FromSeed(HashFnv64(Fnv64Basis, StringFromStruct(&desc))); + u64 hash = MixU64(HashFnv64(Fnv64Basis, StringFromStruct(&desc))); // Fetch pipeline from cache G_D12_Pipeline *pipeline = 0; diff --git a/src/meta/meta.c b/src/meta/meta.c index e4466d14..acaa931c 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -283,7 +283,7 @@ void BuildEntryPoint(WaveLaneCtx *lane) for (StringListNode *n = check_files.first; n; n = n->next) { String file = n->s; - new_metahash = RandU64FromSeeds(HashFnv64(new_metahash, file), OS_LastWriteTimestampFromPath(file)); + new_metahash = MixU64s(HashFnv64(new_metahash, file), OS_LastWriteTimestampFromPath(file)); } } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 0e7f7ae1..64446451 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -1393,7 +1393,7 @@ void V_TickForever(WaveLaneCtx *lane) String display_name; V_CmdDesc cmd_desc; - TweakVarDesc tweak_desc; + TweakVar tweak_var; }; PaletteItem *first_item = 0; PaletteItem *last_item = 0; @@ -1424,16 +1424,18 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Push tweak variables + TweakVarArray tweak_vars = GetAllTweakVars(frame->arena); + { - for (TweakVarKind tweak_var_kind = 0; tweak_var_kind < TweakVarKind_COUNT; ++tweak_var_kind) + for (i64 tweak_idx = 0; tweak_idx < tweak_vars.count; ++tweak_idx) { - TweakVarDesc tweak_desc = TweakVarDescFromKind(tweak_var_kind); + TweakVar tweak_var = tweak_vars.v[tweak_idx]; PaletteItem *item = PushStruct(frame->arena, PaletteItem); { - item->key = UI_KeyF("tweak var palette item %F", FmtString(tweak_desc.name)); - item->display_name = tweak_desc.name; + item->key = UI_KeyF("tweak var palette item %F", FmtString(tweak_var.name)); + item->display_name = tweak_var.name; item->flags |= PaletteItemFlag_IsTweakVar; - item->tweak_desc = tweak_desc; + item->tweak_var = tweak_var; } DllQueuePush(first_item, last_item, item); } @@ -1442,8 +1444,6 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Build items - TweakVars tweak_vars = GetAllGlobalTweakVars(); - for (PaletteItem *item = first_item; item; item = item->next) { UI_BuildDivider(UI_PIX(1, 1), theme.divider_color, Axis_Y); @@ -1491,34 +1491,24 @@ void V_TickForever(WaveLaneCtx *lane) // Tweak if (item->flags & PaletteItemFlag_IsTweakVar) { - TweakVarDesc tweak_desc = item->tweak_desc; + TweakVar tweak_var = item->tweak_var; UI_Key tweak_key = UI_KeyF("tweak"); UI_Report tweak_rep = UI_ReportFromKey(tweak_key); + TweakValue new_tweak_value = tweak_var.value; - switch (tweak_desc.type) + switch (tweak_var.kind) { // Tweak checkbox - case TweakVarType_b32: + case TweakKind_Bool: { - b32 *tweak_val = (b32 *)(&tweak_vars + tweak_desc.offset); - - Vec4 tweak_bg_color = Zi; - if (*tweak_val) - { - tweak_bg_color = theme.color_positive; - } - else - { - tweak_bg_color = theme.color_negative; - } - + Vec4 tweak_bg_color = LerpSrgb(theme.color_negative, theme.color_positive, new_tweak_value.x); Vec4 tweak_border_color = Zi; tweak_border_color = LerpSrgb(tweak_border_color, theme.button_hot_color, tweak_rep.hot); tweak_border_color = LerpSrgb(tweak_border_color, theme.button_active_color, tweak_rep.active); if (tweak_rep.m1.downs) { - *tweak_val = !*tweak_val; + new_tweak_value.x = !new_tweak_value.x; } UI_SetNext(BackgroundColor, tweak_bg_color); @@ -1532,6 +1522,11 @@ void V_TickForever(WaveLaneCtx *lane) { } UI_PopCP(UI_TopCP()); + + if (!MatchTweakValue(tweak_var.value, new_tweak_value)) + { + TweakEx(tweak_var.name, tweak_var.kind, new_tweak_value, 1); + } } break; } } @@ -1589,8 +1584,6 @@ void V_TickForever(WaveLaneCtx *lane) } UI_PopCP(UI_TopCP()); } - - SetAllGlobalTweakVars(&tweak_vars); } UI_PopCP(UI_TopCP()); } diff --git a/src/ttf/ttf_dwrite/ttf_dwrite.c b/src/ttf/ttf_dwrite/ttf_dwrite.c index 4521ec1b..ad422882 100644 --- a/src/ttf/ttf_dwrite/ttf_dwrite.c +++ b/src/ttf/ttf_dwrite/ttf_dwrite.c @@ -91,7 +91,7 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res HRESULT hr = 0; TTF_DW_Font *font = 0; { - u64 hash = RandU64FromSeeds(ttf.v, (u64)(*(u32 *)&font_size)); + u64 hash = MixU64s(ttf.v, (u64)(*(u32 *)&font_size)); { Lock lock = LockS(&TTF_DW.font_bins_mutex); { diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index a7faf7b7..39e18475 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -55,7 +55,7 @@ UI_Key UI_RandKey(void) { u64 seed = ++UI.rand_key_seed; UI_Key key = Zi; - key.hash = RandU64FromSeed(seed); + key.hash = MixU64(seed); return key; } @@ -390,7 +390,7 @@ void UI_PushStyle(UI_StyleDesc desc) { if (n->next != 0) { - n->style.Tag = RandU64FromSeeds(n->next->style.Tag, n->style.Tag); + n->style.Tag = MixU64s(n->next->style.Tag, n->style.Tag); } } break; @@ -804,12 +804,13 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) UI_Box *box = last_frame->boxes_pre[pre_index]; UI_Report *report = &box->report; report->is_hot = box == hot_box; + report->is_selected = !!(box->desc.flags & UI_BoxFlag_Selected); f32 target_exists = box->last_build_tick >= (frame->tick - 1); f32 target_hovered = report->is_hovered; f32 target_hot = report->is_hot; f32 target_active = box == active_box; - f32 target_selected = !!(box->desc.flags & UI_BoxFlag_Selected); + f32 target_selected = report->is_selected; f32 exists_blend_rate = (40 * frame->dt); f32 hot_blend_rate = target_hot == 1 ? 1 : (15 * frame->dt); diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 295362ca..91596a3f 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -190,6 +190,7 @@ Struct(UI_Report) { b32 is_hovered; b32 is_hot; + b32 is_selected; f32 exists; f32 hovered;