sprite layer refactor progress

This commit is contained in:
jacob 2025-08-01 00:14:57 -05:00
parent 32631d0ae9
commit 9d8758b2b4
4 changed files with 791 additions and 527 deletions

View File

@ -405,9 +405,8 @@ internal void test_spawn_tile(Snapshot *world, Vec2 world_pos)
internal MergesortCompareFuncDef(tile_chunk_sort_x, arg_a, arg_b, udata)
internal MergesortCompareFuncDef(tile_chunk_sort_x, arg_a, arg_b, _)
{
(UNUSED)udata;
Ent *a = *(Ent **)arg_a;
Ent *b = *(Ent **)arg_b;
i32 a_x = a->tile_chunk_index.x;
@ -418,9 +417,8 @@ internal MergesortCompareFuncDef(tile_chunk_sort_x, arg_a, arg_b, udata)
return result;
}
internal MergesortCompareFuncDef(tile_chunk_sort_y, arg_a, arg_b, udata)
internal MergesortCompareFuncDef(tile_chunk_sort_y, arg_a, arg_b, _)
{
(UNUSED)udata;
Ent *a = *(Ent **)arg_a;
Ent *b = *(Ent **)arg_b;
i32 a_y = a->tile_chunk_index.y;

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +1,17 @@
/* ========================== *
* Startup
* ========================== */
////////////////////////////////
//~ Tag
typedef struct S_StartupReceipt S_StartupReceipt;
struct S_StartupReceipt { i32 _; };
S_StartupReceipt sprite_startup(void);
/* ========================== *
* Tag
* ========================== */
typedef struct S_Tag S_Tag;
struct S_Tag {
Struct(S_Tag)
{
u64 hash;
String path;
};
Inline S_Tag sprite_tag_nil(void) { return (S_Tag) { 0 }; }
////////////////////////////////
//~ Texture
S_Tag sprite_tag_from_path(String path);
b32 sprite_tag_is_nil(S_Tag tag);
b32 sprite_tag_eq(S_Tag t1, S_Tag t2);
/* ========================== *
* Scope
* ========================== */
typedef struct S_Scope S_Scope;
struct S_Scope {
struct sprite_scope_cache_ref **ref_node_bins;
struct sprite_scope_cache_ref *ref_node_pool;
u64 num_references;
S_Scope *next_free;
};
S_Scope *sprite_scope_begin(void);
void sprite_scope_end(S_Scope *scope);
/* ========================== *
* Texture load
* ========================== */
typedef struct S_Texture S_Texture;
struct S_Texture {
Struct(S_Texture)
{
b32 loaded;
b32 valid;
GPU_Resource *gp_texture;
@ -50,60 +19,25 @@ struct S_Texture {
u32 height;
};
S_Texture *sprite_texture_from_tag_await(S_Scope *scope, S_Tag tag);
S_Texture *sprite_texture_from_tag_async(S_Scope *scope, S_Tag tag);
void sprite_texture_from_tag_prefetch(S_Scope *scope, S_Tag tag);
////////////////////////////////
//~ Sheet
/* ========================== *
* Sheet load
* ========================== */
typedef struct S_SheetSliceGroup S_SheetSliceGroup;
typedef struct S_SheetSpan S_SheetSpan;
typedef struct S_SheetFrame S_SheetFrame;
typedef struct S_Sheet S_Sheet;
struct S_Sheet {
b32 loaded;
b32 valid;
Vec2 image_size;
Vec2 frame_size;
u32 frames_count;
S_SheetFrame *frames;
u32 spans_count;
S_SheetSpan *spans;
Dict *spans_dict;
u32 slice_groups_count;
S_SheetSliceGroup *slice_groups;
Dict *slice_groups_dict;
};
S_Sheet *sprite_sheet_from_tag_await(S_Scope *scope, S_Tag tag);
S_Sheet *sprite_sheet_from_tag_async(S_Scope *scope, S_Tag tag);
void sprite_sheet_from_tag_prefetch(S_Scope *scope, S_Tag tag);
/* ========================== *
* Sheet query
* ========================== */
typedef struct S_SheetFrame S_SheetFrame;
struct S_SheetFrame {
Struct(S_SheetFrame)
{
u32 index;
f64 duration;
ClipRect clip;
};
typedef struct S_SheetSpan S_SheetSpan;
struct S_SheetSpan {
Struct(S_SheetSpan)
{
String name;
u32 start;
u32 end;
};
typedef struct S_SheetSlice S_SheetSlice;
struct S_SheetSlice {
Struct(S_SheetSlice)
{
/* If 1, this slice was not copied over from another frame in the sprite sheet */
b32 original;
@ -121,14 +55,14 @@ struct S_SheetSlice {
Vec2 dir_px;
};
typedef struct S_SheetSliceArray S_SheetSliceArray;
struct S_SheetSliceArray {
Struct(S_SheetSliceArray)
{
u64 count;
S_SheetSlice *slices;
};
typedef struct S_SheetSliceGroup S_SheetSliceGroup;
struct S_SheetSliceGroup {
Struct(S_SheetSliceGroup)
{
String name;
u64 per_frame_count;
@ -137,6 +71,321 @@ struct S_SheetSliceGroup {
S_SheetSlice *frame_slices;
};
Struct(S_Sheet)
{
b32 loaded;
b32 valid;
Vec2 image_size;
Vec2 frame_size;
u32 frames_count;
S_SheetFrame *frames;
u32 spans_count;
S_SheetSpan *spans;
Dict *spans_dict;
u32 slice_groups_count;
S_SheetSliceGroup *slice_groups;
Dict *slice_groups_dict;
};
////////////////////////////////
//~ Cache
typedef i32 S_CacheEntryKind; enum
{
CACHE_ENTRY_KIND_TEXTURE,
CACHE_ENTRY_KIND_SHEET,
NUM_CACHE_ENTRY_KINDS
};
typedef i32 S_CacheEntryState; enum
{
CACHE_ENTRY_STATE_NONE,
CACHE_ENTRY_STATE_QUEUED,
CACHE_ENTRY_STATE_WORKING,
CACHE_ENTRY_STATE_LOADED
};
Struct(S_Refcount)
{
i32 count; /* Number of scopes currently holding a reference to this entry */
i32 last_ref_cycle; /* Last evictor cycle that the refcount was modified */
};
StaticAssert(sizeof(S_Refcount) == 8); /* Must fit into 64 bit atomic */
Struct(S_Hash)
{
u64 v;
};
Struct(S_CacheEntry)
{
S_CacheEntryKind kind;
S_Hash hash;
Atomic32 state;
Atomic64Padded refcount_struct; /* Cast fetched result to `cache_refcount` */
/* Allocated data */
/* NOTE: This data is finalized once entry state = loaded */
i64 load_time_ns;
u64 memory_usage;
Arena *arena;
S_Texture *texture;
S_Sheet *sheet;
/* Hash list */
S_CacheEntry *next_in_bin;
S_CacheEntry *prev_in_bin;
/* Free list */
S_CacheEntry *next_free;
#if RESOURCE_RELOADING
Atomic32 out_of_date; /* Has the resource changed since this entry was loaded */
#endif
};
Struct(S_CacheEntryBin)
{
P_Mutex mutex;
S_CacheEntry *first;
S_CacheEntry *last;
};
Struct(S_Cache)
{
Atomic64Padded memory_usage;
Arena *arena;
S_CacheEntryBin *bins;
P_Mutex entry_pool_mutex;
S_CacheEntry *entry_pool_first_free;
};
/* Represents a reference that can be used to safely access cache entry without it becoming evicted during the reference's lifetime */
Struct(S_Ref)
{
S_CacheEntry *e;
};
////////////////////////////////
//~ Scope
/* A cache reference whose lifetime is bound to the scope it was retrieved from */
Struct(S_ScopeCacheRef)
{
S_Ref ref;
S_ScopeCacheRef *next_in_bin;
};
Struct(S_Scope)
{
S_ScopeCacheRef **ref_node_bins;
S_ScopeCacheRef *ref_node_pool;
u64 num_references;
S_Scope *next_free;
};
////////////////////////////////
//~ Cmd
Struct(S_Cmd)
{
S_Cmd *next_free;
S_Scope *scope;
S_Ref ref;
S_Tag tag;
u8 tag_path_buff[512];
};
////////////////////////////////
//~ Evictor
Struct(S_EvictorNode)
{
i32 last_ref_cycle;
S_CacheEntry *cache_entry;
S_CacheEntryBin *cache_bin;
S_EvictorNode *next_evicted;
};
////////////////////////////////
//~ Cache constants
/* The evictor will begin evicting once cache usage is > threshold.
* It will entries until the budget has shrunk < target. */
#define S_CacheMemoryBudgetThreshold (Mebi(256))
#define S_CacheMemoryBudgetTarget (Mebi(128))
StaticAssert(S_CacheMemoryBudgetThreshold >= S_CacheMemoryBudgetTarget);
#define S_CacheBinsCount 1024
#define S_MaxScopeReferences 1024
/* Texture arena only used to store texture struct at the moment. Actual image data is allocated on GPU. */
#define S_TextureArenaReserve Mebi(1)
#define S_SheetArenaReserve Mebi(64)
#define S_SheetSpanLookupTableBinRatio 2.0
#define S_SliceLookupTableBinRatio 2.0
/* How long between evictor cycles */
#define S_EvictorCycleIntervalNs NsFromSeconds(0.500)
/* How many cycles a cache entry spends unused until it's considered evictable */
#define S_EvictorGracePeriodCycles (NsFromSeconds(10.000) / S_EvictorCycleIntervalNs)
////////////////////////////////
//~ Shared state
Struct(S_SharedState)
{
Arena *perm_arena;
S_Texture *nil_texture;
S_Texture *loading_texture;
S_Sheet *nil_sheet;
S_Sheet *loading_sheet;
/* Cache */
S_Cache cache;
/* Cmds */
P_Mutex cmds_mutex;
Arena *cmds_arena;
S_Cmd *first_free_cmd;
/* Scopes */
P_Mutex scopes_mutex;
Arena *scopes_arena;
S_Scope *first_free_scope;
/* Evictor */
Atomic32Padded evictor_cycle;
P_Counter shutdown_counter;
b32 evictor_scheduler_shutdown;
P_Mutex evictor_scheduler_mutex;
P_Cv evictor_scheduler_shutdown_cv;
};
extern S_SharedState S_shared_state;
////////////////////////////////
//~ Startup
Struct(S_StartupReceipt) { i32 _; };
S_StartupReceipt sprite_startup(void);
////////////////////////////////
//~ Tag operations
Inline S_Tag sprite_tag_nil(void) { return (S_Tag) { 0 }; }
S_Tag sprite_tag_from_path(String path);
b32 sprite_tag_is_nil(S_Tag tag);
b32 sprite_tag_eq(S_Tag t1, S_Tag t2);
////////////////////////////////
//~ Scope operations
S_Scope *sprite_scope_begin(void);
void sprite_scope_end(S_Scope *scope);
/* ========================== *
* Texture load
* ========================== */
/* ========================== *
* Cache structs
* ========================== */
/* ========================== *
* Purple-black image
* ========================== */
u32 *generate_purple_black_image(Arena *arena, u32 width, u32 height);
/* ========================== *
* Startup
* ========================== */
S_StartupReceipt sprite_startup(void);
P_ExitFuncDef(sprite_shutdown);
/* ========================== *
* Tag
* ========================== */
S_Tag sprite_tag_from_path(String path);
b32 sprite_tag_is_nil(S_Tag tag);
b32 sprite_tag_eq(S_Tag t1, S_Tag t2);
S_Hash cache_entry_hash_from_tag_hash(u64 tag_hash, S_CacheEntryKind kind);
/* ========================== *
* Load
* ========================== */
S_ScopeCacheRef *scope_ensure_ref_from_ref(S_Scope *scope, S_Ref ref);
void push_load_job(S_Ref ref, S_Tag tag);
void cache_entry_load_texture(S_Ref ref, S_Tag tag);
S_Sheet init_sheet_from_ase_result(Arena *arena, ASE_DecodedSheet ase);
void cache_entry_load_sheet(S_Ref ref, S_Tag tag);
/* ========================== *
* Scope
* ========================== */
void refcount_add(S_CacheEntry *e, i32 amount);
S_ScopeCacheRef *scope_ensure_ref_unsafe(S_Scope *scope, S_CacheEntry *e);
S_ScopeCacheRef *scope_ensure_ref_from_entry(S_Scope *scope, S_CacheEntry *e, P_Lock *bin_lock);
S_ScopeCacheRef *scope_ensure_ref_from_ref(S_Scope *scope, S_Ref ref);
S_Scope *sprite_scope_begin(void);
void sprite_scope_end(S_Scope *scope);
/* ========================== *
* Cache interface
* ========================== */
S_ScopeCacheRef *cache_lookup(S_Scope *scope, S_Hash hash, P_Lock *bin_lock);
S_ScopeCacheRef *cache_entry_from_tag(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind, b32 force_new);
void *data_from_tag_internal(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind, b32 await);
/* ========================== *
* Texture
* ========================== */
S_Texture *sprite_texture_from_tag_await(S_Scope *scope, S_Tag tag);
S_Texture *sprite_texture_from_tag_async(S_Scope *scope, S_Tag tag);
void sprite_texture_from_tag_prefetch(S_Scope *scope, S_Tag tag);
/* ========================== *
* Sheet
* ========================== */
S_Sheet *sprite_sheet_from_tag_await(S_Scope *scope, S_Tag tag);
S_Sheet *sprite_sheet_from_tag_async(S_Scope *scope, S_Tag tag);
void sprite_sheet_from_tag_prefetch(S_Scope *scope, S_Tag tag);
S_SheetFrame sprite_sheet_get_frame(S_Sheet *sheet, u32 index);
S_SheetSpan sprite_sheet_get_span(S_Sheet *sheet, String name);
@ -146,3 +395,26 @@ S_SheetSlice sprite_sheet_get_slice(S_Sheet *sheet, String name, u32 frame_index
/* Returns all slices with name in frame */
S_SheetSliceArray sprite_sheet_get_slices(S_Sheet *sheet, String name, u32 frame_index);
/* ========================== *
* Load job
* ========================== */
P_JobDef(S_SpriteLoadJob, job);
////////////////////////////////
//~ Resource watch
#if RESOURCE_RELOADING
void S_ReloadSpriteFromTag(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind);
W_CallbackFuncDef(S_WatchSpriteCallback, name);
#endif
////////////////////////////////
//~ Evictor job
MergesortCompareFuncDef(S_EvictorSortCmp, arg_a, arg_b, udata);
P_JobDef(S_EvictorJob, _);

View File

@ -506,9 +506,8 @@ internal void draw_debug_console(i32 level, b32 minimized)
* Sort entities
* ========================== */
internal MergesortCompareFuncDef(ent_draw_order_cmp, arg_a, arg_b, udata)
internal MergesortCompareFuncDef(ent_draw_order_cmp, arg_a, arg_b, _)
{
(UNUSED)udata;
Ent *a = *(Ent **)arg_a;
Ent *b = *(Ent **)arg_b;