From 892daa5ed26123aaf583450f6cb6799fc4521335 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 1 Jul 2025 01:03:06 -0500 Subject: [PATCH] call resource callbacks in job --- res/sprite/tim.ase | 4 +- src/app.c | 6 +-- src/arena.c | 2 +- src/ase.c | 2 +- src/host.c | 8 ++-- src/job.c | 49 +++++++++++++++++++++---- src/mixer.c | 10 ++--- src/phys.c | 4 +- src/prof_tracy.h | 11 ++++-- src/resource.c | 42 +++++++++++++++++---- src/sim.c | 2 +- src/sprite.c | 91 ++++++++++++++++++++++++---------------------- src/sys.h | 1 + src/sys_win32.c | 29 +++++++++------ src/thread_local.c | 4 +- src/user.c | 12 +++--- 16 files changed, 177 insertions(+), 100 deletions(-) diff --git a/res/sprite/tim.ase b/res/sprite/tim.ase index fec5c35b..9d61e25c 100644 --- a/res/sprite/tim.ase +++ b/res/sprite/tim.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec441d678c8163a5fed2ff71d5a88589183791064748fbda0974fa05712e06a2 -size 766 +oid sha256:b27ca6fcda7a773805a4b5b0960fa4d5923d6b671c79b5afeec576176f0fa7e5 +size 810 diff --git a/src/app.c b/src/app.c index 9aeb718d..325b49c1 100644 --- a/src/app.c +++ b/src/app.c @@ -236,13 +236,13 @@ void app_entry_point(struct string args_str) i32 worker_count; { /* FIXME: Switch this on to utilize all cores. Only decreasing worker count for testing purposes. */ -#if !PROFILING && !RTC || 1 - i32 min_worker_count = min_i32(NUM_APP_DEDICATED_WORKERS + 2, JOB_MIN_WORKERS); +#if !PROFILING && !RTC i32 max_worker_count = JOB_MAX_WORKERS; + i32 min_worker_count = clamp_i32(NUM_APP_DEDICATED_WORKERS + 2, JOB_MIN_WORKERS, max_worker_count); i32 target_worker_count = (i32)sys_num_logical_processors() * 0.75; worker_count = clamp_i32(target_worker_count, min_worker_count, max_worker_count); #else - worker_count = 8; + worker_count = 5; #endif } diff --git a/src/arena.c b/src/arena.c index 46139804..bb2370a6 100644 --- a/src/arena.c +++ b/src/arena.c @@ -76,7 +76,7 @@ void *arena_push_bytes_no_zero(struct arena *arena, u64 size, u64 align) u64 new_pos = aligned_start_pos + size; if (new_pos > arena->committed) { - __profscope(_arena_push_bytes_COMMIT); + __profscope(Arena commit); /* Commit new block(s) */ u64 blocks_needed = (new_pos - arena->committed + ARENA_BLOCK_SIZE - 1) / ARENA_BLOCK_SIZE; u64 commit_bytes = blocks_needed * ARENA_BLOCK_SIZE; diff --git a/src/ase.c b/src/ase.c index 3e89acd5..8247f008 100644 --- a/src/ase.c +++ b/src/ase.c @@ -732,7 +732,7 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri } { - __profscope(assemble_image); + __profscope(Build image from cels); /* Assemble image from cels */ for (struct cel *cel = cel_head; cel; cel = cel->next) { diff --git a/src/host.c b/src/host.c index 2aac9403..ac24bfef 100644 --- a/src/host.c +++ b/src/host.c @@ -653,7 +653,7 @@ struct host_event_list host_update_begin(struct arena *arena, struct host *host) i64 now_ns = sys_time_ns(); { - __profscope(host_update_read_packets); + __profscope(Read host packets); struct string read_buff = ZI; read_buff.len = PACKET_DATA_MAX_LEN; read_buff.text = arena_push_array_no_zero(scratch.arena, u8, read_buff.len); @@ -829,7 +829,7 @@ struct host_event_list host_update_begin(struct arena *arena, struct host *host) /* Update channels */ { - __profscope(host_update_channels); + __profscope(Update host channels); for (u64 i = 0; i < host->num_channels_reserved; ++i) { struct host_channel *channel = &host->channels[i]; if (channel->valid) { @@ -903,7 +903,7 @@ void host_update_end(struct host *host) /* Process cmds into sendable packets */ /* TODO: Unreliable packets don't need to be allocated into unreliable packet queue, should just send them and forget */ { - __profscope(host_update_process_cmds); + __profscope(Process host cmds); for (struct host_cmd *cmd = host->first_cmd; cmd; cmd = cmd->next) { enum host_cmd_kind kind = cmd->kind; struct host_channel_id channel_id = cmd->channel_id; @@ -1018,7 +1018,7 @@ void host_update_end(struct host *host) /* Send packets */ /* TODO: Aggregate small packets */ { - __profscope(host_update_send_packets); + __profscope(Send host packets); for (u64 i = 0; i < host->num_channels_reserved; ++i) { struct sock *sock = host->sock; struct host_channel *channel = &host->channels[i]; diff --git a/src/job.c b/src/job.c index b1d46509..0b112316 100644 --- a/src/job.c +++ b/src/job.c @@ -236,17 +236,27 @@ INTERNAL struct job_handle job_dispatch_ex(struct job_desc desc) /* Queue job */ if (job_queue) { - __profscope(Queue); + __profscope(Queue job); struct sys_lock lock = sys_mutex_lock_e(G.queued_jobs_mutex); { /* Push to queue */ { + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } + + if (job_queue->last) { job_queue->last->next = job; } else { job_queue->first = job; } + job->prev = job_queue->last; job_queue->last = job; + + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } } ++G.queue_submit_gen; /* Signal workers */ @@ -272,8 +282,12 @@ INTERNAL struct job_handle job_dispatch_ex(struct job_desc desc) while (!stop) { /* Remove job from queue */ if (job_queue && job_id == (job_count - 1)) { + __profscope(Dequeue job); struct sys_lock queue_lock = sys_mutex_lock_e(G.queued_jobs_mutex); { + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } struct worker_job *prev = job->prev; struct worker_job *next = job->next; if (prev) { @@ -286,11 +300,15 @@ INTERNAL struct job_handle job_dispatch_ex(struct job_desc desc) } else { job_queue->last = prev; } + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } } sys_mutex_unlock(&queue_lock); } /* Run */ { + __profscope(Run job); data.id = job_id; job_func(data); } @@ -313,8 +331,12 @@ INTERNAL struct job_handle job_dispatch_ex(struct job_desc desc) } } if (should_release) { + __profscope(Release job); struct sys_lock fj_lock = sys_mutex_lock_e(G.free_jobs_mutex); { + if (job->queue && (job->queue->first == job || job->queue->last == job)) { + DEBUGBREAK; + } job->next_free = G.first_free_job; G.first_free_job = job; } @@ -326,7 +348,7 @@ INTERNAL struct job_handle job_dispatch_ex(struct job_desc desc) /* Wait for job completion */ if (wait && !is_done) { __profscope(Wait for job); - struct sys_lock lock = sys_mutex_lock_s(job->mutex); + struct sys_lock lock = sys_mutex_lock_e(job->mutex); is_done = atomic_u64_eval(&job->gen) != handle.gen; while (!is_done) { sys_condition_variable_wait(job->gen_cv, &lock); @@ -411,6 +433,9 @@ void job_wait(struct job_handle handle) __profscope(Dequeue job); struct sys_lock queue_lock = sys_mutex_lock_e(G.queued_jobs_mutex); { + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } struct worker_job *prev = job->prev; struct worker_job *next = job->next; if (prev) { @@ -423,6 +448,9 @@ void job_wait(struct job_handle handle) } else { job_queue->last = prev; } + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } } sys_mutex_unlock(&queue_lock); } @@ -451,6 +479,9 @@ void job_wait(struct job_handle handle) __profscope(Release job); struct sys_lock fj_lock = sys_mutex_lock_e(G.free_jobs_mutex); { + if (job->queue && (job->queue->first == job || job->queue->last == job)) { + DEBUGBREAK; + } job->next_free = G.first_free_job; G.first_free_job = job; } @@ -462,7 +493,7 @@ void job_wait(struct job_handle handle) /* Wait for job completion */ if (!is_done) { __profscope(Wait for job); - struct sys_lock lock = sys_mutex_lock_s(job->mutex); + struct sys_lock lock = sys_mutex_lock_e(job->mutex); is_done = atomic_u64_eval(&job->gen) != handle.gen; while (!is_done) { sys_condition_variable_wait(job->gen_cv, &lock); @@ -485,7 +516,7 @@ INTERNAL SYS_THREAD_DEF(worker_thread_entry_point, thread_arg) struct worker_ctx *ctx = thread_local_var_eval(&tl_worker_ctx); ctx->worker_id = worker_id; - struct worker_job_queues *queues[] = { &G.pinned_queues[worker_id], &G.global_queue }; + struct worker_job_queue *queues[] = { &G.pinned_queues[worker_id], &G.global_queue }; u64 seen_queue_submit_gen = 0; struct sys_lock queue_lock = sys_mutex_lock_s(G.queued_jobs_mutex); @@ -503,7 +534,7 @@ INTERNAL SYS_THREAD_DEF(worker_thread_entry_point, thread_arg) queue_lock = sys_mutex_lock_s(G.queued_jobs_mutex); { seen_queue_submit_gen = G.queue_submit_gen; - for (i32 queue_index = 0; queue_index < ARRAY_COUNT(queues); ++queue_index) { + for (i32 queue_index = 0; queue_index < (i32)ARRAY_COUNT(queues); ++queue_index) { struct worker_job_queue *queue = queues[queue_index]; struct worker_job *tmp = queue->first; while (!job && tmp) { @@ -511,8 +542,6 @@ INTERNAL SYS_THREAD_DEF(worker_thread_entry_point, thread_arg) { i32 tmp_id = tmp->num_dispatched; i32 tmp_count = tmp->count; - i32 tmp_pinned_worker = tmp->pinned_worker_id; - b32 tmp_is_pinned_to_worker = tmp_pinned_worker == worker_id; if (tmp_id < tmp_count) { /* Pick job */ ++tmp->num_workers; @@ -537,6 +566,9 @@ INTERNAL SYS_THREAD_DEF(worker_thread_entry_point, thread_arg) __profscope(Dequeue job); queue_lock = sys_mutex_lock_e(G.queued_jobs_mutex); { + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } struct worker_job *prev = job->prev; struct worker_job *next = job->next; if (prev) { @@ -549,6 +581,9 @@ INTERNAL SYS_THREAD_DEF(worker_thread_entry_point, thread_arg) } else { job_queue->last = prev; } + if (job->prev == job || job->next == job) { + DEBUGBREAKABLE; + } } sys_mutex_unlock(&queue_lock); } diff --git a/src/mixer.c b/src/mixer.c index 70f08a98..11e39faf 100644 --- a/src/mixer.c +++ b/src/mixer.c @@ -291,7 +291,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) /* Update & read mixes */ mixes = arena_push_array_no_zero(scratch.arena, struct mix *, G.track_playing_count); for (struct track *track = G.track_first_playing; track; track = track->next) { - __profscope(prepare_track); + __profscope(Prepare track); struct mix *mix = &track->mix; mix->desc = track->desc; mixes[mixes_count++] = mix; @@ -301,7 +301,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) } for (u64 mix_index = 0; mix_index < mixes_count; ++mix_index) { - __profscope(mix_track); + __profscope(Mix track); struct mix *mix = mixes[mix_index]; if (mix->source->pcm.count <= 0) { @@ -354,7 +354,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) /* Transform 16 bit source -> 32 bit stereo at output duration */ { - __profscope(resample); + __profscope(Resample); f32 *out_samples = mix_pcm.samples; u64 out_frames_count = mix_pcm.count / 2; @@ -408,7 +408,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) * ========================== */ if (desc.flags & MIXER_FLAG_SPATIALIZE) { - __profscope(spatialize); + __profscope(Spatialize); /* Algorithm constants */ const f32 rolloff_height = 1.2f; @@ -469,7 +469,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count) } { - __profscope(update_track_effect_data); + __profscope(Update track effect data); struct sys_lock lock = sys_mutex_lock_e(G.mutex); for (u64 i = 0; i < mixes_count; ++i) { struct mix *mix = mixes[i]; diff --git a/src/phys.c b/src/phys.c index b043ce6b..5065a5ab 100644 --- a/src/phys.c +++ b/src/phys.c @@ -1254,7 +1254,7 @@ void phys_step(struct phys_step_ctx *ctx, f32 timestep) f32 remaining_dt = timestep; while (remaining_dt > 0) { - __profscope(step_part); + __profscope(Step part); ++phys_iteration; struct arena_temp scratch = scratch_begin_no_conflict(); @@ -1282,7 +1282,7 @@ void phys_step(struct phys_step_ctx *ctx, f32 timestep) f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS; for (u32 i = 0; i < SIM_PHYSICS_SUBSTEPS; ++i) { - __profscope(substep); + __profscope(Substep); /* Warm start */ #if SIM_PHYSICS_ENABLE_WARM_STARTING diff --git a/src/prof_tracy.h b/src/prof_tracy.h index 45e7d31f..ac8e89c9 100644 --- a/src/prof_tracy.h +++ b/src/prof_tracy.h @@ -9,9 +9,12 @@ /* Include tracy client */ #define TRACY_ENABLE -#define TRACY_MANUAL_LIFETIME -#define TRACY_DELAYED_INIT -#if 1 +#if 0 +/* Enable manual lifetime */ +# define TRACY_MANUAL_LIFETIME +# define TRACY_DELAYED_INIT +#endif +#if 0 /* Disable system tracing (very slow) */ # define TRACY_NO_CALLSTACK # define TRACY_NO_SYSTEM_TRACING @@ -19,7 +22,7 @@ #include STRINGIZE(TRACY_INCLUDE_PATH) #define PROFILING_CAPTURE_FRAME_IMAGE 0 -#define PROFILING_LOCKS 0 +#define PROFILING_LOCKS 1 #define PROFILING_D3D 1 #define PROFILING_CMD_WSTR L"tracy-profiler.exe -a 127.0.0.1" diff --git a/src/resource.c b/src/resource.c index 2b2970ea..16674b30 100644 --- a/src/resource.c +++ b/src/resource.c @@ -4,6 +4,7 @@ #include "tar.h" #include "incbin.h" #include "util.h" +#include "job.h" /* ========================== * * Global data @@ -218,6 +219,20 @@ INTERNAL SYS_THREAD_DEF(resource_watch_monitor_thread_entry_point, _) #define WATCH_DISPATCHER_DELAY_SECONDS 0.050 #define WATCH_DISPATCHER_DEDUP_DICT_BINS 128 +struct resource_watch_callback_job_sig { + struct string name; + resource_watch_callback **callbacks; +}; + +INTERNAL JOB_DEF(resource_watch_callback_job, job) +{ + __prof; + struct resource_watch_callback_job_sig *sig = job.sig; + struct string name = sig->name; + resource_watch_callback *callback = sig->callbacks[job.id]; + callback(name); +} + INTERNAL SYS_THREAD_DEF(resource_watch_dispatcher_thread_entry_point, _) { (UNUSED)_; @@ -227,8 +242,10 @@ INTERNAL SYS_THREAD_DEF(resource_watch_dispatcher_thread_entry_point, _) while (!atomic_i32_eval(&G.watch_shutdown)) { sys_condition_variable_wait(G.watch_dispatcher_cv, &watch_dispatcher_lock); if (!atomic_i32_eval(&G.watch_shutdown) && G.watch_dispatcher_info_arena->pos > 0) { + __profscope(Dispatch resource watch callbacks); /* Unlock and sleep a bit so duplicate events pile up */ { + __profscope(Delay); sys_mutex_unlock(&watch_dispatcher_lock); sys_sleep(WATCH_DISPATCHER_DELAY_SECONDS); watch_dispatcher_lock = sys_mutex_lock_e(G.watch_dispatcher_mutex); @@ -241,12 +258,25 @@ INTERNAL SYS_THREAD_DEF(resource_watch_dispatcher_thread_entry_point, _) MEMZERO_STRUCT(&G.watch_dispatcher_info_list); arena_reset(G.watch_dispatcher_info_arena); + /* Build callbacks array */ + u64 num_callbacks = 0; + resource_watch_callback **callbacks = NULL; + struct sys_lock callbacks_lock = sys_mutex_lock_s(G.watch_callbacks_mutex); + { + num_callbacks = G.num_watch_callbacks; + callbacks = arena_push_array_no_zero(temp.arena, resource_watch_callback *, num_callbacks); + for (u64 i = 0; i < num_callbacks; ++i) { + callbacks[i] = G.watch_callbacks[i]; + } + } + sys_mutex_unlock(&callbacks_lock); + /* Unlock and run callbacks */ sys_mutex_unlock(&watch_dispatcher_lock); { - __profscope(run_resource_watch_callbacks); struct dict *dedup_dict = dict_init(temp.arena, WATCH_DISPATCHER_DEDUP_DICT_BINS); for (struct sys_watch_info *info = watch_info_list.first; info; info = info->next) { + __profscope(Dispatch); /* Do not run callbacks for the same file more than once */ b32 skip = false; u64 hash = hash_fnv64(HASH_FNV64_BASIS, info->name); @@ -256,12 +286,10 @@ INTERNAL SYS_THREAD_DEF(resource_watch_dispatcher_thread_entry_point, _) dict_set(temp.arena, dedup_dict, hash, 1); } if (!skip) { - struct sys_lock callbacks_lock = sys_mutex_lock_s(G.watch_callbacks_mutex); - for (u64 i = 0; i < G.num_watch_callbacks; ++i) { - resource_watch_callback *callback = G.watch_callbacks[i]; - callback(info->name); - } - sys_mutex_unlock(&callbacks_lock); + struct resource_watch_callback_job_sig sig = ZI; + sig.name = info->name; + sig.callbacks = callbacks; + job_dispatch_wait(num_callbacks, resource_watch_callback_job, &sig); } } } diff --git a/src/sim.c b/src/sim.c index 373a7df2..4898169f 100644 --- a/src/sim.c +++ b/src/sim.c @@ -649,7 +649,7 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_client *client, str /* Blend entities */ { - __profscope(snapshot_lerp_entities); + __profscope(Lerp snapshot entities); u64 num_entities = min_u64(ss0->num_ents_reserved, ss1->num_ents_reserved); for (u64 i = 0; i < num_entities; ++i) { struct sim_ent *e = &ss->ents[i]; diff --git a/src/sprite.c b/src/sprite.c index a704f937..3cb005af 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -153,11 +153,10 @@ GLOBAL struct { /* Evictor thread */ struct atomic_i32 evictor_cycle; - b32 evictor_shutdown; - struct sys_mutex *evictor_mutex; - struct sys_condition_variable *evictor_cv; - - struct sys_thread *evictor_thread; + b32 evictor_scheduler_shutdown; + struct sys_mutex *evictor_scheduler_mutex; + struct sys_condition_variable *evictor_scheduler_shutdown_cv; + struct sys_thread *evictor_scheduler_thread; } G = ZI, DEBUG_ALIAS(G, G_sprite); /* ========================== * @@ -204,7 +203,7 @@ INTERNAL struct image_rgba generate_purple_black_image(struct arena *arena, u32 INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown); INTERNAL JOB_DEF(sprite_load_job, arg); -INTERNAL SYS_THREAD_DEF(sprite_evictor_thread_entry_point, arg); +INTERNAL SYS_THREAD_DEF(sprite_evictor_scheduler_thread_entry_point, arg); #if RESOURCE_RELOADING INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, info); @@ -256,10 +255,10 @@ struct sprite_startup_receipt sprite_startup(struct gp_startup_receipt *gp_sr, G.scopes_arena = arena_alloc(GIGABYTE(64)); - G.evictor_mutex = sys_mutex_alloc(); - G.evictor_cv = sys_condition_variable_alloc(); + G.evictor_scheduler_mutex = sys_mutex_alloc(); + G.evictor_scheduler_shutdown_cv = sys_condition_variable_alloc(); - G.evictor_thread = sys_thread_alloc(sprite_evictor_thread_entry_point, NULL, LIT("[P2] Sprite evictor")); + G.evictor_scheduler_thread = sys_thread_alloc(sprite_evictor_scheduler_thread_entry_point, NULL, LIT("[P2] Sprite evictor scheduler")); app_register_exit_callback(&sprite_shutdown); resource_register_watch_callback(&sprite_resource_watch_callback); @@ -272,12 +271,12 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown) __prof; /* Signal evictor shutdown */ { - struct sys_lock lock = sys_mutex_lock_e(G.evictor_mutex); - G.evictor_shutdown = true; - sys_condition_variable_broadcast(G.evictor_cv); + struct sys_lock lock = sys_mutex_lock_e(G.evictor_scheduler_mutex); + G.evictor_scheduler_shutdown = true; + sys_condition_variable_broadcast(G.evictor_scheduler_shutdown_cv); sys_mutex_unlock(&lock); } - sys_thread_wait_release(G.evictor_thread); + sys_thread_wait_release(G.evictor_scheduler_thread); } /* ========================== * @@ -430,7 +429,7 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str /* Init frames */ { - __profscope(init_frames); + __profscope(Init frames); sheet.image_size = ase.image_size; sheet.frame_size = ase.frame_size; sheet.frames = arena_push_array(arena, struct sprite_sheet_frame, ase.num_frames); @@ -452,7 +451,7 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str /* Init spans */ sheet.spans_count = ase.num_spans; if (ase.num_spans > 0) { - __profscope(init_spans); + __profscope(Init spans); sheet.spans = arena_push_array(arena, struct sprite_sheet_span, sheet.spans_count); sheet.spans_dict = dict_init(arena, (u64)(ase.num_spans * SHEET_SPAN_LOOKUP_TABLE_BIN_RATIO)); u64 index = 0; @@ -470,7 +469,7 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str /* Init slices */ if (ase.num_slice_keys > 0) { - __profscope(init_slices); + __profscope(Init slices); struct arena_temp scratch = scratch_begin(arena); struct temp_ase_slice_key_node { @@ -1209,7 +1208,7 @@ INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, name) #endif /* ========================== * - * Evictor thread + * Evictor job * ========================== */ struct evict_node { @@ -1230,34 +1229,21 @@ INTERNAL SORT_COMPARE_FUNC_DEF(evict_sort, arg_a, arg_b, udata) return (b_cycle > a_cycle) - (a_cycle > b_cycle); } -/* NOTE: - * A cache node is safe from eviction as long as: - * - Its bin mutex is locked - * - Any references are held to the node (its refcount > 0) - * - * An attempt to evict a cache node will occur when: - * - Its refcount = 0 and - * - The cache is over its memory budget and the node's last reference is longer ago than the grace period - * - Resource reloading is enabled and the node is out of date due to a change to its original resource file - */ -INTERNAL SYS_THREAD_DEF(sprite_evictor_thread_entry_point, arg) +INTERNAL JOB_DEF(sprite_evictor_job, _) { - (UNUSED)arg; - - struct sys_lock evictor_lock = sys_mutex_lock_e(G.evictor_mutex); - while (!G.evictor_shutdown) { - struct arena_temp scratch = scratch_begin_no_conflict(); - + (UNUSED)_; + __prof; + struct arena_temp scratch = scratch_begin_no_conflict(); + { u64 evict_array_count = 0; struct evict_node *evict_array = arena_push_dry(scratch.arena, struct evict_node); - - if (!G.evictor_shutdown) { + { i32 cur_cycle = atomic_i32_eval(&G.evictor_cycle); /* Scan for evictable nodes */ b32 cache_over_budget_threshold = atomic_u64_eval(&G.cache.memory_usage) > CACHE_MEMORY_BUDGET_THRESHOLD; if (cache_over_budget_threshold || RESOURCE_RELOADING) { - __profscope(eviction_scan); + __profscope(Evictor scan); for (u64 i = 0; i < CACHE_BINS_COUNT; ++i) { struct cache_bin *bin = &G.cache.bins[i]; struct sys_lock bin_lock = sys_mutex_lock_s(bin->mutex); @@ -1298,14 +1284,14 @@ INTERNAL SYS_THREAD_DEF(sprite_evictor_thread_entry_point, arg) /* Sort evict nodes */ { - __profscope(eviction_sort); + __profscope(Evictor sort); merge_sort(evict_array, evict_array_count, sizeof(*evict_array), evict_sort, NULL); } /* Remove evictable nodes from cache until under budget */ struct evict_node *first_evicted = NULL; { - __profscope(eviction_cache_removal); + __profscope(Evictor cache removal); b32 stop_evicting = false; for (u64 i = 0; i < evict_array_count && !stop_evicting; ++i) { struct evict_node *en = &evict_array[i]; @@ -1351,7 +1337,7 @@ INTERNAL SYS_THREAD_DEF(sprite_evictor_thread_entry_point, arg) if (first_evicted) { /* Release evicted node memory */ { - __profscope(eviction_memory_release); + __profscope(Evictor memory release); for (struct evict_node *en = first_evicted; en; en = en->next_evicted) { struct cache_entry *n = en->cache_entry; if (n->kind == CACHE_ENTRY_KIND_TEXTURE && n->texture->valid) { @@ -1363,7 +1349,7 @@ INTERNAL SYS_THREAD_DEF(sprite_evictor_thread_entry_point, arg) /* Add evicted nodes to free list */ { - __profscope(eviction_free_list_append); + __profscope(Evictor free list append); struct sys_lock pool_lock = sys_mutex_lock_e(G.cache.entry_pool_mutex); for (struct evict_node *en = first_evicted; en; en = en->next_evicted) { struct cache_entry *n = en->cache_entry; @@ -1376,9 +1362,28 @@ INTERNAL SYS_THREAD_DEF(sprite_evictor_thread_entry_point, arg) } atomic_i32_eval_add(&G.evictor_cycle, 1); scratch_end(scratch); + } - /* Wait */ - sys_condition_variable_wait_time(G.evictor_cv, &evictor_lock, SECONDS_FROM_NS(EVICTOR_CYCLE_INTERVAL_NS)); +} + +/* NOTE: + * A cache node is safe from eviction as long as: + * - Its bin mutex is locked + * - Any references are held to the node (its refcount > 0) + * + * An attempt to evict a cache node will occur when: + * - Its refcount = 0 and + * - The cache is over its memory budget and the node's last reference is longer ago than the grace period + * - Resource reloading is enabled and the node is out of date due to a change to its original resource file + */ +INTERNAL SYS_THREAD_DEF(sprite_evictor_scheduler_thread_entry_point, arg) +{ + (UNUSED)arg; + + struct sys_lock evictor_lock = sys_mutex_lock_e(G.evictor_scheduler_mutex); + while (!G.evictor_scheduler_shutdown) { + job_dispatch_wait(1, sprite_evictor_job, NULL); + sys_condition_variable_wait_time(G.evictor_scheduler_shutdown_cv, &evictor_lock, SECONDS_FROM_NS(EVICTOR_CYCLE_INTERVAL_NS)); } sys_mutex_unlock(&evictor_lock); } diff --git a/src/sys.h b/src/sys.h index 11d0ae40..0706b5e3 100644 --- a/src/sys.h +++ b/src/sys.h @@ -282,6 +282,7 @@ struct sys_watch_info { struct sys_watch_info_list { struct sys_watch_info *first; struct sys_watch_info *last; + u64 count; }; struct sys_watch *sys_watch_alloc(struct string path); diff --git a/src/sys_win32.c b/src/sys_win32.c index fda76878..4ff7ff42 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -418,7 +418,7 @@ struct sys_file sys_file_open_read_wait(struct string path) HANDLE handle; while ((handle = CreateFileW(path_wstr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_SHARING_VIOLATION) { - __profscope(file_share_conflict_delay); + __profscope(File share conflict delay); Sleep(delay_ms); if (delay_ms < 1024) { delay_ms *= 2; @@ -800,11 +800,11 @@ struct sys_watch_info_list sys_watch_wait(struct arena *arena, struct sys_watch if (list.last) { list.last->next = info; info->prev = list.last; - list.last = info; } else { list.first = info; - list.last = info; } + list.last = info; + ++list.count; struct string16 name16 = ZI; name16.text = res->FileName; @@ -856,6 +856,7 @@ struct sys_watch_info_list sys_watch_wait(struct arena *arena, struct sys_watch } } } else if (wait_res == WAIT_OBJECT_0 + 1) { + ResetEvent(w32_watch->wake_handle); done = true; } else { ASSERT(false); @@ -886,6 +887,7 @@ struct sys_watch_info_list sys_watch_info_copy(struct arena *arena, struct sys_w dst_list.first = dst; dst_list.last = dst; } + dst_list.count = src_list.count; } return dst_list; } @@ -1634,6 +1636,7 @@ void sys_mutex_release(struct sys_mutex *mutex) struct sys_lock sys_mutex_lock_e(struct sys_mutex *mutex) { + __prof; struct win32_mutex *m = (struct win32_mutex *)mutex; __proflock_before_exclusive_lock(m->profiling_ctx); AcquireSRWLockExclusive((SRWLOCK *)&m->srwlock); @@ -1650,6 +1653,7 @@ struct sys_lock sys_mutex_lock_e(struct sys_mutex *mutex) struct sys_lock sys_mutex_lock_s(struct sys_mutex *mutex) { + __prof; struct win32_mutex *m = (struct win32_mutex *)mutex; __proflock_before_shared_lock(m->profiling_ctx); AcquireSRWLockShared((SRWLOCK *)&m->srwlock); @@ -1664,6 +1668,7 @@ struct sys_lock sys_mutex_lock_s(struct sys_mutex *mutex) void sys_mutex_unlock(struct sys_lock *lock) { + __prof; struct win32_mutex *m = (struct win32_mutex *)lock->mutex; #if RTC atomic_i64_eval_add(&m->count, -1); @@ -1743,6 +1748,7 @@ void sys_condition_variable_release(struct sys_condition_variable *sys_cv) void sys_condition_variable_wait(struct sys_condition_variable *sys_cv, struct sys_lock *lock) { + __prof; struct win32_condition_variable *cv = (struct win32_condition_variable *)sys_cv; struct win32_mutex *m = (struct win32_mutex *)lock->mutex; b32 exclusive = lock->exclusive; @@ -1780,6 +1786,7 @@ void sys_condition_variable_wait(struct sys_condition_variable *sys_cv, struct s void sys_condition_variable_wait_time(struct sys_condition_variable *sys_cv, struct sys_lock *lock, f64 seconds) { + __prof; struct win32_condition_variable *cv = (struct win32_condition_variable *)sys_cv; struct win32_mutex *m = (struct win32_mutex *)lock->mutex; b32 exclusive = lock->exclusive; @@ -1818,6 +1825,7 @@ void sys_condition_variable_wait_time(struct sys_condition_variable *sys_cv, str void sys_condition_variable_signal(struct sys_condition_variable *sys_cv, u32 count) { + __prof; struct win32_condition_variable *cv = (struct win32_condition_variable *)sys_cv; /* Windows will wake all waiters if many single-wakes occur anyway, so we * might as well wake all ourselves. @@ -1833,6 +1841,7 @@ void sys_condition_variable_signal(struct sys_condition_variable *sys_cv, u32 co void sys_condition_variable_broadcast(struct sys_condition_variable *sys_cv) { + __prof; struct win32_condition_variable *cv = (struct win32_condition_variable *)sys_cv; WakeAllConditionVariable(&cv->condition_variable); } @@ -2232,7 +2241,7 @@ INTERNAL void win32_precise_sleep_timer(f64 seconds, HANDLE timer) i64 max_ticks = (i64)scheduler_period_ms * 9500; while (true) { - __profscope(win32_sleep_part); + __profscope(Sleep part); /* Break sleep up into parts that are lower than scheduler period */ f64 remaining_seconds = (f64)(target_qpc - qpc.QuadPart) / (f64)qpc_per_second; i64 sleep_ticks = (i64)((remaining_seconds - tolerance) * 10000000); @@ -2248,7 +2257,7 @@ INTERNAL void win32_precise_sleep_timer(f64 seconds, HANDLE timer) /* Spin for any remaining time */ { - __profscope(sleep_spin); + __profscope(Sleep spin); while (qpc.QuadPart < target_qpc) { YieldProcessor(); QueryPerformanceCounter(&qpc); @@ -2276,18 +2285,14 @@ INTERNAL void win32_precise_sleep_legacy(f64 seconds) f64 sleep_ms = (seconds * 1000) - tolerance; i32 sleep_slices = (i32)(sleep_ms / scheduler_period_ms); if (sleep_slices > 0) { - __profscope(win32_sleep); + __profscope(Legacy sleep part); Sleep((DWORD)sleep_slices * scheduler_period_ms); } - - { - __profscope(win32_qpc); - QueryPerformanceCounter(&qpc); - } + QueryPerformanceCounter(&qpc); /* Spin for any remaining time */ { - __profscope(sleep_spin); + __profscope(Legacy sleep spin); while (qpc.QuadPart < target_qpc) { YieldProcessor(); QueryPerformanceCounter(&qpc); diff --git a/src/thread_local.c b/src/thread_local.c index 19d81d65..4796c3cf 100644 --- a/src/thread_local.c +++ b/src/thread_local.c @@ -63,7 +63,7 @@ volatile void *_thread_local_var_eval(struct thread_local_var_meta *meta) { u64 id_plus_one = atomic_u64_eval(&meta->id_plus_one); if (id_plus_one == 0) { - __profscope(_thread_local_var_eval__REGISTER); + __profscope(Register thread local var); metas_lock(); { id_plus_one = atomic_u64_eval(&meta->id_plus_one); /* Reevaluate now that we've locked */ @@ -89,7 +89,7 @@ volatile void *_thread_local_var_eval(struct thread_local_var_meta *meta) /* Allocate var for thread if unallocated */ if (!data) { - __profscope(_thread_local_var_eval__ALLOC); + __profscope(Alloc thread local var); /* Allocate */ arena_align(t->arena, meta->align); data = arena_push_array_no_zero(t->arena, u8, meta->size); diff --git a/src/user.c b/src/user.c index 3eb76175..3510cf0a 100644 --- a/src/user.c +++ b/src/user.c @@ -1162,7 +1162,7 @@ INTERNAL void user_update(void) { /* Copy valid entities */ { - __profscope(copy_sprites_for_sorting); + __profscope(Build ents list for sorting); for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { struct sim_ent *ent = &G.ss_blended->ents[ent_index]; if (sim_ent_is_valid_and_active(ent)) { @@ -1173,7 +1173,7 @@ INTERNAL void user_update(void) } /* Sort */ { - __profscope(sort_sprites); + __profscope(Sort ents); merge_sort(sorted, sorted_count, sizeof(*sorted), ent_draw_order_cmp, NULL); } } @@ -1183,7 +1183,7 @@ INTERNAL void user_update(void) * ========================== */ { - __profscope(draw_entities); + __profscope(Draw entities); for (u64 sorted_index = 0; sorted_index < sorted_count; ++sorted_index) { struct sim_ent *ent = sorted[sorted_index]; if (!sim_ent_is_valid_and_active(ent)) continue; @@ -1709,7 +1709,7 @@ INTERNAL void user_update(void) /* Draw crosshair or show cursor */ if (!G.debug_camera) { - __profscope(draw_crosshair); + __profscope(Draw crosshair); struct v2 crosshair_pos = G.user_cursor; struct sprite_tag crosshair_tag = sprite_tag_from_path(LIT("sprite/crosshair.ase")); @@ -1726,7 +1726,7 @@ INTERNAL void user_update(void) sys_window_cursor_hide(G.window); sys_window_cursor_enable_clip(G.window, cursor_clip); } else { - __profscope(update_window_cursor); + __profscope(Update windows cursor); sys_window_cursor_disable_clip(G.window); sys_window_cursor_show(G.window); } @@ -2055,7 +2055,7 @@ INTERNAL void user_update(void) * ========================== */ { - __profscope(render); + __profscope(Render); struct rect user_viewport = RECT_FROM_V2(V2(0, 0), G.user_size); struct v2i32 user_resolution = v2_round_to_int(user_viewport.size);