/* 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 RtcIsEnabled 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); }