power_play/src/asset_cache/asset_cache.c

206 lines
4.6 KiB
C

/* TODO: Remove this entire layer */
AC_SharedState AC_shared_state = ZI;
////////////////////////////////////////////////////////////
//~ Startup
void AC_Startup(void)
{
__prof;
AC_SharedState *g = &AC_shared_state;
g->store_arena = AcquireArena(Gibi(64));
}
////////////////////////////////////////////////////////////
//~ Hash
u64 AC_HashFromKey(String key)
{
/* TODO: Better hash */
return HashFnv64(Fnv64Basis, key);
}
////////////////////////////////////////////////////////////
//~ Cache
void AC_RefreshDebugTable(void)
{
#if IsRtcEnabled
AC_SharedState *g = &AC_shared_state;
Lock lock = LockE(&g->dbg_table_mutex);
ZeroArray(g->dbg_table);
g->dbg_table_count = 0;
for (u64 i = 0; i < countof(g->lookup); ++i)
{
AC_Asset *asset = &g->lookup[i];
if (asset->hash != 0)
{
g->dbg_table[g->dbg_table_count++] = asset;
}
}
Unlock(&lock);
#endif
}
/* Returns first matching slot or first empty slot if not found.
* Check returned slot->hash != 0 for presence. */
AC_Asset *AC_GetAssetCacheSlotLocked(Lock *lock, String key, u64 hash)
{
AC_SharedState *g = &AC_shared_state;
AssertLockedES(lock, &g->lookup_mutex);
LAX lock;
u64 index = hash % countof(g->lookup);
for (;;)
{
AC_Asset *slot = &g->lookup[index];
if (slot->hash)
{
/* Occupied */
if (hash == slot->hash && MatchString(key, slot->key))
{
/* Matched slot */
return slot;
}
else
{
++index;
if (index >= countof(g->lookup))
{
index = 0;
}
}
}
else
{
/* Empty slot */
return slot;
}
}
}
/* `key` text is copied by this function
*
* Returns existing asset entry or inserts a new one.
*
* If is_first_touch (out parameter) is set to 1, then the caller has
* inserted the asset into the cache.
*
* */
AC_Asset *AC_TouchCache(String key, u64 hash, b32 *is_first_touch)
{
AC_Asset *asset = 0;
AC_SharedState *g = &AC_shared_state;
/* Lookup */
{
Lock lock = LockS(&g->lookup_mutex);
asset = AC_GetAssetCacheSlotLocked(&lock, key, hash);
Unlock(&lock);
}
/* Insert if not found */
if (asset->hash)
{
if (is_first_touch)
{
*is_first_touch = 0;
}
}
else
{
Lock lock = LockE(&g->lookup_mutex);
/* Re-check asset presence in case it was inserted since lock */
asset = AC_GetAssetCacheSlotLocked(&lock, key, hash);
if (!asset->hash)
{
if (g->num_assets >= AC_MaxAssets)
{
Panic(Lit("Max assets reached"));
}
String key_stored = ZI;
{
/* Copy key to store */
AC_Store store = AC_OpenStore();
key_stored = PushString(store.arena, key);
AC_CloseStore(&store);
}
/* Initialize asset data */
LogInfoF("Inserting asset cache entry for \"%F\"", FmtString(key));
*asset = (AC_Asset) {
.status = ASSET_STATUS_UNINITIALIZED,
.hash = hash,
.key = key_stored
};
if (is_first_touch)
{
*is_first_touch = 1;
}
++g->num_assets;
AC_RefreshDebugTable();
}
Unlock(&lock);
}
return asset;
}
////////////////////////////////////////////////////////////
//~ Status
/* Call this once asset job has been created */
void AC_MarkLoading(AC_Asset *asset)
{
asset->status = ASSET_STATUS_LOADING;
}
/* Call this once asset job has finished */
void AC_MarkReady(AC_Asset *asset, void *store_data)
{
asset->store_data = store_data;
asset->status = ASSET_STATUS_READY;
SetFence(&asset->ready_fence, 1);
}
void AC_YieldOnAssetReady(AC_Asset *asset)
{
YieldOnFence(&asset->ready_fence, 1);
}
////////////////////////////////////////////////////////////
//~ Store
void *AC_DataFromStore(AC_Asset *asset)
{
if (asset->status == ASSET_STATUS_READY)
{
return asset->store_data;
}
else
{
return 0;
}
}
/* Asset store should be opened to allocate memory to the store arena */
AC_Store AC_OpenStore(void)
{
AC_SharedState *g = &AC_shared_state;
Lock lock = LockE(&g->store_mutex);
AC_Store store = {
.lock = lock,
.arena = g->store_arena
};
return store;
}
void AC_CloseStore(AC_Store *store)
{
Unlock(&store->lock);
}