Asset refactor, phase one.

This commit is contained in:
2026-05-29 14:27:40 -05:00
parent 957980b3c5
commit 0bcde064af
29 changed files with 751 additions and 555 deletions
-2
View File
@@ -8,8 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
asset.c
assetfile.c
assetcache.c
assetbatch.c
)
# Subdirs
+120 -15
View File
@@ -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.");
+36 -13
View File
@@ -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.
*
-81
View File
@@ -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();
}
-90
View File
@@ -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);
-81
View File
@@ -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));
}
-81
View File
@@ -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);
+1
View File
@@ -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;
+48
View File
@@ -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;
+6
View File
@@ -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)
+85
View File
@@ -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();
}
+94
View File
@@ -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);
+36
View File
@@ -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;
+30
View File
@@ -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] = {
// }
};
+29
View File
@@ -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];
+26 -26
View File
@@ -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);
}
// assetmeshoutput_t output = {
// outMesh,
// outVertices,
// inputAxis
// };
// return assetMeshLoadToOutput(path, &output);
// }
@@ -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
);
errorret_t assetMeshLoader(assetfile_t *file);
@@ -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,
&params,
&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, &params, 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,
&params, sizeof(params),
sizeof(texture_t),
assetTextureCacheDispose,
(void**)out
);
}
@@ -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
);
errorret_t assetTextureLoader(assetfile_t *file);
@@ -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
);
}
// errorret_t assetTilesetLoad(
// const char_t *path,
// tileset_t *out
// ) {
// return assetLoad(path, assetTilesetLoader, NULL, out);
// }
@@ -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
);
errorret_t assetTilesetLoader(assetfile_t *file);
+6 -6
View File
@@ -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);
}
// errorret_t assetJsonLoad(
// const char_t *path,
// yyjson_doc **outDoc
// ) {
// return assetLoad(path, assetJsonLoader, NULL, outDoc);
// }
+2 -14
View File
@@ -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
);
errorret_t assetJsonLoader(assetfile_t *file);
@@ -6,7 +6,7 @@
*/
#pragma once
#include "asset/asset.h"
#include "asset/assetfile.h"
#define ASSET_LOCALE_FILE_PLURAL_FORM_COUNT 6
+25 -8
View File
@@ -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();
+1
View File
@@ -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();
+1
View File
@@ -13,4 +13,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
string.c
math.c
sort.c
ref.c
)
+52
View File
@@ -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;
}
+54
View File
@@ -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);