sheet eviction & reload detection
This commit is contained in:
parent
35ded9dbac
commit
5a32c90813
@ -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()
|
||||||
|
|||||||
12
src/arena.c
12
src/arena.c
@ -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
|
||||||
|
}
|
||||||
|
|||||||
42
src/arena.h
42
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_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
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
42
src/common.h
42
src/common.h
@ -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 __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 __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 );
|
# 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
|
||||||
# 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
|
#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
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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];
|
||||||
|
|||||||
15
src/memory.c
15
src/memory.c
@ -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 */
|
||||||
|
|||||||
17
src/memory.h
17
src/memory.h
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
358
src/sheet.c
358
src/sheet.c
@ -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")));
|
||||||
|
|
||||||
/* 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->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).",
|
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, ¤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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
57
src/sys.h
57
src/sys.h
@ -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
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|||||||
125
src/sys_win32.c
125
src/sys_win32.c
@ -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(<);
|
||||||
|
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(<);
|
|
||||||
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
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|||||||
10
src/user.c
10
src/user.c
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 0
|
||||||
if (G.debug_draw && !skip_debug_draw) {
|
if (G.debug_draw && !skip_debug_draw) {
|
||||||
|
|||||||
32
src/util.h
32
src/util.h
@ -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];
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user