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); typedef ExitFuncDef(ExitFunc);
#endif #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 //~ @hookdecl Core api

View File

@ -6,31 +6,10 @@ u64 RandU64FromState(RandState *state)
TrueRand(StringFromStruct(&seed)); TrueRand(StringFromStruct(&seed));
state->seed = seed; state->seed = seed;
} }
return seed ^ RandU64FromSeed(++state->counter); return seed ^ MixU64(++state->counter);
} }
f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end) f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end)
{ {
return range_start + (range_end - range_start) * ((f64)(RandU64FromState(state) % RandMaxF64) / (f64)RandMaxF64); 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 //~ Rand ops
//- Stateful
u64 RandU64FromState(RandState *state); u64 RandU64FromState(RandState *state);
f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end); 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) Struct(BaseCtx)
{ {
TweakVarsCtx tweak; TweakCtx tweak;
CmdLineCtx cmdline; CmdLineCtx cmdline;
ResourceCtx resource; ResourceCtx resource;
GstatCtx gstat; GstatCtx gstat;

View File

@ -4,78 +4,79 @@
void BootstrapTweakVars(void) void BootstrapTweakVars(void)
{ {
// TODO: Swap in/out for persistence // 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 TweakVar TweakEx(String name, TweakKind kind, TweakValue value, b32 set_new_value)
#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)
{ {
Readonly PERSIST TweakVarDesc descs[TweakVarKind_COUNT] = { TweakVar result = Zi;
#define X(_name, _type, _default_value) \ u64 hash = MixU64(HashFnv64(Fnv64Basis, name));
{ \ TweakVarEntryBin *bin = &Base.tweak.entry_bins[hash % countof(Base.tweak.entry_bins)];
.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;
LockTicketMutex(&Base.tweak.tm); 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); UnlockTicketMutex(&Base.tweak.tm);
return result; return result;
} }
void SetAllGlobalTweakVars(TweakVars *v) TweakVarArray GetAllTweakVars(Arena *arena)
{ {
TweakVarArray result = Zi;
LockTicketMutex(&Base.tweak.tm); 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); 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 //~ Tweak var types
Enum(TweakVarType) Enum(TweakKind)
{ {
TweakVarType_b32, TweakKind_Bool,
}; };
Enum(TweakVarKind) Struct(TweakValue)
{ {
#define X(name, ...) TweakVarKind_##name, f64 x, y, z, w;
TweakVarsXMacro(X)
#undef X
TweakVarKind_COUNT
}; };
Struct(TweakVars) Struct(TweakVar)
{
#define X(name, type, ...) type name;
TweakVarsXMacro(X)
#undef X
i32 __; // Prevent empty struct
};
Struct(TweakVarDesc)
{ {
String name; String name;
TweakVarType type; TweakKind kind;
i32 offset; TweakValue value;
union TweakValue default_value;
{ };
b32 default_b32;
}; 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 //~ State types
Struct(TweakVarsCtx) Struct(TweakCtx)
{ {
TicketMutex tm; 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 //~ Tweak var operations
//- Auto-generated functions TweakVar TweakEx(String name, TweakKind kind, TweakValue value, b32 set_new_value);
#define X(name, type, ...) \ TweakVarArray GetAllTweakVars(Arena *arena);
type GetGlobalTweakVar_##name(void); \
void SetGlobalTweakVar_##name(type v);
#undef X
//- Helpers //- Helpers
TweakVarDesc TweakVarDescFromKind(TweakVarKind kind); b32 MatchTweakValue(TweakValue a, TweakValue b);
TweakVars GetAllGlobalTweakVars(void); #define TweakB32(_name, _default_value) (TweakEx(Lit(_name), TweakKind_Bool, (TweakValue) { .x = !!(_default_value) }, 0).value.x >= 0.5)
void SetAllGlobalTweakVars(TweakVars *v);
#define GetGlobalTweakVar(name) GetGlobalTweakVar_TweakVarKind_##name()
#define SetGlobalTweakVar(name, v) SetGlobalTweakVar_TweakVarKind_##name((v))

View File

@ -14,8 +14,8 @@ Uid CombineUid(Uid a, Uid b)
result.lo = (a.lo * 3) + b.lo; result.lo = (a.lo * 3) + b.lo;
result.hi += result.lo; result.hi += result.lo;
result.lo += result.hi; result.lo += result.hi;
result.hi = RandU64FromSeed(result.hi); result.hi = MixU64(result.hi);
result.lo = RandU64FromSeed(result.lo); result.lo = MixU64(result.lo);
result.hi += result.lo; result.hi += result.lo;
result.lo += result.hi; result.lo += result.hi;
return result; return result;

View File

@ -44,19 +44,6 @@ Struct(Dict)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Hash utils //~ 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) #define HashF(fmt_cstr, ...) HashF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
Inline u64 HashF_(String fmt, ...) Inline u64 HashF_(String fmt, ...)
{ {

View File

@ -1,12 +1,4 @@
//////////////////////////////////////////////////////////// // Project-wide configurable constants
//~ Debug tweak vars
#define TweakVarsXMacro(X) \
X(CeilGlyphAdvances, b32, 0) \
/* --------------------------------- */
////////////////////////////////////////////////////////////
//~ Project-wide configurable constants
#define WRITE_DIR "power_play" #define WRITE_DIR "power_play"

View File

@ -21,7 +21,7 @@ GC_FontKey GC_FontKeyFromResource(ResourceKey resource)
u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc) u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc)
{ {
// TODO: Lower font-size precision to prevent unique hashes for slightly-different font sizes // 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]; GC_RunRect *rect = &result.rects[glyph_idx];
f32 advance = 0; f32 advance = 0;
if (GetGlobalTweakVar(CeilGlyphAdvances)) if (TweakB32("Ceil glyph advances", 1))
{ {
advance = CeilF32(glyph->advance * scale); 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) 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 // Fetch pipeline from cache
G_D12_Pipeline *pipeline = 0; 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) for (StringListNode *n = check_files.first; n; n = n->next)
{ {
String file = n->s; 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; String display_name;
V_CmdDesc cmd_desc; V_CmdDesc cmd_desc;
TweakVarDesc tweak_desc; TweakVar tweak_var;
}; };
PaletteItem *first_item = 0; PaletteItem *first_item = 0;
PaletteItem *last_item = 0; PaletteItem *last_item = 0;
@ -1424,16 +1424,18 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Push tweak variables //- 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); PaletteItem *item = PushStruct(frame->arena, PaletteItem);
{ {
item->key = UI_KeyF("tweak var palette item %F", FmtString(tweak_desc.name)); item->key = UI_KeyF("tweak var palette item %F", FmtString(tweak_var.name));
item->display_name = tweak_desc.name; item->display_name = tweak_var.name;
item->flags |= PaletteItemFlag_IsTweakVar; item->flags |= PaletteItemFlag_IsTweakVar;
item->tweak_desc = tweak_desc; item->tweak_var = tweak_var;
} }
DllQueuePush(first_item, last_item, item); DllQueuePush(first_item, last_item, item);
} }
@ -1442,8 +1444,6 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Build items //- Build items
TweakVars tweak_vars = GetAllGlobalTweakVars();
for (PaletteItem *item = first_item; item; item = item->next) for (PaletteItem *item = first_item; item; item = item->next)
{ {
UI_BuildDivider(UI_PIX(1, 1), theme.divider_color, Axis_Y); UI_BuildDivider(UI_PIX(1, 1), theme.divider_color, Axis_Y);
@ -1491,34 +1491,24 @@ void V_TickForever(WaveLaneCtx *lane)
// Tweak // Tweak
if (item->flags & PaletteItemFlag_IsTweakVar) 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_Key tweak_key = UI_KeyF("tweak");
UI_Report tweak_rep = UI_ReportFromKey(tweak_key); 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 // Tweak checkbox
case TweakVarType_b32: case TweakKind_Bool:
{ {
b32 *tweak_val = (b32 *)(&tweak_vars + tweak_desc.offset); Vec4 tweak_bg_color = LerpSrgb(theme.color_negative, theme.color_positive, new_tweak_value.x);
Vec4 tweak_bg_color = Zi;
if (*tweak_val)
{
tweak_bg_color = theme.color_positive;
}
else
{
tweak_bg_color = theme.color_negative;
}
Vec4 tweak_border_color = Zi; 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_hot_color, tweak_rep.hot);
tweak_border_color = LerpSrgb(tweak_border_color, theme.button_active_color, tweak_rep.active); tweak_border_color = LerpSrgb(tweak_border_color, theme.button_active_color, tweak_rep.active);
if (tweak_rep.m1.downs) if (tweak_rep.m1.downs)
{ {
*tweak_val = !*tweak_val; new_tweak_value.x = !new_tweak_value.x;
} }
UI_SetNext(BackgroundColor, tweak_bg_color); UI_SetNext(BackgroundColor, tweak_bg_color);
@ -1532,6 +1522,11 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
if (!MatchTweakValue(tweak_var.value, new_tweak_value))
{
TweakEx(tweak_var.name, tweak_var.kind, new_tweak_value, 1);
}
} break; } break;
} }
} }
@ -1589,8 +1584,6 @@ void V_TickForever(WaveLaneCtx *lane)
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }
SetAllGlobalTweakVars(&tweak_vars);
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }

View File

@ -91,7 +91,7 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
HRESULT hr = 0; HRESULT hr = 0;
TTF_DW_Font *font = 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); 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; u64 seed = ++UI.rand_key_seed;
UI_Key key = Zi; UI_Key key = Zi;
key.hash = RandU64FromSeed(seed); key.hash = MixU64(seed);
return key; return key;
} }
@ -390,7 +390,7 @@ void UI_PushStyle(UI_StyleDesc desc)
{ {
if (n->next != 0) 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; } 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_Box *box = last_frame->boxes_pre[pre_index];
UI_Report *report = &box->report; UI_Report *report = &box->report;
report->is_hot = box == hot_box; 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_exists = box->last_build_tick >= (frame->tick - 1);
f32 target_hovered = report->is_hovered; f32 target_hovered = report->is_hovered;
f32 target_hot = report->is_hot; f32 target_hot = report->is_hot;
f32 target_active = box == active_box; 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 exists_blend_rate = (40 * frame->dt);
f32 hot_blend_rate = target_hot == 1 ? 1 : (15 * 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_hovered;
b32 is_hot; b32 is_hot;
b32 is_selected;
f32 exists; f32 exists;
f32 hovered; f32 hovered;