From 0bcde064af519c7134e477d0251358ed6cd81b3d Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Fri, 29 May 2026 14:27:40 -0500 Subject: [PATCH] Asset refactor, phase one. --- src/dusk/asset/CMakeLists.txt | 2 - src/dusk/asset/asset.c | 135 ++++++++++++++++-- src/dusk/asset/asset.h | 49 +++++-- src/dusk/asset/assetbatch.c | 81 ----------- src/dusk/asset/assetbatch.h | 90 ------------ src/dusk/asset/assetcache.c | 81 ----------- src/dusk/asset/assetcache.h | 81 ----------- src/dusk/asset/assetfile.h | 1 + src/dusk/asset/assetloader.h | 48 +++++++ src/dusk/asset/loader/CMakeLists.txt | 6 + src/dusk/asset/loader/assetentry.c | 85 +++++++++++ src/dusk/asset/loader/assetentry.h | 94 ++++++++++++ src/dusk/asset/loader/assetloader.h | 36 +++++ src/dusk/asset/loader/assetloading.c | 30 ++++ src/dusk/asset/loader/assetloading.h | 29 ++++ .../asset/loader/display/assetmeshloader.c | 52 +++---- .../asset/loader/display/assetmeshloader.h | 20 +-- .../asset/loader/display/assettextureloader.c | 63 ++++---- .../asset/loader/display/assettextureloader.h | 50 +++---- .../asset/loader/display/assettilesetloader.c | 54 ++++--- .../asset/loader/display/assettilesetloader.h | 48 +++---- src/dusk/asset/loader/json/assetjsonloader.c | 12 +- src/dusk/asset/loader/json/assetjsonloader.h | 16 +-- .../asset/loader/locale/assetlocaleloader.h | 2 +- src/dusk/display/text/text.c | 33 +++-- src/dusk/engine/engine.c | 1 + src/dusk/util/CMakeLists.txt | 1 + src/dusk/util/ref.c | 52 +++++++ src/dusk/util/ref.h | 54 +++++++ 29 files changed, 751 insertions(+), 555 deletions(-) delete mode 100644 src/dusk/asset/assetbatch.c delete mode 100644 src/dusk/asset/assetbatch.h delete mode 100644 src/dusk/asset/assetcache.c delete mode 100644 src/dusk/asset/assetcache.h create mode 100644 src/dusk/asset/assetloader.h create mode 100644 src/dusk/asset/loader/assetentry.c create mode 100644 src/dusk/asset/loader/assetentry.h create mode 100644 src/dusk/asset/loader/assetloader.h create mode 100644 src/dusk/asset/loader/assetloading.c create mode 100644 src/dusk/asset/loader/assetloading.h create mode 100644 src/dusk/util/ref.c create mode 100644 src/dusk/util/ref.h diff --git a/src/dusk/asset/CMakeLists.txt b/src/dusk/asset/CMakeLists.txt index 6dff1022..39e32cab 100644 --- a/src/dusk/asset/CMakeLists.txt +++ b/src/dusk/asset/CMakeLists.txt @@ -8,8 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC asset.c assetfile.c - assetcache.c - assetbatch.c ) # Subdirs diff --git a/src/dusk/asset/asset.c b/src/dusk/asset/asset.c index 729d95d6..db34b431 100644 --- a/src/dusk/asset/asset.c +++ b/src/dusk/asset/asset.c @@ -21,7 +21,6 @@ errorret_t assetInit(void) { errorChain(assetInitPlatform()); assertNotNull(ASSET.zip, "Asset zip null without error."); - assetCacheInit(&ASSET.cache); errorOk(); } @@ -33,26 +32,132 @@ bool_t assetFileExists(const char_t *filename) { return true; } -errorret_t assetLoad( - const char_t *filename, - assetfileloader_t loader, - void *params, - void *output +assetentry_t * assetGetEntry( + const char_t *name, + const assetloadertype_t type, + assetloaderinput_t *input ) { - assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long."); - assertNotNull(output, "Output pointer cannot be NULL."); - assertNotNull(loader, "Asset file loader cannot be NULL."); + // Is there an existing asset? + assetentry_t *entry = ASSET.entries; + do { + if(entry->type == ASSET_LOADER_TYPE_NULL) { + entry++; + continue; + } + if(stringEquals(entry->name, name)) { + assertTrue(entry->type == type, "Asset entry type mismatch."); + return entry; + } + entry++; + } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); + + // We did not find one existing, Find first available slot. + entry = ASSET.entries; + do { + if(entry->type != ASSET_LOADER_TYPE_NULL) { + entry++; + continue; + } - assetfile_t file; - errorChain(assetFileInit(&file, filename, params, output)); - errorChain(loader(&file)); - errorChain(assetFileDispose(&file)); + if(entry->state == ASSET_ENTRY_STATE_NOT_STARTED) { + assetEntryInit(entry, name, type, input); + return entry; + } + entry++; + } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); + + assertUnreachable("No available asset entry slots."); + return NULL; +} + +errorret_t assetRequireLoaded(assetentry_t *entry) { + assertNotNull(entry, "Entry cannot be NULL."); + assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); + + // Already loaded? + if(entry->state == ASSET_ENTRY_STATE_LOADED) { + errorOk(); + } + + // Not loaded, just spin the wheel + while(entry->state != ASSET_ENTRY_STATE_LOADED) { + errorChain(assetUpdate()); + } + + errorOk(); +} + +errorret_t assetUpdate(void) { + // Is there any pending entries? + assetentry_t *entry = ASSET.entries; + assetloading_t *loading; + do { + // Is this asset "ready to start loading" ? + if(entry->type == ASSET_LOADER_TYPE_NULL) { + entry++; + continue; + } + + if(entry->state != ASSET_ENTRY_STATE_NOT_STARTED) { + entry++; + continue; + } + + // Yes, this is ready to load, but we need to see if we have a loading slot + loading = ASSET.loading; + bool_t found = false; + do { + if(loading->type != ASSET_LOADER_TYPE_NULL) { + loading++; + continue; + } + + found = true; + break; + } while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX); + + if(!found) { + // No loading slot, try again next frame. + entry++; + continue; + } + + // Start loading this asset. + assetEntryStartLoading(entry, loading); + entry++; + } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); + + // At this point we have to see the state of all the loading assets. + loading = ASSET.loading; + do { + if(loading->entry == NULL) { + loading++; + continue; + } + + switch(loading->entry->state) { + case ASSET_ENTRY_STATE_PENDING_SYNC: + // Begin sync loading + loading->entry->state = ASSET_ENTRY_STATE_LOADING_SYNC; + errorret_t ret = ASSET_LOADING_CALLBACKS[loading->type].loadSync(loading); + if(ret.code != ERROR_OK) { + loading->entry->state = ASSET_ENTRY_STATE_ERROR; + return ret; + } + + loading->entry->state = ASSET_ENTRY_STATE_LOADED; + loading++; + break; + + default: + loading++; + continue; + } + } while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX); errorOk(); } errorret_t assetDispose(void) { - assetCacheDispose(&ASSET.cache); - if(ASSET.zip != NULL) { if(zip_close(ASSET.zip) != 0) { errorThrow("Failed to close asset zip archive."); diff --git a/src/dusk/asset/asset.h b/src/dusk/asset/asset.h index cc880abe..a7bd7931 100644 --- a/src/dusk/asset/asset.h +++ b/src/dusk/asset/asset.h @@ -9,7 +9,9 @@ #include "error/error.h" #include "asset/assetplatform.h" #include "assetfile.h" -#include "assetcache.h" + +#include "asset/loader/assetentry.h" +#include "asset/loader/assetloading.h" #ifndef assetInitPlatform #error "Platform must define assetInitPlatform function." @@ -21,16 +23,24 @@ #define ASSET_FILE_NAME "dusk.dsk" #define ASSET_HEADER_SIZE 3 +#define ASSET_LOADING_COUNT_MAX 4 +#define ASSET_ENTRY_COUNT_MAX 128 + typedef struct asset_s { zip_t *zip; assetplatform_t platform; - assetcache_t cache; + + // Assets that are mid loading. + assetloading_t loading[ASSET_LOADING_COUNT_MAX]; + assetentry_t entries[ASSET_ENTRY_COUNT_MAX]; } asset_t; extern asset_t ASSET; /** * Initializes the asset system. + * + * @return An error code if the asset system could not be initialized properly. */ errorret_t assetInit(void); @@ -43,21 +53,34 @@ errorret_t assetInit(void); bool_t assetFileExists(const char_t *filename); /** - * Loads an asset by its filename, + * Gets, or creates, a new asset entry. You will need to lock the asset soon + * after creating or else it will be freed up on the next update cycle. * - * @param filename The filename of the asset to retrieve. - * @param loader Loader to use for loading the asset file. - * @param params Parameters to pass to the loader. - * @param output The output pointer to store the loaded asset data. - * @return An error code if the asset could not be loaded. + * @param name Filename of the asset. + * @param type Type of the asset. */ -errorret_t assetLoad( - const char_t *filename, - assetfileloader_t loader, - void *params, - void *output +assetentry_t * assetGetEntry( + const char_t *name, + const assetloadertype_t type, + assetloaderinput_t *input ); +/** + * Requires an asset entry to be loaded. This will block until the asset entry + * is fully loaded. + * + * @param entry The asset entry to require. + * @return An error code if the asset entry could not be loaded properly. + */ +errorret_t assetRequireLoaded(assetentry_t *entry); + +/** + * Updates the asset system. + * + * @return An error code if the asset system could not be updated properly. + */ +errorret_t assetUpdate(void); + /** * Disposes/cleans up the asset system. * diff --git a/src/dusk/asset/assetbatch.c b/src/dusk/asset/assetbatch.c deleted file mode 100644 index bf22601e..00000000 --- a/src/dusk/asset/assetbatch.c +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetbatch.h" -#include "asset.h" -#include "util/memory.h" -#include "util/string.h" -#include "assert/assert.h" - -void assetBatchInit( - assetbatch_t *batch, - assetbatchcomplete_t onComplete, - assetbatcherror_t onError, - void *context -) { - memoryZero(batch, sizeof(assetbatch_t)); - batch->onComplete = onComplete; - batch->onError = onError; - batch->context = context; -} - -void assetBatchAdd( - assetbatch_t *batch, - const char_t *path, - assetfileloader_t loader, - const void *params, - size_t paramsSize, - size_t dataSize, - assetcachedisposer_t disposer, - void **outPtr -) { - assertTrue(batch->count < ASSET_BATCH_MAX, "Asset batch is full."); - assertTrue(paramsSize <= ASSET_BATCH_PARAMS_SIZE, "Asset batch params too large."); - assertNotNull(outPtr, "Batch output pointer cannot be NULL."); - - assetbatchitem_t *item = &batch->items[batch->count++]; - stringCopy(item->path, path, ASSET_FILE_NAME_MAX); - item->loader = loader; - if (params != NULL && paramsSize > 0) { - memoryCopy(item->params, params, paramsSize); - } - item->dataSize = dataSize; - item->disposer = disposer; - item->outPtr = outPtr; -} - -errorret_t assetBatchLoad(assetbatch_t *batch) { - for (uint8_t i = 0; i < batch->count; i++) { - assetbatchitem_t *item = &batch->items[i]; - - void *cached = assetCacheLookup(&ASSET.cache, item->path); - if (cached != NULL) { - assetCacheRetain(&ASSET.cache, item->path); - *item->outPtr = cached; - continue; - } - - void *data = memoryAllocate(item->dataSize); - errorret_t err = assetLoad(item->path, item->loader, item->params, data); - if (err.code != ERROR_OK) { - memoryFree(data); - if (batch->onError != NULL) { - batch->onError(batch, batch->context, err); - } - return errorChainImpl(err, __FILE__, __func__, __LINE__); - } - - assetCacheInsert(&ASSET.cache, item->path, data, item->disposer); - *item->outPtr = data; - } - - if (batch->onComplete != NULL) { - batch->onComplete(batch, batch->context); - } - - errorOk(); -} diff --git a/src/dusk/asset/assetbatch.h b/src/dusk/asset/assetbatch.h deleted file mode 100644 index 7e6a1a3e..00000000 --- a/src/dusk/asset/assetbatch.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "assetcache.h" -#include "error/error.h" - -#ifndef ASSET_BATCH_MAX - #define ASSET_BATCH_MAX 16 -#endif - -#define ASSET_BATCH_PARAMS_SIZE 16 - -typedef struct assetbatch_s assetbatch_t; - -typedef void (*assetbatchcomplete_t)(assetbatch_t *batch, void *context); -typedef void (*assetbatcherror_t)( - assetbatch_t *batch, - void *context, - errorret_t error -); - -typedef struct { - char_t path[ASSET_FILE_NAME_MAX]; - assetfileloader_t loader; - uint8_t params[ASSET_BATCH_PARAMS_SIZE]; - size_t dataSize; - assetcachedisposer_t disposer; - void **outPtr; -} assetbatchitem_t; - -struct assetbatch_s { - assetbatchitem_t items[ASSET_BATCH_MAX]; - uint8_t count; - assetbatchcomplete_t onComplete; - assetbatcherror_t onError; - void *context; -}; - -/** - * Initializes a batch, optionally with completion and error callbacks. - * - * @param batch The batch to initialize. - * @param onComplete Called when all items have loaded successfully. May be NULL. - * @param onError Called if any item fails to load. May be NULL. - * @param context Passed through to both callbacks. - */ -void assetBatchInit( - assetbatch_t *batch, - assetbatchcomplete_t onComplete, - assetbatcherror_t onError, - void *context -); - -/** - * Adds an item to the batch. Params are copied into inline storage. - * - * @param batch The batch to add to. - * @param path Asset path, used as the cache key. - * @param loader The loader function for this asset type. - * @param params Loader-specific parameters. Copied — safe to pass stack address. - * @param paramsSize Size of params in bytes. Must be <= ASSET_BATCH_PARAMS_SIZE. - * @param dataSize Size in bytes to allocate for the output struct. - * @param disposer Called on the data before freeing when released. May be NULL. - * @param outPtr Caller's pointer variable. Set to the cache-owned data on load. - */ -void assetBatchAdd( - assetbatch_t *batch, - const char_t *path, - assetfileloader_t loader, - const void *params, - size_t paramsSize, - size_t dataSize, - assetcachedisposer_t disposer, - void **outPtr -); - -/** - * Loads all items in the batch, checking the cache for each. Fires onComplete - * on success or onError on the first failure. Also returns the error for - * synchronous callers. - * - * @param batch The batch to execute. - * @return Error if any item failed to load. - */ -errorret_t assetBatchLoad(assetbatch_t *batch); diff --git a/src/dusk/asset/assetcache.c b/src/dusk/asset/assetcache.c deleted file mode 100644 index c11a1a07..00000000 --- a/src/dusk/asset/assetcache.c +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetcache.h" -#include "util/memory.h" -#include "util/string.h" -#include "assert/assert.h" - -void assetCacheInit(assetcache_t *cache) { - memoryZero(cache, sizeof(assetcache_t)); -} - -void *assetCacheLookup(assetcache_t *cache, const char_t *path) { - for(uint8_t i = 0; i < cache->count; i++) { - if(stringCompare(cache->entries[i].path, path) == 0) { - return cache->entries[i].data; - } - } - return NULL; -} - -void assetCacheInsert( - assetcache_t *cache, - const char_t *path, - void *data, - assetcachedisposer_t disposer -) { - assertTrue(cache->count < ASSET_CACHE_MAX, "Asset cache is full."); - - assetcacheentry_t *entry = &cache->entries[cache->count++]; - stringCopy(entry->path, path, ASSET_FILE_NAME_MAX); - entry->data = data; - entry->refcount = 1; - entry->disposer = disposer; -} - -void assetCacheRetain(assetcache_t *cache, const char_t *path) { - for (uint8_t i = 0; i < cache->count; i++) { - if (stringCompare(cache->entries[i].path, path) == 0) { - cache->entries[i].refcount++; - return; - } - } - assertTrue(false, "Asset not found in cache for retain."); -} - -static void assetCacheEntryDispose(assetcacheentry_t *entry) { - if (entry->disposer != NULL) { - entry->disposer(entry->data); - } - memoryFree(entry->data); -} - -void assetCacheRelease(assetcache_t *cache, const char_t *path) { - for (uint8_t i = 0; i < cache->count; i++) { - if (stringCompare(cache->entries[i].path, path) == 0) { - assetcacheentry_t *entry = &cache->entries[i]; - entry->refcount--; - if (entry->refcount == 0) { - assetCacheEntryDispose(entry); - for (uint8_t j = i; j < cache->count - 1; j++) { - cache->entries[j] = cache->entries[j + 1]; - } - cache->count--; - } - return; - } - } - assertTrue(false, "Asset not found in cache for release."); -} - -void assetCacheDispose(assetcache_t *cache) { - for (uint8_t i = 0; i < cache->count; i++) { - assetCacheEntryDispose(&cache->entries[i]); - } - memoryZero(cache, sizeof(assetcache_t)); -} diff --git a/src/dusk/asset/assetcache.h b/src/dusk/asset/assetcache.h deleted file mode 100644 index 4ab13657..00000000 --- a/src/dusk/asset/assetcache.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "assetfile.h" - -#ifndef ASSET_CACHE_MAX - #define ASSET_CACHE_MAX 64 -#endif - -typedef void (*assetcachedisposer_t)(void *data); - -typedef struct { - char_t path[ASSET_FILE_NAME_MAX]; - void *data; - uint32_t refcount; - assetcachedisposer_t disposer; -} assetcacheentry_t; - -typedef struct { - assetcacheentry_t entries[ASSET_CACHE_MAX]; - uint8_t count; -} assetcache_t; - -/** - * Initializes the asset cache. - * - * @param cache The cache to initialize. - */ -void assetCacheInit(assetcache_t *cache); - -/** - * Looks up a cached asset by path. - * - * @param cache The cache to search. - * @param path The asset path to look up. - * @return Pointer to the cached data, or NULL if not found. - */ -void *assetCacheLookup(assetcache_t *cache, const char_t *path); - -/** - * Inserts a newly loaded asset into the cache with refcount 1. - * - * @param cache The cache to insert into. - * @param path The asset path key. - * @param data Heap-allocated asset data. The cache takes ownership. - * @param disposer Called with data before freeing when refcount reaches 0. - */ -void assetCacheInsert( - assetcache_t *cache, - const char_t *path, - void *data, - assetcachedisposer_t disposer -); - -/** - * Increments the refcount for a cached asset. - * - * @param cache The cache containing the asset. - * @param path The asset path. - */ -void assetCacheRetain(assetcache_t *cache, const char_t *path); - -/** - * Decrements the refcount. Disposes and frees the asset when it reaches 0. - * - * @param cache The cache containing the asset. - * @param path The asset path. - */ -void assetCacheRelease(assetcache_t *cache, const char_t *path); - -/** - * Disposes all remaining cache entries and resets the cache. - * - * @param cache The cache to dispose. - */ -void assetCacheDispose(assetcache_t *cache); diff --git a/src/dusk/asset/assetfile.h b/src/dusk/asset/assetfile.h index 5c547971..afeddbe7 100644 --- a/src/dusk/asset/assetfile.h +++ b/src/dusk/asset/assetfile.h @@ -15,6 +15,7 @@ typedef struct assetfile_s assetfile_t; typedef errorret_t (*assetfileloader_t)(assetfile_t *file); +// Describes a file not yet loaded. typedef struct assetfile_s { char_t filename[ASSET_FILE_NAME_MAX]; void *params; diff --git a/src/dusk/asset/assetloader.h b/src/dusk/asset/assetloader.h new file mode 100644 index 00000000..2944fd2b --- /dev/null +++ b/src/dusk/asset/assetloader.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef enum assetloadertype_e { + ASSET_LOADER_TYPE_NULL, + + ASSET_LOADER_TYPE_MESH, + ASSET_LOADER_TYPE_TEXTURE, + ASSET_LOADER_TYPE_SHADER, + + ASSET_LOADER_TYPE_COUNT +} assetloadertype_t; + +typedef union { + void *nothing; +} assetloaderloading_t; + +typedef union { + void *nothing; +} assetloaderoutput_t; + +typedef enum { + ASSET_LOADER_LOADING_STATE_NOT_STARTED, + ASSET_LOADER_LOADING_STATE_PENDING_ASYNC, + ASSET_LOADER_LOADING_STATE_LOADING_ASYNC, + ASSET_LOADER_LOADING_STATE_PENDING_SYNC, + ASSET_LOADER_LOADING_STATE_LOADING_SYNC, + ASSET_LOADER_LOADING_STATE_LOADED, + ASSET_LOADER_LOADING_STATE_ERROR +} assetloaderloadingstate_t; + +typedef struct { + assetloadertype_t type; + assetloaderloadingstate_t state; + assetloaderloading_t loading; +} assetloadingentry_t; + +typedef struct { + assetloadertype_t type; + assetloaderoutput_t output; +} assetentry_t; \ No newline at end of file diff --git a/src/dusk/asset/loader/CMakeLists.txt b/src/dusk/asset/loader/CMakeLists.txt index d81bf24a..54757457 100644 --- a/src/dusk/asset/loader/CMakeLists.txt +++ b/src/dusk/asset/loader/CMakeLists.txt @@ -4,6 +4,12 @@ # https://opensource.org/licenses/MIT # Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + assetentry.c + assetloading.c +) + # Subdirs add_subdirectory(display) diff --git a/src/dusk/asset/loader/assetentry.c b/src/dusk/asset/loader/assetentry.c new file mode 100644 index 00000000..5f55edf8 --- /dev/null +++ b/src/dusk/asset/loader/assetentry.c @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "assetentry.h" +#include "assert/assert.h" +#include "util/memory.h" +#include "util/string.h" + +assetentrycallbacks_t ASSET_ENTRY_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { + [ASSET_LOADER_TYPE_NULL] = { 0 }, + + [ASSET_LOADER_TYPE_TEXTURE] = { + .dispose = &assetTextureDisposeNEW + }, + + [ASSET_LOADER_TYPE_TILESET] = { + .dispose = &assetTilesetDisposeNEW + }, +}; + +void assetEntryInit( + assetentry_t *entry, + const char_t *name, + const assetloadertype_t type, + assetloaderinput_t *input +) { + assertNotNull(entry, "Entry cannot be NULL"); + assertStrLenMin(name, 1, "Name cannot be empty"); + assertStrLenMax(name, ASSET_FILE_NAME_MAX - 1, "Name too long"); + assertTrue(type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); + assertTrue(type < ASSET_LOADER_TYPE_COUNT, "Invalid loader type."); + + memoryZero(entry, sizeof(assetentry_t)); + stringCopy(entry->name, name, ASSET_FILE_NAME_MAX); + entry->type = type; + entry->input = input; + entry->state = ASSET_ENTRY_STATE_NOT_STARTED; + refInit(&entry->refs, entry, NULL, NULL, NULL); +} + +void assetEntryLock(assetentry_t *entry) { + assertNotNull(entry, "Entry cannot be NULL"); + assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); + refLock(&entry->refs); +} + +void assetEntryUnlock(assetentry_t *entry) { + assertNotNull(entry, "Entry cannot be NULL"); + assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); + refUnlock(&entry->refs); +} + +void assetEntryStartLoading( + assetentry_t *entry, + assetloading_t *loading +) { + assertNotNull(entry, "Entry cannot be NULL"); + assertNotNull(loading, "Loading cannot be NULL"); + assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); + assertTrue( + entry->state == ASSET_ENTRY_STATE_NOT_STARTED, + "Can only start loading from NOT_STARTED state." + ); + + entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; + memoryZero(loading, sizeof(assetloading_t)); + loading->type = entry->type; + loading->entry = entry; + // At this point the asset manager will manage this thing's loading +} + +errorret_t assetEntryDispose(assetentry_t *entry) { + assertNotNull(entry, "Entry cannot be NULL"); + + assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); + assertTrue(entry->type < ASSET_LOADER_TYPE_COUNT, "Invalid loader type."); + + errorChain(ASSET_ENTRY_CALLBACKS[entry->type].dispose(entry)); + memoryZero(entry, sizeof(assetentry_t)); + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/asset/loader/assetentry.h b/src/dusk/asset/loader/assetentry.h new file mode 100644 index 00000000..9283a61e --- /dev/null +++ b/src/dusk/asset/loader/assetentry.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "asset/loader/assetloading.h" +#include "util/ref.h" + +typedef enum { + ASSET_ENTRY_STATE_NOT_STARTED, + // ASSET_ENTRY_STATE_PENDING_ASYNC, + // ASSET_ENTRY_STATE_LOADING_ASYNC, + ASSET_ENTRY_STATE_PENDING_SYNC, + ASSET_ENTRY_STATE_LOADING_SYNC, + ASSET_ENTRY_STATE_LOADED, + ASSET_ENTRY_STATE_ERROR +} assetentrystate_t; + +typedef struct assetentry_s { + // Filename and cache key + char_t name[ASSET_FILE_NAME_MAX]; + // What type of asset is this? + assetloadertype_t type; + // Data + assetloaderoutput_t data; + // What state is this asset entry in currently? + assetentrystate_t state; + // What is referencing this asset entry. + ref_t refs; + // Data that will be passed to the loader about how it should load. + assetloaderinput_t *input; +} assetentry_t; + +typedef errorret_t (*assetentrydisposecallback_t)(assetentry_t *entry); + +typedef struct { + assetentrydisposecallback_t dispose; +} assetentrycallbacks_t; + +extern assetentrycallbacks_t ASSET_ENTRY_CALLBACKS[ASSET_LOADER_TYPE_COUNT]; + +/** + * Initializes an asset entry with the given name and type. This does not load + * the asset. + * + * @param entry The asset entry to initialize. + * @param name The name of the asset, used as a key for loading and caching. + * @param type The type of asset this entry represents. + * @param input Data that will be passed to the loader about how it should load. + */ +void assetEntryInit( + assetentry_t *entry, + const char_t *name, + const assetloadertype_t type, + assetloaderinput_t *input +); + +/** + * Locks an asset entry, preventing it from being freed until it is unlocked. + * + * @param entry The asset entry to lock. + */ +void assetEntryLock(assetentry_t *entry); + +/** + * Unlocks an asset entry, allowing it to be freed if there are no more locks. + * + * @param entry The asset entry to unlock. + */ +void assetEntryUnlock(assetentry_t *entry); + +/** + * Starts loading the given asset entry using an assetloading slot. This will + * be called by the asset manager when it deems it's a good time to begin the + * loading of this asset entry. + * + * Currently we return the error but in future this will not be returned. + * + * @param entry The asset entry to start loading. + * @param loading The assetloading slot to use for loading this asset entry. + * @return Any error that occurs during loading. + */ +void assetEntryStartLoading(assetentry_t *entry, assetloading_t *loading); + +/** + * Disposes an asset entry, freeing any resources it holds. + * + * @param entry The asset entry to dispose. + * @return Any error that occurs during disposal. + */ +errorret_t assetEntryDispose(assetentry_t *entry); \ No newline at end of file diff --git a/src/dusk/asset/loader/assetloader.h b/src/dusk/asset/loader/assetloader.h new file mode 100644 index 00000000..9c193714 --- /dev/null +++ b/src/dusk/asset/loader/assetloader.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "asset/loader/display/assetmeshloader.h" +#include "asset/loader/display/assettextureloader.h" +#include "asset/loader/display/assettilesetloader.h" + +typedef enum { + ASSET_LOADER_TYPE_NULL, + + // ASSET_LOADER_TYPE_MESH, + ASSET_LOADER_TYPE_TEXTURE, + ASSET_LOADER_TYPE_TILESET, + + ASSET_LOADER_TYPE_COUNT +} assetloadertype_t; + +typedef union { + assettextureloaderinput_t texture; + assettilesetloaderinput_t tileset; +} assetloaderinput_t; + +typedef union { + assettextureloaderloading_t texture; + assettilesetloaderloading_t tileset; +} assetloaderloading_t; + +typedef union { + assettextureoutput_t texture; + assettilesetoutput_t tileset; +} assetloaderoutput_t; \ No newline at end of file diff --git a/src/dusk/asset/loader/assetloading.c b/src/dusk/asset/loader/assetloading.c new file mode 100644 index 00000000..6fb3ae79 --- /dev/null +++ b/src/dusk/asset/loader/assetloading.c @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "assetloading.h" +#include "asset/loader/display/assetmeshloader.h" + +assetloadingcallbacks_t ASSET_LOADING_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { + [ASSET_LOADER_TYPE_NULL] = { 0 }, + + // [ASSET_LOADER_TYPE_MESH] = { + // .loadSync = assetMeshLoaderNEW, + // }, + + [ASSET_LOADER_TYPE_TEXTURE] = { + .loadSync = assetTextureLoaderNEW + }, + + + [ASSET_LOADER_TYPE_TILESET] = { + .loadSync = assetTilesetLoaderNEW + }, + + // [ASSET_LOADER_TYPE_SHADER] = { + + // } +}; \ No newline at end of file diff --git a/src/dusk/asset/loader/assetloading.h b/src/dusk/asset/loader/assetloading.h new file mode 100644 index 00000000..41ebf883 --- /dev/null +++ b/src/dusk/asset/loader/assetloading.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "assetloader.h" +#include "asset/assetfile.h" + +typedef struct assetentry_s assetentry_t; + +typedef struct assetloading_s { + // What type of asset is being loaded. + assetloadertype_t type; + // Referral back to the asset entry that will be kept alive after load done. + assetentry_t *entry; + // Information used during the load operation only. + assetloaderloading_t loading; +} assetloading_t; + +typedef errorret_t (assetloadingcallback_t)(assetloading_t *loading); + +typedef struct { + assetloadingcallback_t *loadSync; +} assetloadingcallbacks_t; + +extern assetloadingcallbacks_t ASSET_LOADING_CALLBACKS[ASSET_LOADER_TYPE_COUNT]; \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assetmeshloader.c b/src/dusk/asset/loader/display/assetmeshloader.c index b43505dd..3e3f6a70 100644 --- a/src/dusk/asset/loader/display/assetmeshloader.c +++ b/src/dusk/asset/loader/display/assetmeshloader.c @@ -151,32 +151,32 @@ errorret_t assetMeshLoader(assetfile_t *file) { errorOk(); } -errorret_t assetMeshLoadToOutput( - const char_t *path, - assetmeshoutput_t *output -) { - assertNotNull(path, "Path cannot be null"); - assertNotNull(output, "Output cannot be null"); - assertNotNull(output->outMesh, "Output mesh cannot be null"); - assertNotNull(output->outVertices, "Output vertices cannot be null"); +// errorret_t assetMeshLoadToOutput( +// const char_t *path, +// assetmeshoutput_t *output +// ) { +// assertNotNull(path, "Path cannot be null"); +// assertNotNull(output, "Output cannot be null"); +// assertNotNull(output->outMesh, "Output mesh cannot be null"); +// assertNotNull(output->outVertices, "Output vertices cannot be null"); - return assetLoad(path, &assetMeshLoader, NULL, output); -} +// return assetLoad(path, &assetMeshLoader, NULL, output); +// } -errorret_t assetMeshLoad( - const char_t *path, - mesh_t *outMesh, - meshvertex_t **outVertices, - const assetmeshinputaxis_t inputAxis -) { - assertNotNull(path, "Path cannot be null"); - assertNotNull(outMesh, "Output mesh cannot be null"); - assertNotNull(outVertices, "Output vertices cannot be null"); +// errorret_t assetMeshLoad( +// const char_t *path, +// mesh_t *outMesh, +// meshvertex_t **outVertices, +// const assetmeshinputaxis_t inputAxis +// ) { +// assertNotNull(path, "Path cannot be null"); +// assertNotNull(outMesh, "Output mesh cannot be null"); +// assertNotNull(outVertices, "Output vertices cannot be null"); - assetmeshoutput_t output = { - outMesh, - outVertices, - inputAxis - }; - return assetMeshLoadToOutput(path, &output); -} \ No newline at end of file +// assetmeshoutput_t output = { +// outMesh, +// outVertices, +// inputAxis +// }; +// return assetMeshLoadToOutput(path, &output); +// } \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assetmeshloader.h b/src/dusk/asset/loader/display/assetmeshloader.h index 00af5837..09d78a14 100644 --- a/src/dusk/asset/loader/display/assetmeshloader.h +++ b/src/dusk/asset/loader/display/assetmeshloader.h @@ -6,7 +6,7 @@ */ #pragma once -#include "asset/asset.h" +#include "asset/assetfile.h" #include "display/mesh/mesh.h" #include "assert/assert.h" @@ -42,20 +42,4 @@ assertStructSize(assetmeshstltriangle_t, 50); * @param file Asset file to load the mesh from. * @return Any error that occurs during loading. */ -errorret_t assetMeshLoader(assetfile_t *file); - -/** - * Handler for mesh assets. - * - * @param file Asset file to load the mesh from. - * @param outMesh Output mesh to load the data into. - * @param outVertices Output pointer to the vertex data, used for cleanup. - * @param inputAxis The axis orientation of the input mesh data. - * @return Any error that occurs during loading. - */ -errorret_t assetMeshLoad( - const char_t *path, - mesh_t *outMesh, - meshvertex_t **outVertices, - const assetmeshinputaxis_t inputAxis -); \ No newline at end of file +errorret_t assetMeshLoader(assetfile_t *file); \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assettextureloader.c b/src/dusk/asset/loader/display/assettextureloader.c index 17275962..ca806ecb 100644 --- a/src/dusk/asset/loader/display/assettextureloader.c +++ b/src/dusk/asset/loader/display/assettextureloader.c @@ -6,13 +6,41 @@ */ #include "assettextureloader.h" -#include "asset/assetbatch.h" #include "assert/assert.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include "log/log.h" #include "util/endian.h" + +#include "asset/loader/assetloading.h" +#include "asset/loader/assetentry.h" + +errorret_t assetTextureLoaderNEW(assetloading_t *loading) { + assertNotNull(loading, "Loading cannot be NULL"); + + assettextureloaderparams_t params; + params.format = loading->entry->input->texture; + + assetfile_t *file = &loading->loading.texture.file; + errorChain(assetFileInit( + file, + loading->entry->name, + ¶ms, + &loading->entry->data.texture + )); + errorChain(assetTextureLoader(file)); + assetFileDispose(file); + errorOk(); +} + +errorret_t assetTextureDisposeNEW(assetentry_t *entry) { + assertNotNull(entry, "Asset entry cannot be NULL"); + return textureDispose(&entry->data.texture); +} + + + stbi_io_callbacks ASSET_TEXTURE_STB_CALLBACKS = { .read = assetTextureReader, .skip = assetTextureSkipper, @@ -102,37 +130,4 @@ errorret_t assetTextureLoader(assetfile_t *file) { stbi_image_free(data); errorOk(); -} - -errorret_t assetTextureLoad( - const char_t *path, - texture_t *out, - const textureformat_t format -) { - assettextureloaderparams_t params = { - .format = format - }; - return assetLoad(path, assetTextureLoader, ¶ms, out); -} - -static void assetTextureCacheDispose(void *data) { - errorCatch(textureDispose((texture_t*)data)); -} - -void assetBatchTexture( - assetbatch_t *batch, - const char_t *path, - textureformat_t format, - texture_t **out -) { - assettextureloaderparams_t params = { .format = format }; - assetBatchAdd( - batch, - path, - assetTextureLoader, - ¶ms, sizeof(params), - sizeof(texture_t), - assetTextureCacheDispose, - (void**)out - ); } \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assettextureloader.h b/src/dusk/asset/loader/display/assettextureloader.h index df1d78a8..4905b83d 100644 --- a/src/dusk/asset/loader/display/assettextureloader.h +++ b/src/dusk/asset/loader/display/assettextureloader.h @@ -6,14 +6,28 @@ */ #pragma once -#include "asset/asset.h" -#include "asset/assetbatch.h" +#include "asset/assetfile.h" #include "display/texture/texture.h" typedef struct { textureformat_t format; } assettextureloaderparams_t; +// NEW STUFF +typedef struct assetloading_s assetloading_t; +typedef struct assetentry_s assetentry_t; + +typedef textureformat_t assettextureloaderinput_t; +typedef struct { + assetfile_t file; +} assettextureloaderloading_t; +typedef texture_t assettextureoutput_t; + +errorret_t assetTextureLoaderNEW(assetloading_t *loading); +errorret_t assetTextureDisposeNEW(assetentry_t *entry); + +// END NEW STUFF + /** * STB image read callback for asset files. * @@ -34,34 +48,4 @@ int assetTextureEOF(void *user); * @param file Asset file to load the texture from. * @return Any error that occurs during loading. */ -errorret_t assetTextureLoader(assetfile_t *file); - -/** - * Loads a texture from the specified path. - * - * @param path Path to the texture asset. - * @param out Output texture to load into. - * @param format Format of the texture to load. - * @return Any error that occurs during loading. - */ -errorret_t assetTextureLoad( - const char_t *path, - texture_t *out, - const textureformat_t format -); - -/** - * Adds a texture load request to a batch. The cache-owned texture_t* is - * written to *out when the batch completes. - * - * @param batch The batch to add to. - * @param path Path to the texture asset. - * @param format Format of the texture to load. - * @param out Receives a pointer to the loaded texture on completion. - */ -void assetBatchTexture( - assetbatch_t *batch, - const char_t *path, - textureformat_t format, - texture_t **out -); \ No newline at end of file +errorret_t assetTextureLoader(assetfile_t *file); \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assettilesetloader.c b/src/dusk/asset/loader/display/assettilesetloader.c index 88066b34..7c164bef 100644 --- a/src/dusk/asset/loader/display/assettilesetloader.c +++ b/src/dusk/asset/loader/display/assettilesetloader.c @@ -6,11 +6,35 @@ */ #include "assettilesetloader.h" -#include "asset/assetbatch.h" #include "assert/assert.h" #include "util/memory.h" #include "util/endian.h" +#include "asset/loader/assetloading.h" +#include "asset/loader/assetentry.h" + +errorret_t assetTilesetLoaderNEW(assetloading_t *loading) { + assertNotNull(loading, "Loading cannot be NULL"); + assertTrue(loading->type == ASSET_LOADER_TYPE_TILESET, "Invalid type."); + + assetfile_t *file = &loading->loading.tileset.file; + tileset_t *out = &loading->entry->data.tileset; + assertNotNull(file, "File cannot be NULL"); + assertNotNull(out, "Output cannot be NULL"); + + errorChain(assetFileInit(file, loading->entry->name, NULL, out)); + errorChain(assetTilesetLoader(file)); + errorChain(assetFileDispose(file)); + + errorOk(); +} + +errorret_t assetTilesetDisposeNEW(assetentry_t *entry) { + assertNotNull(entry, "Entry cannot be NULL"); + assertTrue(entry->type == ASSET_LOADER_TYPE_TILESET, "Invalid type."); + errorOk(); +} + errorret_t assetTilesetLoader(assetfile_t *file) { assertNotNull(file, "Asset file pointer for tileset loader is null."); @@ -73,25 +97,9 @@ errorret_t assetTilesetLoader(assetfile_t *file) { errorOk(); } -errorret_t assetTilesetLoad( - const char_t *path, - tileset_t *out -) { - return assetLoad(path, assetTilesetLoader, NULL, out); -} - -void assetBatchTileset( - assetbatch_t *batch, - const char_t *path, - tileset_t **out -) { - assetBatchAdd( - batch, - path, - assetTilesetLoader, - NULL, 0, - sizeof(tileset_t), - NULL, - (void**)out - ); -} \ No newline at end of file +// errorret_t assetTilesetLoad( +// const char_t *path, +// tileset_t *out +// ) { +// return assetLoad(path, assetTilesetLoader, NULL, out); +// } \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assettilesetloader.h b/src/dusk/asset/loader/display/assettilesetloader.h index 938dae33..1624b974 100644 --- a/src/dusk/asset/loader/display/assettilesetloader.h +++ b/src/dusk/asset/loader/display/assettilesetloader.h @@ -6,40 +6,30 @@ */ #pragma once -#include "asset/asset.h" -#include "asset/assetbatch.h" +#include "asset/assetfile.h" #include "display/texture/tileset.h" +typedef struct { + void *nothing; +} assettilesetloaderinput_t; + +// NEW STUFF +typedef struct assetloading_s assetloading_t; +typedef struct assetentry_s assetentry_t; + +typedef struct { + assetfile_t file; +} assettilesetloaderloading_t; + +typedef tileset_t assettilesetoutput_t; + +errorret_t assetTilesetLoaderNEW(assetloading_t *loading); +errorret_t assetTilesetDisposeNEW(assetentry_t *entry); + /** * Handler for tileset assets. * * @param file Asset file to load the tileset from. * @return Any error that occurs during loading. */ -errorret_t assetTilesetLoader(assetfile_t *file); - -/** - * Loads a tileset from the specified path. - * - * @param path Path to the tileset asset. - * @param out Output tileset to load into. - * @return Any error that occurs during loading. - */ -errorret_t assetTilesetLoad( - const char_t *path, - tileset_t *out -); - -/** - * Adds a tileset load request to a batch. The cache-owned tileset_t* is - * written to *out when the batch completes. - * - * @param batch The batch to add to. - * @param path Path to the tileset asset. - * @param out Receives a pointer to the loaded tileset on completion. - */ -void assetBatchTileset( - assetbatch_t *batch, - const char_t *path, - tileset_t **out -); \ No newline at end of file +errorret_t assetTilesetLoader(assetfile_t *file); \ No newline at end of file diff --git a/src/dusk/asset/loader/json/assetjsonloader.c b/src/dusk/asset/loader/json/assetjsonloader.c index 0b88da66..be157f81 100644 --- a/src/dusk/asset/loader/json/assetjsonloader.c +++ b/src/dusk/asset/loader/json/assetjsonloader.c @@ -45,9 +45,9 @@ errorret_t assetJsonLoader(assetfile_t *file) { return assetJsonLoadFileToDoc(file, outDoc); } -errorret_t assetJsonLoad( - const char_t *path, - yyjson_doc **outDoc -) { - return assetLoad(path, assetJsonLoader, NULL, outDoc); -} \ No newline at end of file +// errorret_t assetJsonLoad( +// const char_t *path, +// yyjson_doc **outDoc +// ) { +// return assetLoad(path, assetJsonLoader, NULL, outDoc); +// } \ No newline at end of file diff --git a/src/dusk/asset/loader/json/assetjsonloader.h b/src/dusk/asset/loader/json/assetjsonloader.h index ed494149..6d651771 100644 --- a/src/dusk/asset/loader/json/assetjsonloader.h +++ b/src/dusk/asset/loader/json/assetjsonloader.h @@ -6,7 +6,7 @@ */ #pragma once -#include "asset/asset.h" +#include "asset/assetfile.h" #include "yyjson.h" #define ASSET_JSON_FILE_SIZE_MAX 1024*256 @@ -30,16 +30,4 @@ errorret_t assetJsonLoadFileToDoc(assetfile_t *file, yyjson_doc **outDoc); * @param file Asset file to load the locale from. * @return Any error that occurs during loading. */ -errorret_t assetJsonLoader(assetfile_t *file); - -/** - * Loads a locale from the specified path. - * - * @param path Path to the locale asset. - * @param outDoc Pointer to store the loaded JSON document. - * @return Any error that occurs during loading. - */ -errorret_t assetJsonLoad( - const char_t *path, - yyjson_doc **outDoc -); \ No newline at end of file +errorret_t assetJsonLoader(assetfile_t *file); \ No newline at end of file diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.h b/src/dusk/asset/loader/locale/assetlocaleloader.h index 26f27d08..a261448e 100644 --- a/src/dusk/asset/loader/locale/assetlocaleloader.h +++ b/src/dusk/asset/loader/locale/assetlocaleloader.h @@ -6,7 +6,7 @@ */ #pragma once -#include "asset/asset.h" +#include "asset/assetfile.h" #define ASSET_LOCALE_FILE_PLURAL_FORM_COUNT 6 diff --git a/src/dusk/display/text/text.c b/src/dusk/display/text/text.c index f935b327..0cc31eaf 100644 --- a/src/dusk/display/text/text.c +++ b/src/dusk/display/text/text.c @@ -10,7 +10,6 @@ #include "util/memory.h" #include "display/spritebatch/spritebatch.h" #include "asset/asset.h" -#include "asset/assetbatch.h" #include "asset/loader/display/assettextureloader.h" #include "asset/loader/display/assettilesetloader.h" #include "display/shader/shaderunlit.h" @@ -18,17 +17,35 @@ font_t FONT_DEFAULT; errorret_t textInit(void) { - assetbatch_t batch; - assetBatchInit(&batch, NULL, NULL, NULL); - assetBatchTexture(&batch, "ui/minogram.png", TEXTURE_FORMAT_RGBA, &FONT_DEFAULT.texture); - assetBatchTileset(&batch, "ui/minogram.dtf", &FONT_DEFAULT.tileset); - errorChain(assetBatchLoad(&batch)); + assetloaderinput_t input = { + .texture = TEXTURE_FORMAT_RGBA + }; + assetentry_t *entryTexture = assetGetEntry( + "ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input + ); + assetentry_t *entryTileset = assetGetEntry( + "ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET, NULL + ); + errorChain(assetRequireLoaded(entryTexture)); + errorChain(assetRequireLoaded(entryTileset)); + FONT_DEFAULT.texture = &entryTexture->data.texture; + FONT_DEFAULT.tileset = &entryTileset->data.tileset; + + // assetentry_t *entryTileset = assetGetEntry( + // "ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET + // ); + + // assetbatch_t batch; + // assetBatchInit(&batch, NULL, NULL, NULL); + // assetBatchTexture(&batch, "ui/minogram.png", TEXTURE_FORMAT_RGBA, &FONT_DEFAULT.texture); + // assetBatchTileset(&batch, "ui/minogram.dtf", &FONT_DEFAULT.tileset); + // errorChain(assetBatchLoad(&batch)); errorOk(); } errorret_t textDispose(void) { - assetCacheRelease(&ASSET.cache, "ui/minogram.png"); - assetCacheRelease(&ASSET.cache, "ui/minogram.dtf"); + // assetCacheRelease(&ASSET.cache, "ui/minogram.png"); + // assetCacheRelease(&ASSET.cache, "ui/minogram.dtf"); FONT_DEFAULT.texture = NULL; FONT_DEFAULT.tileset = NULL; errorOk(); diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index a9cda295..797fdcec 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -74,6 +74,7 @@ errorret_t engineUpdate(void) { errorChain(displayUpdate()); errorChain(cutsceneUpdate()); errorChain(sceneUpdate()); + errorChain(assetUpdate()); if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; errorOk(); diff --git a/src/dusk/util/CMakeLists.txt b/src/dusk/util/CMakeLists.txt index df1d282d..1beed776 100644 --- a/src/dusk/util/CMakeLists.txt +++ b/src/dusk/util/CMakeLists.txt @@ -13,4 +13,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} string.c math.c sort.c + ref.c ) \ No newline at end of file diff --git a/src/dusk/util/ref.c b/src/dusk/util/ref.c new file mode 100644 index 00000000..4ea507e0 --- /dev/null +++ b/src/dusk/util/ref.c @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "ref.h" +#include "memory.h" +#include "assert/assert.h" + +void refInit( + ref_t *ref, + void *data, + refcallback_t onLock, + refcallback_t onUnlock, + refcallback_t onAllUnlocked +) { + assertNotNull(ref, "Ref cannot be NULL."); + memoryZero(ref, sizeof(ref_t)); + ref->count = 1; + ref->data = data; + ref->onLock = onLock; + ref->onUnlock = onUnlock; + ref->onAllUnlocked = onAllUnlocked; +} + +void refLock(ref_t *ref) { + assertNotNull(ref, "Ref cannot be NULL."); + assertTrue(ref->count > 0, "Cannot lock a ref with zero count."); + ref->count++; + if(ref->onLock != NULL) ref->onLock(ref); +} + +bool_t refUnlock(ref_t *ref) { + assertNotNull(ref, "Ref cannot be NULL."); + assertTrue(ref->count > 0, "Cannot unlock a ref with zero count."); + + ref->count--; + + if(ref->count > 0) { + if(ref->onUnlock != NULL) ref->onUnlock(ref); + return false; + } + + void *data = ref->data; + refcallback_t onAllUnlocked = ref->onAllUnlocked; + memoryZero(ref, sizeof(ref_t)); + ref->data = data; + if(onAllUnlocked != NULL) onAllUnlocked(ref); + return true; +} diff --git a/src/dusk/util/ref.h b/src/dusk/util/ref.h new file mode 100644 index 00000000..f709730c --- /dev/null +++ b/src/dusk/util/ref.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct ref_s ref_t; +typedef void (*refcallback_t)(ref_t *ref); + +typedef struct ref_s { + uint32_t count; + void *data; + refcallback_t onLock; + refcallback_t onUnlock; + refcallback_t onAllUnlocked; +} ref_t; + +/** + * Initializes a ref with a count of 1. + * + * @param ref The ref to initialize. + * @param data Opaque context pointer accessible to all callbacks. + * @param onLock Called each time the ref is locked. May be NULL. + * @param onUnlock Called each time the ref is unlocked (count still > 0). May be NULL. + * @param onAllUnlocked Called when the count reaches zero. Responsible for + * any cleanup. May be NULL. + */ +void refInit( + ref_t *ref, + void *data, + refcallback_t onLock, + refcallback_t onUnlock, + refcallback_t onAllUnlocked +); + +/** + * Increments the lock count. + * + * @param ref The ref to lock. + */ +void refLock(ref_t *ref); + +/** + * Decrements the lock count. When it reaches zero onAllUnlocked is called + * (if set) and the ref struct is zeroed. + * + * @param ref The ref to unlock. + * @return true if the count reached zero. + */ +bool_t refUnlock(ref_t *ref);