From 726ad9078476e919c76e15d2a207582e2e5d7bfc Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 13 May 2025 06:08:28 -0500 Subject: [PATCH] fix sprite force evict sorting --- res/graphics/tim.ase | 4 +- src/sprite.c | 109 ++++++++++++++++++++++--------------------- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/res/graphics/tim.ase b/res/graphics/tim.ase index f975bd8d..c01f80d8 100644 --- a/res/graphics/tim.ase +++ b/res/graphics/tim.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd0019692bfc14d9c0ad8b9140145ff81e49b26615a3636007d9b5fc01566aa8 -size 6087 +oid sha256:7e515b2d3f37e14b647f231aea4884ff52944020198818234f0a02829aea07aa +size 6180 diff --git a/src/sprite.c b/src/sprite.c index a25697ca..b825af14 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1107,12 +1107,31 @@ struct evict_node { struct cache_node_refcount refcount; struct cache_node *cache_node; struct cache_bin *cache_bin; - struct evict_node *next_consider; - struct evict_node *next_consider_lru; - struct evict_node *next_evicted; + struct evict_node *next_evicted; }; +INTERNAL SORT_COMPARE_FUNC_DEF(evict_sort, arg_a, arg_b, udata) +{ + (UNUSED)udata; + struct evict_node *a = arg_a; + struct evict_node *b = arg_b; + + u64 refcount_uncast_a = atomic_u64_eval(&a->cache_node->refcount_struct); + u64 refcount_uncast_b = atomic_u64_eval(&b->cache_node->refcount_struct); + u32 cycle_a = ((struct cache_node_refcount *)&refcount_uncast_a)->last_modified_cycle; + u32 cycle_b = ((struct cache_node_refcount *)&refcount_uncast_b)->last_modified_cycle; + + i32 res = (cycle_b > cycle_a) - (cycle_a > cycle_b); + res += ((a->force_evict > b->force_evict) - (a->force_evict < b->force_evict)) * 2; + + if (a->force_evict != b->force_evict) { + DEBUGBREAKABLE; + } + + return res; +} + INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) { (UNUSED)arg; @@ -1120,9 +1139,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) struct sys_lock evictor_lock = sys_mutex_lock_e(&G.evictor_mutex); while (!G.evictor_shutdown) { struct temp_arena scratch = scratch_begin_no_conflict(); - struct evict_node *head_consider = NULL; - struct evict_node *head_consider_lru = NULL; - struct evict_node *head_evicted = NULL; + + u64 evict_array_count = 0; + struct evict_node *evict_array = arena_dry_push(scratch.arena, struct evict_node); + if (!G.evictor_shutdown) { u32 cur_cycle = *atomic_u32_raw(&G.evictor_cycle); @@ -1145,15 +1165,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) /* Check if file changed for resource reloading */ if (!consider_for_eviction) { if (atomic_i32_eval(&n->out_of_date)) { - switch (n->kind) { - case CACHE_NODE_KIND_TEXTURE: { - logf_info("Resource file for sprite texture [%F] has changed. Evicting to allow for reloading.", FMT_HEX(n->hash.v)); - } break; - case CACHE_NODE_KIND_SHEET: { - logf_info("Resource file for sprite sheet [%F] has changed. Evicting to allow for reloading.", FMT_HEX(n->hash.v)); - } break; - default: { sys_panic(LIT("Unknown sprite cache node kind")); } break; - } + logf_info("Resource file for sprite texture/sheet [%F] has changed. Evicting to allow for reloading.", FMT_HEX(n->hash.v)); consider_for_eviction = true; force_evict = true; } @@ -1161,24 +1173,24 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) #endif /* Check usage time */ - u32 last_used_cycle = refcount.last_modified_cycle; - i64 time_since_use_ns = ((i64)cur_cycle - (i64)last_used_cycle) * EVICTOR_CYCLE_INTERVAL_NS; - if (time_since_use_ns > EVICTOR_GRACE_PERIOD_NS) { - /* Cache is over budget and node hasn't been referenced in a while */ - consider_for_eviction = true; + if (cache_over_budget) { + u32 last_used_cycle = refcount.last_modified_cycle; + i64 time_since_use_ns = ((i64)cur_cycle - (i64)last_used_cycle) * EVICTOR_CYCLE_INTERVAL_NS; + if (time_since_use_ns > EVICTOR_GRACE_PERIOD_NS) { + /* Cache is over budget and node hasn't been referenced in a while */ + consider_for_eviction = true; + } } - } /* Add node to evict list */ if (consider_for_eviction) { - struct evict_node *evict_node = arena_push_zero(scratch.arena, struct evict_node); - evict_node->cache_node = n; - evict_node->cache_bin = bin; - evict_node->refcount = refcount; - evict_node->force_evict = force_evict; - evict_node->next_consider = head_consider; - head_consider = evict_node; + struct evict_node *en = arena_push_zero(scratch.arena, struct evict_node); + en->cache_node = n; + en->cache_bin = bin; + en->refcount = refcount; + en->force_evict = force_evict; + ++evict_array_count; } n = n->next_in_bin; @@ -1188,32 +1200,22 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) } } - /* Sort evict nodes by usage time */ - if (head_consider) { - /* TODO: Optimize sort if necessary. Currently O(n^2). */ + /* Scratch arena should only contain evict array at this point */ + ASSERT(scratch.arena->pos == (sizeof(*evict_array) * evict_array_count)); + + /* Sort evict nodes */ + { __profscope(eviction_sort); - for (struct evict_node *en = head_consider; en; en = en->next_consider) { - u32 last_modified_cycle = en->refcount.last_modified_cycle; - struct evict_node *prev = NULL; - struct evict_node *next = head_consider_lru; - while (next && !(last_modified_cycle <= next->refcount.last_modified_cycle || en->force_evict)) { - prev = next; - next = next->next_consider_lru; - } - if (prev) { - prev->next_consider_lru = en; - } else { - head_consider_lru = en; - } - en->next_consider_lru = next; - } + merge_sort(evict_array, evict_array_count, sizeof(*evict_array), evict_sort, NULL); } /* Remove evictable nodes from cache table until under budget */ - if (head_consider_lru) { + struct evict_node *first_evicted = NULL; + { __profscope(eviction_cache_removal); b32 stop_evicting = false; - for (struct evict_node *en = head_consider_lru; en && !stop_evicting; en = en->next_consider_lru) { + for (u64 i = 0; i < evict_array_count && !stop_evicting; ++i) { + struct evict_node *en = &evict_array[i]; struct cache_bin *bin = en->cache_bin; struct cache_node *n = en->cache_node; struct sys_lock bin_lock = sys_mutex_lock_e(&bin->mutex); @@ -1232,9 +1234,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) n->next_in_bin->prev_in_bin = n->prev_in_bin; } atomic_u64_eval_add_i64(&G.cache.memory_usage, -((i64)n->memory_usage)); + /* Add to evicted list */ - en->next_evicted = head_evicted; - head_evicted = en; + en->next_evicted = first_evicted; + first_evicted = en; } else { /* Cache is no longer over budget or force evicting, stop iteration */ stop_evicting = true; @@ -1244,11 +1247,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) } } - if (head_evicted) { + if (first_evicted) { /* Release evicted node memory */ { __profscope(eviction_memory_release); - for (struct evict_node *en = head_evicted; en; en = en->next_evicted) { + for (struct evict_node *en = first_evicted; en; en = en->next_evicted) { struct cache_node *n = en->cache_node; if (n->kind == CACHE_NODE_KIND_TEXTURE && n->texture->valid) { renderer_texture_release(n->texture->texture); @@ -1261,7 +1264,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) { __profscope(eviction_free_list_append); struct sys_lock pool_lock = sys_mutex_lock_e(&G.cache.node_pool_mutex); - for (struct evict_node *en = head_evicted; en; en = en->next_evicted) { + for (struct evict_node *en = first_evicted; en; en = en->next_evicted) { struct cache_node *n = en->cache_node; n->next_free = G.cache.node_pool_first_free; G.cache.node_pool_first_free = n;