From 5a32c9081372ef7d2fd36ed125d833a6af79cba2 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 29 Apr 2024 18:59:32 -0500 Subject: [PATCH] sheet eviction & reload detection --- CMakeLists.txt | 6 +- src/arena.c | 12 +- src/arena.h | 42 +++--- src/ase.c | 2 - src/asset_cache.c | 2 +- src/common.h | 42 +++--- src/config.h | 3 +- src/log.c | 2 +- src/memory.c | 15 +- src/memory.h | 17 ++- src/scratch.c | 6 +- src/sheet.c | 358 ++++++++++++++++++++++++++++++++-------------- src/sheet.h | 2 +- src/sys.h | 57 +++++--- src/sys_win32.c | 125 ++++++++++------ src/user.c | 10 +- src/util.h | 32 ++--- 17 files changed, 481 insertions(+), 252 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 077e46d5..086aef3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,11 +232,11 @@ if(PROFILING) message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with PROFILING") endif() set(COMPILER_FLAGS "${COMPILER_FLAGS} -DPROFILING=1") + # Tracy flags set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_ENABLE=1") - set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_SAMPLING=1") - set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_SYSTEM_TRACING=1") - set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_CALLSTACK=1") + set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_CALLSTACK=5") + set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_SAMPLING -DTRACY_NO_SYSTEM_TRACING -DTRACY_NO_CALLSTACK") # Disable warnings when compiling tracy client set(COMPILER_WARNINGS "-Wno-everything") endif() diff --git a/src/arena.c b/src/arena.c index 0454a0e3..bc077a60 100644 --- a/src/arena.c +++ b/src/arena.c @@ -45,7 +45,7 @@ struct arena arena_alloc(u64 reserve) void arena_release(struct arena *arena) { __prof; - sys_memory_decommit(arena->base, arena->committed); + __proffree(arena->base); sys_memory_release(arena->base); } @@ -53,6 +53,7 @@ void arena_release(struct arena *arena) void *_arena_push_bytes(struct arena *arena, u64 size, u64 align) { ASSERT(align > 0); + ASSERT(!arena->readonly); void *start = NULL; @@ -105,6 +106,7 @@ void arena_copy_replace(struct arena *dest, struct arena *src) void arena_decommit_unused_blocks(struct arena *arena) { + ASSERT(!arena->readonly); u64 next_block_pos = ARENA_BLOCK_SIZE * ((arena->pos + (ARENA_BLOCK_SIZE - 1)) / ARENA_BLOCK_SIZE); if (arena->committed > next_block_pos) { u8 *decommit_start = arena->base + next_block_pos; @@ -113,3 +115,11 @@ void arena_decommit_unused_blocks(struct arena *arena) arena->committed = next_block_pos; } } + +void arena_set_readonly(struct arena *arena) +{ + sys_memory_set_committed_readonly(arena->base, arena->committed); +#if RTC + arena->readonly = true; +#endif +} diff --git a/src/arena.h b/src/arena.h index 194d6f53..daa48c30 100644 --- a/src/arena.h +++ b/src/arena.h @@ -32,6 +32,7 @@ void arena_release(struct arena *arena); void *_arena_push_bytes(struct arena *arena, u64 size, u64 align); void arena_copy_replace(struct arena *dest, struct arena *src); void arena_decommit_unused_blocks(struct arena *arena); +void arena_set_readonly(struct arena *arena); INLINE void *_arena_push_bytes_zero(struct arena *arena, u64 size, u64 align) { @@ -43,6 +44,8 @@ INLINE void *_arena_push_bytes_zero(struct arena *arena, u64 size, u64 align) INLINE void _arena_pop_to(struct arena *arena, u64 pos) { ASSERT(arena->pos >= pos); + ASSERT(!arena->readonly); + ASAN_POISON(arena->base + pos, arena->pos - pos); arena->pos = pos; } @@ -50,6 +53,7 @@ INLINE void _arena_pop_to(struct arena *arena, u64 pos) INLINE void _arena_pop_struct(struct arena *arena, u64 size, void *copy_dest) { ASSERT(arena->pos >= size); + ASSERT(!arena->readonly); u64 new_pos = arena->pos - size; void *src = (void *)(arena->base + new_pos); @@ -59,6 +63,26 @@ INLINE void _arena_pop_struct(struct arena *arena, u64 size, void *copy_dest) arena->pos = new_pos; } +INLINE void *_arena_align(struct arena *arena, u64 align) +{ + ASSERT(!arena->readonly); + + if (align > 0) { + u64 aligned_start_pos = (arena->pos + (align - 1)); + aligned_start_pos -= aligned_start_pos % align; + u64 align_bytes = aligned_start_pos - (u64)arena->pos; + if (align_bytes > 0) { + return (void *)arena_push_array(arena, u8, align_bytes); + } else { + return (void *)(arena->base + arena->pos); + } + } else { + /* 0 alignment */ + ASSERT(false); + return (void *)(arena->base + arena->pos); + } +} + INLINE struct temp_arena arena_temp_begin(struct arena *arena) { struct temp_arena t; @@ -93,22 +117,4 @@ INLINE void *_arena_dry_push(struct arena *arena, u64 align) return ptr; } -INLINE void *_arena_align(struct arena *arena, u64 align) -{ - if (align > 0) { - u64 aligned_start_pos = (arena->pos + (align - 1)); - aligned_start_pos -= aligned_start_pos % align; - u64 align_bytes = aligned_start_pos - (u64)arena->pos; - if (align_bytes > 0) { - return (void *)arena_push_array(arena, u8, align_bytes); - } else { - return (void *)(arena->base + arena->pos); - } - } else { - /* 0 alignment */ - ASSERT(false); - return (void *)(arena->base + arena->pos); - } -} - #endif diff --git a/src/ase.c b/src/ase.c index a3fe11ee..302f33cf 100644 --- a/src/ase.c +++ b/src/ase.c @@ -642,7 +642,6 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff br_seek(&br, sizeof(u8) * 3); - /* TODO: Decode utf-8 */ u16 str_len = br_read_u16(&br); u8 *str_bytes = br_read_raw(&br, str_len); layer->name = (struct string) { @@ -888,7 +887,6 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct buff span->end = br_read_u16(&br); br_seek(&br, 13); - /* TODO: Decode utf-8 */ u16 str_len = br_read_u16(&br); u8 *str_bytes = br_read_raw(&br, str_len); span->name = (struct string) { diff --git a/src/asset_cache.c b/src/asset_cache.c index 8d07e0ba..d3e660e7 100644 --- a/src/asset_cache.c +++ b/src/asset_cache.c @@ -102,7 +102,7 @@ INTERNAL struct asset *asset_cache_get_slot_assume_locked(struct string key, u64 u64 asset_cache_hash(struct string key) { /* TODO: Better hash */ - return hash_fnv64(HASH_FNV64_SEED, BUFFER_FROM_STRING(key)); + return hash_fnv64(HASH_FNV64_BASIS, BUFFER_FROM_STRING(key)); } /* `key` text is copied by this function diff --git a/src/common.h b/src/common.h index fb5daaf8..675ebe70 100644 --- a/src/common.h +++ b/src/common.h @@ -8,6 +8,12 @@ extern "C" { #endif +/* ========================== * + * Configurable constants + * ========================== */ + +#include "config.h" + /* ========================== * * Compiler headers * ========================== */ @@ -254,23 +260,23 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t); * Primitive types * ========================== */ -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; +typedef __int128_t i128; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef __uint128_t u128; typedef float f32; typedef double f64; typedef i8 b8; typedef i32 b32; -#if 0 -/* Unsigned memory (like size_t) */ -typedef u64 umm; -#endif +#define I128(hi64, lo64) (((i128)(hi64) << 64) | (lo64)) +#define U128(hi64, lo64) (((u128)(hi64) << 64) | (lo64)) #define U8_MAX (0xFF) #define U16_MAX (0xFFFF) @@ -326,6 +332,9 @@ struct arena { u64 committed; u64 reserved; u8 *base; +#if RTC + b32 readonly; +#endif }; struct string { @@ -522,12 +531,13 @@ INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ? /* Clang/GCC cleanup macros */ #if COMPILER_CLANG || COMPILER_GCC -# if !TRACY_NO_CALLSTACK -# define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__, __FILE__, (uint32_t)__LINE__, 0 }; TracyCZoneCtx __tracy_ctx = __attribute((cleanup(__prof_zone_cleanup_func))) ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true ); -# define __proscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, name, __FILE__, (uint32_t)__LINE__, 0 }; TracyCZoneCtx __tracy_ctx = __attribute((cleanup(__prof_zone_cleanup_func))) ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true ); +# ifdef TRACY_NO_CALLSTACK +# define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true ); +# define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true ); +# else +# define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true ); +# define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true ); # endif -# define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true ); -# define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true ); #endif INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *__tracy_ctx) { TracyCZoneEnd(*__tracy_ctx); } @@ -556,12 +566,6 @@ INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *__tracy_ctx) { TracyCZoneEnd #endif /* PROFILING */ -/* ========================== * - * Configurable constants - * ========================== */ - -#include "config.h" - #ifdef __cplusplus } #endif diff --git a/src/config.h b/src/config.h index 51fca89a..a976eeab 100644 --- a/src/config.h +++ b/src/config.h @@ -21,7 +21,8 @@ /* If we are not compiling in developer mode, assume resources are embedded as * a tar archive in the executable. Otherwise, look for resources in the file * system. */ -#define RESOURCES_EMBEDDED !(DEVELOPER) +#define RESOURCES_EMBEDDED (!DEVELOPER) +#define RESOURCE_RELOADING (DEVELOPER && !RESOURCES_EMBEDDED) #define DEFAULT_CAMERA_WIDTH (7.0) #define DEFAULT_CAMERA_HEIGHT (DEFAULT_CAMERA_WIDTH / (16.0 / 9.0)) diff --git a/src/log.c b/src/log.c index d078a830..a53067a8 100644 --- a/src/log.c +++ b/src/log.c @@ -130,7 +130,7 @@ void _log(i32 level, struct string msg) struct temp_arena scratch = scratch_begin_no_conflict(); - struct sys_local_time_info lt = sys_local_time(); + struct sys_datetime lt = sys_local_time(); u32 tid = sys_thread_id(); struct log_level_settings settings = g_log_level_settings[level]; diff --git a/src/memory.c b/src/memory.c index b21c294b..d23cc8e0 100644 --- a/src/memory.c +++ b/src/memory.c @@ -16,10 +16,23 @@ __attribute((section(".text.memset"))) void *memset(void *dest, i32 c, u64 n) { /* TODO: Faster memset */ - for (u64 i = 0; i < (n); ++i) { + for (u64 i = 0; i < n; ++i) { ((u8 *)dest)[i] = c; } return dest; } +__attribute((section(".text.memcmp"))) +i32 memcmp(const void *p1, const void *p2, u64 n) +{ + i32 res = 0; + for (u64 i = 0; i < n; ++i) { + res = ((u8 *)p1)[i] - ((u8 *)p2)[i]; + if (res != 0) { + break; + } + } + return res; +} + #endif /* !CRTLIB */ diff --git a/src/memory.h b/src/memory.h index 58f0cd75..f39bc031 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,20 +1,25 @@ #ifndef MEMORY_H #define MEMORY_H -#define MEMZERO_STRUCT(ptr) MEMZERO(ptr, sizeof(*ptr)) -#define MEMZERO_ARRAY(a) MEMZERO(a, sizeof(a)) -#define MEMZERO(ptr, count) MEMSET(ptr, 0, count) +#define MEMZERO_STRUCT(ptr) MEMZERO((ptr), sizeof(*(ptr))) +#define MEMZERO_ARRAY(a) MEMZERO((a), sizeof((a))) +#define MEMZERO(ptr, count) MEMSET((ptr), 0, (count)) -#define MEMCPY_STRUCT(ptr_dest, ptr_src) MEMCPY(ptr_dest, ptr_src, sizeof(*ptr_dest)); -#define MEMCPY(dest, src, count) memcpy(dest, src, count) +#define MEMCPY_STRUCT(ptr_dest, ptr_src) MEMCPY((ptr_dest), (ptr_src), sizeof(*(ptr_dest))); +#define MEMCPY(dest, src, count) memcpy((dest), (src), (count)) -#define MEMSET(ptr, val, count) memset(ptr, val, count) +#define MEMCMP_STRUCT(p1, p2) MEMCMP((p1), (p2), sizeof(*p1)) +#define MEMCMP(p1, p2, n) memcmp((p1), (p2), (n)) + +#define MEMSET(ptr, val, count) memset((ptr), (val), (count)) #if CRTLIB # include #else +/* TODO: restrict args? */ void *memcpy(void *__restrict dest, const void *__restrict src, u64 n); void *memset(void *dest, i32 c, u64 n); +i32 memcmp(const void *p1, const void *p2, u64 n); #endif #endif diff --git a/src/scratch.c b/src/scratch.c index 90554a27..619c7c0b 100644 --- a/src/scratch.c +++ b/src/scratch.c @@ -1,6 +1,6 @@ #include "scratch.h" -INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(test_scratch_context_alloc, vctx) +INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(scratch_context_alloc, vctx) { __prof; struct scratch_ctx *ctx = vctx; @@ -9,7 +9,7 @@ INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(test_scratch_context_alloc, vctx) } } -INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(test_scratch_context_release, vctx) +INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(scratch_context_release, vctx) { __prof; struct scratch_ctx *ctx = vctx; @@ -26,4 +26,4 @@ INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(test_scratch_context_release, vctx) } } -THREAD_LOCAL_VAR_DEF_EXTERN(tl_scratch_ctx, struct scratch_ctx, &test_scratch_context_alloc, &test_scratch_context_release); +THREAD_LOCAL_VAR_DEF_EXTERN(tl_scratch_ctx, struct scratch_ctx, &scratch_context_alloc, &scratch_context_release); diff --git a/src/sheet.c b/src/sheet.c index 7fd9ca91..5520817f 100644 --- a/src/sheet.c +++ b/src/sheet.c @@ -21,13 +21,24 @@ #define MAX_LOADER_THREADS 4 +#if 0 +/* Size of cache memory until evictor starts evicting */ +#define CACHE_MEMORY_BUDGET MEGABYTE(8) +#endif + +/* How long between evictor thread checks */ +#define EVICTOR_CHECK_INTERVAl 0.500 + +/* Time a cache entry spends unused it's considered evictable */ +#define EVICTOR_GRACE_TIME 10.000 + /* ========================== * - * Load cmd structs + * Loader cmd structs * ========================== */ -struct load_cmd { - struct load_cmd *next; - struct load_cmd *next_free; +struct loader_cmd { + struct loader_cmd *next; + struct loader_cmd *next_free; struct cache_node *cache_node; struct sheet_tag tag; @@ -42,23 +53,32 @@ enum cache_node_state { CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED, CACHE_NODE_STATE_WORKING, - CACHE_NODE_STATE_LOADED + CACHE_NODE_STATE_LOADED, + CACHE_NODE_STATE_EVICTED }; struct cache_node { + u128 hash; struct atomic_u32 state; struct atomic_i32 refcount; /* Number of scopes currently holding a reference to this sheet */ + struct atomic_u64 last_refcount0_ts; /* Last time that refcount reached 0 */ /* Allocated data */ - struct sheet sheet; - struct sheet_tag tag; struct arena arena; + struct sheet *sheet; /* Hash list */ struct cache_node *next_hash; + struct cache_node *prev_hash; /* Free list */ struct cache_node *next_free; + +#if RESOURCE_RELOADING + struct sys_datetime initial_resource_file_modified_time; + u64 tag_path_len; + u8 tag_path[4096]; +#endif }; struct cache { @@ -82,25 +102,31 @@ GLOBAL struct { /* Cache */ struct cache cache; - /* Load cmds */ - b32 shutdown; - struct arena load_cmd_arena; - struct sys_mutex load_cmd_mutex; - struct sys_condition_variable load_cmd_or_shutdown_cv; - struct load_cmd *first_free_load_cmd; - struct load_cmd *first_load_cmd; - struct load_cmd *last_load_cmd; - - /* Threads */ + /* Loader threads */ + b32 loaders_shutdown; + struct sys_mutex loaders_mutex; + struct sys_condition_variable loaders_cv; + struct arena loader_cmd_arena; + struct loader_cmd *first_free_loader_cmd; + struct loader_cmd *first_loader_cmd; + struct loader_cmd *last_loader_cmd; u64 loader_threads_count; struct sys_thread loader_threads[MAX_LOADER_THREADS]; + + /* Evictor thread */ + b32 evictor_shutdown; + struct sys_mutex evictor_mutex; + struct sys_condition_variable evictor_cv; + struct sys_thread evictor_thread; } G = { 0 }, DEBUG_ALIAS(G, G_sheet); +GLOBAL READONLY struct sheet g_sheet_nil = { 0 }; GLOBAL READONLY struct sheet g_sheet_loading = { .loading = true }; + /* ========================== * * Thread local state * ========================== */ @@ -131,6 +157,7 @@ GLOBAL THREAD_LOCAL_VAR_DEF(tl_sheet_tctx, struct sheet_tctx, sheet_tctx_alloc, INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown); INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg); +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg); struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, struct asset_cache_startup_receipt *asset_cache_sr, @@ -144,11 +171,13 @@ struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, G.cache.arena = arena_alloc(GIGABYTE(64)); G.cache.buckets = arena_push_array_zero(&G.cache.arena, struct cache_node *, CACHE_BUCKETS_COUNT); - G.load_cmd_arena = arena_alloc(GIGABYTE(64)); - G.load_cmd_mutex = sys_mutex_alloc(); - G.load_cmd_or_shutdown_cv = sys_condition_variable_alloc(); + G.loader_cmd_arena = arena_alloc(GIGABYTE(64)); + G.loaders_mutex = sys_mutex_alloc(); + G.loaders_cv = sys_condition_variable_alloc(); + + G.evictor_mutex = sys_mutex_alloc(); + G.evictor_cv = sys_condition_variable_alloc(); - /* Startup threads */ { struct temp_arena scratch = scratch_begin_no_conflict(); G.loader_threads_count = clamp_i64(1, MAX_LOADER_THREADS, sys_num_logical_processors() - 1); @@ -160,7 +189,7 @@ struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, } scratch_end(scratch); } - //G.evictor_thread = sys_thread_alloc(sheet_evictor_thread_entry_point, NULL, STR("[P0] Sheet evictor thread")); + G.evictor_thread = sys_thread_alloc(sheet_evictor_thread_entry_point, NULL, STR("[P0] Sheet evictor thread")); app_register_exit_callback(&sheet_shutdown); @@ -171,61 +200,43 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown) { __prof; - /* Signal shutdown */ - sys_mutex_lock(&G.load_cmd_mutex); + /* Signal loaders shutdown */ + sys_mutex_lock(&G.loaders_mutex); { - G.shutdown = true; - sys_condition_variable_broadcast(&G.load_cmd_or_shutdown_cv); + G.loaders_shutdown = true; + sys_condition_variable_broadcast(&G.loaders_cv); } - sys_mutex_unlock(&G.load_cmd_mutex); + sys_mutex_unlock(&G.loaders_mutex); + + /* Signal evictor shutdown */ + sys_mutex_lock(&G.evictor_mutex); + { + G.evictor_shutdown = true; + sys_condition_variable_broadcast(&G.evictor_cv); + } + sys_mutex_unlock(&G.evictor_mutex); + /* Wait on threads */ for (u64 i = 0; i < G.loader_threads_count; ++i) { sys_thread_wait_release(&G.loader_threads[i]); } - //sys_thread_wait_release(&G.evictor_thread); + sys_thread_wait_release(&G.evictor_thread); } /* ========================== * * Tag * ========================== */ -INTERNAL b32 sheet_tag_eq(struct sheet_tag t1, struct sheet_tag t2) -{ - b32 eq = false; - if (t1.hash == t2.hash) { - eq = string_eq(t1.path, t2.path); - } - return eq; -} - -#if 0 -INTERNAL void sheet_tag_copy_to_node(struct sheet_tag tag, struct cache_node *n) -{ - n->tag = sheet_tag_copy_to_buff(tag, n->tag_path_text); - u64 copy_len = min_u64(tag.path.len, ARRAY_COUNT(n->tag_path_text)); - n->tag = tag; - n->tag.path.text = n->path_text_dest; - MEMCPY(n->tag.path.text, tag.path.text, copy_len); -} -#endif - struct sheet_tag sheet_tag_from_path(struct string path) { struct sheet_tag res = { 0 }; - res.hash = HASH_FNV64_SEED; - res.hash = hash_fnv64(res.hash, BUFFER_FROM_STRING(path)); + res.hash = HASH_FNV128_BASIS; + res.hash = hash_fnv128(res.hash, BUFFER_FROM_STRING(path)); res.path = path; return res; } -INTERNAL struct sheet_tag sheet_tag_copy(struct arena *arena, struct sheet_tag src) -{ - struct sheet_tag res = src; - res.path = string_copy(arena, src.path); - return res; -} - /* ========================== * * Load * ========================== */ @@ -283,21 +294,30 @@ INTERNAL void sheet_load(struct cache_node *n, struct sheet_tag tag) ASSERT(string_ends_with(path, STR(".ase"))); - /* Decode */ - struct ase_decode_sheet_result decoded = { 0 }; - if (resource_exists(path)) { - struct resource sheet_rs = resource_open(path); - decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); - resource_close(sheet_rs); - } else { - /* FIXME: Working default */ - logf_error("Resource \"%F\" not found", path); - } - - /* Allocate & initialize */ n->arena = arena_alloc(SHEET_ARENA_RESERVE); - n->sheet = init_sheet_from_ase_result(&n->arena, decoded); - n->tag = sheet_tag_copy(&n->arena, tag); + { + /* Decode */ + struct ase_decode_sheet_result decoded = { 0 }; + if (resource_exists(path)) { + struct resource sheet_rs = resource_open(path); + decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); +#if RESOURCE_RELOADING + n->initial_resource_file_modified_time = sys_file_get_time(sheet_rs.file).modified; + u64 cpy_len = min_u64(tag.path.len, ARRAY_COUNT(n->tag_path)); + n->tag_path_len = cpy_len; + MEMCPY(n->tag_path, tag.path.text, cpy_len); +#endif + resource_close(sheet_rs); + + /* Initialize */ + n->sheet = arena_push(&n->arena, struct sheet); + *n->sheet = init_sheet_from_ase_result(&n->arena, decoded); + } else { + n->sheet = &g_sheet_nil; + logf_error("Resource \"%F\" not found", path); + } + } + arena_set_readonly(&n->arena); logf_info("Finished loading sheet \"%F\" in %F seconds (final size: %F bytes).", FMT_STR(path), @@ -365,10 +385,14 @@ struct sheet_scope *sheet_scope_begin(void) void sheet_scope_end(struct sheet_scope *scope) { struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); + sys_timestamp_t cur_timestamp = sys_timestamp(); for (u64 i = 0; i < SCOPE_BUCKETS_COUNT; ++i) { struct sheet_scope_reference *ref = scope->reference_buckets[i]; while (ref) { - atomic_i32_dec_eval(&ref->cache_node->refcount); + if (atomic_i32_dec_eval(&ref->cache_node->refcount) == 0) { + /* Refcount is now 0, mark timestamp on cache node */ + atomic_u64_eval_exchange(&ref->cache_node->last_refcount0_ts, cur_timestamp); + } ref->next_free = tctx->first_free_reference; tctx->first_free_reference = ref; ref = ref->next_hash; @@ -384,23 +408,26 @@ void sheet_scope_end(struct sheet_scope *scope) INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct sheet_tag tag) { - struct cache_node **n_next = NULL; struct cache_node *n = NULL; + struct cache_node *nonmatching = NULL; + struct cache_node **nonmatching_next = NULL; u64 cache_bucket_index = tag.hash % CACHE_BUCKETS_COUNT; /* Lookup */ + /* TODO: Spinlock */ sys_rw_mutex_lock_shared(&G.cache.rw_mutex); { - n_next = &G.cache.buckets[cache_bucket_index]; - n = *n_next; + nonmatching_next = &G.cache.buckets[cache_bucket_index]; + n = *nonmatching_next; while (n) { - if (sheet_tag_eq(n->tag, tag)) { + if (n->hash == tag.hash) { scope_ensure_reference(scope, n, cache_bucket_index); break; } else { - n_next = &n->next_hash; - n = *n_next; + nonmatching = n; + nonmatching_next = &nonmatching->next_hash; + n = *nonmatching_next; } } } @@ -408,18 +435,22 @@ INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct /* Allocate new node if necessary */ if (!n) { - /* Alloc */ sys_rw_mutex_lock_exclusive(&G.cache.rw_mutex); { if (G.cache.first_free) { n = G.cache.first_free; - G.cache.first_free->next_free = n->next_free; + G.cache.first_free = n->next_free; MEMZERO_STRUCT(n); } else { n = arena_push_zero(&G.cache.arena, struct cache_node); } scope_ensure_reference(scope, n, cache_bucket_index); - *n_next = n; + *nonmatching_next = n; + if (nonmatching) { + nonmatching->next_hash = n; + n->prev_hash = nonmatching; + } + n->hash = tag.hash; } sys_rw_mutex_unlock_exclusive(&G.cache.rw_mutex); } @@ -430,28 +461,27 @@ INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct sheet_tag tag, b32 await) { struct sheet *res = &g_sheet_loading; - struct cache_node *n = node_lookup_touch(scope, tag); u32 state = atomic_u32_eval(&n->state); if (state == CACHE_NODE_STATE_LOADED) { - res = &n->sheet; + res = n->sheet; } else if (state == CACHE_NODE_STATE_NONE) { if (atomic_u32_eval_compare_exchange(&n->state, CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED) == CACHE_NODE_STATE_NONE) { /* Node is new, load sheet */ if (await) { sheet_load(n, tag); - res = &n->sheet; + res = n->sheet; } else { - sys_mutex_lock(&G.load_cmd_mutex); + sys_mutex_lock(&G.loaders_mutex); { /* Allocate cmd */ - struct load_cmd *cmd = NULL; - if (G.first_free_load_cmd) { - cmd = G.first_free_load_cmd; - G.first_free_load_cmd = cmd->next_free; + struct loader_cmd *cmd = NULL; + if (G.first_free_loader_cmd) { + cmd = G.first_free_loader_cmd; + G.first_free_loader_cmd = cmd->next_free; } else { - cmd = arena_push(&G.load_cmd_arena, struct load_cmd); + cmd = arena_push(&G.loader_cmd_arena, struct loader_cmd); } /* Initialize cmd */ @@ -464,13 +494,13 @@ INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct } /* Add cmd to queue */ - *(G.last_load_cmd ? &G.last_load_cmd->next : &G.first_load_cmd) = cmd; - G.last_load_cmd = cmd; + *(G.last_loader_cmd ? &G.last_loader_cmd->next : &G.first_loader_cmd) = cmd; + G.last_loader_cmd = cmd; /* Signal work ready */ - sys_condition_variable_signal(&G.load_cmd_or_shutdown_cv); + sys_condition_variable_signal(&G.loaders_cv); } - sys_mutex_unlock(&G.load_cmd_mutex); + sys_mutex_unlock(&G.loaders_mutex); } } @@ -525,37 +555,151 @@ struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index) INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg) { + __prof; (UNUSED)arg; while (true) { - sys_mutex_lock(&G.load_cmd_mutex); - if (G.shutdown) { + sys_mutex_lock(&G.loaders_mutex); + + if (G.loaders_shutdown) { /* Thread shutdown */ - sys_mutex_unlock(&G.load_cmd_mutex); + sys_mutex_unlock(&G.loaders_mutex); break; - } else if (!G.first_load_cmd) { + } else if (!G.first_loader_cmd) { /* Wait for work */ - sys_condition_variable_wait(&G.load_cmd_or_shutdown_cv, &G.load_cmd_mutex); +#if 1 + sys_condition_variable_wait(&G.loaders_cv, &G.loaders_mutex); +#else + sys_mutex_unlock(&G.loaders_mutex); + sys_sleep(0.5); + sys_mutex_lock(&G.loaders_mutex); +#endif + } - if (G.first_load_cmd) { + if (G.first_loader_cmd) { /* Pull cmd from queue */ - struct load_cmd *cmd = G.first_load_cmd; - G.first_load_cmd = cmd->next; + struct loader_cmd *cmd = G.first_loader_cmd; + G.first_loader_cmd = cmd->next; + if (G.last_loader_cmd == cmd) { + G.last_loader_cmd = NULL; + } /* Do work (temporarily unlock) */ - sys_mutex_unlock(&G.load_cmd_mutex); + sys_mutex_unlock(&G.loaders_mutex); { sheet_load(cmd->cache_node, cmd->tag); } - sys_mutex_lock(&G.load_cmd_mutex); + sys_mutex_lock(&G.loaders_mutex); /* Free cmd */ - cmd->next_free = G.first_free_load_cmd; - G.first_free_load_cmd = cmd; + cmd->next_free = G.first_free_loader_cmd; + G.first_free_loader_cmd = cmd; } - sys_mutex_unlock(&G.load_cmd_mutex); + sys_mutex_unlock(&G.loaders_mutex); + } +} + +/* ========================== * + * Evictor thread + * ========================== */ + +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg) +{ + (UNUSED)arg; + + while (true) { + struct temp_arena scratch = scratch_begin_no_conflict(); + + b32 abort_thread_loop = false; + sys_mutex_lock(&G.evictor_mutex); + { + if (G.evictor_shutdown) { + /* Thread shutdown */ + abort_thread_loop = true; + goto abort_thread_loop; + } + + /* Wait */ + sys_condition_variable_wait_time(&G.evictor_cv, &G.evictor_mutex, EVICTOR_CHECK_INTERVAl); + + sys_timestamp_t cur_timestamp = sys_timestamp(); + f64 cur_time = sys_timestamp_seconds(cur_timestamp); + + sys_rw_mutex_lock_exclusive(&G.cache.rw_mutex); + { + for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) { + struct cache_node *n = G.cache.buckets[i]; + + while (n) { + if ((*atomic_u32_raw(&n->state) == CACHE_NODE_STATE_LOADED) && (*atomic_i32_raw(&n->refcount) <= 0)) { + b32 should_evict = false; + + /* TODO: Only evict if over memory budget (in LRU order until under memory budget) */ + sys_timestamp_t last_used_ts = atomic_u64_raw(&n->last_refcount0_ts); + f64 last_used_time = sys_timestamp_seconds(last_used_ts); + if (cur_time - last_used_time > EVICTOR_GRACE_TIME) { + /* Cache entry unused for too long */ + should_evict = true; + } + +#if RESOURCE_RELOADING + if (!should_evict) { + should_evict = true; + struct string path = string_from_cstr_len((char *)n->tag_path, n->tag_path_len); + if (sys_is_file(path)) { + struct sys_file file = sys_file_open_read(path); + struct sys_file_time ft = sys_file_get_time(file); + sys_file_close(file); + + struct sys_datetime initial_file_time = n->initial_resource_file_modified_time; + struct sys_datetime current_file_time = ft.modified; + + if (MEMCMP_STRUCT(&initial_file_time, ¤t_file_time) == 0) { + /* File unchanged */ + should_evict = false; + } else { + logf_info("Resource file for sheet \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); + should_evict = true; + } + } + } +#endif + + if (should_evict) { + __profscope(evict_sheet); + *atomic_u32_raw(&n->state) = CACHE_NODE_STATE_EVICTED; + /* Remove from hash table */ + if (n->prev_hash) { + n->prev_hash->next_hash = n->next_hash; + } else { + u64 cache_bucket_index = n->hash % CACHE_BUCKETS_COUNT; + G.cache.buckets[cache_bucket_index] = n->next_hash; + } + if (n->next_hash) { + n->next_hash->prev_hash = n->prev_hash; + } + /* Add to free list */ + n->next_free = G.cache.first_free; + G.cache.first_free = n; + arena_release(&n->arena); + } + } + n = n->next_hash; + } + } + } + sys_rw_mutex_unlock_exclusive(&G.cache.rw_mutex); + } + abort_thread_loop: + sys_mutex_unlock(&G.evictor_mutex); + + scratch_end(scratch); + + if (abort_thread_loop) { + break; + } } } diff --git a/src/sheet.h b/src/sheet.h index 6398b19c..ba0f282e 100644 --- a/src/sheet.h +++ b/src/sheet.h @@ -10,7 +10,7 @@ struct asset_cache_startup_receipt; struct resource_startup_receipt; struct sheet_tag { - u64 hash; + u128 hash; struct string path; }; diff --git a/src/sys.h b/src/sys.h index bf43aaec..7b11d577 100644 --- a/src/sys.h +++ b/src/sys.h @@ -167,6 +167,32 @@ void *sys_memory_commit(void *address, u64 size); * address space) */ void sys_memory_decommit(void *address, u64 size); +void sys_memory_set_committed_readonly(void *address, u64 size); + +/* ========================== * + * Time + * ========================== */ + +struct sys_datetime { + u32 year; + u32 month; + u32 day_of_week; + u32 day; + u32 hour; + u32 minute; + u32 second; + u32 milliseconds; +}; + +typedef u64 sys_timestamp_t; + +sys_timestamp_t sys_timestamp(void); +f64 sys_timestamp_seconds(sys_timestamp_t ts); + +struct sys_datetime sys_local_time(void); + +i32 sys_datetime_cmp(struct sys_datetime t1, struct sys_datetime t2); + /* ========================== * * File system * @@ -177,6 +203,12 @@ struct sys_file { u64 handle; }; +struct sys_file_time { + struct sys_datetime created; + struct sys_datetime accessed; + struct sys_datetime modified; +}; + struct string sys_get_write_path(struct arena *arena); b32 sys_is_file(struct string path); b32 sys_is_dir(struct string path); @@ -189,7 +221,8 @@ void sys_file_close(struct sys_file file); struct buffer sys_file_read_all(struct arena *arena, struct sys_file file); void sys_file_write(struct sys_file file, struct buffer data); -u64 sys_file_size(struct sys_file file); +u64 sys_file_get_size(struct sys_file file); +struct sys_file_time sys_file_get_time(struct sys_file file); /* ========================== * * File map @@ -416,28 +449,6 @@ enum sys_message_box_kind { void sys_message_box(enum sys_message_box_kind kind, struct string message); -/* ========================== * - * Time - * ========================== */ - -struct sys_local_time_info { - u32 year; - u32 month; - u32 dayOfWeek; - u32 day; - u32 hour; - u32 minute; - u32 second; - u32 milliseconds; -}; - -typedef u64 sys_timestamp_t; - -sys_timestamp_t sys_timestamp(void); -f64 sys_timestamp_seconds(sys_timestamp_t ts); - -struct sys_local_time_info sys_local_time(void); - /* ========================== * * Clipboard * ========================== */ diff --git a/src/sys_win32.c b/src/sys_win32.c index 1b44c0a9..512a94f2 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -204,6 +204,58 @@ void sys_memory_decommit(void *address, u64 size) VirtualFree(address, size, MEM_DECOMMIT); } +void sys_memory_set_committed_readonly(void *address, u64 size) +{ + DWORD old = PAGE_READWRITE; + VirtualProtect(address, size, PAGE_READONLY, &old); +} + +/* ========================== * + * Time + * ========================== */ + +INTERNAL struct sys_datetime win32_time_to_sys_time(SYSTEMTIME st) +{ + return (struct sys_datetime) { + .year = st.wYear, + .month = st.wMonth, + .day_of_week = st.wDayOfWeek, + .day = st.wDay, + .hour = st.wHour, + .minute = st.wMinute, + .second = st.wSecond, + .milliseconds = st.wMilliseconds + }; +} + +/* prevent 64-bit overflow when computing relative timestamp + * https://github.com/floooh/sokol/blob/d4ac122f36d7659a18b312fd4fa2317fb9e06a63/sokol_time.h#L203 + */ +INTERNAL i64 _win32_i64_muldiv(i64 value, i64 numer, i64 denom) { + i64 q = value / denom; + i64 r = value % denom; + return q * numer + r * numer / denom; +} + +sys_timestamp_t sys_timestamp(void) +{ + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + return (u64)_win32_i64_muldiv(time.QuadPart - G.timer_start.QuadPart, 1000000000, G.timer_frequency.QuadPart); +} + +f64 sys_timestamp_seconds(sys_timestamp_t ts) +{ + return (f64)ts / 1000000000.0; +} + +struct sys_datetime sys_local_time(void) +{ + SYSTEMTIME lt; + GetLocalTime(<); + return win32_time_to_sys_time(lt); +} + /* ========================== * * File system * ========================== */ @@ -424,20 +476,48 @@ void sys_file_write(struct sys_file file, struct buffer data) ); } -u64 sys_file_size(struct sys_file file) +u64 sys_file_get_size(struct sys_file file) { LARGE_INTEGER li_file_size; GetFileSizeEx((HANDLE)file.handle, &li_file_size); return (u64)(li_file_size.QuadPart > 0 ? li_file_size.QuadPart : 0); } +struct sys_file_time sys_file_get_time(struct sys_file file) +{ + /* Get file times */ + FILETIME ft_created; + FILETIME ft_accessed; + FILETIME ft_modified; + GetFileTime((HANDLE)file.handle, &ft_created, &ft_accessed, &ft_modified); + + /* Convert file times to local file time */ + FileTimeToLocalFileTime(&ft_created, &ft_created); + FileTimeToLocalFileTime(&ft_accessed, &ft_accessed); + FileTimeToLocalFileTime(&ft_modified, &ft_modified); + + /* Convert local file times to system times */ + SYSTEMTIME st_created; + SYSTEMTIME st_accessed; + SYSTEMTIME st_modified; + FileTimeToSystemTime(&ft_created, &st_created); + FileTimeToSystemTime(&ft_accessed, &st_accessed); + FileTimeToSystemTime(&ft_modified, &st_modified); + + return (struct sys_file_time) { + .created = win32_time_to_sys_time(st_created), + .accessed = win32_time_to_sys_time(st_accessed), + .modified = win32_time_to_sys_time(st_modified) + }; +} + /* ========================== * * File map * ========================== */ struct sys_file_map sys_file_map_open_read(struct sys_file file) { - u64 size = sys_file_size(file); + u64 size = sys_file_get_size(file); u8 *base_ptr = NULL; HANDLE map_handle = 0; @@ -1693,47 +1773,6 @@ void sys_message_box(enum sys_message_box_kind kind, struct string message) scratch_end(scratch); } -/* ========================== * - * Time - * ========================== */ - -/* prevent 64-bit overflow when computing relative timestamp - * https://github.com/floooh/sokol/blob/d4ac122f36d7659a18b312fd4fa2317fb9e06a63/sokol_time.h#L203 - */ -INTERNAL i64 _win32_i64_muldiv(i64 value, i64 numer, i64 denom) { - i64 q = value / denom; - i64 r = value % denom; - return q * numer + r * numer / denom; -} - -sys_timestamp_t sys_timestamp(void) -{ - LARGE_INTEGER time; - QueryPerformanceCounter(&time); - return (u64)_win32_i64_muldiv(time.QuadPart - G.timer_start.QuadPart, 1000000000, G.timer_frequency.QuadPart); -} - -f64 sys_timestamp_seconds(sys_timestamp_t ts) -{ - return (f64)ts / 1000000000.0; -} - -struct sys_local_time_info sys_local_time(void) -{ - SYSTEMTIME lt; - GetLocalTime(<); - return (struct sys_local_time_info) { - .year = lt.wYear, - .month = lt.wMonth, - .dayOfWeek = lt.wDayOfWeek, - .day = lt.wDay, - .hour = lt.wHour, - .minute = lt.wMinute, - .second = lt.wSecond, - .milliseconds = lt.wMilliseconds - }; -} - /* ========================== * * Clipboard * ========================== */ diff --git a/src/user.c b/src/user.c index 334af61f..9a7cf65c 100644 --- a/src/user.c +++ b/src/user.c @@ -769,11 +769,9 @@ INTERNAL void user_update(void) u32 tint = ent->sprite_tint; struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = tint); + struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, ent->sprite_sheet_tag); { - struct sheet_tag sheet_tag = ent->sprite_sheet_tag; - struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, sheet_tag); struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); - u64 frame_index = span.start + ent->animation_frame; struct sheet_frame frame = sheet_get_frame(sheet, frame_index); if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) { @@ -792,7 +790,11 @@ INTERNAL void user_update(void) params.clip = frame.clip; } - draw_texture_quad(G.world_canvas, params, quad); + /* TODO: Check for texture loading as well */ + if (!sheet->loading) { + /* TODO: Fade in a placeholder or something instead of drawing nothing. */ + draw_texture_quad(G.world_canvas, params, quad); + } #if 0 if (G.debug_draw && !skip_debug_draw) { diff --git a/src/util.h b/src/util.h index 312f9094..520ee078 100644 --- a/src/util.h +++ b/src/util.h @@ -10,39 +10,35 @@ /* Utility functions and stuff that don't have a home :( */ /* ========================== * - * String utils + * Hash utils * ========================== */ -#if 0 -struct string util_file_name_from_path(struct string path); -#endif +/* FNV-1a parameters for different hash sizes: + * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters + */ -/* FNV-1a hash function - * TODO: Something faster if necessary */ -#define HASH_FNV64_SEED 0xcbf29ce484222325 +#define HASH_FNV64_BASIS 0xCBF29CE484222325 INLINE u64 hash_fnv64(u64 seed, struct buffer buff) { u64 hash = seed; for (u64 i = 0; i < buff.size; ++i) { hash ^= (u8)buff.data[i]; - hash *= 0x100000001b3; + hash *= 0x100000001B3; } return hash; } -#if 0 -/* FNV-1a hash function - * TODO: Something faster if necessary */ -INLINE u32 hash_fnv32(struct buffer buff) +#define HASH_FNV128_BASIS U128(0x6C62272E07BB0142, 0x62B821756295C58D) +INLINE u128 hash_fnv128(u128 seed, struct buffer buff) { - u32 hash = 2166136261; + u128 hash = seed; for (u64 i = 0; i < buff.size; ++i) { - hash ^= (u8)buff.data[i]; - hash *= 16777619; + u8 c = (u8)buff.data[i]; + hash ^= c; + hash *= U128(0x1000000, 0x000000000000013B); } return hash; } -#endif /* ========================== * * Fixed Dict @@ -81,7 +77,7 @@ INLINE void fixed_dict_set(struct arena *arena, struct fixed_dict *dict, struct { __prof; - u64 hash = hash_fnv64(HASH_FNV64_SEED, BUFFER_FROM_STRING(key)); + u64 hash = hash_fnv64(HASH_FNV64_BASIS, BUFFER_FROM_STRING(key)); u64 index = hash % dict->buckets_count; struct fixed_dict_bucket *bucket = &dict->buckets[index]; @@ -110,7 +106,7 @@ INLINE void *fixed_dict_get(const struct fixed_dict *dict, struct string key) { __prof; - u64 hash = hash_fnv64(HASH_FNV64_SEED, BUFFER_FROM_STRING(key)); + u64 hash = hash_fnv64(HASH_FNV64_BASIS, BUFFER_FROM_STRING(key)); u64 index = hash % dict->buckets_count; struct fixed_dict_bucket *bucket = &dict->buckets[index];