Prepping for async
This commit is contained in:
+137
-32
@@ -11,15 +11,23 @@
|
||||
#include "assert/assert.h"
|
||||
#include "engine/engine.h"
|
||||
#include "util/string.h"
|
||||
#include "console/console.h"
|
||||
#include <unistd.h>
|
||||
|
||||
asset_t ASSET;
|
||||
|
||||
errorret_t assetInit(void) {
|
||||
memoryZero(&ASSET, sizeof(asset_t));
|
||||
|
||||
for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
|
||||
threadMutexInit(&ASSET.loading[i].mutex);
|
||||
}
|
||||
|
||||
// assetInitPlatform must either define ASSET.zip or throw an error.
|
||||
errorChain(assetInitPlatform());
|
||||
assertNotNull(ASSET.zip, "Asset zip null without error.");
|
||||
threadInit(&ASSET.loadThread, assetUpdateAsync);
|
||||
threadStart(&ASSET.loadThread);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -88,68 +96,120 @@ errorret_t assetRequireLoaded(assetentry_t *entry) {
|
||||
}
|
||||
|
||||
errorret_t assetUpdate(void) {
|
||||
// Is there any pending entries?
|
||||
assetentry_t *entry = ASSET.entries;
|
||||
assetloading_t *loading;
|
||||
// Determine how many available loading slots we have.
|
||||
assetloading_t *availableLoading[ASSET_LOADING_COUNT_MAX];
|
||||
uint8_t availableLoadingCount = 0;
|
||||
assetloading_t *loading = ASSET.loading;
|
||||
assetentry_t *entry;
|
||||
|
||||
|
||||
do {
|
||||
// Is this asset "ready to start loading" ?
|
||||
if(entry->type == ASSET_LOADER_TYPE_NULL) {
|
||||
entry++;
|
||||
// We only care about NULL entry references. Nothing async touches this so
|
||||
// it's fine to use raw here.
|
||||
if(loading->entry != NULL) {
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
availableLoading[availableLoadingCount++] = loading;
|
||||
loading++;
|
||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||
|
||||
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;
|
||||
// Now we can check for pending asset entries, we can't do anything if there
|
||||
// is no available slots though.
|
||||
if(availableLoadingCount > 0) {
|
||||
entry = ASSET.entries;
|
||||
do {
|
||||
if(loading->type != ASSET_LOADER_TYPE_NULL) {
|
||||
loading++;
|
||||
// Is this asset "ready to start loading" ?
|
||||
if(entry->type == ASSET_LOADER_TYPE_NULL) {
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||
// We only care about assets not started.
|
||||
if(entry->state != ASSET_ENTRY_STATE_NOT_STARTED) {
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
// No loading slot, try again next frame.
|
||||
// Pop a loading slot for this asset entry.
|
||||
loading = availableLoading[--availableLoadingCount];
|
||||
|
||||
// Start loading this asset.
|
||||
assetEntryStartLoading(entry, loading);
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start loading this asset.
|
||||
assetEntryStartLoading(entry, loading);
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
// Did we run out of loading slots?
|
||||
if(availableLoadingCount == 0) {
|
||||
break;
|
||||
}
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
}
|
||||
|
||||
// At this point we have to see the state of all the loading assets.
|
||||
// Now walk over all the loading slots and see what needs to be done.
|
||||
loading = ASSET.loading;
|
||||
do {
|
||||
// Is the loading slot in use? Entry can only be modified synchronously.
|
||||
if(loading->entry == NULL) {
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lock the loading slot. This will prevent any async modifications.
|
||||
threadMutexLock(&loading->mutex);
|
||||
|
||||
// Check the state of the entry.
|
||||
switch(loading->entry->state) {
|
||||
// This thing is pending synchronous loading.
|
||||
case ASSET_ENTRY_STATE_PENDING_SYNC:
|
||||
// Begin sync loading
|
||||
// Perform sync load.
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADING_SYNC;
|
||||
errorret_t ret = ASSET_LOADER_CALLBACKS[loading->type].loadSync(loading);
|
||||
errorret_t ret = (
|
||||
ASSET_LOADER_CALLBACKS[loading->type].loadSync(loading)
|
||||
);
|
||||
|
||||
// After a sync load, these are the only valid states.
|
||||
assertTrue(
|
||||
loading->entry->state == ASSET_ENTRY_STATE_LOADED ||
|
||||
loading->entry->state == ASSET_ENTRY_STATE_ERROR ||
|
||||
loading->entry->state == ASSET_ENTRY_STATE_PENDING_SYNC ||
|
||||
loading->entry->state == ASSET_ENTRY_STATE_PENDING_ASYNC,
|
||||
"Loader did not set entry state to loaded or error on finished load."
|
||||
);
|
||||
|
||||
// If an error occured these things need to be true, basically just
|
||||
// ensuring the sync loader is setting the error correctly.
|
||||
if(ret.code != ERROR_OK) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
return ret;
|
||||
assertTrue(
|
||||
loading->entry->state == ASSET_ENTRY_STATE_ERROR,
|
||||
"Loader did not set entry state to error on failed load."
|
||||
);
|
||||
}
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
break;
|
||||
|
||||
case ASSET_ENTRY_STATE_LOADING_SYNC:
|
||||
assertUnreachable(
|
||||
"Entry is in a pending sync state still?"
|
||||
);
|
||||
break;
|
||||
|
||||
// Done loading, we can just free it up.
|
||||
case ASSET_ENTRY_STATE_LOADED:
|
||||
loading->entry = NULL;
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
break;
|
||||
|
||||
case ASSET_ENTRY_STATE_ERROR:
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
errorThrow("Failed to load asset asynchronously.");
|
||||
break;
|
||||
|
||||
default:
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
@@ -157,7 +217,52 @@ errorret_t assetUpdate(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void assetUpdateAsync(thread_t *thread) {
|
||||
while(!threadShouldStop(thread)) {
|
||||
// Walk over each asset
|
||||
assetloading_t *loading;
|
||||
loading = ASSET.loading;
|
||||
|
||||
do {
|
||||
threadMutexLock(&loading->mutex);
|
||||
|
||||
if(loading->entry == NULL) {
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(loading->entry->state) {
|
||||
case ASSET_ENTRY_STATE_PENDING_ASYNC:
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADING_ASYNC;
|
||||
printf("Simulated async load\n");
|
||||
sleep(1);
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
break;
|
||||
|
||||
case ASSET_ENTRY_STATE_LOADING_ASYNC:
|
||||
assertUnreachable(
|
||||
"Entry is in a pending async state still?"
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t assetDispose(void) {
|
||||
threadStop(&ASSET.loadThread);
|
||||
|
||||
for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
|
||||
threadMutexDispose(&ASSET.loading[i].mutex);
|
||||
}
|
||||
|
||||
// Cleanup zip file.
|
||||
if(ASSET.zip != NULL) {
|
||||
if(zip_close(ASSET.zip) != 0) {
|
||||
|
||||
+13
-2
@@ -9,7 +9,7 @@
|
||||
#include "error/error.h"
|
||||
#include "asset/assetplatform.h"
|
||||
#include "assetfile.h"
|
||||
|
||||
#include "thread/thread.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
|
||||
@@ -30,6 +30,9 @@ typedef struct asset_s {
|
||||
zip_t *zip;
|
||||
assetplatform_t platform;
|
||||
|
||||
// Background loading thread.
|
||||
thread_t loadThread;
|
||||
|
||||
// Assets that are mid loading.
|
||||
assetloading_t loading[ASSET_LOADING_COUNT_MAX];
|
||||
assetentry_t entries[ASSET_ENTRY_COUNT_MAX];
|
||||
@@ -76,11 +79,19 @@ 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);
|
||||
|
||||
/**
|
||||
* Starts the background asset loading thread. The thread runs assetUpdate
|
||||
* in a loop with a short sleep until stopped.
|
||||
*
|
||||
* @param thread The thread runner.
|
||||
*/
|
||||
void assetUpdateAsync(thread_t *thread);
|
||||
|
||||
/**
|
||||
* Disposes/cleans up the asset system.
|
||||
*
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
@@ -55,7 +55,7 @@ void assetEntryStartLoading(
|
||||
);
|
||||
|
||||
entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
memoryZero(loading, sizeof(assetloading_t));
|
||||
memoryZero(&loading->loading, sizeof(assetloaderloading_t));
|
||||
loading->type = entry->type;
|
||||
loading->entry = entry;
|
||||
// At this point the asset manager will manage this thing's loading
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
typedef enum {
|
||||
ASSET_ENTRY_STATE_NOT_STARTED,
|
||||
// ASSET_ENTRY_STATE_PENDING_ASYNC,
|
||||
// ASSET_ENTRY_STATE_LOADING_ASYNC,
|
||||
ASSET_ENTRY_STATE_PENDING_ASYNC,
|
||||
ASSET_ENTRY_STATE_LOADING_ASYNC,
|
||||
ASSET_ENTRY_STATE_PENDING_SYNC,
|
||||
ASSET_ENTRY_STATE_LOADING_SYNC,
|
||||
ASSET_ENTRY_STATE_LOADED,
|
||||
|
||||
@@ -59,4 +59,30 @@ typedef struct {
|
||||
assetloaderdisposecallback_t *dispose;
|
||||
} assetloadercallbacks_t;
|
||||
|
||||
extern assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT];
|
||||
extern assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT];
|
||||
|
||||
/**
|
||||
* Shorthand method to both chain an error (against the loader state) and to
|
||||
* set the asset entry state to error.
|
||||
*
|
||||
* @param loading The asset loading slot.
|
||||
* @param ret The error return value to check and chain if it's an error.
|
||||
*/
|
||||
#define assetLoaderErrorChain(loading, ret) {\
|
||||
if(ret.code != ERROR_OK) { \
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR; \
|
||||
errorChain(ret); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand method to both throw an error (against the loader state) and to
|
||||
* set the asset entry state to error.
|
||||
*
|
||||
* @param loading The asset loading slot.
|
||||
* @param ... Format string and arguments for the error message.
|
||||
*/
|
||||
#define assetLoaderErrorThrow(loading, ...) {\
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR; \
|
||||
errorThrow(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
#pragma once
|
||||
#include "assetloader.h"
|
||||
#include "asset/assetfile.h"
|
||||
#include "thread/threadmutex.h"
|
||||
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct assetloading_s {
|
||||
// Protects entry pointer and entry->state from concurrent access.
|
||||
threadmutex_t mutex;
|
||||
// What type of asset is being loaded.
|
||||
assetloadertype_t type;
|
||||
// Referral back to the asset entry that will be kept alive after load done.
|
||||
|
||||
@@ -20,16 +20,24 @@ errorret_t assetMeshLoaderSync(assetloading_t *loading) {
|
||||
assetfile_t *file = &loading->loading.mesh.file;
|
||||
assetmeshinputaxis_t axis = loading->entry->input->mesh;
|
||||
|
||||
errorChain(assetFileInit(file, loading->entry->name, NULL, NULL));
|
||||
errorChain(assetFileOpen(file));
|
||||
assetLoaderErrorChain(loading, assetFileInit(
|
||||
file, loading->entry->name, NULL, NULL
|
||||
));
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
|
||||
// Skip the 80-byte STL header
|
||||
errorChain(assetFileRead(file, NULL, 80));
|
||||
if(file->lastRead != 80) errorThrow("Failed to skip STL header");
|
||||
assetLoaderErrorChain(loading, assetFileRead(file, NULL, 80));
|
||||
if(file->lastRead != 80) {
|
||||
assetLoaderErrorThrow(loading, "Failed to skip STL header.");
|
||||
}
|
||||
|
||||
uint32_t triangleCount;
|
||||
errorChain(assetFileRead(file, &triangleCount, sizeof(uint32_t)));
|
||||
if(file->lastRead != sizeof(uint32_t)) errorThrow("Failed to read tri count");
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileRead(file, &triangleCount, sizeof(uint32_t))
|
||||
);
|
||||
if(file->lastRead != sizeof(uint32_t)) {
|
||||
assetLoaderErrorThrow(loading, "Failed to read tri count");
|
||||
}
|
||||
triangleCount = endianLittleToHost32(triangleCount);
|
||||
|
||||
out->vertices = memoryAllocate(sizeof(meshvertex_t) * triangleCount * 3);
|
||||
@@ -42,19 +50,25 @@ errorret_t assetMeshLoaderSync(assetloading_t *loading) {
|
||||
if(ret.code != ERROR_OK) {
|
||||
memoryFree(verts);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
assetLoaderErrorChain(loading, ret);
|
||||
}
|
||||
if(file->lastRead != sizeof(triData)) {
|
||||
memoryFree(verts);
|
||||
out->vertices = NULL;
|
||||
errorThrow("Failed to read triangle data");
|
||||
assetLoaderErrorThrow(loading, "Failed to read triangle data");
|
||||
}
|
||||
|
||||
for(uint8_t j = 0; j < 3; j++) {
|
||||
#if MESH_ENABLE_COLOR
|
||||
verts[i * 3 + j].color.r = (uint8_t)(endianLittleToHostFloat(triData.normal[0]) * 255.0f);
|
||||
verts[i * 3 + j].color.g = (uint8_t)(endianLittleToHostFloat(triData.normal[1]) * 255.0f);
|
||||
verts[i * 3 + j].color.b = (uint8_t)(endianLittleToHostFloat(triData.normal[2]) * 255.0f);
|
||||
verts[i * 3 + j].color.r = (
|
||||
(uint8_t)(endianLittleToHostFloat(triData.normal[0]) * 255.0f)
|
||||
);
|
||||
verts[i * 3 + j].color.g = (
|
||||
(uint8_t)(endianLittleToHostFloat(triData.normal[1]) * 255.0f)
|
||||
);
|
||||
verts[i * 3 + j].color.b = (
|
||||
(uint8_t)(endianLittleToHostFloat(triData.normal[2]) * 255.0f)
|
||||
);
|
||||
verts[i * 3 + j].color.a = 0xFF;
|
||||
#endif
|
||||
|
||||
@@ -62,7 +76,9 @@ errorret_t assetMeshLoaderSync(assetloading_t *loading) {
|
||||
verts[i * 3 + j].uv[1] = 0.0f;
|
||||
|
||||
for(uint8_t k = 0; k < 3; k++) {
|
||||
verts[i * 3 + j].pos[k] = endianLittleToHostFloat(triData.positions[j][k]);
|
||||
verts[i * 3 + j].pos[k] = endianLittleToHostFloat(
|
||||
triData.positions[j][k]
|
||||
);
|
||||
}
|
||||
|
||||
switch(axis) {
|
||||
@@ -104,17 +120,21 @@ errorret_t assetMeshLoaderSync(assetloading_t *loading) {
|
||||
if(ret.code != ERROR_OK) {
|
||||
memoryFree(verts);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
assetLoaderErrorChain(loading, ret);
|
||||
}
|
||||
assetFileDispose(file);
|
||||
|
||||
ret = meshInit(&out->mesh, MESH_PRIMITIVE_TYPE_TRIANGLES, triangleCount * 3, verts);
|
||||
ret = meshInit(
|
||||
&out->mesh, MESH_PRIMITIVE_TYPE_TRIANGLES, triangleCount * 3, verts
|
||||
);
|
||||
if(ret.code != ERROR_OK) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
memoryFree(verts);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
assetLoaderErrorChain(loading, ret);
|
||||
}
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,16 +55,15 @@ int assetTextureEOF(void *user) {
|
||||
errorret_t assetTextureLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
|
||||
|
||||
// Init the file
|
||||
assetfile_t *file = &loading->loading.texture.file;
|
||||
errorChain(assetFileInit(
|
||||
assetLoaderErrorChain(loading, assetFileInit(
|
||||
file,
|
||||
loading->entry->name,
|
||||
NULL,
|
||||
&loading->entry->data.texture
|
||||
));
|
||||
errorChain(assetFileOpen(file));
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
|
||||
// Determine channels
|
||||
int channelsDesired;
|
||||
@@ -74,7 +73,7 @@ errorret_t assetTextureLoaderSync(assetloading_t *loading) {
|
||||
break;
|
||||
|
||||
default:
|
||||
errorThrow("Bad texture format.");
|
||||
assetLoaderErrorThrow(loading, "Bad texture format.");
|
||||
}
|
||||
|
||||
// Load image pixels.
|
||||
@@ -89,13 +88,13 @@ errorret_t assetTextureLoaderSync(assetloading_t *loading) {
|
||||
);
|
||||
|
||||
// Close out the file.
|
||||
errorChain(assetFileClose(file));
|
||||
errorChain(assetFileDispose(file));
|
||||
assetLoaderErrorChain(loading, assetFileClose(file));
|
||||
assetLoaderErrorChain(loading, assetFileDispose(file));
|
||||
|
||||
// Ensure we loaded correctly.
|
||||
if(loading->loading.texture.data == NULL) {
|
||||
const char_t *errorStr = stbi_failure_reason();
|
||||
errorThrow("Failed to load texture from file %s.", errorStr);
|
||||
assetLoaderErrorThrow(loading, "Failed to load texture from file %s.", errorStr);
|
||||
}
|
||||
|
||||
// Fixes a specific bug probably with Dolphin but for now just assuming endian
|
||||
@@ -107,7 +106,7 @@ errorret_t assetTextureLoaderSync(assetloading_t *loading) {
|
||||
}
|
||||
|
||||
// Create the texture.
|
||||
errorChain(textureInit(
|
||||
assetLoaderErrorChain(loading, textureInit(
|
||||
(texture_t*)&loading->entry->data.texture,
|
||||
(int32_t)width, (int32_t)height,
|
||||
loading->entry->input->texture,
|
||||
@@ -118,6 +117,8 @@ errorret_t assetTextureLoaderSync(assetloading_t *loading) {
|
||||
|
||||
// Free the pixels.
|
||||
stbi_image_free(loading->loading.texture.data);
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,23 +19,25 @@ errorret_t assetTilesetLoaderSync(assetloading_t *loading) {
|
||||
assetfile_t *file = &loading->loading.tileset.file;
|
||||
tileset_t *out = &loading->entry->data.tileset;
|
||||
|
||||
errorChain(assetFileInit(file, loading->entry->name, NULL, NULL));
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||
);
|
||||
|
||||
uint8_t *entire = memoryAllocate(file->size);
|
||||
errorChain(assetFileOpen(file));
|
||||
errorChain(assetFileRead(file, entire, file->size));
|
||||
errorChain(assetFileClose(file));
|
||||
errorChain(assetFileDispose(file));
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
assetLoaderErrorChain(loading, assetFileRead(file, entire, file->size));
|
||||
assetLoaderErrorChain(loading, assetFileClose(file));
|
||||
assetLoaderErrorChain(loading, assetFileDispose(file));
|
||||
assertTrue(file->lastRead == file->size, "Failed to read entire file.");
|
||||
|
||||
if(entire[0] != 'D' || entire[1] != 'T' || entire[2] != 'F') {
|
||||
memoryFree(entire);
|
||||
errorThrow("Invalid tileset header");
|
||||
assetLoaderErrorThrow(loading, "Invalid tileset header");
|
||||
}
|
||||
|
||||
if(entire[3] != 0x00) {
|
||||
memoryFree(entire);
|
||||
errorThrow("Unsupported tileset version");
|
||||
assetLoaderErrorThrow(loading, "Unsupported tileset version");
|
||||
}
|
||||
|
||||
out->tileWidth = endianLittleToHost16(*(uint16_t *)(entire + 4));
|
||||
@@ -43,21 +45,36 @@ errorret_t assetTilesetLoaderSync(assetloading_t *loading) {
|
||||
out->columns = endianLittleToHost16(*(uint16_t *)(entire + 8));
|
||||
out->rows = endianLittleToHost16(*(uint16_t *)(entire + 10));
|
||||
|
||||
if(out->tileWidth == 0) { memoryFree(entire); errorThrow("Tile width cannot be 0"); }
|
||||
if(out->tileHeight == 0) { memoryFree(entire); errorThrow("Tile height cannot be 0"); }
|
||||
if(out->columns == 0) { memoryFree(entire); errorThrow("Column count cannot be 0"); }
|
||||
if(out->rows == 0) { memoryFree(entire); errorThrow("Row count cannot be 0"); }
|
||||
if(out->tileWidth == 0) {
|
||||
memoryFree(entire);
|
||||
assetLoaderErrorThrow(loading, "Tile width cannot be 0");
|
||||
}
|
||||
|
||||
if(out->tileHeight == 0) {
|
||||
memoryFree(entire);
|
||||
assetLoaderErrorThrow(loading, "Tile height cannot be 0");
|
||||
}
|
||||
if(out->columns == 0) {
|
||||
memoryFree(entire);
|
||||
assetLoaderErrorThrow(loading, "Column count cannot be 0");
|
||||
}
|
||||
if(out->rows == 0) {
|
||||
memoryFree(entire);
|
||||
assetLoaderErrorThrow(loading, "Row count cannot be 0");
|
||||
}
|
||||
|
||||
out->uv[0] = endianLittleToHostFloat(*(float *)(entire + 16));
|
||||
out->uv[1] = endianLittleToHostFloat(*(float *)(entire + 20));
|
||||
|
||||
if(out->uv[1] < 0.0f || out->uv[1] > 1.0f) {
|
||||
memoryFree(entire);
|
||||
errorThrow("Invalid v0 value in tileset");
|
||||
assetLoaderErrorThrow(loading, "Invalid v0 value in tileset");
|
||||
}
|
||||
|
||||
out->tileCount = out->columns * out->rows;
|
||||
memoryFree(entire);
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,18 +16,28 @@ errorret_t assetJsonLoaderSync(assetloading_t *loading) {
|
||||
assertTrue(loading->type == ASSET_LOADER_TYPE_JSON, "Invalid type.");
|
||||
|
||||
assetfile_t *file = &loading->loading.json.file;
|
||||
errorChain(assetFileInit(file, loading->entry->name, NULL, NULL));
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||
);
|
||||
|
||||
if(file->size > ASSET_JSON_FILE_SIZE_MAX) {
|
||||
errorThrow("JSON exceeds maximum allowed size");
|
||||
assetLoaderErrorThrow(loading, "JSON exceeds maximum allowed size");
|
||||
}
|
||||
|
||||
uint8_t *buffer = memoryAllocate(file->size);
|
||||
errorChain(assetFileOpen(file));
|
||||
errorChain(assetFileRead(file, buffer, file->size));
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileOpen(file)
|
||||
);
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileRead(file, buffer, file->size)
|
||||
);
|
||||
assertTrue(file->lastRead == file->size, "Failed to read entire JSON file.");
|
||||
errorChain(assetFileClose(file));
|
||||
errorChain(assetFileDispose(file));
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileClose(file)
|
||||
);
|
||||
assetLoaderErrorChain(
|
||||
loading, assetFileDispose(file)
|
||||
);
|
||||
|
||||
loading->entry->data.json = yyjson_read(
|
||||
(char *)buffer,
|
||||
@@ -36,7 +46,11 @@ errorret_t assetJsonLoaderSync(assetloading_t *loading) {
|
||||
);
|
||||
memoryFree(buffer);
|
||||
|
||||
if(!loading->entry->data.json) errorThrow("Failed to parse JSON");
|
||||
if(!loading->entry->data.json) {
|
||||
assetLoaderErrorThrow(loading, "Failed to parse JSON");
|
||||
}
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@ errorret_t assetLocaleLoaderSync(assetloading_t *loading) {
|
||||
|
||||
assetlocalefile_t *localeFile = &loading->entry->data.locale;
|
||||
memoryZero(localeFile, sizeof(assetlocalefile_t));
|
||||
errorChain(assetFileInit(&localeFile->file, loading->entry->name, NULL, NULL));
|
||||
errorChain(assetFileOpen(&localeFile->file));
|
||||
assetLoaderErrorChain(loading, assetFileInit(&localeFile->file, loading->entry->name, NULL, NULL));
|
||||
assetLoaderErrorChain(loading, assetFileOpen(&localeFile->file));
|
||||
|
||||
char_t buffer[1024];
|
||||
errorChain(assetLocaleGetString(localeFile, "", 0, buffer, sizeof(buffer)));
|
||||
errorChain(assetLocaleParseHeader(localeFile, buffer, sizeof(buffer)));
|
||||
assetLoaderErrorChain(loading, assetLocaleGetString(localeFile, "", 0, buffer, sizeof(buffer)));
|
||||
assetLoaderErrorChain(loading, assetLocaleParseHeader(localeFile, buffer, sizeof(buffer)));
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ errorret_t textInit(void) {
|
||||
);
|
||||
assetentry_t *entryTileset = assetGetEntry(
|
||||
"ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET, NULL
|
||||
// "ui/minogram.dtx", ASSET_LOADER_TYPE_TILESET, NULL
|
||||
);
|
||||
errorChain(assetRequireLoaded(entryTexture));
|
||||
errorChain(assetRequireLoaded(entryTileset));
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "util/string.h"
|
||||
#include "log/log.h"
|
||||
|
||||
errorstate_t ERROR_STATE = { 0 };
|
||||
THREAD_LOCAL errorstate_t ERROR_STATE = { 0 };
|
||||
|
||||
errorret_t errorThrowImpl(
|
||||
errorstate_t *state,
|
||||
@@ -64,7 +64,7 @@ errorret_t errorOkImpl() {
|
||||
ERROR_STATE.code == ERROR_OK,
|
||||
"Global error state is not OK (Likely missing errorCatch)"
|
||||
);
|
||||
|
||||
|
||||
return (errorret_t) {
|
||||
.code = ERROR_OK,
|
||||
.state = NULL
|
||||
@@ -118,6 +118,7 @@ void errorCatch(errorret_t retval) {
|
||||
|
||||
// Clear the error state
|
||||
memoryFree((void*)retval.state->message);
|
||||
memoryFree((void*)retval.state->lines);
|
||||
retval.state->code = ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
+17
-30
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "thread/threadlocal.h"
|
||||
|
||||
typedef uint8_t errorcode_t;
|
||||
|
||||
@@ -26,7 +27,7 @@ static const errorcode_t ERROR_NOT_OK = 1;
|
||||
|
||||
static const char_t *ERROR_PRINT_FORMAT = "Error (%d): %s\n%s";
|
||||
static const char_t *ERROR_LINE_FORMAT = " at %s:%d in function %s\n";
|
||||
extern errorstate_t ERROR_STATE;
|
||||
extern THREAD_LOCAL errorstate_t ERROR_STATE;
|
||||
|
||||
/**
|
||||
* Sets the error state with the provided code and message.
|
||||
@@ -52,7 +53,7 @@ errorret_t errorThrowImpl(
|
||||
|
||||
/**
|
||||
* Returns an error state with no error.
|
||||
*
|
||||
*
|
||||
* @return An error state with code ERROR_OK.
|
||||
*/
|
||||
errorret_t errorOkImpl();
|
||||
@@ -88,34 +89,6 @@ void errorCatch(errorret_t retval);
|
||||
*/
|
||||
errorret_t errorPrint(const errorret_t retval);
|
||||
|
||||
/**
|
||||
* Creates an error with a specific error state.
|
||||
*
|
||||
* @param state The error state to set.
|
||||
* @param message The format string for the error message.
|
||||
* @param ... Additional arguments for the format string.
|
||||
* @return The error code.
|
||||
*/
|
||||
#define errorCreate(state, message, ... ) \
|
||||
errorThrowImpl(\
|
||||
(state), ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \
|
||||
##__VA_ARGS__ \
|
||||
)
|
||||
|
||||
/**
|
||||
* Throws an error with a specific error state.
|
||||
*
|
||||
* @param state The error state to set.
|
||||
* @param message The format string for the error message.
|
||||
* @param ... Additional arguments for the format string.
|
||||
* @return The error code.
|
||||
*/
|
||||
#define errorThrowState(state, message, ... ) \
|
||||
return errorThrowImpl(\
|
||||
(state), ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \
|
||||
##__VA_ARGS__ \
|
||||
)
|
||||
|
||||
/**
|
||||
* Throws an error with a formatted message.
|
||||
*
|
||||
@@ -162,4 +135,18 @@ errorret_t errorPrint(const errorret_t retval);
|
||||
#define errorOk() \
|
||||
return errorOkImpl()
|
||||
|
||||
/**
|
||||
* Returns non-zero if retval indicates success.
|
||||
*
|
||||
* @param retval errorret_t to test.
|
||||
*/
|
||||
#define errorIsOk(retval) ((retval).code == ERROR_OK)
|
||||
|
||||
/**
|
||||
* Returns non-zero if retval indicates failure.
|
||||
*
|
||||
* @param retval errorret_t to test.
|
||||
*/
|
||||
#define errorIsNotOk(retval) ((retval).code != ERROR_OK)
|
||||
|
||||
// EOF
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "thread/threadlocal.h"
|
||||
#include "thread/threadmutex.h"
|
||||
|
||||
typedef struct thread_s thread_t;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#ifdef DUSK_THREAD_PTHREAD
|
||||
#define THREAD_LOCAL __thread
|
||||
#endif
|
||||
|
||||
#ifndef THREAD_LOCAL
|
||||
#error "No threading implementation found, cannot define THREAD_LOCAL."
|
||||
#endif
|
||||
Reference in New Issue
Block a user