From 1e8311fc04c014f4ead958b97c2e81d35d71826c Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 1 Jun 2026 22:34:44 -0500 Subject: [PATCH] Add asset batch --- src/dusk/asset/CMakeLists.txt | 1 + src/dusk/asset/assetbatch.c | 94 +++++++++++++++++++++++++++++++++++ src/dusk/asset/assetbatch.h | 82 ++++++++++++++++++++++++++++++ src/dusk/display/text/text.c | 36 ++++++++------ 4 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 src/dusk/asset/assetbatch.c create mode 100644 src/dusk/asset/assetbatch.h diff --git a/src/dusk/asset/CMakeLists.txt b/src/dusk/asset/CMakeLists.txt index 39e32cab..0e87c96f 100644 --- a/src/dusk/asset/CMakeLists.txt +++ b/src/dusk/asset/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC asset.c + assetbatch.c assetfile.c ) diff --git a/src/dusk/asset/assetbatch.c b/src/dusk/asset/assetbatch.c new file mode 100644 index 00000000..4bcb646d --- /dev/null +++ b/src/dusk/asset/assetbatch.c @@ -0,0 +1,94 @@ +/** + * 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 "assert/assert.h" +#include "util/memory.h" +#include + +void assetBatchInit( + assetbatch_t *batch, + const uint16_t count, + const assetbatchdesc_t *descs +) { + assertNotNull(batch, "Batch cannot be NULL."); + assertNotNull(descs, "Descs cannot be NULL."); + assertTrue(count > 0, "Count must be greater than 0."); + assertTrue(count <= ASSET_BATCH_COUNT_MAX, "Count exceeds ASSET_BATCH_COUNT_MAX."); + + memoryZero(batch, sizeof(assetbatch_t)); + batch->count = count; + + for(uint16_t i = 0; i < count; i++) { + // Copy input into batch-owned storage so the descriptor need not persist. + batch->inputs[i] = descs[i].input; + batch->entries[i] = assetLock(descs[i].path, descs[i].type, &batch->inputs[i]); + } +} + +void assetBatchLock(assetbatch_t *batch) { + assertNotNull(batch, "Batch cannot be NULL."); + for(uint16_t i = 0; i < batch->count; i++) { + assetEntryLock(batch->entries[i]); + } +} + +void assetBatchUnlock(assetbatch_t *batch) { + assertNotNull(batch, "Batch cannot be NULL."); + for(uint16_t i = 0; i < batch->count; i++) { + assetEntryUnlock(batch->entries[i]); + } +} + +bool_t assetBatchIsLoaded(const assetbatch_t *batch) { + assertNotNull(batch, "Batch cannot be NULL."); + for(uint16_t i = 0; i < batch->count; i++) { + if(batch->entries[i]->state != ASSET_ENTRY_STATE_LOADED) return false; + } + return true; +} + +bool_t assetBatchHasError(const assetbatch_t *batch) { + assertNotNull(batch, "Batch cannot be NULL."); + for(uint16_t i = 0; i < batch->count; i++) { + if(batch->entries[i]->state == ASSET_ENTRY_STATE_ERROR) return true; + } + return false; +} + +errorret_t assetBatchRequireLoaded(assetbatch_t *batch) { + assertNotNull(batch, "Batch cannot be NULL."); + + bool_t allDone; + do { + allDone = true; + for(uint16_t i = 0; i < batch->count; i++) { + const assetentrystate_t state = batch->entries[i]->state; + if(state == ASSET_ENTRY_STATE_ERROR) { + errorThrow("Asset '%s' failed to load.", batch->entries[i]->name); + } + if(state != ASSET_ENTRY_STATE_LOADED) { + allDone = false; + } + } + if(!allDone) { + usleep(1000); + errorChain(assetUpdate()); + } + } while(!allDone); + + errorOk(); +} + +void assetBatchDispose(assetbatch_t *batch) { + assertNotNull(batch, "Batch cannot be NULL."); + for(uint16_t i = 0; i < batch->count; i++) { + assetUnlockEntry(batch->entries[i]); + } + memoryZero(batch, sizeof(assetbatch_t)); +} diff --git a/src/dusk/asset/assetbatch.h b/src/dusk/asset/assetbatch.h new file mode 100644 index 00000000..db71a115 --- /dev/null +++ b/src/dusk/asset/assetbatch.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "asset/loader/assetentry.h" +#include "asset/loader/assetloader.h" + +#define ASSET_BATCH_COUNT_MAX 64 + +typedef struct { + const char_t *path; + assetloadertype_t type; + assetloaderinput_t input; +} assetbatchdesc_t; + +typedef struct { + assetentry_t *entries[ASSET_BATCH_COUNT_MAX]; + assetloaderinput_t inputs[ASSET_BATCH_COUNT_MAX]; + uint16_t count; +} assetbatch_t; + +/** + * Initialises the batch from an array of descriptors. Each entry is locked + * and queued for loading immediately. + * + * @param batch Batch to initialise. + * @param descs Array of entry descriptors (need not outlive this call). + * @param count Number of descriptors (must be <= ASSET_BATCH_COUNT_MAX). + */ +void assetBatchInit( + assetbatch_t *batch, + uint16_t count, + const assetbatchdesc_t *descs +); + +/** + * Acquires one additional lock on every entry in the batch. + * + * @param batch Batch to lock. + */ +void assetBatchLock(assetbatch_t *batch); + +/** + * Releases one lock from every entry in the batch. When an entry's lock + * count reaches zero it will be reaped on the next assetUpdate. + * + * @param batch Batch to unlock. + */ +void assetBatchUnlock(assetbatch_t *batch); + +/** + * Returns true if every entry in the batch has finished loading. + * + * @param batch Batch to query. + */ +bool_t assetBatchIsLoaded(const assetbatch_t *batch); + +/** + * Returns true if any entry in the batch is in an error state. + * + * @param batch Batch to query. + */ +bool_t assetBatchHasError(const assetbatch_t *batch); + +/** + * Blocks until every entry is loaded. Returns an error if any entry fails. + * + * @param batch Batch to wait on. + */ +errorret_t assetBatchRequireLoaded(assetbatch_t *batch); + +/** + * Releases the batch's lock on every entry and clears the batch. After this + * call the batch struct may be reused with assetBatchInit. + * + * @param batch Batch to dispose. + */ +void assetBatchDispose(assetbatch_t *batch); diff --git a/src/dusk/display/text/text.c b/src/dusk/display/text/text.c index fe8cc2ba..d87f33ff 100644 --- a/src/dusk/display/text/text.c +++ b/src/dusk/display/text/text.c @@ -10,35 +10,37 @@ #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" font_t FONT_DEFAULT; +assetbatch_t TEXT_BATCH; errorret_t textInit(void) { - assetloaderinput_t input = { - .texture = TEXTURE_FORMAT_RGBA - }; - assetentry_t *entryTexture = assetLock( - "ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input - ); - assetentry_t *entryTileset = assetLock( - "ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET, NULL - ); - errorChain(assetRequireLoaded(entryTexture)); - errorChain(assetRequireLoaded(entryTileset)); + assetBatchInit(&TEXT_BATCH, 2, (assetbatchdesc_t[]){ + { + .path = "ui/minogram.png", + .type = ASSET_LOADER_TYPE_TEXTURE, + .input = { .texture = TEXTURE_FORMAT_RGBA } + }, + { + .path = "ui/minogram.dtf", + .type = ASSET_LOADER_TYPE_TILESET + }, + }); + errorChain(assetBatchRequireLoaded(&TEXT_BATCH)); - FONT_DEFAULT.texture = &entryTexture->data.texture; - FONT_DEFAULT.tileset = &entryTileset->data.tileset; + FONT_DEFAULT.texture = &TEXT_BATCH.entries[0]->data.texture; + FONT_DEFAULT.tileset = &TEXT_BATCH.entries[1]->data.tileset; errorOk(); } errorret_t textDispose(void) { FONT_DEFAULT.texture = NULL; FONT_DEFAULT.tileset = NULL; - assetUnlock("ui/minogram.png"); - assetUnlock("ui/minogram.dtf"); + assetBatchDispose(&TEXT_BATCH); errorOk(); } @@ -95,7 +97,9 @@ errorret_t textDraw( float_t posX = x; float_t posY = y; - errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, font->texture)); + errorChain(shaderSetTexture( + &SHADER_UNLIT, SHADER_UNLIT_TEXTURE, font->texture + )); #if MESH_ENABLE_COLOR #else