immediate mode tweak vars

This commit is contained in:
jacob 2025-12-30 00:06:00 -06:00
parent e8b5edb371
commit bed51e195e
16 changed files with 169 additions and 178 deletions

View File

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

View File

@ -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);
}

View File

@ -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);

View File

@ -3,7 +3,7 @@
Struct(BaseCtx)
{
TweakVarsCtx tweak;
TweakCtx tweak;
CmdLineCtx cmdline;
ResourceCtx resource;
GstatCtx gstat;

View File

@ -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);
}

View File

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

View File

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

View File

@ -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, ...)
{

View File

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

View File

@ -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);
}

View File

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

View File

@ -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));
}
}

View File

@ -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());
}

View File

@ -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);
{

View File

@ -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);

View File

@ -190,6 +190,7 @@ Struct(UI_Report)
{
b32 is_hovered;
b32 is_hot;
b32 is_selected;
f32 exists;
f32 hovered;