Merge branch 'main' into scriptentity
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
asset.c
|
asset.c
|
||||||
|
assetbatch.c
|
||||||
assetfile.c
|
assetfile.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+17
-6
@@ -82,17 +82,23 @@ errorret_t assetRequireLoaded(assetentry_t *entry) {
|
|||||||
assertNotNull(entry, "Entry cannot be NULL.");
|
assertNotNull(entry, "Entry cannot be NULL.");
|
||||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||||
|
|
||||||
// Already loaded?
|
|
||||||
if(entry->state == ASSET_ENTRY_STATE_LOADED) {
|
if(entry->state == ASSET_ENTRY_STATE_LOADED) {
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not loaded, just spin the wheel
|
// Lock to prevent the reaper from collecting the entry mid-spin.
|
||||||
|
assetEntryLock(entry);
|
||||||
|
|
||||||
while(entry->state != ASSET_ENTRY_STATE_LOADED) {
|
while(entry->state != ASSET_ENTRY_STATE_LOADED) {
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
errorChain(assetUpdate());
|
errorret_t ret = assetUpdate();
|
||||||
|
if(errorIsNotOk(ret)) {
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
errorChain(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetEntryUnlock(entry);
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,9 +252,9 @@ errorret_t assetUpdate(void) {
|
|||||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||||
|
|
||||||
|
|
||||||
// Reap entries that have no external locks (refs.count == 1 means only the
|
// Reap entries that have no external locks (refs.count == 0) AND have been
|
||||||
// system hold remains). Only safe to reap LOADED and NOT_STARTED states —
|
// explicitly locked at least once. Entries that were never locked are left
|
||||||
// mid-load entries are left for the next cycle.
|
// alone — raw-pointer callers hold no ref but should not lose the entry.
|
||||||
entry = ASSET.entries;
|
entry = ASSET.entries;
|
||||||
do {
|
do {
|
||||||
if(entry->state != ASSET_ENTRY_STATE_LOADED) {
|
if(entry->state != ASSET_ENTRY_STATE_LOADED) {
|
||||||
@@ -261,6 +267,11 @@ errorret_t assetUpdate(void) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!entry->wasLocked) {
|
||||||
|
entry++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(entry->refs.count > 0) {
|
if(entry->refs.count > 0) {
|
||||||
entry++;
|
entry++;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -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 <unistd.h>
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -33,6 +33,7 @@ void assetEntryInit(
|
|||||||
void assetEntryLock(assetentry_t *entry) {
|
void assetEntryLock(assetentry_t *entry) {
|
||||||
assertNotNull(entry, "Entry cannot be NULL");
|
assertNotNull(entry, "Entry cannot be NULL");
|
||||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||||
|
entry->wasLocked = true;
|
||||||
refLock(&entry->refs);
|
refLock(&entry->refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ typedef struct assetentry_s {
|
|||||||
assetentrystate_t state;
|
assetentrystate_t state;
|
||||||
// What is referencing this asset entry.
|
// What is referencing this asset entry.
|
||||||
ref_t refs;
|
ref_t refs;
|
||||||
|
// True once assetEntryLock has been called at least once. The reaper only
|
||||||
|
// collects entries that have been explicitly locked (and later unlocked to
|
||||||
|
// zero). Entries that nobody has ever locked are left alone so raw-pointer
|
||||||
|
// callers (tests, requireLoaded before locking) are not surprised.
|
||||||
|
bool_t wasLocked;
|
||||||
// Data that will be passed to the loader about how it should load.
|
// Data that will be passed to the loader about how it should load.
|
||||||
assetloaderinput_t *input;
|
assetloaderinput_t *input;
|
||||||
} assetentry_t;
|
} assetentry_t;
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
font_t FONT_DEFAULT;
|
font_t FONT_DEFAULT;
|
||||||
|
|
||||||
errorret_t textInit(void) {
|
errorret_t textInit(void) {
|
||||||
assetloaderinput_t input = {
|
assetloaderinput_t input = { .texture = TEXTURE_FORMAT_RGBA };
|
||||||
.texture = TEXTURE_FORMAT_RGBA
|
|
||||||
};
|
|
||||||
assetentry_t *entryTexture = assetLock(
|
assetentry_t *entryTexture = assetLock(
|
||||||
"ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input
|
"ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input
|
||||||
);
|
);
|
||||||
@@ -95,7 +93,9 @@ errorret_t textDraw(
|
|||||||
float_t posX = x;
|
float_t posX = x;
|
||||||
float_t posY = y;
|
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
|
#if MESH_ENABLE_COLOR
|
||||||
#else
|
#else
|
||||||
|
|||||||
Reference in New Issue
Block a user