352 lines
8.2 KiB
C
352 lines
8.2 KiB
C
////////////////////////////////
|
|
//~ Tag
|
|
|
|
Struct(S_Tag)
|
|
{
|
|
u64 hash;
|
|
String path;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Texture
|
|
|
|
Struct(S_Texture)
|
|
{
|
|
b32 loaded;
|
|
b32 valid;
|
|
GPU_Resource *gp_texture;
|
|
u32 width;
|
|
u32 height;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Sheet types
|
|
|
|
Struct(S_Frame)
|
|
{
|
|
u32 index;
|
|
f64 duration;
|
|
ClipRect clip;
|
|
};
|
|
|
|
Struct(S_Span)
|
|
{
|
|
String name;
|
|
u32 start;
|
|
u32 end;
|
|
};
|
|
|
|
Struct(S_Slice)
|
|
{
|
|
/* If 1, this slice was not copied over from another frame in the sprite sheet */
|
|
b32 original;
|
|
|
|
/* If 1, the slice has a corresponding '.ray' slice affecting the 'dir' fields */
|
|
b32 has_ray;
|
|
|
|
/* Values are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge) */
|
|
Rect rect;
|
|
Vec2 center;
|
|
Vec2 dir;
|
|
|
|
/* '_px' values retain the original sprite pixel dimensions */
|
|
Rect rect_px;
|
|
Vec2 center_px;
|
|
Vec2 dir_px;
|
|
};
|
|
|
|
Struct(S_SliceArray)
|
|
{
|
|
u64 count;
|
|
S_Slice *slices;
|
|
};
|
|
|
|
Struct(S_SheetSliceGroup)
|
|
{
|
|
String name;
|
|
u64 per_frame_count;
|
|
|
|
/* 2d array of slices with length (num frames) * (num slices per frame).
|
|
* Index with [(frame index * num slices per frame) + slice index in frame] */
|
|
S_Slice *frame_slices;
|
|
};
|
|
|
|
Struct(S_Sheet)
|
|
{
|
|
b32 loaded;
|
|
b32 valid;
|
|
Vec2 image_size;
|
|
Vec2 frame_size;
|
|
|
|
u32 frames_count;
|
|
S_Frame *frames;
|
|
|
|
u32 spans_count;
|
|
S_Span *spans;
|
|
Dict *spans_dict;
|
|
|
|
u32 slice_groups_count;
|
|
S_SheetSliceGroup *slice_groups;
|
|
Dict *slice_groups_dict;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Cache types
|
|
|
|
typedef i32 S_CacheEntryKind; enum
|
|
{
|
|
S_CacheEntryKind_Texture,
|
|
S_CacheEntryKind_Sheet,
|
|
|
|
S_CacheEntryKind_Count
|
|
};
|
|
|
|
typedef i32 S_CacheEntryState; enum
|
|
{
|
|
S_CacheEntryState_None,
|
|
S_CacheEntryState_Queued,
|
|
S_CacheEntryState_Working,
|
|
S_CacheEntryState_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)
|
|
{
|
|
Mutex mutex;
|
|
S_CacheEntry *first;
|
|
S_CacheEntry *last;
|
|
};
|
|
|
|
Struct(S_Cache)
|
|
{
|
|
Atomic64Padded memory_usage;
|
|
Arena *arena;
|
|
S_CacheEntryBin *bins;
|
|
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_CacheEntryRef)
|
|
{
|
|
S_CacheEntry *e;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Scope
|
|
|
|
/* A cache reference whose lifetime is bound to the scope it was retrieved from */
|
|
Struct(S_ScopeCacheEntryRef)
|
|
{
|
|
S_CacheEntryRef ref;
|
|
S_ScopeCacheEntryRef *next_in_bin;
|
|
};
|
|
|
|
Struct(S_Scope)
|
|
{
|
|
S_ScopeCacheEntryRef **ref_node_bins;
|
|
S_ScopeCacheEntryRef *ref_node_pool;
|
|
u64 num_references;
|
|
S_Scope *next_free;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Evictor
|
|
|
|
Struct(S_EvictorNode)
|
|
{
|
|
i32 last_ref_cycle;
|
|
S_CacheEntry *cache_entry;
|
|
S_CacheEntryBin *cache_bin;
|
|
S_EvictorNode *next_evicted;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Cache constants
|
|
|
|
/* 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_CacheBinsCount 1024
|
|
#define S_MaxScopeReferences 1024
|
|
#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)
|
|
|
|
/* 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);
|
|
|
|
////////////////////////////////
|
|
//~ 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;
|
|
|
|
/* Scopes */
|
|
Mutex scopes_mutex;
|
|
Arena *scopes_arena;
|
|
S_Scope *first_free_scope;
|
|
|
|
/* Evictor */
|
|
Atomic32Padded evictor_cycle;
|
|
Counter shutdown_counter;
|
|
b32 evictor_scheduler_shutdown;
|
|
Mutex evictor_scheduler_mutex;
|
|
Cv evictor_scheduler_shutdown_cv;
|
|
};
|
|
|
|
extern S_SharedState S_shared_state;
|
|
|
|
////////////////////////////////
|
|
//~ Startup
|
|
|
|
Struct(S_StartupReceipt) { i32 _; };
|
|
S_StartupReceipt S_Startup(void);
|
|
|
|
////////////////////////////////
|
|
//~ Shutdown
|
|
|
|
P_ExitFuncDef(S_Shutdown);
|
|
|
|
////////////////////////////////
|
|
//~ Purple-black image
|
|
|
|
u32 *S_GeneratePurpleBlackImage(Arena *arena, u32 width, u32 height);
|
|
|
|
////////////////////////////////
|
|
//~ Tag operations
|
|
|
|
S_Tag S_TagFromPath(String path);
|
|
b32 S_IsTagNil(S_Tag tag);
|
|
b32 S_EqTag(S_Tag t1, S_Tag t2);
|
|
S_Hash S_CacheEntryFromTagHash(u64 tag_hash, S_CacheEntryKind kind);
|
|
|
|
////////////////////////////////
|
|
//~ Sheet init
|
|
|
|
S_Sheet S_SheetFromAseResult(Arena *arena, ASE_DecodedSheet ase);
|
|
|
|
////////////////////////////////
|
|
//~ Load job
|
|
|
|
void S_PushLoadJob(S_CacheEntryRef ref, S_Tag tag);
|
|
JobDecl(S_LoadSpriteJob, { S_Scope *scope; S_CacheEntryRef ref; S_Tag tag; });
|
|
|
|
////////////////////////////////
|
|
//~ Cache load operations
|
|
|
|
void S_LoadCacheEntryTexture(S_CacheEntryRef ref, S_Tag tag);
|
|
void S_LoadCacheEntrySheet(S_CacheEntryRef ref, S_Tag tag);
|
|
|
|
////////////////////////////////
|
|
//~ Ref operations
|
|
|
|
void S_AddRef(S_CacheEntry *e, i32 amount);
|
|
S_ScopeCacheEntryRef *S_EnsureRefUnsafely(S_Scope *scope, S_CacheEntry *e);
|
|
S_ScopeCacheEntryRef *S_EnsureRefFromEntryLocked(S_Scope *scope, S_CacheEntry *e, Lock *bin_lock);
|
|
S_ScopeCacheEntryRef *S_EnsureRefFromRef(S_Scope *scope, S_CacheEntryRef ref);
|
|
|
|
////////////////////////////////
|
|
//~ Scope operations
|
|
|
|
S_Scope *S_BeginScope(void);
|
|
void S_EndScope(S_Scope *scope);
|
|
|
|
////////////////////////////////
|
|
//~ Cache lookup operations
|
|
|
|
S_ScopeCacheEntryRef *S_EntryFromHashLocked(S_Scope *scope, S_Hash hash, Lock *bin_lock);
|
|
S_ScopeCacheEntryRef *S_EntryFromTag(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind, b32 force_new);
|
|
void *S_DataFromTag(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind, b32 await);
|
|
|
|
////////////////////////////////
|
|
//~ Texture retrieval operations
|
|
|
|
S_Texture *S_TextureFromTagAwait(S_Scope *scope, S_Tag tag);
|
|
S_Texture *S_TextureFromTagAsync(S_Scope *scope, S_Tag tag);
|
|
void S_PrefetchTextureFromTag(S_Scope *scope, S_Tag tag);
|
|
|
|
////////////////////////////////
|
|
//~ Sheet retrieval operations
|
|
|
|
S_Sheet *S_SheetFromTagAwait(S_Scope *scope, S_Tag tag);
|
|
S_Sheet *S_SheetFromTagAsync(S_Scope *scope, S_Tag tag);
|
|
void S_PrefetchSheetFromTag(S_Scope *scope, S_Tag tag);
|
|
|
|
////////////////////////////////
|
|
//~ Sheet data operations
|
|
|
|
S_Frame S_FrameFromIndex(S_Sheet *sheet, u32 index);
|
|
S_Span S_SpanFromName(S_Sheet *sheet, String name);
|
|
S_Slice S_SliceFromNameIndex(S_Sheet *sheet, String name, u32 frame_index);
|
|
S_SliceArray S_SlicesFromNameIndex(S_Sheet *sheet, String name, u32 frame_index);
|
|
|
|
////////////////////////////////
|
|
//~ Resource reload
|
|
|
|
#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);
|
|
JobDecl(S_EvictorJob, EmptySig);
|