diff --git a/src/app.c b/src/app.c index adfa34ef..40bd63a9 100644 --- a/src/app.c +++ b/src/app.c @@ -83,9 +83,9 @@ INTERNAL struct sys_window_settings default_window_settings(struct sys_window *w struct v2 monitor_size = sys_window_get_monitor_size(window); i32 width = 1280; - i32 height = math_round(width / (f32)(DEFAULT_CAMERA_WIDTH / DEFAULT_CAMERA_HEIGHT)); - i32 x = math_round(monitor_size.x / 2.f - width / 2); - i32 y = math_round(monitor_size.y / 2.f - height / 2); + i32 height = math_round_to_int(width / (f32)(DEFAULT_CAMERA_WIDTH / DEFAULT_CAMERA_HEIGHT)); + i32 x = math_round_to_int(monitor_size.x / 2.f - width / 2); + i32 y = math_round_to_int(monitor_size.y / 2.f - height / 2); return (struct sys_window_settings) { .title = WINDOW_TITLE, diff --git a/src/arena.c b/src/arena.c index 435aedcd..0454a0e3 100644 --- a/src/arena.c +++ b/src/arena.c @@ -10,6 +10,7 @@ * memory. */ struct arena arena_alloc(u64 reserve) { + __prof; struct arena arena = { 0 }; /* Round up to nearest block size */ @@ -43,6 +44,7 @@ struct arena arena_alloc(u64 reserve) void arena_release(struct arena *arena) { + __prof; sys_memory_decommit(arena->base, arena->committed); sys_memory_release(arena->base); } @@ -91,7 +93,7 @@ void *_arena_push_bytes(struct arena *arena, u64 size, u64 align) } /* Copies the memory from the source arena into the destination arena, - * replacing old contents. Destination arena will be expanded if necessary contents. */ + * replacing old contents. Destination arena will be expanded if necessary. */ void arena_copy_replace(struct arena *dest, struct arena *src) { arena_reset(dest); diff --git a/src/ase.c b/src/ase.c index e1466d6a..a3fe11ee 100644 --- a/src/ase.c +++ b/src/ase.c @@ -9,6 +9,7 @@ #include "scratch.h" #include "byteio.h" #include "string.h" +#include "log.h" /* ========================== * * Bitbuf @@ -383,9 +384,6 @@ INTERNAL void inflate(struct arena *arena, u8 *dest, u8 *encoded) * Decoder structs * ========================== */ -#define LAYER_FLAG_NONE 0x0 -#define LAYER_FLAG_VISIBLE 0x1 - enum chunk_type { CHUNK_TYPE_OLD_PALETTE1 = 0x0004, CHUNK_TYPE_OLD_PALETTE2 = 0x0011, @@ -444,6 +442,7 @@ struct frame_header { INTERNAL void push_error_copy_msg(struct arena *arena, struct ase_error_list *list, struct string msg_src) { + logf_error("Error while decoding .ase: \"%F\"", FMT_STR(msg_src)); struct ase_error *e = arena_push(arena, struct ase_error); *e = (struct ase_error) { .msg = string_copy(arena, msg_src) diff --git a/src/atomic.h b/src/atomic.h index 23dac097..ae468069 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -9,6 +9,7 @@ FORCE_INLINE i32 atomic_i32_dec_eval(struct atomic_i32 *x) { return _Interlocked FORCE_INLINE i32 atomic_i32_eval_add(struct atomic_i32 *x, i32 a) { return _InterlockedExchangeAdd((volatile long *)&x->_v, a); } FORCE_INLINE i32 atomic_i32_eval_exchange(struct atomic_i32 *x, i32 e) { return _InterlockedExchange((volatile long *)&x->_v, e); } FORCE_INLINE i32 atomic_i32_eval_compare_exchange(struct atomic_i32 *x, i32 c, i32 e) { return _InterlockedCompareExchange((volatile long *)&x->_v, e, c); } +FORCE_INLINE volatile i32 *atomic_i32_raw(struct atomic_i32 *x) { return &x->_v; } FORCE_INLINE i64 atomic_i64_eval(struct atomic_i64 *x) { return _InterlockedOr64(&x->_v, 0); } FORCE_INLINE i64 atomic_i64_inc_eval(struct atomic_i64 *x) { return _InterlockedIncrement64(&x->_v); } @@ -16,6 +17,7 @@ FORCE_INLINE i64 atomic_i64_dec_eval(struct atomic_i64 *x) { return _Interlocked FORCE_INLINE i64 atomic_i64_eval_add(struct atomic_i64 *x, i64 a) { return _InterlockedExchangeAdd64(&x->_v, a); } FORCE_INLINE i64 atomic_i64_eval_exchange(struct atomic_i64 *x, i64 e) { return _InterlockedExchange64(&x->_v, e); } FORCE_INLINE i64 atomic_i64_eval_compare_exchange(struct atomic_i64 *x, i64 c, i64 e) { return _InterlockedCompareExchange64(&x->_v, e, c); } +FORCE_INLINE volatile i64 *atomic_i64_raw(struct atomic_i64 *x) { return &x->_v; } FORCE_INLINE u32 atomic_u32_eval(struct atomic_u32 *x) { return _InterlockedExchangeAdd((volatile long *)&x->_v, 0); } FORCE_INLINE u32 atomic_u32_inc_eval(struct atomic_u32 *x) { return _InterlockedIncrement((volatile long *)&x->_v); } @@ -23,6 +25,7 @@ FORCE_INLINE u32 atomic_u32_dec_eval(struct atomic_u32 *x) { return _Interlocked FORCE_INLINE u32 atomic_u32_eval_add(struct atomic_u32 *x, u32 a) { return _InterlockedExchangeAdd((volatile long *)&x->_v, a); } FORCE_INLINE u32 atomic_u32_eval_exchange(struct atomic_u32 *x, u32 e) { return _InterlockedExchange((volatile long *)&x->_v, e); } FORCE_INLINE u32 atomic_u32_eval_compare_exchange(struct atomic_u32 *x, u32 c, u32 e) { return _InterlockedCompareExchange((volatile long *)&x->_v, e, c); } +FORCE_INLINE volatile u32 *atomic_u32_raw(struct atomic_u32 *x) { return &x->_v; } FORCE_INLINE u64 atomic_u64_eval(struct atomic_u64 *x) { return _InterlockedOr64((volatile i64 *)&x->_v, 0); } FORCE_INLINE u64 atomic_u64_inc_eval(struct atomic_u64 *x) { return _InterlockedIncrement64((volatile i64 *)&x->_v); } @@ -30,9 +33,11 @@ FORCE_INLINE u64 atomic_u64_dec_eval(struct atomic_u64 *x) { return _Interlocked FORCE_INLINE u64 atomic_u64_eval_add(struct atomic_u64 *x, u64 a) { return _InterlockedExchangeAdd64((volatile i64 *)&x->_v, a); } FORCE_INLINE u64 atomic_u64_eval_exchange(struct atomic_u64 *x, u64 e) { return _InterlockedExchange64((volatile i64 *)&x->_v, e); } FORCE_INLINE u64 atomic_u64_eval_compare_exchange(struct atomic_u64 *x, u64 c, u64 e) { return _InterlockedCompareExchange64((volatile i64 *)&x->_v, e, c); } +FORCE_INLINE volatile u64 *atomic_u64_raw(struct atomic_u64 *x) { return &x->_v; } FORCE_INLINE void *atomic_ptr_eval(struct atomic_ptr *x) { return (void *)_InterlockedOr64((volatile i64 *)&x->_v, 0); } FORCE_INLINE void *atomic_ptr_eval_compare_exchange(struct atomic_ptr *x, void *c, void *e) { return (void *)_InterlockedCompareExchange64((volatile i64 *)&x->_v, (i64)e, (i64)c); } +FORCE_INLINE volatile void **atomic_ptr_raw(struct atomic_ptr *x) { return &x->_v; } #else # error "Atomics not implemented" diff --git a/src/common.h b/src/common.h index 317a5900..fb5daaf8 100644 --- a/src/common.h +++ b/src/common.h @@ -192,6 +192,8 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t); #endif #define ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0])) + +/* field macros */ #define FIELD_SIZEOF(type, field) sizeof(((type *)0)->field) #if COMPILER_MSVC && !defined _CRT_USE_BUILTIN_OFFSETOF # define FIELD_OFFSETOF(type, field) ((u64)&(((type *)0)->field)) @@ -199,8 +201,6 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t); # define FIELD_OFFSETOF(type, field) __builtin_offsetof(type, field) #endif -#define FIELD_SIZEOF(type, field) sizeof(((type *)0)->field) - /* Bool */ #ifndef __cplusplus # define true 1 @@ -288,8 +288,7 @@ typedef u64 umm; #define I64_MIN ((i64)-0x8000000000000000LL) #define F32_INFINITY (1.0 / 0.0f) -#define F32_MAX (3.402823466e+38F) -#define F32_MIN (1.175494351e-38F) +#define F64_INFINITY (1.0 / 0.0) #define PI ((f32)3.14159265358979323846) #define TAU ((f32)6.28318530717958647693) diff --git a/src/entity.h b/src/entity.h index 68112402..9de8d830 100644 --- a/src/entity.h +++ b/src/entity.h @@ -1,6 +1,7 @@ #ifndef ENTITY_H #define ENTITY_H +#include "mixer.h" #include "sheet.h" #include "mixer.h" @@ -66,6 +67,7 @@ struct entity { /* Sprite */ struct string sprite_name; + struct sheet_tag sprite_sheet_tag; struct string sprite_span_name; struct xform sprite_quad_xform; u32 sprite_tint; diff --git a/src/font.c b/src/font.c index f90a57b6..fe16dac4 100644 --- a/src/font.c +++ b/src/font.c @@ -162,7 +162,7 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams) logf_info("Finished loading font \"%F\" (point size %F) in %F seconds", FMT_STR(path), FMT_FLOAT((f64)point_size), FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts))); asset_cache_mark_ready(asset, font); - scratch_end_and_decommit(scratch); + scratch_end(scratch); } /* Returns the asset from the asset cache */ diff --git a/src/game.c b/src/game.c index f453f212..17afcf90 100644 --- a/src/game.c +++ b/src/game.c @@ -157,6 +157,12 @@ INTERNAL void game_update(void) struct temp_arena scratch = scratch_begin_no_conflict(); + /* ========================== * + * Begin frame cache scopes + * ========================== */ + + struct sheet_scope *sheet_frame_scope = sheet_scope_begin(); + /* TODO: remove this (testing) */ /* Initialize entities */ static b32 run = 0; @@ -176,9 +182,18 @@ INTERNAL void game_update(void) struct string sprite_name = STR("res/graphics/tim.ase"); struct string sprite_span_name = STR("UNARMED"); - struct sheet *sheet = sheet_load(sprite_name); - f32 meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; - f32 meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; + struct sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name); + + f32 meters_width, meters_height; + { + struct sheet_scope *scope = sheet_scope_begin(); + { + struct sheet *sheet = sheet_from_tag_await(scope, sprite_sheet_tag); + meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; + meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; + } + sheet_scope_end(scope); + } struct v2 sprite_pos = V2(0, 0); f32 sprite_rot = 0; @@ -198,6 +213,7 @@ INTERNAL void game_update(void) e->sprite_quad_xform = sprite_xf; e->sprite_name = sprite_name; + e->sprite_sheet_tag = sprite_sheet_tag; e->sprite_span_name = sprite_span_name; e->sprite_tint = COLOR_WHITE; @@ -227,9 +243,18 @@ INTERNAL void game_update(void) struct string sprite_name = STR("res/graphics/tim.ase"); struct string sprite_span_name = STR("UNARMED"); - struct sheet *sheet = sheet_load(sprite_name); - f32 meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; - f32 meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; + struct sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name); + + f32 meters_width, meters_height; + { + struct sheet_scope *scope = sheet_scope_begin(); + { + struct sheet *sheet = sheet_from_tag_await(scope, sprite_sheet_tag); + meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; + meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; + } + sheet_scope_end(scope); + } struct v2 sprite_pos = V2(0, 0); f32 sprite_rot = 0; @@ -249,6 +274,7 @@ INTERNAL void game_update(void) e->sprite_quad_xform = sprite_xf; e->sprite_name = sprite_name; + e->sprite_sheet_tag = sprite_sheet_tag; e->sprite_span_name = sprite_span_name; e->sprite_tint = RGBA_F(0.5, 0.5, 0, 1); @@ -296,14 +322,24 @@ INTERNAL void game_update(void) e->rel_xform = XFORM_POS(V2(-3, -3)); struct string sprite_name = STR("res/graphics/sound.ase"); - struct sheet *sheet = sheet_load(sprite_name); - f32 meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; - f32 meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; + struct sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name); + + f32 meters_width, meters_height; + { + struct sheet_scope *scope = sheet_scope_begin(); + { + struct sheet *sheet = sheet_from_tag_await(scope, sprite_sheet_tag); + meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; + meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; + } + sheet_scope_end(scope); + } struct v2 sprite_size = V2(meters_width, meters_height); e->sprite_quad_xform = xform_with_scale(XFORM_IDENT, sprite_size); e->sprite_name = sprite_name; + e->sprite_sheet_tag = sprite_sheet_tag; e->sprite_tint = RGBA_F(1, 1, 0, 1); entity_enable_prop(e, ENTITY_PROP_TEST_SOUND_EMITTER); @@ -380,33 +416,29 @@ INTERNAL void game_update(void) * Update animation * ========================== */ -#if 0 if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) { f64 time_in_frame = ent->animation_time_in_frame + G.world.dt; u64 span_frame_offset = ent->animation_frame; - struct sheet *sheet = sheet_load(ent->sprite_name); - if (sheet) { - struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); - u64 frame_index = span.start + span_frame_offset; + struct sheet *sheet = sheet_from_tag_await(sheet_frame_scope, ent->sprite_sheet_tag); + struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); + u64 frame_index = span.start + span_frame_offset; - struct sheet_frame frame = sheet_get_frame(sheet, frame_index); - while (time_in_frame > frame.duration) { - time_in_frame -= frame.duration; - ++frame_index; - if (frame_index > span.end) { - /* Loop animation */ - frame_index = span.start; - } - frame = sheet_get_frame(sheet, frame_index); + struct sheet_frame frame = sheet_get_frame(sheet, frame_index); + while (time_in_frame > frame.duration) { + time_in_frame -= frame.duration; + ++frame_index; + if (frame_index > span.end) { + /* Loop animation */ + frame_index = span.start; } - span_frame_offset = frame_index - span.start; + frame = sheet_get_frame(sheet, frame_index); } + span_frame_offset = frame_index - span.start; ent->animation_time_in_frame = time_in_frame; ent->animation_frame = span_frame_offset; } -#endif /* ========================== * * Test @@ -579,6 +611,12 @@ INTERNAL void game_update(void) publish_game_tick(); __profframe("Game"); + /* ========================== * + * End frame cache scopes + * ========================== */ + + sheet_scope_end(sheet_frame_scope); + scratch_end(scratch); } diff --git a/src/intrinsics.h b/src/intrinsics.h index ad306cfa..e0175547 100644 --- a/src/intrinsics.h +++ b/src/intrinsics.h @@ -5,6 +5,8 @@ * Math * ========================== */ +/* Sqrt */ + INLINE f32 ix_sqrt_f32(f32 f) { __m128 n = _mm_set_ss(f); @@ -19,25 +21,82 @@ INLINE f32 ix_rsqrt_f32(f32 f) return _mm_cvtss_f32(n); } +/* Round */ + INLINE i32 ix_round_f32_to_i32(f32 f) { - __m128 n = _mm_set_ss(f); - n = _mm_round_ss(_mm_setzero_ps(), n, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); - return _mm_cvtss_si32(n); + return _mm_cvtss_si32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); } +INLINE f32 ix_round_f32_to_f32(f32 f) +{ + return _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +INLINE i64 ix_round_f64_to_i64(f64 f) +{ + return _mm_cvtsd_si64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +INLINE f64 ix_round_f64_to_f64(f64 f) +{ + return _mm_cvtsd_f64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +/* Floor */ + INLINE i32 ix_floor_f32_to_i32(f32 f) { - __m128 n = _mm_set_ss(f); - n = _mm_floor_ss(_mm_setzero_ps(), n); - return _mm_cvtss_si32(n); + return _mm_cvtss_si32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(f))); } +INLINE f32 ix_floor_f32_to_f32(f32 f) +{ + return _mm_cvtss_f32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +INLINE i64 ix_floor_f64_to_i64(f64 f) +{ + return _mm_cvtsd_si64(_mm_floor_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +INLINE f64 ix_floor_f64_to_f64(f64 f) +{ + return _mm_cvtsd_f64(_mm_floor_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +/* Ceil */ + INLINE i32 ix_ceil_f32_to_i32(f32 f) { - __m128 n = _mm_set_ss(f); - n = _mm_ceil_ss(_mm_setzero_ps(), n); - return _mm_cvtss_si32(n); + return _mm_cvtss_si32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +INLINE f32 ix_ceil_f32_to_f32(f32 f) +{ + return _mm_cvtss_f32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +INLINE i64 ix_ceil_f64_to_i64(f64 f) +{ + return _mm_cvtsd_si64(_mm_ceil_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +INLINE f64 ix_ceil_f64_to_f64(f64 f) +{ + return _mm_cvtsd_f64(_mm_ceil_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +/* Truncate */ + +INLINE f32 ix_trunc_f32_to_f32(f32 f) +{ + return _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC)); +} + +INLINE f64 ix_trunc_f64_to_f64(f64 f) +{ + return _mm_cvtsd_f64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC)); } /* ========================== * diff --git a/src/math.h b/src/math.h index 183eece0..de2e170a 100644 --- a/src/math.h +++ b/src/math.h @@ -1,34 +1,109 @@ #ifndef MATH_H #define MATH_H +/* Math functions are default 32 bit (f32, i32, etc) unless specified */ + #include "intrinsics.h" INLINE f32 math_sqrt(f32 x); /* ========================== * - * Rounding + * Float rounding * ========================== */ /* TODO: Don't use intrinsics for these. */ -INLINE i32 math_round(f32 f) +/* Round */ + +INLINE f32 math_round(f32 f) +{ + return ix_round_f32_to_f32(f); +} + +INLINE f64 math_round64(f64 f) +{ + return ix_round_f64_to_f64(f); +} + +INLINE i32 math_round_to_int(f32 f) { return ix_round_f32_to_i32(f); } -INLINE i32 math_floor(f32 f) +INLINE i64 math_round_to_int64(f64 f) +{ + return ix_round_f64_to_i64(f); +} + +/* Floor */ + +INLINE f32 math_floor(f32 f) +{ + return ix_floor_f32_to_f32(f); +} + +INLINE f64 math_floor64(f64 f) +{ + return ix_floor_f64_to_f64(f); +} + +INLINE i32 math_floor_to_int(f32 f) { return ix_floor_f32_to_i32(f); } -INLINE i32 math_ceil(f32 f) + +INLINE i64 math_floor_to_int64(f64 f) +{ + return ix_floor_f64_to_i64(f); +} + +/* Ceil */ + +INLINE f32 math_ceil(f32 f) +{ + return ix_ceil_f32_to_f32(f); +} + +INLINE f64 math_ceil64(f64 f) +{ + return ix_ceil_f64_to_f64(f); +} + +INLINE i32 math_ceil_to_int(f32 f) { return ix_ceil_f32_to_i32(f); } +INLINE i64 math_ceil_to_int64(f64 f) +{ + return ix_ceil_f64_to_i64(f); +} + +/* Truncate */ + +INLINE f32 math_trunc(f32 f) +{ + return ix_trunc_f32_to_f32(f); +} + +INLINE f64 math_trunc64(f64 f) +{ + return ix_trunc_f64_to_f64(f); +} + +/* ========================== * + * Float properties + * ========================== */ + INLINE f32 math_fmod(f32 x, f32 m) { - return x - m * (i32)(x / m); + return x - m * math_trunc(x / m); +} + +INLINE f64 math_fmod64(f64 x, f64 m) +{ + return x - m * math_trunc64(x / m); } INLINE f32 math_fabs(f32 f) @@ -37,12 +112,24 @@ INLINE f32 math_fabs(f32 f) return *(f32 *)&truncated; } +INLINE f64 math_fabs64(f64 f) +{ + u64 truncated = *(u64 *)&f & 0x7FFFFFFFFFFFFFFFULL; + return *(f64 *)&truncated; +} + INLINE i32 math_fsign(f32 f) { u32 sign_bit = (*(u32 *)&f >> 31) & 1; return 1 + -(sign_bit << 1); } +INLINE i64 math_fsign64(f64 f) +{ + u64 sign_bit = (*(u64 *)&f >> 63) & 1; + return 1 + -(sign_bit << 1); +} + /* ========================== * * Exponential * ========================== */ @@ -288,7 +375,7 @@ INLINE f32 math_pow(f32 a, f32 b) return math_exp(math_ln(a) * b); } else { /* a is negative */ - i32 res_sign = math_round(b) % 2 == 0 ? 1 : -1; + i32 res_sign = math_round_to_int(b) % 2 == 0 ? 1 : -1; return math_exp(math_ln(-a) * b) * res_sign; } } @@ -334,7 +421,7 @@ INLINE f32 math_rsqrt_fast(f32 x) INLINE f32 math_sin(f32 x) { const f32 c = 0.225; - x -= (TAU * (f32)math_floor(x / TAU)); /* [0, TAU] */ + x -= (TAU * math_trunc(x / TAU)); /* [0, TAU] */ x += (TAU * (x < -PI)) - (TAU * (x > PI)); /* [-PI, PI] */ f32 y = (4.0f/PI) * x + (-4.0f/(PI*PI)) * x * math_fabs(x); y = c * (y * math_fabs(y) - y) + y; @@ -393,7 +480,7 @@ INLINE f32 math_lerp(f32 val0, f32 val1, f32 t) return val0 + ((val1 - val0) * t); } -INLINE f64 math_lerp_f64(f64 val0, f64 val1, f64 t) +INLINE f64 math_lerp64(f64 val0, f64 val1, f64 t) { return val0 + ((val1 - val0) * t); } @@ -482,24 +569,24 @@ INLINE struct v2 v2_norm_fast(struct v2 a) INLINE struct v2 v2_round(struct v2 a) { return V2( - (f32)math_round(a.x), - (f32)math_round(a.y) + math_round(a.x), + math_round(a.y) ); } INLINE struct v2 v2_floor(struct v2 a) { return V2( - (f32)math_floor(a.x), - (f32)math_floor(a.y) + math_floor(a.x), + math_floor(a.y) ); } INLINE struct v2 v2_ceil(struct v2 a) { return V2( - (f32)math_ceil(a.x), - (f32)math_ceil(a.y) + math_ceil(a.x), + math_ceil(a.y) ); } diff --git a/src/mixer.c b/src/mixer.c index 1b5e4b17..5e79cdf9 100644 --- a/src/mixer.c +++ b/src/mixer.c @@ -316,12 +316,12 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) if (source_is_stereo) { source_samples_count = frame_count * 2; /* Round to nearest frame boundary (nearest multiple of 2) */ - source_samples_count = (u64)math_ceil((f32)source_samples_count * speed); + source_samples_count = (u64)math_ceil_to_int((f32)source_samples_count * speed); source_samples_count &= ~1; } else { source_samples_count = frame_count; /* Round to nearest sample */ - source_samples_count = (u64)math_round((f32)source_samples_count * speed); + source_samples_count = (u64)math_round_to_int((f32)source_samples_count * speed); } u64 source_sample_pos_start = mix->source_pos; @@ -361,8 +361,8 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) /* 16 bit Stereo -> 32 bit Stereo */ for (u64 out_frame_pos = 0; out_frame_pos < out_frames_count; ++out_frame_pos) { f32 in_frame_pos_exact = source_frame_pos_start + (((f32)out_frame_pos / (f32)out_frames_count) * (f32)source_frames_count); - u32 in_frame_pos_prev = math_floor(in_frame_pos_exact); - u32 in_frame_pos_next = math_ceil(in_frame_pos_exact); + u32 in_frame_pos_prev = math_floor_to_int(in_frame_pos_exact); + u32 in_frame_pos_next = math_ceil_to_int(in_frame_pos_exact); /* Sample source */ f32 sample1_prev = sample_sound(source, (in_frame_pos_prev * 2) + 0, desc.looping) * (1.f / 32768.f); @@ -382,8 +382,8 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) /* 16 bit Mono -> 32 bit Stereo */ for (u64 out_frame_pos = 0; out_frame_pos < out_frames_count; ++out_frame_pos) { f32 in_frame_pos_exact = source_frame_pos_start + (((f32)out_frame_pos / (f32)out_frames_count) * (f32)source_frames_count); - u32 in_frame_pos_prev = math_floor(in_frame_pos_exact); - u32 in_frame_pos_next = math_ceil(in_frame_pos_exact); + u32 in_frame_pos_prev = math_floor_to_int(in_frame_pos_exact); + u32 in_frame_pos_next = math_ceil_to_int(in_frame_pos_exact); /* Sample source */ f32 sample_prev = sample_sound(source, in_frame_pos_prev, desc.looping) * (1.f / 32768.f); diff --git a/src/playback_wasapi.c b/src/playback_wasapi.c index af19e91f..f8594e9c 100644 --- a/src/playback_wasapi.c +++ b/src/playback_wasapi.c @@ -115,8 +115,7 @@ INTERNAL void wasapi_initialize(void) }; WAVEFORMATEX *wfx = &format_ex.Format; - -#if 1 +#if 0 b32 client_initialized = FALSE; IAudioClient3 *client3; if (SUCCEEDED(IAudioClient_QueryInterface(G.client, &IID_IAudioClient3, (LPVOID *)&client3))) { diff --git a/src/scratch.h b/src/scratch.h index a4c6fed1..ec898bd7 100644 --- a/src/scratch.h +++ b/src/scratch.h @@ -59,7 +59,7 @@ INLINE struct temp_arena _scratch_begin(struct arena *potential_conflict) /* Use `scratch_begin_no_conflict` if no conflicts are present */ ASSERT(potential_conflict != NULL); - struct scratch_ctx *ctx = thread_local_eval(&tl_scratch_ctx); + struct scratch_ctx *ctx = thread_local_var_eval(&tl_scratch_ctx); struct arena *scratch_arena = &ctx->arenas[0]; if (potential_conflict && scratch_arena->base == potential_conflict->base) { scratch_arena = &ctx->arenas[1]; @@ -83,7 +83,7 @@ INLINE struct temp_arena _scratch_begin(struct arena *potential_conflict) INLINE struct temp_arena _scratch_begin_no_conflict(void) { - struct scratch_ctx *ctx = thread_local_eval(&tl_scratch_ctx); + struct scratch_ctx *ctx = thread_local_var_eval(&tl_scratch_ctx); struct arena *scratch_arena = &ctx->arenas[0]; struct temp_arena temp = arena_temp_begin(scratch_arena); scratch_dbg_push(ctx, &temp); @@ -97,7 +97,7 @@ INLINE struct temp_arena _scratch_begin_no_conflict(void) INLINE void scratch_end(struct temp_arena scratch_temp) { #if RTC - struct scratch_ctx *ctx = thread_local_eval(&tl_scratch_ctx); + struct scratch_ctx *ctx = thread_local_var_eval(&tl_scratch_ctx); if (ctx->scratch_id_stack_count > 0) { u64 scratch_id = scratch_temp.scratch_id; u64 expected_id = ctx->scratch_id_stack[--ctx->scratch_id_stack_count]; @@ -112,12 +112,4 @@ INLINE void scratch_end(struct temp_arena scratch_temp) arena_temp_end(scratch_temp); } -INLINE void scratch_end_and_decommit(struct temp_arena scratch_temp) -{ - scratch_end(scratch_temp); - - /* Disabled for now */ - // arena_decommit_unused_blocks(scratch_temp.arena); -} - #endif diff --git a/src/settings.c b/src/settings.c index 958895e1..5903eca8 100644 --- a/src/settings.c +++ b/src/settings.c @@ -104,28 +104,28 @@ struct sys_window_settings *settings_deserialize(struct arena *arena, struct buf error = STR("Expected number for \"x\""); goto abort; } - settings->floating_x = math_round(child->value.number); + settings->floating_x = math_round_to_int(child->value.number); found_x = true; } else if (string_eq(key, STR("y"))) { if (child->type != JSON_TYPE_NUMBER) { error = STR("Expected number for \"y\""); goto abort; } - settings->floating_y = math_round(child->value.number); + settings->floating_y = math_round_to_int(child->value.number); found_y = true; } else if (string_eq(key, STR("width"))) { if (child->type != JSON_TYPE_NUMBER) { error = STR("Expected number for \"width\""); goto abort; } - settings->floating_width = math_round(child->value.number); + settings->floating_width = math_round_to_int(child->value.number); found_width = true; } else if (string_eq(key, STR("height"))) { if (child->type != JSON_TYPE_NUMBER) { error = STR("Expected number for \"height\""); goto abort; } - settings->floating_height = math_round(child->value.number); + settings->floating_height = math_round_to_int(child->value.number); found_height = true; } } diff --git a/src/sheet.c b/src/sheet.c index 8cba6b5f..24940114 100644 --- a/src/sheet.c +++ b/src/sheet.c @@ -1,5 +1,3 @@ -#if 1 - #include "sheet.h" #include "arena.h" #include "log.h" @@ -10,29 +8,111 @@ #include "ase.h" #include "util.h" #include "work.h" +#include "atomic.h" +#include "thread_local.h" -struct sheet_task_params { - struct sheet_task_params *next_free; +#define SHEET_ARENA_RESERVE MEGABYTE(64) +#define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0 - struct asset *asset; - u64 path_len; - char path_cstr[1024]; +#define TCTX_ARENA_RESERVE MEGABYTE(64) +#define CACHE_BUCKETS_COUNT 256 +#define SCOPE_BUCKETS_COUNT 256 + +/* ========================== * + * Load cmd structs + * ========================== */ + +struct load_cmd { + struct cache_node *node; + struct sheet_tag tag; + u8 tag_path_buff[4096]; }; -struct sheet_task_params_store { - struct sheet_task_params *head_free; +struct load_cmd_store { struct arena arena; struct sys_mutex mutex; }; +struct load_cmd_array { + u64 count; + struct load_cmd *cmds; +}; + +/* ========================== * + * Cache structs + * ========================== */ + +enum cache_node_state { + CACHE_NODE_STATE_NONE, + CACHE_NODE_STATE_QUEUED, + CACHE_NODE_STATE_WORKING, + CACHE_NODE_STATE_LOADED +}; + +struct cache_node { + struct atomic_u32 state; + struct atomic_i32 refcount; /* Number of scopes currently holding a reference to this sheet */ + + /* Allocated data */ + struct sheet sheet; + struct sheet_tag tag; + struct arena arena; + + /* Hash list */ + struct cache_node *next_hash; + + /* Free list */ + struct cache_node *next_free; +}; + +struct cache { + struct sys_rw_mutex rw_mutex; + struct arena arena; + struct cache_node **buckets; + struct cache_node *first_free; +}; + +struct sheet_scope_reference { + struct cache_node *cache_node; + struct sheet_scope_reference *next_hash; + struct sheet_scope_reference *next_free; +}; + /* ========================== * * Global state * ========================== */ GLOBAL struct { - struct sheet_task_params_store params; + struct load_cmd_store load_cmd_store; + struct cache cache; } G = { 0 }, DEBUG_ALIAS(G, G_sheet); +GLOBAL READONLY struct sheet g_sheet_nil = { 0 }; + +/* ========================== * + * Thread local state + * ========================== */ + +struct sheet_tctx { + struct arena arena; + struct sheet_scope *first_free_scope; + struct sheet_scope_reference *first_free_reference; +}; + +INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(sheet_tctx_alloc, vtctx) +{ + struct sheet_tctx *tctx = (struct sheet_tctx *)vtctx; + tctx->arena = arena_alloc(MEGABYTE(64)); +} + +INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(sheet_tctx_release, vtctx) +{ + struct sheet_tctx *tctx = (struct sheet_tctx *)vtctx; + arena_release(&tctx->arena); +} + +GLOBAL THREAD_LOCAL_VAR_DEF(tl_sheet_tctx, struct sheet_tctx, sheet_tctx_alloc, sheet_tctx_release); + /* ========================== * * Startup * ========================== */ @@ -45,559 +125,351 @@ struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, (UNUSED)asset_cache_sr; (UNUSED)resource_sr; - G.params.arena = arena_alloc(GIGABYTE(64)); - G.params.mutex = sys_mutex_alloc(); + G.load_cmd_store.arena = arena_alloc(GIGABYTE(64)); + G.load_cmd_store.mutex = sys_mutex_alloc(); + + G.cache.rw_mutex = sys_rw_mutex_alloc(); + G.cache.arena = arena_alloc(GIGABYTE(64)); + G.cache.buckets = arena_push_array_zero(&G.cache.arena, struct cache_node *, CACHE_BUCKETS_COUNT); return (struct sheet_startup_receipt) { 0 }; } /* ========================== * - * Load task param store + * Tag * ========================== */ -INTERNAL struct sheet_task_params *sheet_task_params_alloc(void) +INTERNAL b32 sheet_tag_eq(struct sheet_tag t1, struct sheet_tag t2) { - struct sheet_task_params *p = NULL; - sys_mutex_lock(&G.params.mutex); - { - if (G.params.head_free) { - p = G.params.head_free; - G.params.head_free = p->next_free; - } else { - p = arena_push_zero(&G.params.arena, struct sheet_task_params); - } + b32 eq = false; + if (t1.hash == t2.hash) { + eq = string_eq(t1.path, t2.path); } - sys_mutex_unlock(&G.params.mutex); - return p; + return eq; } -INTERNAL void sheet_task_params_release(struct sheet_task_params *p) +#if 0 +INTERNAL void sheet_tag_copy_to_node(struct sheet_tag tag, struct cache_node *n) { - sys_mutex_lock(&G.params.mutex); - { - p->next_free = G.params.head_free; - G.params.head_free = p; - } - sys_mutex_unlock(&G.params.mutex); + 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); } - -/* ========================== * - * Init - * ========================== */ - -#define SHEET_LOOKUP_TABLE_CAPACITY_FACTOR 2.0 - -INTERNAL struct sheet sheet_from_ase(struct arena *arena, struct ase_decode_sheet_result ase) -{ - struct sheet sheet = { 0 }; - - ASSERT(ase.num_frames >= 1); - - /* Init frames */ - sheet.image_size = ase.image_size; - sheet.frame_size = ase.frame_size; - sheet.frames = arena_push_array_zero(arena, struct sheet_frame, ase.num_frames); - sheet.frames_count = ase.num_frames; - for (struct ase_frame *ase_frame = ase.frame_head; ase_frame; ase_frame = ase_frame->next) { - u32 index = ase_frame->index; - sheet.frames[index] = (struct sheet_frame) { - .index = index, - .duration = ase_frame->duration, - .clip = ase_frame->clip - }; - } - - /* Init spans */ - sheet.spans_count = ase.num_spans; - if (ase.num_spans > 0) { - sheet.spans_dict = fixed_dict_init(arena, (u64)(ase.num_spans * SHEET_LOOKUP_TABLE_CAPACITY_FACTOR)); - for (struct ase_span *ase_span = ase.span_head; ase_span; ase_span = ase_span->next) { - struct string name = string_copy(arena, ase_span->name); - struct sheet_span *span = arena_push(arena, struct sheet_span); - *span = (struct sheet_span) { - .name = name, - .start = ase_span->start, - .end = ase_span->end - }; - fixed_dict_set(arena, &sheet.spans_dict, name, span); - } - - } - - return sheet; -} - -INTERNAL struct sheet sheet_default(struct arena *arena) -{ - struct sheet sheet = { 0 }; - sheet.frames_count = 1; - sheet.frames = arena_push(arena, struct sheet_frame); - sheet.frames[0] = (struct sheet_frame) { - .index = 0, - .duration = 0, - .clip = CLIP_ALL - }; - return sheet; -} - -/* ========================== * - * Load - * ========================== */ - -INTERNAL WORK_TASK_FUNC_DEF(sheet_load_asset_task, vparams) -{ - __prof; - struct sheet_task_params *params = (struct sheet_task_params *)vparams; - struct temp_arena scratch = scratch_begin_no_conflict(); - struct string path = string_from_cstr_len(params->path_cstr, params->path_len); - struct asset *asset = params->asset; - - logf_info("Loading sheet \"%F\"", FMT_STR(path)); - sys_timestamp_t start_ts = sys_timestamp(); - - b32 success = false; - struct string error_msg = STR("Unknown error"); - - ASSERT(string_ends_with(path, STR(".ase"))); - if (resource_exists(path)) { - /* Decode */ - struct resource sheet_rs = resource_open(path); - struct ase_decode_sheet_result decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); - resource_close(sheet_rs); - - /* Failure paths */ - if (decoded.errors.count > 0) { - /* FIXME: Read all errors from decode */ - struct string msg = decoded.errors.first->msg; - if (msg.len > 0) { - error_msg = msg; - } - goto abort; - } else { - success = true; - } - - /* Initialize sheet & its data into store */ - struct sheet *sheet = NULL; - { - struct asset_cache_store store = asset_cache_store_open(); - sheet = arena_push(store.arena, struct sheet); - *sheet = sheet_from_ase(store.arena, decoded); - asset_cache_store_close(&store); - } - - logf_info("Finished loading sheet \"%F\" in %F seconds", - FMT_STR(path), - FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts))); - - asset_cache_mark_ready(asset, sheet); - } else { - success = false; - error_msg = STR("Resource not found"); - goto abort; - } - - abort: - - if (!success) { - logf_error("Error loading sheet \"%F\": %F", FMT_STR(path), FMT_STR(error_msg)); - - /* Store */ - struct sheet *sheet = NULL; - { - struct asset_cache_store store = asset_cache_store_open(); - sheet = arena_push(store.arena, struct sheet); - *sheet = sheet_default(store.arena); - asset_cache_store_close(&store); - } - - asset_cache_mark_ready(asset, sheet); - } - - sheet_task_params_release(params); - - /* Decommit decoded sheet data */ - scratch_end_and_decommit(scratch); -} - -struct asset *sheet_load_asset(struct string path, b32 help) -{ - __prof; - struct temp_arena scratch = scratch_begin_no_conflict(); - - struct string key = string_cat(scratch.arena, path, STR("_sheet")); - u64 hash = asset_cache_hash(key); - b32 is_first_touch; - struct asset *asset = asset_cache_touch(key, hash, &is_first_touch); - - if (is_first_touch) { - /* Assemble task params */ - struct sheet_task_params *params = sheet_task_params_alloc(); - if (path.len > (sizeof(params->path_cstr) - 1)) { - sys_panic(string_format(scratch.arena, - STR("Sheet path \"%F\" too long!"), - FMT_STR(path))); - } - cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path); - params->path_len = path.len; - params->asset = asset; - - /* Push task */ - asset_cache_mark_loading(asset); - struct work_handle wh = { 0 }; - if (help) { - wh = work_push_task_and_help(&sheet_load_asset_task, params, WORK_PRIORITY_NORMAL); - } else { - wh = work_push_task(&sheet_load_asset_task, params, WORK_PRIORITY_NORMAL); - } - asset_cache_set_work(asset, &wh); - } - - scratch_end(scratch); - return asset; -} - -struct sheet *sheet_load_async(struct string path) -{ - __prof; - struct asset *asset = sheet_load_asset(path, false); - struct sheet *sheet = (struct sheet *)asset_cache_get_store_data(asset); - return sheet; -} - - -struct sheet *sheet_load(struct string path) -{ - __prof; - struct asset *asset = sheet_load_asset(path, true); - asset_cache_wait(asset); - struct sheet *sheet = (struct sheet *)asset_cache_get_store_data(asset); - return sheet; -} - -/* ========================== * - * Sheet data - * ========================== */ - -struct sheet_span sheet_get_span(struct sheet *sheet, struct string name) -{ - struct sheet_span res = { 0 }; - if (sheet->spans_count > 0) { - struct sheet_span *entry = fixed_dict_get(&sheet->spans_dict, name); - if (entry) { - res = *entry; - } - } - return res; -} - -struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index) -{ - index = min_u32(sheet->frames_count - 1, index); - return sheet->frames[index]; -} - -#else - -#include "sheet.h" -#include "arena.h" -#include "log.h" -#include "sys.h" -#include "scratch.h" -#include "resource.h" -#include "asset_cache.h" -#include "ase.h" -#include "util.h" -#include "work.h" - -#define SHEET_LOAD_THREAD_COUNT_MAX 4 - -struct sheet_task_params { - struct sheet_task_params *next_free; - - struct asset *asset; - u64 path_len; - char path_cstr[1024]; -}; - -struct sheet_task_params_store { - struct sheet_task_params *head_free; - struct arena arena; - struct sys_mutex mutex; -}; - -/* ========================== * - * Global state - * ========================== */ - -GLOBAL struct { - struct sheet_task_params_store params; - struct sys_thread load_threads[SHEET_LOAD_THREAD_COUNT_MAX]; - -} G = { 0 }, DEBUG_ALIAS(G, G_sheet); - -/* ========================== * - * Startup - * ========================== */ - -struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, - struct asset_cache_startup_receipt *asset_cache_sr, - struct resource_startup_receipt *resource_sr) -{ - (UNUSED)work_sr; - (UNUSED)asset_cache_sr; - (UNUSED)resource_sr; - - G.params.arena = arena_alloc(GIGABYTE(64)); - G.params.mutex = sys_mutex_alloc(); - - return (struct sheet_startup_receipt) { 0 }; -} - -/* ========================== * - * Load task param store - * ========================== */ - -INTERNAL struct sheet_task_params *sheet_task_params_alloc(void) -{ - struct sheet_task_params *p = NULL; - sys_mutex_lock(&G.params.mutex); - { - if (G.params.head_free) { - p = G.params.head_free; - G.params.head_free = p->next_free; - } else { - p = arena_push_zero(&G.params.arena, struct sheet_task_params); - } - } - sys_mutex_unlock(&G.params.mutex); - return p; -} - -INTERNAL void sheet_task_params_release(struct sheet_task_params *p) -{ - sys_mutex_lock(&G.params.mutex); - { - p->next_free = G.params.head_free; - G.params.head_free = p; - } - sys_mutex_unlock(&G.params.mutex); -} - -/* ========================== * - * Init - * ========================== */ - -#define SHEET_LOOKUP_TABLE_CAPACITY_FACTOR 2.0 - -INTERNAL struct sheet sheet_from_ase(struct arena *arena, struct ase_decode_sheet_result ase) -{ - struct sheet sheet = { 0 }; - - ASSERT(ase.num_frames >= 1); - - /* Init frames */ - sheet.image_size = ase.image_size; - sheet.frame_size = ase.frame_size; - sheet.frames = arena_push_array_zero(arena, struct sheet_frame, ase.num_frames); - sheet.frames_count = ase.num_frames; - for (struct ase_frame *ase_frame = ase.frame_head; ase_frame; ase_frame = ase_frame->next) { - u32 index = ase_frame->index; - sheet.frames[index] = (struct sheet_frame) { - .index = index, - .duration = ase_frame->duration, - .clip = ase_frame->clip - }; - } - - /* Init spans */ - sheet.spans_count = ase.num_spans; - if (ase.num_spans > 0) { - sheet.spans_dict = fixed_dict_init(arena, (u64)(ase.num_spans * SHEET_LOOKUP_TABLE_CAPACITY_FACTOR)); - for (struct ase_span *ase_span = ase.span_head; ase_span; ase_span = ase_span->next) { - struct string name = string_copy(arena, ase_span->name); - struct sheet_span *span = arena_push(arena, struct sheet_span); - *span = (struct sheet_span) { - .name = name, - .start = ase_span->start, - .end = ase_span->end - }; - fixed_dict_set(arena, &sheet.spans_dict, name, span); - } - - } - - return sheet; -} - -INTERNAL struct sheet sheet_default(struct arena *arena) -{ - struct sheet sheet = { 0 }; - sheet.frames_count = 1; - sheet.frames = arena_push(arena, struct sheet_frame); - sheet.frames[0] = (struct sheet_frame) { - .index = 0, - .duration = 0, - .clip = CLIP_ALL - }; - return sheet; -} - -/* ========================== * - * Load - * ========================== */ - -INTERNAL WORK_TASK_FUNC_DEF(sheet_load_asset_task, vparams) -{ - __prof; - struct sheet_task_params *params = (struct sheet_task_params *)vparams; - struct temp_arena scratch = scratch_begin_no_conflict(); - struct string path = string_from_cstr_len(params->path_cstr, params->path_len); - struct asset *asset = params->asset; - - logf_info("Loading sheet \"%F\"", FMT_STR(path)); - sys_timestamp_t start_ts = sys_timestamp(); - - b32 success = false; - struct string error_msg = STR("Unknown error"); - - ASSERT(string_ends_with(path, STR(".ase"))); - if (resource_exists(path)) { - /* Decode */ - struct resource sheet_rs = resource_open(path); - struct ase_decode_sheet_result decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); - resource_close(sheet_rs); - - /* Failure paths */ - if (decoded.errors.count > 0) { - /* FIXME: Read all errors from decode */ - struct string msg = decoded.errors.first->msg; - if (msg.len > 0) { - error_msg = msg; - } - goto abort; - } else { - success = true; - } - - /* Initialize sheet & its data into store */ - struct sheet *sheet = NULL; - { - struct asset_cache_store store = asset_cache_store_open(); - sheet = arena_push(store.arena, struct sheet); - *sheet = sheet_from_ase(store.arena, decoded); - asset_cache_store_close(&store); - } - - logf_info("Finished loading sheet \"%F\" in %F seconds", - FMT_STR(path), - FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts))); - - asset_cache_mark_ready(asset, sheet); - } else { - success = false; - error_msg = STR("Resource not found"); - goto abort; - } - - abort: - - if (!success) { - logf_error("Error loading sheet \"%F\": %F", FMT_STR(path), FMT_STR(error_msg)); - - /* Store */ - struct sheet *sheet = NULL; - { - struct asset_cache_store store = asset_cache_store_open(); - sheet = arena_push(store.arena, struct sheet); - *sheet = sheet_default(store.arena); - asset_cache_store_close(&store); - } - - asset_cache_mark_ready(asset, sheet); - } - - sheet_task_params_release(params); - - /* Decommit decoded sheet data */ - scratch_end_and_decommit(scratch); -} - -struct asset *sheet_load_asset(struct string path, b32 help) -{ - __prof; - struct temp_arena scratch = scratch_begin_no_conflict(); - - struct string key = string_cat(scratch.arena, path, STR("_sheet")); - u64 hash = asset_cache_hash(key); - b32 is_first_touch; - struct asset *asset = asset_cache_touch(key, hash, &is_first_touch); - - if (is_first_touch) { - /* Assemble task params */ - struct sheet_task_params *params = sheet_task_params_alloc(); - if (path.len > (sizeof(params->path_cstr) - 1)) { - sys_panic(string_format(scratch.arena, - STR("Sheet path \"%F\" too long!"), - FMT_STR(path))); - } - cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path); - params->path_len = path.len; - params->asset = asset; - - /* Push task */ - asset_cache_mark_loading(asset); - struct work_handle wh = { 0 }; - if (help) { - wh = work_push_task_and_help(&sheet_load_asset_task, params, WORK_PRIORITY_NORMAL); - } else { - wh = work_push_task(&sheet_load_asset_task, params, WORK_PRIORITY_NORMAL); - } - asset_cache_set_work(asset, &wh); - } - - scratch_end(scratch); - return asset; -} - -struct sheet *sheet_load_async(struct string path) -{ - __prof; - struct asset *asset = sheet_load_asset(path, false); - struct sheet *sheet = (struct sheet *)asset_cache_get_store_data(asset); - return sheet; -} - - -struct sheet *sheet_load(struct string path) -{ - __prof; - struct asset *asset = sheet_load_asset(path, true); - asset_cache_wait(asset); - struct sheet *sheet = (struct sheet *)asset_cache_get_store_data(asset); - return sheet; -} - -/* ========================== * - * Sheet data - * ========================== */ - -struct sheet_span sheet_get_span(struct sheet *sheet, struct string name) -{ - struct sheet_span res = { 0 }; - if (sheet->spans_count > 0) { - struct sheet_span *entry = fixed_dict_get(&sheet->spans_dict, name); - if (entry) { - res = *entry; - } - } - return res; -} - -struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index) -{ - index = min_u32(sheet->frames_count - 1, index); - return sheet->frames[index]; -} - #endif + +struct sheet_tag sheet_tag_from_path(struct string path) +{ + struct sheet_tag res = { 0 }; + res.hash = HASH_FNV64_SEED; + res.hash = hash_fnv64(&res.hash, BUFFER_FROM_STRING(path)); + res.path = path; + return res; +} + +INTERNAL struct sheet_tag sheet_tag_copy(struct arena *arena, struct sheet_tag src) +{ + struct sheet_tag res = src; + res.path = string_copy(arena, src.path); + return res; +} + +/* ========================== * + * Load cmd + * ========================== */ + +#if 0 +INTERNAL void push_load_cmd(struct cache_node *n, struct sheet_tag tag) +{ + sys_mutex_lock(&G.load_cmd_store.mutex); + { + struct load_cmd *cmd = arena_push(&G.load_cmd_store.arena, struct load_cmd); + cmd->node = n; + cmd->tag = tag; + { + u64 copy_len = min_u64(tag.path.len, ARRAY_COUNT(cmd->tag_path_text)); + cmd->tag.path.text = cmd->path_text_dest; + MEMCPY(cmd->tag.path.text, tag.path.text, copy_len); + } + } + sys_mutex_unlock(&G.load_cmd_store.mutex); +} + +INTERNAL struct load_cmd_array pop_load_cmds(struct arena *arena) +{ + struct load_cmd_array array = { 0 }; + sys_mutex_lock(&G.load_cmd_store.mutex); + { + struct buffer cmds_buff = arena_to_buffer(&G.load_cmd_store.arena); + arena_align(arena, alignof(struct load_cmd)); + array.cmds = (struct load_cmd *)arena_push_array(arena, u8, cmds_buff.size); + array.count = cmds_buff.size / sizeof(struct load_cmd); + MEMCPY(array.cmds, cmds_buff.data, cmds_buff.size); + arena_reset(&G.load_cmd_store.arena); + } + sys_mutex_unlock(&G.load_cmd_store.mutex); + return array; +} +#endif + +/* ========================== * + * Load + * ========================== */ + +INTERNAL struct sheet init_sheet_from_ase_result(struct arena *arena, struct ase_decode_sheet_result ase) +{ + struct sheet sheet = { 0 }; + + ASSERT(ase.num_frames >= 1); + + /* Init frames */ + sheet.image_size = ase.image_size; + sheet.frame_size = ase.frame_size; + sheet.frames = arena_push_array_zero(arena, struct sheet_frame, ase.num_frames); + sheet.frames_count = ase.num_frames; + for (struct ase_frame *ase_frame = ase.frame_head; ase_frame; ase_frame = ase_frame->next) { + u32 index = ase_frame->index; + sheet.frames[index] = (struct sheet_frame) { + .index = index, + .duration = ase_frame->duration, + .clip = ase_frame->clip + }; + } + + /* Init spans */ + sheet.spans_count = ase.num_spans; + if (ase.num_spans > 0) { + sheet.spans_dict = fixed_dict_init(arena, (u64)(ase.num_spans * SHEET_LOOKUP_TABLE_BUCKET_RATIO)); + for (struct ase_span *ase_span = ase.span_head; ase_span; ase_span = ase_span->next) { + struct string name = string_copy(arena, ase_span->name); + struct sheet_span *span = arena_push(arena, struct sheet_span); + *span = (struct sheet_span) { + .name = name, + .start = ase_span->start, + .end = ase_span->end + }; + fixed_dict_set(arena, &sheet.spans_dict, name, span); + } + + } + + return sheet; +} + +INTERNAL void node_load(struct cache_node *n, struct sheet_tag tag) +{ + __prof; + struct temp_arena scratch = scratch_begin_no_conflict(); + + atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_WORKING); + struct string path = tag.path; + + logf_info("Loading sheet \"%F\"", FMT_STR(path)); + sys_timestamp_t start_ts = sys_timestamp(); + + ASSERT(string_ends_with(path, STR(".ase"))); + + /* Decode */ + struct ase_decode_sheet_result decoded = { 0 }; + if (resource_exists(path)) { + struct resource sheet_rs = resource_open(path); + decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); + resource_close(sheet_rs); + } else { + /* FIXME: Working default */ + logf_error("Resource \"%F\" not found", path); + } + + /* Allocate & initialize */ + n->arena = arena_alloc(SHEET_ARENA_RESERVE); + n->sheet = init_sheet_from_ase_result(&n->arena, decoded); + n->tag = sheet_tag_copy(&n->arena, tag); + + logf_info("Finished loading sheet \"%F\" in %F seconds (final size: %F bytes).", + FMT_STR(path), + FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)), + FMT_UINT(n->arena.pos)); + + atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_LOADED); + + scratch_end(scratch); +} + +/* ========================== * + * Scope + * ========================== */ + +INTERNAL void scope_ensure_reference(struct sheet_scope *scope, struct cache_node *cache_node, u64 cache_bucket_index) +{ + struct sheet_scope_reference **ref_next = &scope->reference_buckets[cache_bucket_index]; + struct sheet_scope_reference *ref = *ref_next; + while (ref) { + if (ref->cache_node == cache_node) { + /* Scope already references node */ + break; + } else { + ref_next = &ref->next_hash; + ref = *ref_next; + } + } + + if (!ref) { + struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); + atomic_i32_inc_eval(&cache_node->refcount); + if (tctx->first_free_reference) { + ref = tctx->first_free_reference; + tctx->first_free_reference = ref->next_free; + MEMZERO_STRUCT(ref); + } else { + ref = arena_push_zero(&tctx->arena, struct sheet_scope_reference); + } + ref->cache_node = cache_node; + *ref_next = ref; + } +} + +struct sheet_scope *sheet_scope_begin(void) +{ + struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); + + struct sheet_scope *res = NULL; + if (tctx->first_free_scope) { + res = tctx->first_free_scope; + tctx->first_free_scope = res->next_free; + MEMZERO(res->reference_buckets, sizeof(*res->reference_buckets) * SCOPE_BUCKETS_COUNT); + *res = (struct sheet_scope) { + .reference_buckets = res->reference_buckets + }; + } else { + res = arena_push_zero(&tctx->arena, struct sheet_scope); + res->reference_buckets = arena_push_array_zero(&tctx->arena, struct sheet_scope_reference *, SCOPE_BUCKETS_COUNT); + } + + return res; +} + +void sheet_scope_end(struct sheet_scope *scope) +{ + struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); + for (u64 i = 0; i < SCOPE_BUCKETS_COUNT; ++i) { + struct sheet_scope_reference *ref = scope->reference_buckets[i]; + while (ref) { + atomic_i32_dec_eval(&ref->cache_node->refcount); + ref->next_free = tctx->first_free_reference; + tctx->first_free_reference = ref; + ref = ref->next_hash; + } + } + scope->next_free = tctx->first_free_scope; + tctx->first_free_scope = scope; +} + +/* ========================== * + * Cache interface + * ========================== */ + +/* FIXME: locking */ + +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; + + u64 cache_bucket_index = tag.hash % CACHE_BUCKETS_COUNT; + + /* Lookup */ + sys_rw_mutex_lock_shared(&G.cache.rw_mutex); + { + n_next = &G.cache.buckets[cache_bucket_index]; + n = *n_next; + while (n) { + if (sheet_tag_eq(n->tag, tag)) { + scope_ensure_reference(scope, n, cache_bucket_index); + break; + } else { + n_next = &n->next_hash; + n = *n_next; + } + } + } + sys_rw_mutex_unlock_shared(&G.cache.rw_mutex); + + /* Allocate new node if necessary */ + if (!n) { + /* Alloc */ + sys_rw_mutex_lock_exclusive(&G.cache.rw_mutex); + { + if (G.cache.first_free) { + n = G.cache.first_free; + G.cache.first_free->next_free = n->next_free; + MEMZERO_STRUCT(n); + } else { + n = arena_push_zero(&G.cache.arena, struct cache_node); + } + scope_ensure_reference(scope, n, cache_bucket_index); + *n_next = n; + } + sys_rw_mutex_unlock_exclusive(&G.cache.rw_mutex); + } + + return n; +} + +INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct sheet_tag tag, b32 await) +{ + struct sheet *res = &g_sheet_nil; + + struct cache_node *n = node_lookup_touch(scope, tag); + + u32 state = atomic_u32_eval(&n->state); + if (state == CACHE_NODE_STATE_LOADED) { + res = &n->sheet; + } 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 (await) { + node_load(n, tag); + res = &n->sheet; + } else { + /* TODO */ + /* Node is new, push load command. */ + //push_load_cmd(n, tag); + } + + } + } + + return res; +} + +struct sheet *sheet_from_tag_await(struct sheet_scope *scope, struct sheet_tag tag) +{ + return sheet_from_tag_internal(scope, tag, true); +} + +struct sheet *sheet_from_tag_async(struct sheet_scope *scope, struct sheet_tag tag) +{ + return sheet_from_tag_internal(scope, tag, false); +} + +/* ========================== * + * Sheet data + * ========================== */ + +struct sheet_span sheet_get_span(struct sheet *sheet, struct string name) +{ + struct sheet_span res = { 0 }; + if (sheet->spans_count > 0) { + struct sheet_span *entry = fixed_dict_get(&sheet->spans_dict, name); + if (entry) { + res = *entry; + } + } + return res; +} + +struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index) +{ + index = min_u32(sheet->frames_count - 1, index); + return sheet->frames[index]; +} diff --git a/src/sheet.h b/src/sheet.h index e0ce9464..89d496b3 100644 --- a/src/sheet.h +++ b/src/sheet.h @@ -9,6 +9,11 @@ struct work_startup_receipt; struct asset_cache_startup_receipt; struct resource_startup_receipt; +struct sheet_tag { + u64 hash; + struct string path; +}; + struct sheet { struct v2 image_size; struct v2 frame_size; @@ -18,10 +23,44 @@ struct sheet { struct fixed_dict spans_dict; }; -struct sheet_tag { - struct string path; +/* ========================== * + * Startup + * ========================== */ + +struct sheet_startup_receipt { i32 _; }; +struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, + struct asset_cache_startup_receipt *asset_cache_srm, + struct resource_startup_receipt *resource_sr); + +/* ========================== * + * Tag + * ========================== */ + +struct sheet_tag sheet_tag_from_path(struct string path); + +/* ========================== * + * Scope + * ========================== */ + +struct sheet_scope { + struct sheet_scope_reference **reference_buckets; + struct sheet_scope *next_free; }; +struct sheet_scope *sheet_scope_begin(void); +void sheet_scope_end(struct sheet_scope *scope); + +/* ========================== * + * Load + * ========================== */ + +struct sheet *sheet_from_tag_await(struct sheet_scope *scope, struct sheet_tag tag); +struct sheet *sheet_from_tag_async(struct sheet_scope *scope, struct sheet_tag tag); + +/* ========================== * + * Inspect + * ========================== */ + struct sheet_span { struct string name; u32 start; @@ -34,15 +73,6 @@ struct sheet_frame { struct clip_rect clip; }; -struct sheet_startup_receipt { i32 _; }; -struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr, - struct asset_cache_startup_receipt *asset_cache_srm, - struct resource_startup_receipt *resource_sr); - -struct asset *sheet_load_asset(struct string path, b32 wait); -struct sheet *sheet_load_async(struct string path); -struct sheet *sheet_load(struct string path); - struct sheet_span sheet_get_span(struct sheet *sheet, struct string name); struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index); diff --git a/src/sound.c b/src/sound.c index ea45ce16..7a88af61 100644 --- a/src/sound.c +++ b/src/sound.c @@ -166,8 +166,7 @@ INTERNAL WORK_TASK_FUNC_DEF(sound_load_asset_task, vparams) sound_task_params_release(params); - /* Decommit decoded sound data */ - scratch_end_and_decommit(scratch); + scratch_end(scratch); } struct asset *sound_load_asset(struct string path, u32 flags, b32 help) diff --git a/src/string.c b/src/string.c index c5d55fec..fe01266d 100644 --- a/src/string.c +++ b/src/string.c @@ -94,23 +94,17 @@ struct string string_from_ptr(struct arena *arena, void *ptr) }; } -/* NOTE: This is an imprecise and inefficient way of doing this */ struct string string_from_float(struct arena *arena, f64 f, u32 precision) { u8 *final_text = arena_dry_push(arena, u8); u64 final_len = 0; - /* Currently this function doesn't support large floats. We should - * rewrite this function if this needs to change. */ - f64 max_representable = (f64)((u64)1 << 62); - b32 too_large = (f >= max_representable) || (f <= -max_representable); - b32 nan = f != f; - - if (nan) { + if (f != f) { final_len += string_copy(arena, STR("NaN")).len; - } else if (too_large) { - string_from_char(arena, '?'); - ++final_len; + } else if (f == F64_INFINITY) { + final_len += string_copy(arena, STR("inf")).len; + } else if (f == -F64_INFINITY) { + final_len += string_copy(arena, STR("-inf")).len; } else { if (f < 0) { string_from_char(arena, '-'); @@ -121,19 +115,39 @@ struct string string_from_float(struct arena *arena, f64 f, u32 precision) /* Add one half of next precision level to round up */ f += 0.5 / (f64)math_pow_u64(10, (u8)precision); + f64 part_whole = math_floor64(f); + f64 part_decimal = f - part_whole; + /* Print whole part */ - u64 whole = (u64)f; - struct string whole_str = string_from_uint(arena, whole, 10); - final_len += whole_str.len; + { + struct temp_arena scratch = scratch_begin(arena); + + /* Build backwards text starting from least significant digit */ + u8 *backwards_text = arena_dry_push(scratch.arena, u8); + u64 backwards_text_len = 0; + do { + u64 digit = (u64)math_round_to_int64(math_fmod64(part_whole, 10.0)); + string_from_char(scratch.arena, INT_CHARS[digit % 10]); + ++backwards_text_len; + part_whole = math_floor64(part_whole / 10.0); + } while (part_whole > 0); + + /* Reverse text into final string */ + arena_push_array(arena, u8, backwards_text_len); + for (u64 i = backwards_text_len; i-- > 0;) { + final_text[final_len++] = backwards_text[i]; + } + + scratch_end(scratch); + } /* Print decimal part */ if (precision > 0) { string_from_char(arena, '.'); - f -= (f64)whole; for (u64 i = 0; i < precision; ++i) { - f *= 10.0; - u64 digit = (u64)f; - f -= (f64)digit; + part_decimal *= 10.0; + u64 digit = (u64)math_floor_to_int64(part_decimal); + part_decimal -= digit; string_from_char(arena, INT_CHARS[digit % 10]); } final_len += (u64)precision + 1; @@ -268,15 +282,18 @@ struct string string_indent(struct arena *arena, struct string str, u32 indent) b32 string_eq(struct string str1, struct string str2) { + b32 eq = true; if (str1.len == str2.len) { for (u64 i = 0; i < str1.len; ++i) { if (str1.text[i] != str2.text[i]) { - return false; + eq = false; + break; } } - return true; + } else { + eq = false; } - return false; + return eq; } b32 string_contains(struct string str, struct string substring) diff --git a/src/sys.h b/src/sys.h index a5e11538..6262f6e7 100644 --- a/src/sys.h +++ b/src/sys.h @@ -163,7 +163,8 @@ void sys_memory_release(void *address); /* Commit a region of reserved address space to make it readable / writable */ void *sys_memory_commit(void *address, u64 size); -/* Decommit a region of committed memory (does not release the address space) */ +/* Decommit a region of committed memory (does not release the reserved + * address space) */ void sys_memory_decommit(void *address, u64 size); /* ========================== * diff --git a/src/sys_win32.c b/src/sys_win32.c index 6f072732..11fe4ce8 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -649,10 +649,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(window_thread_entry_point, arg) /* Clip cursor in window window */ if (cursor_flags & WIN32_WINDOW_CURSOR_SET_FLAG_ENABLE_CLIP) { - i32 left = window->x + math_round(window->cursor_clip_bounds.x); - i32 right = left + math_round(window->cursor_clip_bounds.width); - i32 top = window->y + math_round(window->cursor_clip_bounds.y); - i32 bottom = top + math_round(window->cursor_clip_bounds.height); + i32 left = window->x + math_round_to_int(window->cursor_clip_bounds.x); + i32 right = left + math_round_to_int(window->cursor_clip_bounds.width); + i32 top = window->y + math_round_to_int(window->cursor_clip_bounds.y); + i32 bottom = top + math_round_to_int(window->cursor_clip_bounds.height); RECT clip = { .left = clamp_i32(left, window->x, window->x + window->width), .right = clamp_i32(right, window->x, window->x + window->width), @@ -1415,7 +1415,7 @@ void sys_condition_variable_wait_time(struct sys_condition_variable *cv, struct #if RTC atomic_i64_inc_eval(&cv->num_sleepers); #endif - u32 ms = (u32)math_round((f32)seconds * 1000.f); + u32 ms = (u32)math_round_to_int((f32)seconds * 1000.f); SleepConditionVariableSRW((PCONDITION_VARIABLE)cv->handle, (SRWLOCK *)&mutex->handle, ms, 0); #if RTC atomic_i64_dec_eval(&cv->num_sleepers); @@ -1457,7 +1457,7 @@ void sys_semaphore_wait(struct sys_semaphore *semaphore) void sys_semaphore_wait_timed(struct sys_semaphore *semaphore, f64 seconds) { __prof; - u32 ms = max_u32(1, math_round((f32)(seconds * 1000.0))); + u32 ms = max_u32(1, math_round_to_int((f32)(seconds * 1000.0))); WaitForSingleObjectEx((HANDLE)semaphore->handle, ms, FALSE); } @@ -1974,7 +1974,7 @@ void sys_sleep_precise(f64 seconds) void sys_sleep(f64 seconds) { __prof; - u32 ms = max_u32(1, math_round((f32)(seconds * 1000.0))); + u32 ms = max_u32(1, math_round_to_int((f32)(seconds * 1000.0))); Sleep(ms); } @@ -2056,8 +2056,8 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, wc->hInstance = instance; /* Use first icon resource as window icon (same as explorer) */ - wchar_t path[MAX_PATH] = { 0 }; - GetModuleFileNameW(instance, path, MAX_PATH); + wchar_t path[4096] = { 0 }; + GetModuleFileNameW(instance, path, ARRAY_COUNT(path)); ExtractIconExW(path, 0, &wc->hIcon, &wc->hIconSm, 1); if (!RegisterClassExW(wc)) { diff --git a/src/texture.c b/src/texture.c index 6b1703af..1586e21b 100644 --- a/src/texture.c +++ b/src/texture.c @@ -226,8 +226,7 @@ INTERNAL WORK_TASK_FUNC_DEF(texture_load_asset_task, vparams) texture_task_params_release(params); - /* Decommit decoded texture data */ - scratch_end_and_decommit(scratch); + scratch_end(scratch); } struct asset *texture_load_asset(struct string path, b32 help) diff --git a/src/thread_local.c b/src/thread_local.c index 36d5839f..f479b07f 100644 --- a/src/thread_local.c +++ b/src/thread_local.c @@ -58,14 +58,14 @@ void thread_local_store_release(struct thread_local_store *t) arena_release(&t->arena); } -void *_thread_local_eval(struct thread_local_var_meta *meta) +void *_thread_local_var_eval(struct thread_local_var_meta *meta) { /* Register var if unregistered */ u64 id; { u64 id_plus_one = atomic_u64_eval(&meta->id_plus_one); if (id_plus_one == 0) { - __profscope(_thread_local_eval__REGISTER); + __profscope(_thread_local_var_eval__REGISTER); metas_lock(); { id_plus_one = atomic_u64_eval(&meta->id_plus_one); /* Re-check now that locked */ @@ -90,7 +90,7 @@ void *_thread_local_eval(struct thread_local_var_meta *meta) struct thread_local_store *t = sys_thread_get_thread_local_store(); void *data = t->lookup[id]; if (!data) { - __profscope(_thread_local_eval__ALLOC); + __profscope(_thread_local_var_eval__ALLOC); /* Allocate */ arena_align(&t->arena, meta->align); data = arena_push_array(&t->arena, u8, meta->size); diff --git a/src/thread_local.h b/src/thread_local.h index 5219bba9..f923be54 100644 --- a/src/thread_local.h +++ b/src/thread_local.h @@ -54,7 +54,7 @@ struct thread_local_var_meta { } \ } -#define thread_local_eval(var_ptr) (typeof((var_ptr)->_t))(_thread_local_eval(&(var_ptr)->meta)); -void *_thread_local_eval(struct thread_local_var_meta *meta); +#define thread_local_var_eval(var_ptr) (typeof((var_ptr)->_t))(_thread_local_var_eval(&(var_ptr)->meta)); +void *_thread_local_var_eval(struct thread_local_var_meta *meta); #endif diff --git a/src/user.c b/src/user.c index 521ba38e..ad0c1014 100644 --- a/src/user.c +++ b/src/user.c @@ -1,5 +1,3 @@ -#if 1 - #include "user.h" #include "renderer.h" #include "font.h" @@ -398,6 +396,12 @@ INTERNAL void user_update(void) G.time += G.dt; G.screen_size = sys_window_get_size(G.window); + /* ========================== * + * Begin frame cache scopes + * ========================== */ + + struct sheet_scope *sheet_frame_scope = sheet_scope_begin(); + /* ========================== * * Produce interpolated tick * ========================== */ @@ -430,7 +434,7 @@ INTERNAL void user_update(void) world_copy_replace(&G.world, t1); /* Blend time */ - G.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); + G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend); /* Blend entities */ struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); @@ -452,7 +456,7 @@ INTERNAL void user_update(void) e->player_aim = v2_lerp(e0->player_aim, e1->player_aim, tick_blend); e->sprite_quad_xform = xform_lerp(e0->sprite_quad_xform, e1->sprite_quad_xform, tick_blend); - e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); + e->animation_time_in_frame = math_lerp64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, tick_blend); e->camera_rel_xform_target = xform_lerp(e0->camera_rel_xform_target, e1->camera_rel_xform_target, tick_blend); @@ -603,7 +607,7 @@ INTERNAL void user_update(void) if (width / height > aspect_ratio) { width = height * aspect_ratio; } else { - height = (f32)math_ceil(width / aspect_ratio); + height = math_ceil(width / aspect_ratio); } G.viewport_size = V2(width, height); @@ -757,7 +761,6 @@ INTERNAL void user_update(void) struct string tex_name = ent->sprite_name; /* Draw texture */ - struct texture *texture = texture_load_async(tex_name); if (texture) { struct xform xf = xform_mul(ent->world_xform, ent->sprite_quad_xform); @@ -766,8 +769,9 @@ INTERNAL void user_update(void) u32 tint = ent->sprite_tint; struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = tint); - struct sheet *sheet = sheet_load(ent->sprite_name); - if (sheet) { + { + struct sheet_tag sheet_tag = ent->sprite_sheet_tag; + struct sheet *sheet = sheet_from_tag_await(sheet_frame_scope, sheet_tag); struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); u64 frame_index = span.start + ent->animation_frame; @@ -1069,6 +1073,12 @@ INTERNAL void user_update(void) renderer_canvas_present(canvases, canvases_count, G.screen_size, RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size), vsync); + /* ========================== * + * End frame cache scopes + * ========================== */ + + sheet_scope_end(sheet_frame_scope); + scratch_end(scratch); } @@ -1090,1100 +1100,3 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg) user_update(); } } - -#else -#include "user.h" -#include "renderer.h" -#include "font.h" -#include "texture.h" -#include "draw.h" -#include "intrinsics.h" -#include "app.h" -#include "game.h" -#include "asset_cache.h" -#include "string.h" -#include "scratch.h" -#include "math.h" -#include "sys.h" -#include "world.h" -#include "entity.h" -#include "mixer.h" - -struct bind_state { - b32 is_held; /* Is this bind held down this frame */ - u32 num_presses; /* How many times was this bind pressed since last frame */ - u32 num_releases; /* How many times was this bind released since last frame */ -}; - -struct blend_tick { - struct blend_tick *next; - struct blend_tick *prev; - struct world world; -}; - -GLOBAL struct { - b32 shutdown; - struct sys_thread user_thread; - - struct arena arena; - - struct sys_window *window; - struct renderer_canvas *world_canvas; - struct renderer_canvas *viewport_bg_canvas; - struct renderer_canvas *viewport_canvas; - struct xform world_view; - - struct blend_tick *head_free_blend_tick; - struct blend_tick *head_blend_tick; - struct world world; - - struct bind_state bind_states[USER_BIND_KIND_COUNT]; - - b32 debug_camera; - b32 debug_camera_panning; - struct v2 debug_camera_pan_start; - - b32 debug_draw; - - /* User thread input */ - struct sys_mutex sys_events_mutex; - struct arena sys_events_arena; - - /* Per-frame */ - f64 time; - f64 dt; - struct v2 screen_size; - struct v2 screen_cursor; - struct v2 viewport_screen_offset; - struct v2 viewport_size; - struct v2 viewport_center; - struct v2 viewport_cursor; - struct v2 world_cursor; -} G = { 0 }, DEBUG_ALIAS(G, G_user); - -/* ========================== * - * Bind state - * ========================== */ - -/* TODO: Remove this */ - -GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = { - [SYS_BTN_W] = USER_BIND_KIND_MOVE_UP, - [SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN, - [SYS_BTN_A] = USER_BIND_KIND_MOVE_LEFT, - [SYS_BTN_D] = USER_BIND_KIND_MOVE_RIGHT, - - /* Testing */ - - [SYS_BTN_C] = USER_BIND_KIND_DEBUG_CLEAR, - [SYS_BTN_F6] = USER_BIND_KIND_DEBUG_DRAW, - [SYS_BTN_F7] = USER_BIND_KIND_DEBUG_CAMERA, - [SYS_BTN_F11] = USER_BIND_KIND_FULLSCREEN, - [SYS_BTN_MWHEELUP] = USER_BIND_KIND_ZOOM_IN, - [SYS_BTN_MWHEELDOWN] = USER_BIND_KIND_ZOOM_OUT, - [SYS_BTN_M3] = USER_BIND_KIND_PAN, - [SYS_BTN_CTRL] = USER_BIND_KIND_CTRL_TEST -}; - -/* ========================== * - * Window -> user communication - * ========================== */ - -INTERNAL struct sys_event_array pop_sys_events(struct arena *arena) -{ - struct sys_event_array array = { 0 }; - sys_mutex_lock(&G.sys_events_mutex); - { - struct buffer events_buff = arena_to_buffer(&G.sys_events_arena); - arena_align(arena, alignof(struct sys_event)); - array.events = (struct sys_event *)arena_push_array(arena, u8, events_buff.size); - array.count = events_buff.size / sizeof(struct sys_event); - MEMCPY(array.events, events_buff.data, events_buff.size); - arena_reset(&G.sys_events_arena); - } - sys_mutex_unlock(&G.sys_events_mutex); - return array; -} - -INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event) -{ - sys_mutex_lock(&G.sys_events_mutex); - { - *arena_push(&G.sys_events_arena, struct sys_event) = event; - } - sys_mutex_unlock(&G.sys_events_mutex); -} - -/* ========================== * - * Game -> user communication - * ========================== */ - -INTERNAL struct blend_tick *blend_tick_alloc(void) -{ - struct blend_tick *bt = NULL; - if (G.head_free_blend_tick) { - bt = G.head_free_blend_tick; - G.head_free_blend_tick = bt->next; - *bt = (struct blend_tick) { - .world = bt->world - }; - } else { - bt = arena_push_zero(&G.arena, struct blend_tick); - world_alloc(&bt->world); - } - if (G.head_blend_tick) { - bt->next = G.head_blend_tick; - G.head_blend_tick->prev = bt; - } - G.head_blend_tick = bt; - return bt; -} - -INTERNAL void blend_tick_release(struct blend_tick *bt) -{ - struct blend_tick *next = bt->next; - struct blend_tick *prev = bt->prev; - - /* Remove from list */ - if (next) { - next->prev = prev; - } - if (prev) { - prev->next = next; - } - if (bt == G.head_blend_tick) { - G.head_blend_tick = next; - } - - /* Add to free list */ - bt->next = G.head_free_blend_tick; - bt->prev = NULL; - G.head_free_blend_tick = bt; -} - -struct interp_ticks { - struct world *from_tick; - struct world *to_tick; -}; - -INTERNAL struct interp_ticks pull_ticks(f64 blend_time) -{ - __prof; - - /* Find newest stored tick */ - struct world *newest_tick = NULL; - for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) { - if (!newest_tick || bt->world.tick_id > newest_tick->tick_id) { - newest_tick = &bt->world; - } - } - - /* Pull new tick from game thread if necessary */ - if (!newest_tick || game_get_latest_tick_id() > newest_tick->tick_id) { - struct blend_tick *latest_bt = blend_tick_alloc(); - newest_tick = &latest_bt->world; - game_get_latest_tick(newest_tick); - } - - /* Find oldest tick */ - struct world *oldest_tick = NULL; - for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) { - if (!oldest_tick || bt->world.tick_id < oldest_tick->tick_id) { - oldest_tick = &bt->world; - } - } - - /* Find closest ticks to blend time */ - struct world *from_tick = oldest_tick; - struct world *to_tick = newest_tick; - for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) { - f64 bt_time = sys_timestamp_seconds(bt->world.tick_ts); - - if (bt_time < blend_time && bt_time > sys_timestamp_seconds(from_tick->tick_ts)) { - from_tick = &bt->world; - } - - if (bt_time > blend_time && bt_time < sys_timestamp_seconds(to_tick->tick_ts)) { - to_tick = &bt->world; - } - } - - /* Free any unused old ticks */ - { - struct temp_arena scratch = scratch_begin_no_conflict(); - struct blend_tick **bts_to_free = arena_dry_push(scratch.arena, struct blend_tick *); - u64 bts_to_free_count = 0; - - for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) { - f64 bt_time = sys_timestamp_seconds(bt->world.tick_ts); - if (bt_time < sys_timestamp_seconds(from_tick->tick_ts)) { - *arena_push(scratch.arena, struct blend_tick *) = bt; - ++bts_to_free_count; - } - } - - for (u64 i = 0; i < bts_to_free_count; ++i) { - blend_tick_release(bts_to_free[i]); - } - - scratch_end(scratch); - } - - return (struct interp_ticks) { - .from_tick = from_tick, - .to_tick = to_tick - }; -} - -/* ========================== * - * User -> game communication - * ========================== */ - -struct game_cmd_node { - struct game_cmd cmd; - struct game_cmd_node *next; -}; - -struct game_cmd_list { - struct arena *arena; - struct game_cmd_node *first; - struct game_cmd_node *last; -}; - -INTERNAL void queue_game_cmd(struct game_cmd_list *list, struct game_cmd cmd) -{ - struct game_cmd_node *node = arena_push_zero(list->arena, struct game_cmd_node); - node->cmd = cmd; - if (list->first) { - list->last->next = node; - } else { - list->first = node; - } - list->last = node; -} - -INTERNAL void pubilsh_game_cmds(struct game_cmd_list *list) -{ - struct temp_arena scratch = scratch_begin(list->arena); - - /* Construct array */ - struct game_cmd_array array = { .cmds = arena_dry_push(scratch.arena, struct game_cmd) }; - for (struct game_cmd_node *node = list->first; node; node = node->next) { - struct game_cmd *cmd = arena_push(scratch.arena, struct game_cmd); - *cmd = node->cmd; - ++array.count; - } - - /* Push array to game thread */ - if (array.count > 0) { - game_push_cmds(array); - } - - scratch_end(scratch); -} - -/* ========================== * - * Update - * ========================== */ - -/* TODO: remove this (testing) */ -INTERNAL void debug_draw_xform(struct xform xf) -{ - f32 thickness = 2.f; - f32 arrowhead_len = 15.f; - u32 color = RGBA_F(0, 1, 1, 0.3); - u32 color_x = RGBA_F(1, 0, 0, 0.3); - u32 color_y = RGBA_F(0, 1, 0, 0.3); - - struct v2 pos = xform_mul_v2(G.world_view, xf.og); - struct v2 x_ray = xform_basis_mul_v2(G.world_view, xform_get_right(xf)); - struct v2 y_ray = xform_basis_mul_v2(G.world_view, xform_get_up(xf)); - - struct quad quad = quad_from_rect(RECT(0, 0, 1, -1)); - quad = quad_mul_xform(quad_scale(quad, 0.075), xf); - - draw_solid_arrow_ray(G.viewport_canvas, pos, x_ray, thickness, arrowhead_len, color_x); - draw_solid_arrow_ray(G.viewport_canvas, pos, y_ray, thickness, arrowhead_len, color_y); - draw_solid_quad(G.viewport_canvas, quad, color); -} - -/* TODO: remove this (testing) */ -INTERNAL void debug_draw_movement(struct entity *ent) -{ - f32 thickness = 2.f; - f32 arrow_len = 15.f; - - u32 color_vel = RGBA_F(1, 0.5, 0, 1); - u32 color_acc = RGBA_F(1, 1, 0.5, 1); - - struct v2 pos = xform_mul_v2(G.world_view, ent->world_xform.og); - struct v2 vel_ray = xform_basis_mul_v2(G.world_view, ent->velocity); - struct v2 acc_ray = xform_basis_mul_v2(G.world_view, ent->acceleration); - - draw_solid_arrow_ray(G.viewport_canvas, pos, vel_ray, thickness, arrow_len, color_vel); - draw_solid_arrow_ray(G.viewport_canvas, pos, acc_ray, thickness, arrow_len, color_acc); -} - -INTERNAL void user_update(void) -{ - struct temp_arena scratch = scratch_begin_no_conflict(); - struct game_cmd_list cmd_list = { - .arena = scratch.arena - }; - - /* Get time */ - f64 cur_time = sys_timestamp_seconds(sys_timestamp()); - G.dt = max_f64(0.0, cur_time - G.time); - G.time += G.dt; - G.screen_size = sys_window_get_size(G.window); - - /* ========================== * - * Begin dynamic asset scopes - * ========================== */ - - struct sheet_scope sheet_scope = sheet_scope_begin(); - - /* ========================== * - * Produce interpolated tick - * ========================== */ - - b32 tick_is_first_frame = false; - { - __profscope(produce_interpolated_tick); - -#if USER_INTERP_ENABLED - f64 blend_time_offset = (1.0 / GAME_FPS) * USER_INTERP_OFFSET_TICK_RATIO; - f64 blend_time = G.time > blend_time_offset ? G.time - blend_time_offset : 0; - - /* Pull ticks */ - struct interp_ticks interp_ticks = pull_ticks(blend_time); - struct world *t0 = interp_ticks.from_tick; - struct world *t1 = interp_ticks.to_tick; - - tick_is_first_frame = (t0->tick_id == 0 || t1->tick_id == 0); - - f32 tick_blend = 0; - { - f64 t0_time = sys_timestamp_seconds(t0->tick_ts); - f64 t1_time = sys_timestamp_seconds(t1->tick_ts); - if (t1_time > t0_time) { - tick_blend = (f32)((blend_time - t0_time) / (t1_time - t0_time)); - } - tick_blend = clamp_f32(tick_blend, 0.0f, 1.0f); - } - - world_copy_replace(&G.world, t1); - - /* Blend time */ - G.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); - - /* Blend entities */ - struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); - struct entity_array t1_entities = entity_store_as_array(&t1->entity_store); - struct entity_array world_entities = entity_store_as_array(&G.world.entity_store); - - u64 num_entities = min_u64(t0_entities.count, t1_entities.count); - for (u64 i = 0; i < num_entities; ++i) { - struct entity *e0 = &t0_entities.entities[i]; - struct entity *e1 = &t1_entities.entities[i]; - struct entity *e = &world_entities.entities[i]; - if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) { - e->rel_xform = xform_lerp(e0->rel_xform, e1->rel_xform, tick_blend); - e->world_xform = xform_lerp(e0->world_xform, e1->world_xform, tick_blend); - - e->acceleration = v2_lerp(e0->acceleration, e1->acceleration, tick_blend); - e->velocity = v2_lerp(e0->velocity, e1->velocity, tick_blend); - e->player_acceleration = math_lerp(e0->player_acceleration, e1->player_acceleration, tick_blend); - e->player_aim = v2_lerp(e0->player_aim, e1->player_aim, tick_blend); - - e->sprite_quad_xform = xform_lerp(e0->sprite_quad_xform, e1->sprite_quad_xform, tick_blend); - e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); - - e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, tick_blend); - e->camera_rel_xform_target = xform_lerp(e0->camera_rel_xform_target, e1->camera_rel_xform_target, tick_blend); - } - } -#else - struct interp_ticks interp_ticks = pull_ticks(G.time); - world_copy_replace(&G.world, interp_ticks.to_tick); - tick_is_first_frame = G.world.tick_id == 0; -#endif - } - struct entity_array entities_array = entity_store_as_array(&G.world.entity_store); - - /* ========================== * - * Find important entities - * ========================== */ - - struct entity *player = entity_nil(); - struct entity *active_camera = entity_nil(); - - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; - if (!ent->valid) continue; - - /* Player */ - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - player = ent; - } - - /* Active camera */ - if (entity_has_prop(ent, ENTITY_PROP_CAMERA) && entity_has_prop(ent, ENTITY_PROP_CAMERA_ACTIVE)) { - active_camera = ent; - } - } - - /* ========================== * - * Read sys events - * ========================== */ - - struct sys_event_array events = pop_sys_events(scratch.arena); - - /* Reset bind states "was_pressed" */ - for (u32 i = 0; i < ARRAY_COUNT(G.bind_states); ++i) { - G.bind_states[i] = (struct bind_state) { - .is_held = G.bind_states[i].is_held - }; - } - - for (u64 entity_index = 0; entity_index < events.count; ++entity_index) { - struct sys_event *event = &events.events[entity_index]; - - if (event->kind == SYS_EVENT_KIND_QUIT) { - app_exit(); - } - - if (event->kind == SYS_EVENT_KIND_BUTTON_UP) { -#if DEVELOPER - /* Escape quit */ - if (event->button == SYS_BTN_ESC) { - app_exit(); - } -#endif - } - - /* Update mouse pos */ - if (event->kind == SYS_EVENT_KIND_CURSOR_MOVE) { - G.screen_cursor = event->cursor_position; - } - - /* Update bind states */ - if ((event->kind == SYS_EVENT_KIND_BUTTON_DOWN || event->kind == SYS_EVENT_KIND_BUTTON_UP) && !event->is_repeat) { - enum sys_btn button = event->button; - button = button >= SYS_BTN_COUNT ? SYS_BTN_NONE : button; - enum user_bind_kind bind = g_binds[button]; - if (bind) { - b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN; - b32 out_of_bounds = button >= SYS_BTN_M1 && button <= SYS_BTN_M5 && - (G.viewport_cursor.x < 0 || - G.viewport_cursor.y < 0 || - G.viewport_cursor.x > G.viewport_size.x || - G.viewport_cursor.y > G.viewport_size.y); - G.bind_states[bind].is_held = pressed && !out_of_bounds; - if (pressed) { - if (!out_of_bounds) { - ++G.bind_states[bind].num_presses; - } - } else { - ++G.bind_states[bind].num_releases; - } - } - } - } - - /* ========================== * - * Debug commands - * ========================== */ - - /* Test fullscreen */ - { - struct bind_state state = G.bind_states[USER_BIND_KIND_FULLSCREEN]; - if (state.num_presses) { - struct sys_window_settings settings = sys_window_get_settings(G.window); - settings.flags ^= SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN; - sys_window_update_settings(G.window, &settings); - } - } - - /* Test clear world */ - { - struct bind_state state = G.bind_states[USER_BIND_KIND_DEBUG_CLEAR]; - if (state.num_presses || state.is_held) { - queue_game_cmd(&cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_CLEAR_ALL - }); - } - } - - if (G.bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) { - G.debug_draw = !G.debug_draw; - } - - if (G.bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) { - G.debug_camera = !G.debug_camera; - } - - - /* ========================== * - * Update viewport - * ========================== */ - - /* Calculate screen viewport dimensions */ - if (G.debug_camera) { - G.viewport_size = G.screen_size; - G.viewport_screen_offset = V2(0, 0); - } else { - - /* Determine viewport size by camera & window dimensions */ - f32 aspect_ratio = 1.0; - { - struct xform quad_xf = xform_mul(active_camera->world_xform, active_camera->camera_quad_xform); - struct v2 camera_size = xform_get_scale(quad_xf); - if (!v2_eq(camera_size, V2(0, 0))) { - aspect_ratio = camera_size.x / camera_size.y; - } - } - f32 width = G.screen_size.x; - f32 height = G.screen_size.y; - if (width / height > aspect_ratio) { - width = height * aspect_ratio; - } else { - height = (f32)math_ceil(width / aspect_ratio); - } - G.viewport_size = V2(width, height); - - /* Center viewport in window */ - f32 x = 0; - f32 y = 0; - x = math_round(G.screen_size.x / 2 - width / 2); - y = math_round(G.screen_size.y / 2 - height / 2); - G.viewport_screen_offset = V2(x, y); - } - - G.viewport_center = v2_mul(G.viewport_size, 0.5); - G.viewport_cursor = v2_sub(G.screen_cursor, G.viewport_screen_offset); - - /* ========================== * - * Update view - * ========================== */ - - if (G.debug_camera) { - G.world_view = xform_with_rotation(G.world_view, 0); - - /* Pan view */ - if (G.bind_states[USER_BIND_KIND_PAN].is_held) { - if (!G.debug_camera_panning) { - G.debug_camera_pan_start = xform_invert_mul_v2(G.world_view, G.viewport_cursor); - } - G.debug_camera_panning = true; - struct v2 offset = v2_sub(G.debug_camera_pan_start, xform_invert_mul_v2(G.world_view, G.viewport_cursor)); - G.world_view = xform_translate(G.world_view, v2_neg(offset)); - G.debug_camera_pan_start = xform_invert_mul_v2(G.world_view, G.viewport_cursor); - } else { - G.debug_camera_panning = false; - } - - /* Zoom view */ - i32 input_zooms = G.bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - G.bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses; - if (input_zooms != 0) { - /* Zoom to cursor */ - f32 zoom_rate = 2; - f32 zoom = math_pow(zoom_rate, input_zooms); - struct v2 world_cursor = xform_invert_mul_v2(G.world_view, G.viewport_cursor); - G.world_view = xform_translate(G.world_view, world_cursor); - G.world_view = xform_scale(G.world_view, V2(zoom, zoom)); - G.world_view = xform_translate(G.world_view, v2_neg(world_cursor)); - } - } else { - struct v2 center = active_camera->world_xform.og; - f32 rot = xform_get_rotation(active_camera->world_xform); - - /* Scale view into viewport based on camera size */ - struct v2 size = G.viewport_size; - { - struct xform quad_xf = xform_mul(active_camera->world_xform, active_camera->camera_quad_xform); - struct v2 camera_size = xform_get_scale(quad_xf); - if (!v2_eq(camera_size, V2(0, 0))) { - size = v2_div_v2(size, camera_size); - } - } - f32 scale = min_f32(size.x, size.y); - - struct trs trs = TRS( - .t = v2_sub(G.viewport_center, center), - .r = rot, - .s = V2(scale, scale) - ); - - struct v2 pivot = center; - G.world_view = XFORM_IDENT; - G.world_view = xform_translate(G.world_view, pivot); - G.world_view = xform_trs_pivot_rs(G.world_view, trs, pivot); - } - G.world_cursor = xform_invert_mul_v2(G.world_view, G.viewport_cursor); - - /* ========================== * - * Update listener - * ========================== */ - - { - struct v2 up = V2(0, -1); - struct v2 listener_pos = xform_invert_mul_v2(G.world_view, G.viewport_center); - struct v2 listener_dir = v2_norm(xform_basis_invert_mul_v2(G.world_view, up)); - mixer_set_listener(listener_pos, listener_dir); - } - - /* ========================== * - * Draw test BG - * ========================== */ - - { - u32 color = RGBA_F(0.2f, 0.2f, 0.2f, 1.f); - draw_solid_rect(G.viewport_bg_canvas, RECT(0, 0, G.viewport_size.x, G.viewport_size.y), color); - } - - /* ========================== * - * Draw test grid - * ========================== */ - - { - f32 thickness = 3.f; - u32 color = RGBA(0x3f, 0x3f, 0x3f, 0xFF); - u32 x_color = RGBA(0x3f, 0, 0, 0xFF); - u32 y_color = RGBA(0, 0x3f, 0, 0xFF); - - i64 startx = -10; - i64 starty = -10; - i64 rows = 20; - i64 cols = 20; - - /* Draw column lines */ - struct v2 col_ray = xform_basis_mul_v2(G.world_view, V2(0, rows)); - for (i64 col = starty; col <= (starty + cols); ++col) { - u32 line_color = color; - if (col == 0) { - line_color = y_color; - } - - struct v2 pos = xform_mul_v2(G.world_view, V2(col, starty)); - draw_solid_ray(G.viewport_bg_canvas, pos, col_ray, thickness, line_color); - } - - struct v2 row_ray = xform_basis_mul_v2(G.world_view, V2(cols, 0)); - for (i64 row = startx; row <= (startx + rows); ++row) { - u32 line_color = color; - if (row == 0) { - line_color = x_color; - } - struct v2 pos = xform_mul_v2(G.world_view, V2(startx, row)); - draw_solid_ray(G.viewport_bg_canvas, pos, row_ray, thickness, line_color); - } - } - - /* ---------------------------------------------------------------------- */ - /* ---------------------------------------------------------------------- */ - - /* ========================== * - * Iterate entities - * ========================== */ - - /* Iterate entities */ - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - __profscope(user_entity_iter); - - struct entity *ent = &entities_array.entities[entity_index]; - if (!ent->valid) continue; - - b32 skip_debug_draw = !G.debug_camera && ent == active_camera; - b32 skip_debug_draw_transform = ent == active_camera; - - /* Draw sprite */ - if (ent->sprite_name.len > 0) { - struct string tex_name = ent->sprite_name; - - /* Draw texture */ - struct texture *texture = texture_load_async(tex_name); - if (texture) { - struct xform xf = xform_mul(ent->world_xform, ent->sprite_quad_xform); - struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); - - u32 tint = ent->sprite_tint; - struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = tint); - - struct sheet_tag sheet_tag = sheet_tag_from_path(ent->sprite_name); - struct sheet *sheet = sheet_from_tag(sheet_scope, sheet_tag); - - struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); - u64 frame_index = span.start + ent->animation_frame; - struct sheet_frame frame = sheet_get_frame(sheet, frame_index); - if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) { - f64 time_in_frame = ent->animation_time_in_frame; - while (time_in_frame > frame.duration) { - time_in_frame -= frame.duration; - ++frame_index; - if (frame_index > span.end) { - /* Loop animation */ - frame_index = span.start; - } - frame = sheet_get_frame(sheet, frame_index); - } - } - - params.clip = frame.clip; - - draw_texture_quad(G.world_canvas, params, quad); - -#if 0 - if (G.debug_draw && !skip_debug_draw) { - /* Debug draw sprite quad */ - { - f32 thickness = 2.f; - u32 color = RGBA_F(1, 1, 0, 0.25); - draw_solid_quad_line(G.world_canvas, quad, (thickness / PIXELS_PER_UNIT / G.world_view.zoom), color); - } - - /* Debug draw sprite transform */ - { - debug_draw_xform(xf); - } - - /* Debug draw sprite pivot */ - { - u32 color = RGBA_F(1, 0, 0, 1); - draw_solid_circle(G.world_canvas, ent->world_xform.og, 0.02, color, 20); - } - } -#endif - } - } - - /* Debug draw info */ - if (G.debug_draw && !skip_debug_draw) { - struct temp_arena temp = arena_temp_begin(scratch.arena); - -#if 0 - struct font *disp_font = font_load_async(STR("res/fonts/fixedsys.ttf"), 12.0f); - if (disp_font) { - struct xform xf = ent->world_xform; - struct trs trs = trs_from_xform(xf); - struct v2 velocity = ent->velocity; - struct v2 acceleration = ent->acceleration; - - f32 offset = 1; - struct v2 pos = v2_add(xf.og, v2_mul(V2(0, -1), offset)); - pos = xform_mul_v2(G.world_view, pos); - pos = v2_round(pos); - - struct string disp_name = ent->sprite_name; - - struct string fmt = STR( - "sprite name: \"%F\"\n" - "pos: (%F, %F)\n" - "scale: (%F, %F)\n" - "rot: %F\n" - "velocity: (%F, %F)\n" - "acceleration: (%F, %F)\n" - ); - struct string text = string_format(temp.arena, fmt, - FMT_STR(disp_name), - FMT_FLOAT((f64)trs.t.x), FMT_FLOAT((f64)trs.t.y), - FMT_FLOAT((f64)trs.s.x), FMT_FLOAT((f64)trs.s.y), - FMT_FLOAT((f64)trs.r), - FMT_FLOAT((f64)velocity.x), FMT_FLOAT((f64)velocity.y), - FMT_FLOAT((f64)acceleration.x), FMT_FLOAT((f64)acceleration.y) - ); - - - draw_text(G.viewport_canvas, disp_font, pos, text); - } -#endif - - debug_draw_movement(ent); - - if (!skip_debug_draw_transform) { - debug_draw_xform(ent->world_xform); - } - - /* Draw hierarchy */ - struct entity *parent = entity_from_handle(&G.world.entity_store, ent->parent); - if (parent->valid) { - u32 color = RGBA_F(0.6, 0.6, 1, 0.75); - f32 thickness = 5; - f32 arrow_height = 15; - - struct v2 start = xform_mul_v2(G.world_view, ent->world_xform.og); - struct v2 end = xform_mul_v2(G.world_view, parent->world_xform.og); - draw_solid_arrow_line(G.viewport_canvas, start, end, thickness, arrow_height, color); - } - - /* Draw aim */ - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - u32 color = RGBA_F(0.75, 0, 0.75, 0.5); - f32 thickness = 3; - f32 arrow_height = 10; - struct v2 pos = xform_mul_v2(G.world_view, ent->world_xform.og); - struct v2 aim_ray = xform_basis_mul_v2(G.world_view, ent->player_aim); - draw_solid_arrow_ray(G.viewport_canvas, pos, aim_ray, thickness, arrow_height, color); - } - - /* Draw camera rect */ - if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { - u32 color = ent == active_camera ? RGBA_F(1, 1, 1, 0.5) : RGBA_F(0, 0.75, 0, 0.5); - f32 thickness = 3; - - - struct xform quad_xf = xform_mul(ent->world_xform, ent->camera_quad_xform); - struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, quad_xf); - quad = quad_mul_xform(quad, G.world_view); - - draw_solid_quad_line(G.viewport_canvas, quad, thickness, color); - } - - arena_temp_end(temp); - } - } - - /* Draw crosshair or show cursor */ - if (!G.debug_camera) { - struct v2 crosshair_pos = G.viewport_cursor; - u32 tint = RGBA_F(1, 1, 1, 1); - - struct v2 size = V2(0, 0); - struct texture *t = texture_load_async(STR("res/graphics/crosshair.ase")); - if (t) { - size = t->size; - struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size); - struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); - draw_texture_quad(G.viewport_canvas, DRAW_TEXTURE_PARAMS(.texture = t, .tint = tint), quad); - } - - struct rect cursor_clip = RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size); - cursor_clip.pos = v2_add(cursor_clip.pos, v2_mul(size, 0.5f)); - cursor_clip.pos = v2_add(cursor_clip.pos, V2(1, 1)); - cursor_clip.size = v2_sub(cursor_clip.size, size); - sys_window_cursor_hide(G.window); - sys_window_cursor_enable_clip(G.window, cursor_clip); - } else { - sys_window_cursor_disable_clip(G.window); - sys_window_cursor_show(G.window); - } - - /* ========================== * - * Construct movement input - * ========================== */ - - /* Movement */ - struct v2 input_move_dir = { 0 }; - { - for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(G.bind_states); ++bind) { - struct bind_state state = G.bind_states[bind]; - - if (!state.is_held && state.num_presses <= 0) { - continue; - } - - switch (bind) { - /* Movement */ - case USER_BIND_KIND_MOVE_UP: { - input_move_dir.y -= 1; - } break; - - case USER_BIND_KIND_MOVE_DOWN: { - input_move_dir.y += 1; - } break; - - case USER_BIND_KIND_MOVE_LEFT: { - input_move_dir.x -= 1; - } break; - - case USER_BIND_KIND_MOVE_RIGHT: { - input_move_dir.x += 1; - } break; - - default: break; - } - } - - input_move_dir = xform_basis_invert_mul_v2(G.world_view, input_move_dir); /* Make move dir relative to world view */ - input_move_dir = v2_norm(input_move_dir); - } - - /* Aim */ - struct v2 input_aim = player->player_aim; - if (!G.debug_camera) { - input_aim = v2_sub(G.world_cursor, player->world_xform.og); - } - - /* Queue cmd */ - queue_game_cmd(&cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_PLAYER_MOVE, - .move_dir = input_move_dir, - .aim = input_aim - }); - - /* ---------------------------------------------------------------------- */ - /* ---------------------------------------------------------------------- */ - - /* Debug draw info */ - if (G.debug_draw) { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - f32 spacing = 20; - struct v2 pos = V2(10, 8); - struct font *font = font_load(STR("res/fonts/fixedsys.ttf"), 12.0f); - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("time: %F"), FMT_FLOAT((f64)G.time))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_cursor: (%F, %F)"), FMT_FLOAT((f64)G.screen_cursor.x), FMT_FLOAT((f64)G.screen_cursor.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_screen_offset: (%F, %F)"), FMT_FLOAT((f64)G.viewport_screen_offset.x), FMT_FLOAT((f64)G.viewport_screen_offset.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_size: (%F, %F)"), FMT_FLOAT((f64)G.viewport_size.x), FMT_FLOAT((f64)G.viewport_size.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_center: (%F, %F)"), FMT_FLOAT((f64)G.viewport_center.x), FMT_FLOAT((f64)G.viewport_center.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_cursor: (%F, %F)"), FMT_FLOAT((f64)G.viewport_cursor.x), FMT_FLOAT((f64)G.viewport_cursor.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view.og: (%F, %F)"), FMT_FLOAT((f64)G.world_view.og.x), FMT_FLOAT((f64)G.world_view.og.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view rotation: %F"), FMT_FLOAT((f64)xform_get_rotation(G.world_view)))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view scale: (%F, %F)"), FMT_FLOAT((f64)xform_get_scale(G.world_view).x), FMT_FLOAT((f64)xform_get_scale(G.world_view).x))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_cursor: (%F, %F)"), FMT_FLOAT((f64)G.world_cursor.x), FMT_FLOAT((f64)G.world_cursor.y))); - pos.y += spacing; - - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("debug_camera: %F"), FMT_STR(G.debug_camera ? STR("true") : STR("false")))); - pos.y += spacing; - - arena_temp_end(temp); - } - - /* Push game cmds */ - pubilsh_game_cmds(&cmd_list); - - /* ========================== * - * Present - * ========================== */ - - /* Send canvases to GPU */ - renderer_canvas_send_to_gpu(G.viewport_bg_canvas); - renderer_canvas_send_to_gpu(G.world_canvas); - renderer_canvas_send_to_gpu(G.viewport_canvas); - - /* Set canvas views before presenting */ - renderer_canvas_set_view(G.viewport_bg_canvas, XFORM_IDENT); - renderer_canvas_set_view(G.world_canvas, G.world_view); - renderer_canvas_set_view(G.viewport_canvas, XFORM_IDENT); - - /* Present */ - i32 vsync = VSYNC_ENABLED; - - struct renderer_canvas **canvases = arena_dry_push(scratch.arena, struct renderer_canvas *); - u64 canvases_count = 0; - { - /* Viewport background canvas */ - *arena_push(scratch.arena, struct renderer_canvas *) = G.viewport_bg_canvas; - ++canvases_count; - - /* World canvas */ - if (!tick_is_first_frame) { - /* Only render world if not on first frame */ - *arena_push(scratch.arena, struct renderer_canvas *) = G.world_canvas; - ++canvases_count; - } - - /* Viewport canvas */ - *arena_push(scratch.arena, struct renderer_canvas *) = G.viewport_canvas; - ++canvases_count; - } - - renderer_canvas_present(canvases, canvases_count, G.screen_size, RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size), vsync); - - /* ========================== * - * End dynamic asset scopes - * ========================== */ - - sheet_scope_end(sheet_scope); - - scratch_end(scratch); -} - -/* ========================== * - * Startup - * ========================== */ - -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg) -{ - (UNUSED)arg; - - sys_timestamp_t last_frame_ts = 0; - f64 target_dt = USER_FRAME_LIMIT > (0) ? (1.0 / USER_FRAME_LIMIT) : 0; - - while (!G.shutdown) { - __profscope(user_update_w_sleep); - sleep_frame(last_frame_ts, target_dt); - last_frame_ts = sys_timestamp(); - user_update(); - } -} - -struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, - struct renderer_startup_receipt *renderer_sr, - struct font_startup_receipt *font_sr, - struct texture_startup_receipt *texture_sr, - struct draw_startup_receipt *draw_sr, - struct game_startup_receipt *game_sr, - struct asset_cache_startup_receipt *asset_cache_sr, - struct mixer_startup_receipt *mixer_sr, - struct sys_window *window) -{ - (UNUSED)work_sr; - (UNUSED)renderer_sr; - (UNUSED)font_sr; - (UNUSED)texture_sr; - (UNUSED)draw_sr; - (UNUSED)game_sr; - (UNUSED)asset_cache_sr; - (UNUSED)mixer_sr; - - G.arena = arena_alloc(GIGABYTE(64)); - - G.sys_events_mutex = sys_mutex_alloc(); - G.sys_events_arena = arena_alloc(GIGABYTE(64)); - - world_alloc(&G.world); - - G.world_canvas = renderer_canvas_alloc(); - G.world_view = XFORM_TRS(.t = V2(0, 0), .r = 0, .s = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT)); - - G.viewport_bg_canvas = renderer_canvas_alloc(); - G.viewport_canvas = renderer_canvas_alloc(); - - G.window = window; - sys_window_register_event_callback(G.window, &window_event_callback); - - G.user_thread = sys_thread_alloc(&user_thread_entry_point, NULL, STR("[P1] User thread")); - - return (struct user_startup_receipt) { 0 }; -} - -void user_shutdown(void) -{ - G.shutdown = true; - sys_thread_wait_release(&G.user_thread); -} - -#endif diff --git a/src/work.c b/src/work.c index 07ebaa37..446d5100 100644 --- a/src/work.c +++ b/src/work.c @@ -382,7 +382,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(worker_thread_entry_point, thread_data) { (UNUSED)thread_data; - struct worker_ctx *ctx = thread_local_eval(&tl_worker_ctx); + struct worker_ctx *ctx = thread_local_var_eval(&tl_worker_ctx); *ctx = (struct worker_ctx) { .is_worker = true }; @@ -444,7 +444,7 @@ INTERNAL struct work_handle work_push_from_slate_assume_locked(struct work_slate * does not occur. However it is not ideal since it creates situations in * which work is not done asynchronously. */ - struct worker_ctx *ctx = thread_local_eval(&tl_worker_ctx); + struct worker_ctx *ctx = thread_local_var_eval(&tl_worker_ctx); if (ctx->is_worker) { b32 work_done = false; while (!work_done && G.idle_worker_count == 0 && work->workers == 0) {