sheet eviction & reload detection

This commit is contained in:
jacob 2024-04-29 18:59:32 -05:00
parent 35ded9dbac
commit 5a32c90813
17 changed files with 481 additions and 252 deletions

View File

@ -232,11 +232,11 @@ if(PROFILING)
message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with PROFILING") message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with PROFILING")
endif() endif()
set(COMPILER_FLAGS "${COMPILER_FLAGS} -DPROFILING=1") set(COMPILER_FLAGS "${COMPILER_FLAGS} -DPROFILING=1")
# Tracy flags # Tracy flags
set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_ENABLE=1") set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_ENABLE=1")
set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_SAMPLING=1") set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_CALLSTACK=5")
set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_SYSTEM_TRACING=1") set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_SAMPLING -DTRACY_NO_SYSTEM_TRACING -DTRACY_NO_CALLSTACK")
set(COMPILER_FLAGS "${COMPILER_FLAGS} -DTRACY_NO_CALLSTACK=1")
# Disable warnings when compiling tracy client # Disable warnings when compiling tracy client
set(COMPILER_WARNINGS "-Wno-everything") set(COMPILER_WARNINGS "-Wno-everything")
endif() endif()

View File

@ -45,7 +45,7 @@ struct arena arena_alloc(u64 reserve)
void arena_release(struct arena *arena) void arena_release(struct arena *arena)
{ {
__prof; __prof;
sys_memory_decommit(arena->base, arena->committed); __proffree(arena->base);
sys_memory_release(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) void *_arena_push_bytes(struct arena *arena, u64 size, u64 align)
{ {
ASSERT(align > 0); ASSERT(align > 0);
ASSERT(!arena->readonly);
void *start = NULL; 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) 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); u64 next_block_pos = ARENA_BLOCK_SIZE * ((arena->pos + (ARENA_BLOCK_SIZE - 1)) / ARENA_BLOCK_SIZE);
if (arena->committed > next_block_pos) { if (arena->committed > next_block_pos) {
u8 *decommit_start = arena->base + 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; 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
}

View File

@ -32,6 +32,7 @@ void arena_release(struct arena *arena);
void *_arena_push_bytes(struct arena *arena, u64 size, u64 align); void *_arena_push_bytes(struct arena *arena, u64 size, u64 align);
void arena_copy_replace(struct arena *dest, struct arena *src); void arena_copy_replace(struct arena *dest, struct arena *src);
void arena_decommit_unused_blocks(struct arena *arena); 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) 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) INLINE void _arena_pop_to(struct arena *arena, u64 pos)
{ {
ASSERT(arena->pos >= pos); ASSERT(arena->pos >= pos);
ASSERT(!arena->readonly);
ASAN_POISON(arena->base + pos, arena->pos - pos); ASAN_POISON(arena->base + pos, arena->pos - 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) INLINE void _arena_pop_struct(struct arena *arena, u64 size, void *copy_dest)
{ {
ASSERT(arena->pos >= size); ASSERT(arena->pos >= size);
ASSERT(!arena->readonly);
u64 new_pos = arena->pos - size; u64 new_pos = arena->pos - size;
void *src = (void *)(arena->base + new_pos); 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; 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) INLINE struct temp_arena arena_temp_begin(struct arena *arena)
{ {
struct temp_arena t; struct temp_arena t;
@ -93,22 +117,4 @@ INLINE void *_arena_dry_push(struct arena *arena, u64 align)
return ptr; 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 #endif

View File

@ -642,7 +642,6 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff
br_seek(&br, sizeof(u8) * 3); br_seek(&br, sizeof(u8) * 3);
/* TODO: Decode utf-8 */
u16 str_len = br_read_u16(&br); u16 str_len = br_read_u16(&br);
u8 *str_bytes = br_read_raw(&br, str_len); u8 *str_bytes = br_read_raw(&br, str_len);
layer->name = (struct string) { 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); span->end = br_read_u16(&br);
br_seek(&br, 13); br_seek(&br, 13);
/* TODO: Decode utf-8 */
u16 str_len = br_read_u16(&br); u16 str_len = br_read_u16(&br);
u8 *str_bytes = br_read_raw(&br, str_len); u8 *str_bytes = br_read_raw(&br, str_len);
span->name = (struct string) { span->name = (struct string) {

View File

@ -102,7 +102,7 @@ INTERNAL struct asset *asset_cache_get_slot_assume_locked(struct string key, u64
u64 asset_cache_hash(struct string key) u64 asset_cache_hash(struct string key)
{ {
/* TODO: Better hash */ /* 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 /* `key` text is copied by this function

View File

@ -8,6 +8,12 @@
extern "C" { extern "C" {
#endif #endif
/* ========================== *
* Configurable constants
* ========================== */
#include "config.h"
/* ========================== * /* ========================== *
* Compiler headers * Compiler headers
* ========================== */ * ========================== */
@ -254,23 +260,23 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
* Primitive types * Primitive types
* ========================== */ * ========================== */
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8; typedef int8_t i8;
typedef int16_t i16; typedef int16_t i16;
typedef int32_t i32; typedef int32_t i32;
typedef int64_t i64; 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 float f32;
typedef double f64; typedef double f64;
typedef i8 b8; typedef i8 b8;
typedef i32 b32; typedef i32 b32;
#if 0 #define I128(hi64, lo64) (((i128)(hi64) << 64) | (lo64))
/* Unsigned memory (like size_t) */ #define U128(hi64, lo64) (((u128)(hi64) << 64) | (lo64))
typedef u64 umm;
#endif
#define U8_MAX (0xFF) #define U8_MAX (0xFF)
#define U16_MAX (0xFFFF) #define U16_MAX (0xFFFF)
@ -326,6 +332,9 @@ struct arena {
u64 committed; u64 committed;
u64 reserved; u64 reserved;
u8 *base; u8 *base;
#if RTC
b32 readonly;
#endif
}; };
struct string { 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 */ /* Clang/GCC cleanup macros */
#if COMPILER_CLANG || COMPILER_GCC #if COMPILER_CLANG || COMPILER_GCC
# if !TRACY_NO_CALLSTACK # 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 }; 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 );
# 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 __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 ); # 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
#endif #endif
INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *__tracy_ctx) { TracyCZoneEnd(*__tracy_ctx); } 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 */ #endif /* PROFILING */
/* ========================== *
* Configurable constants
* ========================== */
#include "config.h"
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -21,7 +21,8 @@
/* If we are not compiling in developer mode, assume resources are embedded as /* 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 * a tar archive in the executable. Otherwise, look for resources in the file
* system. */ * system. */
#define RESOURCES_EMBEDDED !(DEVELOPER) #define RESOURCES_EMBEDDED (!DEVELOPER)
#define RESOURCE_RELOADING (DEVELOPER && !RESOURCES_EMBEDDED)
#define DEFAULT_CAMERA_WIDTH (7.0) #define DEFAULT_CAMERA_WIDTH (7.0)
#define DEFAULT_CAMERA_HEIGHT (DEFAULT_CAMERA_WIDTH / (16.0 / 9.0)) #define DEFAULT_CAMERA_HEIGHT (DEFAULT_CAMERA_WIDTH / (16.0 / 9.0))

View File

@ -130,7 +130,7 @@ void _log(i32 level, struct string msg)
struct temp_arena scratch = scratch_begin_no_conflict(); 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(); u32 tid = sys_thread_id();
struct log_level_settings settings = g_log_level_settings[level]; struct log_level_settings settings = g_log_level_settings[level];

View File

@ -16,10 +16,23 @@ __attribute((section(".text.memset")))
void *memset(void *dest, i32 c, u64 n) void *memset(void *dest, i32 c, u64 n)
{ {
/* TODO: Faster memset */ /* TODO: Faster memset */
for (u64 i = 0; i < (n); ++i) { for (u64 i = 0; i < n; ++i) {
((u8 *)dest)[i] = c; ((u8 *)dest)[i] = c;
} }
return dest; 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 */ #endif /* !CRTLIB */

View File

@ -1,20 +1,25 @@
#ifndef MEMORY_H #ifndef MEMORY_H
#define MEMORY_H #define MEMORY_H
#define MEMZERO_STRUCT(ptr) MEMZERO(ptr, sizeof(*ptr)) #define MEMZERO_STRUCT(ptr) MEMZERO((ptr), sizeof(*(ptr)))
#define MEMZERO_ARRAY(a) MEMZERO(a, sizeof(a)) #define MEMZERO_ARRAY(a) MEMZERO((a), sizeof((a)))
#define MEMZERO(ptr, count) MEMSET(ptr, 0, count) #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_STRUCT(ptr_dest, ptr_src) MEMCPY((ptr_dest), (ptr_src), sizeof(*(ptr_dest)));
#define MEMCPY(dest, src, count) memcpy(dest, src, count) #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 #if CRTLIB
# include <memory.h> # include <memory.h>
#else #else
/* TODO: restrict args? */
void *memcpy(void *__restrict dest, const void *__restrict src, u64 n); void *memcpy(void *__restrict dest, const void *__restrict src, u64 n);
void *memset(void *dest, i32 c, u64 n); void *memset(void *dest, i32 c, u64 n);
i32 memcmp(const void *p1, const void *p2, u64 n);
#endif #endif
#endif #endif

View File

@ -1,6 +1,6 @@
#include "scratch.h" #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; __prof;
struct scratch_ctx *ctx = vctx; 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; __prof;
struct scratch_ctx *ctx = vctx; 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);

View File

@ -21,13 +21,24 @@
#define MAX_LOADER_THREADS 4 #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 loader_cmd {
struct load_cmd *next; struct loader_cmd *next;
struct load_cmd *next_free; struct loader_cmd *next_free;
struct cache_node *cache_node; struct cache_node *cache_node;
struct sheet_tag tag; struct sheet_tag tag;
@ -42,23 +53,32 @@ enum cache_node_state {
CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_NONE,
CACHE_NODE_STATE_QUEUED, CACHE_NODE_STATE_QUEUED,
CACHE_NODE_STATE_WORKING, CACHE_NODE_STATE_WORKING,
CACHE_NODE_STATE_LOADED CACHE_NODE_STATE_LOADED,
CACHE_NODE_STATE_EVICTED
}; };
struct cache_node { struct cache_node {
u128 hash;
struct atomic_u32 state; struct atomic_u32 state;
struct atomic_i32 refcount; /* Number of scopes currently holding a reference to this sheet */ 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 */ /* Allocated data */
struct sheet sheet;
struct sheet_tag tag;
struct arena arena; struct arena arena;
struct sheet *sheet;
/* Hash list */ /* Hash list */
struct cache_node *next_hash; struct cache_node *next_hash;
struct cache_node *prev_hash;
/* Free list */ /* Free list */
struct cache_node *next_free; 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 { struct cache {
@ -82,25 +102,31 @@ GLOBAL struct {
/* Cache */ /* Cache */
struct cache cache; struct cache cache;
/* Load cmds */ /* Loader threads */
b32 shutdown; b32 loaders_shutdown;
struct arena load_cmd_arena; struct sys_mutex loaders_mutex;
struct sys_mutex load_cmd_mutex; struct sys_condition_variable loaders_cv;
struct sys_condition_variable load_cmd_or_shutdown_cv; struct arena loader_cmd_arena;
struct load_cmd *first_free_load_cmd; struct loader_cmd *first_free_loader_cmd;
struct load_cmd *first_load_cmd; struct loader_cmd *first_loader_cmd;
struct load_cmd *last_load_cmd; struct loader_cmd *last_loader_cmd;
/* Threads */
u64 loader_threads_count; u64 loader_threads_count;
struct sys_thread loader_threads[MAX_LOADER_THREADS]; 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; struct sys_thread evictor_thread;
} G = { 0 }, DEBUG_ALIAS(G, G_sheet); } G = { 0 }, DEBUG_ALIAS(G, G_sheet);
GLOBAL READONLY struct sheet g_sheet_nil = { 0 };
GLOBAL READONLY struct sheet g_sheet_loading = { GLOBAL READONLY struct sheet g_sheet_loading = {
.loading = true .loading = true
}; };
/* ========================== * /* ========================== *
* Thread local state * 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 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_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 sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
struct asset_cache_startup_receipt *asset_cache_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.arena = arena_alloc(GIGABYTE(64));
G.cache.buckets = arena_push_array_zero(&G.cache.arena, struct cache_node *, CACHE_BUCKETS_COUNT); 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.loader_cmd_arena = arena_alloc(GIGABYTE(64));
G.load_cmd_mutex = sys_mutex_alloc(); G.loaders_mutex = sys_mutex_alloc();
G.load_cmd_or_shutdown_cv = sys_condition_variable_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(); struct temp_arena scratch = scratch_begin_no_conflict();
G.loader_threads_count = clamp_i64(1, MAX_LOADER_THREADS, sys_num_logical_processors() - 1); 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); 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); app_register_exit_callback(&sheet_shutdown);
@ -171,61 +200,43 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown)
{ {
__prof; __prof;
/* Signal shutdown */ /* Signal loaders shutdown */
sys_mutex_lock(&G.load_cmd_mutex); sys_mutex_lock(&G.loaders_mutex);
{ {
G.shutdown = true; G.loaders_shutdown = true;
sys_condition_variable_broadcast(&G.load_cmd_or_shutdown_cv); 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 */ /* Wait on threads */
for (u64 i = 0; i < G.loader_threads_count; ++i) { for (u64 i = 0; i < G.loader_threads_count; ++i) {
sys_thread_wait_release(&G.loader_threads[i]); sys_thread_wait_release(&G.loader_threads[i]);
} }
//sys_thread_wait_release(&G.evictor_thread); sys_thread_wait_release(&G.evictor_thread);
} }
/* ========================== * /* ========================== *
* Tag * 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 sheet_tag_from_path(struct string path)
{ {
struct sheet_tag res = { 0 }; struct sheet_tag res = { 0 };
res.hash = HASH_FNV64_SEED; res.hash = HASH_FNV128_BASIS;
res.hash = hash_fnv64(res.hash, BUFFER_FROM_STRING(path)); res.hash = hash_fnv128(res.hash, BUFFER_FROM_STRING(path));
res.path = path; res.path = path;
return res; 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 * Load
* ========================== */ * ========================== */
@ -283,21 +294,30 @@ INTERNAL void sheet_load(struct cache_node *n, struct sheet_tag tag)
ASSERT(string_ends_with(path, STR(".ase"))); ASSERT(string_ends_with(path, STR(".ase")));
n->arena = arena_alloc(SHEET_ARENA_RESERVE);
{
/* Decode */ /* Decode */
struct ase_decode_sheet_result decoded = { 0 }; struct ase_decode_sheet_result decoded = { 0 };
if (resource_exists(path)) { if (resource_exists(path)) {
struct resource sheet_rs = resource_open(path); struct resource sheet_rs = resource_open(path);
decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); 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); resource_close(sheet_rs);
/* Initialize */
n->sheet = arena_push(&n->arena, struct sheet);
*n->sheet = init_sheet_from_ase_result(&n->arena, decoded);
} else { } else {
/* FIXME: Working default */ n->sheet = &g_sheet_nil;
logf_error("Resource \"%F\" not found", path); logf_error("Resource \"%F\" not found", path);
} }
}
/* Allocate & initialize */ arena_set_readonly(&n->arena);
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);
logf_info("Finished loading sheet \"%F\" in %F seconds (final size: %F bytes).", logf_info("Finished loading sheet \"%F\" in %F seconds (final size: %F bytes).",
FMT_STR(path), FMT_STR(path),
@ -365,10 +385,14 @@ struct sheet_scope *sheet_scope_begin(void)
void sheet_scope_end(struct sheet_scope *scope) void sheet_scope_end(struct sheet_scope *scope)
{ {
struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); 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) { for (u64 i = 0; i < SCOPE_BUCKETS_COUNT; ++i) {
struct sheet_scope_reference *ref = scope->reference_buckets[i]; struct sheet_scope_reference *ref = scope->reference_buckets[i];
while (ref) { 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; ref->next_free = tctx->first_free_reference;
tctx->first_free_reference = ref; tctx->first_free_reference = ref;
ref = ref->next_hash; 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) 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 *n = NULL;
struct cache_node *nonmatching = NULL;
struct cache_node **nonmatching_next = NULL;
u64 cache_bucket_index = tag.hash % CACHE_BUCKETS_COUNT; u64 cache_bucket_index = tag.hash % CACHE_BUCKETS_COUNT;
/* Lookup */ /* Lookup */
/* TODO: Spinlock */
sys_rw_mutex_lock_shared(&G.cache.rw_mutex); sys_rw_mutex_lock_shared(&G.cache.rw_mutex);
{ {
n_next = &G.cache.buckets[cache_bucket_index]; nonmatching_next = &G.cache.buckets[cache_bucket_index];
n = *n_next; n = *nonmatching_next;
while (n) { while (n) {
if (sheet_tag_eq(n->tag, tag)) { if (n->hash == tag.hash) {
scope_ensure_reference(scope, n, cache_bucket_index); scope_ensure_reference(scope, n, cache_bucket_index);
break; break;
} else { } else {
n_next = &n->next_hash; nonmatching = n;
n = *n_next; 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 */ /* Allocate new node if necessary */
if (!n) { if (!n) {
/* Alloc */
sys_rw_mutex_lock_exclusive(&G.cache.rw_mutex); sys_rw_mutex_lock_exclusive(&G.cache.rw_mutex);
{ {
if (G.cache.first_free) { if (G.cache.first_free) {
n = 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); MEMZERO_STRUCT(n);
} else { } else {
n = arena_push_zero(&G.cache.arena, struct cache_node); n = arena_push_zero(&G.cache.arena, struct cache_node);
} }
scope_ensure_reference(scope, n, cache_bucket_index); 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); 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) INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct sheet_tag tag, b32 await)
{ {
struct sheet *res = &g_sheet_loading; struct sheet *res = &g_sheet_loading;
struct cache_node *n = node_lookup_touch(scope, tag); struct cache_node *n = node_lookup_touch(scope, tag);
u32 state = atomic_u32_eval(&n->state); u32 state = atomic_u32_eval(&n->state);
if (state == CACHE_NODE_STATE_LOADED) { if (state == CACHE_NODE_STATE_LOADED) {
res = &n->sheet; res = n->sheet;
} else if (state == CACHE_NODE_STATE_NONE) { } 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) { 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 */ /* Node is new, load sheet */
if (await) { if (await) {
sheet_load(n, tag); sheet_load(n, tag);
res = &n->sheet; res = n->sheet;
} else { } else {
sys_mutex_lock(&G.load_cmd_mutex); sys_mutex_lock(&G.loaders_mutex);
{ {
/* Allocate cmd */ /* Allocate cmd */
struct load_cmd *cmd = NULL; struct loader_cmd *cmd = NULL;
if (G.first_free_load_cmd) { if (G.first_free_loader_cmd) {
cmd = G.first_free_load_cmd; cmd = G.first_free_loader_cmd;
G.first_free_load_cmd = cmd->next_free; G.first_free_loader_cmd = cmd->next_free;
} else { } else {
cmd = arena_push(&G.load_cmd_arena, struct load_cmd); cmd = arena_push(&G.loader_cmd_arena, struct loader_cmd);
} }
/* Initialize cmd */ /* Initialize cmd */
@ -464,13 +494,13 @@ INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct
} }
/* Add cmd to queue */ /* Add cmd to queue */
*(G.last_load_cmd ? &G.last_load_cmd->next : &G.first_load_cmd) = cmd; *(G.last_loader_cmd ? &G.last_loader_cmd->next : &G.first_loader_cmd) = cmd;
G.last_load_cmd = cmd; G.last_loader_cmd = cmd;
/* Signal work ready */ /* 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) INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg)
{ {
__prof;
(UNUSED)arg; (UNUSED)arg;
while (true) { while (true) {
sys_mutex_lock(&G.load_cmd_mutex);
if (G.shutdown) { sys_mutex_lock(&G.loaders_mutex);
if (G.loaders_shutdown) {
/* Thread shutdown */ /* Thread shutdown */
sys_mutex_unlock(&G.load_cmd_mutex); sys_mutex_unlock(&G.loaders_mutex);
break; break;
} else if (!G.first_load_cmd) { } else if (!G.first_loader_cmd) {
/* Wait for work */ /* 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 */ /* Pull cmd from queue */
struct load_cmd *cmd = G.first_load_cmd; struct loader_cmd *cmd = G.first_loader_cmd;
G.first_load_cmd = cmd->next; G.first_loader_cmd = cmd->next;
if (G.last_loader_cmd == cmd) {
G.last_loader_cmd = NULL;
}
/* Do work (temporarily unlock) */ /* Do work (temporarily unlock) */
sys_mutex_unlock(&G.load_cmd_mutex); sys_mutex_unlock(&G.loaders_mutex);
{ {
sheet_load(cmd->cache_node, cmd->tag); sheet_load(cmd->cache_node, cmd->tag);
} }
sys_mutex_lock(&G.load_cmd_mutex); sys_mutex_lock(&G.loaders_mutex);
/* Free cmd */ /* Free cmd */
cmd->next_free = G.first_free_load_cmd; cmd->next_free = G.first_free_loader_cmd;
G.first_free_load_cmd = 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, &current_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;
}
} }
} }

View File

@ -10,7 +10,7 @@ struct asset_cache_startup_receipt;
struct resource_startup_receipt; struct resource_startup_receipt;
struct sheet_tag { struct sheet_tag {
u64 hash; u128 hash;
struct string path; struct string path;
}; };

View File

@ -167,6 +167,32 @@ void *sys_memory_commit(void *address, u64 size);
* address space) */ * address space) */
void sys_memory_decommit(void *address, u64 size); 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 * File system
* *
@ -177,6 +203,12 @@ struct sys_file {
u64 handle; 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); struct string sys_get_write_path(struct arena *arena);
b32 sys_is_file(struct string path); b32 sys_is_file(struct string path);
b32 sys_is_dir(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); struct buffer sys_file_read_all(struct arena *arena, struct sys_file file);
void sys_file_write(struct sys_file file, struct buffer data); 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 * File map
@ -416,28 +449,6 @@ enum sys_message_box_kind {
void sys_message_box(enum sys_message_box_kind kind, struct string message); 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 * Clipboard
* ========================== */ * ========================== */

View File

@ -204,6 +204,58 @@ void sys_memory_decommit(void *address, u64 size)
VirtualFree(address, size, MEM_DECOMMIT); 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(&lt);
return win32_time_to_sys_time(lt);
}
/* ========================== * /* ========================== *
* File system * 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; LARGE_INTEGER li_file_size;
GetFileSizeEx((HANDLE)file.handle, &li_file_size); GetFileSizeEx((HANDLE)file.handle, &li_file_size);
return (u64)(li_file_size.QuadPart > 0 ? li_file_size.QuadPart : 0); 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 * File map
* ========================== */ * ========================== */
struct sys_file_map sys_file_map_open_read(struct sys_file file) 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; u8 *base_ptr = NULL;
HANDLE map_handle = 0; HANDLE map_handle = 0;
@ -1693,47 +1773,6 @@ void sys_message_box(enum sys_message_box_kind kind, struct string message)
scratch_end(scratch); 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(&lt);
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 * Clipboard
* ========================== */ * ========================== */

View File

@ -769,11 +769,9 @@ INTERNAL void user_update(void)
u32 tint = ent->sprite_tint; u32 tint = ent->sprite_tint;
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = 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); struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
u64 frame_index = span.start + ent->animation_frame; u64 frame_index = span.start + ent->animation_frame;
struct sheet_frame frame = sheet_get_frame(sheet, frame_index); struct sheet_frame frame = sheet_get_frame(sheet, frame_index);
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) { if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) {
@ -792,7 +790,11 @@ INTERNAL void user_update(void)
params.clip = frame.clip; params.clip = frame.clip;
} }
/* 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); draw_texture_quad(G.world_canvas, params, quad);
}
#if 0 #if 0
if (G.debug_draw && !skip_debug_draw) { if (G.debug_draw && !skip_debug_draw) {

View File

@ -10,39 +10,35 @@
/* Utility functions and stuff that don't have a home :( */ /* Utility functions and stuff that don't have a home :( */
/* ========================== * /* ========================== *
* String utils * Hash utils
* ========================== */ * ========================== */
#if 0 /* FNV-1a parameters for different hash sizes:
struct string util_file_name_from_path(struct string path); * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
#endif */
/* FNV-1a hash function #define HASH_FNV64_BASIS 0xCBF29CE484222325
* TODO: Something faster if necessary */
#define HASH_FNV64_SEED 0xcbf29ce484222325
INLINE u64 hash_fnv64(u64 seed, struct buffer buff) INLINE u64 hash_fnv64(u64 seed, struct buffer buff)
{ {
u64 hash = seed; u64 hash = seed;
for (u64 i = 0; i < buff.size; ++i) { for (u64 i = 0; i < buff.size; ++i) {
hash ^= (u8)buff.data[i]; hash ^= (u8)buff.data[i];
hash *= 0x100000001b3; hash *= 0x100000001B3;
} }
return hash; return hash;
} }
#if 0 #define HASH_FNV128_BASIS U128(0x6C62272E07BB0142, 0x62B821756295C58D)
/* FNV-1a hash function INLINE u128 hash_fnv128(u128 seed, struct buffer buff)
* TODO: Something faster if necessary */
INLINE u32 hash_fnv32(struct buffer buff)
{ {
u32 hash = 2166136261; u128 hash = seed;
for (u64 i = 0; i < buff.size; ++i) { for (u64 i = 0; i < buff.size; ++i) {
hash ^= (u8)buff.data[i]; u8 c = (u8)buff.data[i];
hash *= 16777619; hash ^= c;
hash *= U128(0x1000000, 0x000000000000013B);
} }
return hash; return hash;
} }
#endif
/* ========================== * /* ========================== *
* Fixed Dict * Fixed Dict
@ -81,7 +77,7 @@ INLINE void fixed_dict_set(struct arena *arena, struct fixed_dict *dict, struct
{ {
__prof; __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; u64 index = hash % dict->buckets_count;
struct fixed_dict_bucket *bucket = &dict->buckets[index]; 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; __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; u64 index = hash % dict->buckets_count;
struct fixed_dict_bucket *bucket = &dict->buckets[index]; struct fixed_dict_bucket *bucket = &dict->buckets[index];