diff --git a/src/dusk/asset/loader/assetloader.c b/src/dusk/asset/loader/assetloader.c index 121ab02c..5987246f 100644 --- a/src/dusk/asset/loader/assetloader.c +++ b/src/dusk/asset/loader/assetloader.c @@ -12,7 +12,7 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { [ASSET_LOADER_TYPE_MESH] = { .loadSync = assetMeshLoaderSync, - .loadAsync = NULL, + .loadAsync = assetMeshLoaderAsync, .dispose = assetMeshDispose }, @@ -24,19 +24,19 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { [ASSET_LOADER_TYPE_TILESET] = { .loadSync = assetTilesetLoaderSync, - .loadAsync = NULL, + .loadAsync = assetTilesetLoaderAsync, .dispose = assetTilesetDispose }, [ASSET_LOADER_TYPE_LOCALE] = { .loadSync = assetLocaleLoaderSync, - .loadAsync = NULL, + .loadAsync = assetLocaleLoaderAsync, .dispose = assetLocaleDispose }, [ASSET_LOADER_TYPE_JSON] = { .loadSync = assetJsonLoaderSync, - .loadAsync = NULL, + .loadAsync = assetJsonLoaderAsync, .dispose = assetJsonDispose }, }; diff --git a/src/dusk/asset/loader/display/assetmeshloader.c b/src/dusk/asset/loader/display/assetmeshloader.c index aa9d418a..4bc13c37 100644 --- a/src/dusk/asset/loader/display/assetmeshloader.c +++ b/src/dusk/asset/loader/display/assetmeshloader.c @@ -12,29 +12,28 @@ #include "asset/loader/assetloading.h" #include "asset/loader/assetentry.h" -errorret_t assetMeshLoaderSync(assetloading_t *loading) { +errorret_t assetMeshLoaderAsync(assetloading_t *loading) { assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_MESH, "Invalid type."); + + if(loading->loading.mesh.state != ASSET_MESH_LOADING_STATE_READ_FILE) { + errorOk(); + } assetmeshoutput_t *out = &loading->entry->data.mesh; assetfile_t *file = &loading->loading.mesh.file; assetmeshinputaxis_t axis = loading->entry->input->mesh; - assetLoaderErrorChain(loading, assetFileInit( - file, loading->entry->name, NULL, NULL - )); + assetLoaderErrorChain(loading, assetFileInit(file, loading->entry->name, NULL, NULL)); assetLoaderErrorChain(loading, assetFileOpen(file)); - // Skip the 80-byte STL header + // Skip the 80-byte STL header. assetLoaderErrorChain(loading, assetFileRead(file, NULL, 80)); if(file->lastRead != 80) { assetLoaderErrorThrow(loading, "Failed to skip STL header."); } uint32_t triangleCount; - assetLoaderErrorChain( - loading, assetFileRead(file, &triangleCount, sizeof(uint32_t)) - ); + assetLoaderErrorChain(loading, assetFileRead(file, &triangleCount, sizeof(uint32_t))); if(file->lastRead != sizeof(uint32_t)) { assetLoaderErrorThrow(loading, "Failed to read tri count"); } @@ -76,9 +75,7 @@ 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) { @@ -124,12 +121,42 @@ errorret_t assetMeshLoaderSync(assetloading_t *loading) { } assetFileDispose(file); - ret = meshInit( - &out->mesh, MESH_PRIMITIVE_TYPE_TRIANGLES, triangleCount * 3, verts + loading->loading.mesh.triangleCount = triangleCount; + loading->loading.mesh.state = ASSET_MESH_LOADING_STATE_CREATE_MESH; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; + errorOk(); +} + +errorret_t assetMeshLoaderSync(assetloading_t *loading) { + assertNotNull(loading, "Loading cannot be NULL"); + assertTrue(loading->type == ASSET_LOADER_TYPE_MESH, "Invalid type."); + + switch(loading->loading.mesh.state) { + case ASSET_MESH_LOADING_STATE_INITIAL: + loading->loading.mesh.state = ASSET_MESH_LOADING_STATE_READ_FILE; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; + errorOk(); + break; + + case ASSET_MESH_LOADING_STATE_CREATE_MESH: + break; + + default: + errorOk(); + } + + assetmeshoutput_t *out = &loading->entry->data.mesh; + assertNotNull(out->vertices, "Mesh vertices should have been loaded by now."); + + errorret_t ret = meshInit( + &out->mesh, + MESH_PRIMITIVE_TYPE_TRIANGLES, + loading->loading.mesh.triangleCount * 3, + out->vertices ); if(errorIsNotOk(ret)) { loading->entry->state = ASSET_ENTRY_STATE_ERROR; - memoryFree(verts); + memoryFree(out->vertices); out->vertices = NULL; assetLoaderErrorChain(loading, ret); } diff --git a/src/dusk/asset/loader/display/assetmeshloader.h b/src/dusk/asset/loader/display/assetmeshloader.h index 0eab0c3a..c803c526 100644 --- a/src/dusk/asset/loader/display/assetmeshloader.h +++ b/src/dusk/asset/loader/display/assetmeshloader.h @@ -25,8 +25,17 @@ typedef enum { typedef assetmeshinputaxis_t assetmeshloaderinput_t; +typedef enum { + ASSET_MESH_LOADING_STATE_INITIAL, + ASSET_MESH_LOADING_STATE_READ_FILE, + ASSET_MESH_LOADING_STATE_CREATE_MESH, + ASSET_MESH_LOADING_STATE_DONE +} assetmeshloadingstate_t; + typedef struct { assetfile_t file; + assetmeshloadingstate_t state; + uint32_t triangleCount; } assetmeshloaderloading_t; typedef struct { @@ -44,5 +53,6 @@ typedef struct { assertStructSize(assetmeshstltriangle_t, 50); +errorret_t assetMeshLoaderAsync(assetloading_t *loading); errorret_t assetMeshLoaderSync(assetloading_t *loading); errorret_t assetMeshDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/display/assettilesetloader.c b/src/dusk/asset/loader/display/assettilesetloader.c index 99ba5378..3803baf8 100644 --- a/src/dusk/asset/loader/display/assettilesetloader.c +++ b/src/dusk/asset/loader/display/assettilesetloader.c @@ -12,67 +12,97 @@ #include "asset/loader/assetloading.h" #include "asset/loader/assetentry.h" +errorret_t assetTilesetLoaderAsync(assetloading_t *loading) { + assertNotNull(loading, "Loading cannot be NULL"); + + if(loading->loading.tileset.state != ASSET_TILESET_LOADING_STATE_READ_FILE) { + errorOk(); + } + + assertNull(loading->loading.tileset.data, "Data already defined?"); + + assetfile_t *file = &loading->loading.tileset.file; + assetLoaderErrorChain(loading, assetFileInit(file, loading->entry->name, NULL, NULL)); + + uint8_t *data = memoryAllocate(file->size); + assetLoaderErrorChain(loading, assetFileOpen(file)); + assetLoaderErrorChain(loading, assetFileRead(file, data, file->size)); + assetLoaderErrorChain(loading, assetFileClose(file)); + assetLoaderErrorChain(loading, assetFileDispose(file)); + assertTrue(file->lastRead == file->size, "Failed to read entire tileset file."); + + loading->loading.tileset.data = data; + loading->loading.tileset.state = ASSET_TILESET_LOADING_STATE_PARSE; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; + errorOk(); +} + errorret_t assetTilesetLoaderSync(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; + switch(loading->loading.tileset.state) { + case ASSET_TILESET_LOADING_STATE_INITIAL: + loading->loading.tileset.state = ASSET_TILESET_LOADING_STATE_READ_FILE; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; + errorOk(); + break; + + case ASSET_TILESET_LOADING_STATE_PARSE: + break; + + default: + errorOk(); + } + + uint8_t *data = loading->loading.tileset.data; + assertNotNull(data, "Tileset data should have been loaded by now."); + tileset_t *out = &loading->entry->data.tileset; - assetLoaderErrorChain( - loading, assetFileInit(file, loading->entry->name, NULL, NULL) - ); - - uint8_t *entire = memoryAllocate(file->size); - 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); + if(data[0] != 'D' || data[1] != 'T' || data[2] != 'F') { + memoryFree(data); assetLoaderErrorThrow(loading, "Invalid tileset header"); } - if(entire[3] != 0x00) { - memoryFree(entire); + if(data[3] != 0x00) { + memoryFree(data); assetLoaderErrorThrow(loading, "Unsupported tileset version"); } - out->tileWidth = endianLittleToHost16(*(uint16_t *)(entire + 4)); - out->tileHeight = endianLittleToHost16(*(uint16_t *)(entire + 6)); - out->columns = endianLittleToHost16(*(uint16_t *)(entire + 8)); - out->rows = endianLittleToHost16(*(uint16_t *)(entire + 10)); + out->tileWidth = endianLittleToHost16(*(uint16_t *)(data + 4)); + out->tileHeight = endianLittleToHost16(*(uint16_t *)(data + 6)); + out->columns = endianLittleToHost16(*(uint16_t *)(data + 8)); + out->rows = endianLittleToHost16(*(uint16_t *)(data + 10)); if(out->tileWidth == 0) { - memoryFree(entire); + memoryFree(data); assetLoaderErrorThrow(loading, "Tile width cannot be 0"); } - if(out->tileHeight == 0) { - memoryFree(entire); + memoryFree(data); assetLoaderErrorThrow(loading, "Tile height cannot be 0"); } if(out->columns == 0) { - memoryFree(entire); + memoryFree(data); assetLoaderErrorThrow(loading, "Column count cannot be 0"); } if(out->rows == 0) { - memoryFree(entire); + memoryFree(data); assetLoaderErrorThrow(loading, "Row count cannot be 0"); } - out->uv[0] = endianLittleToHostFloat(*(float *)(entire + 16)); - out->uv[1] = endianLittleToHostFloat(*(float *)(entire + 20)); + out->uv[0] = endianLittleToHostFloat(*(float *)(data + 16)); + out->uv[1] = endianLittleToHostFloat(*(float *)(data + 20)); if(out->uv[1] < 0.0f || out->uv[1] > 1.0f) { - memoryFree(entire); + memoryFree(data); assetLoaderErrorThrow(loading, "Invalid v0 value in tileset"); } out->tileCount = out->columns * out->rows; - memoryFree(entire); + memoryFree(data); + loading->loading.tileset.data = NULL; loading->entry->state = ASSET_ENTRY_STATE_LOADED; errorOk(); diff --git a/src/dusk/asset/loader/display/assettilesetloader.h b/src/dusk/asset/loader/display/assettilesetloader.h index 6456e423..646b4064 100644 --- a/src/dusk/asset/loader/display/assettilesetloader.h +++ b/src/dusk/asset/loader/display/assettilesetloader.h @@ -16,11 +16,45 @@ typedef struct { void *nothing; } assettilesetloaderinput_t; +typedef enum { + ASSET_TILESET_LOADING_STATE_INITIAL, + ASSET_TILESET_LOADING_STATE_READ_FILE, + ASSET_TILESET_LOADING_STATE_PARSE, + ASSET_TILESET_LOADING_STATE_DONE +} assettilesetloadingstate_t; + typedef struct { assetfile_t file; + assettilesetloadingstate_t state; + + uint8_t *data; } assettilesetloaderloading_t; typedef tileset_t assettilesetoutput_t; +/** + * Asynchronous loader for tileset assets. Reads the raw DTF file bytes into + * the loading buffer so the sync phase can parse without blocking the main + * thread on I/O. + * + * @param loading Loading information for the asset being loaded. + * @return Error code indicating success or failure of the load operation. + */ +errorret_t assetTilesetLoaderAsync(assetloading_t *loading); + +/** + * Synchronous loader for tileset assets. Parses the DTF binary previously + * read by the async phase and populates the output tileset_t. + * + * @param loading Loading information for the asset being loaded. + * @return Error code indicating success or failure of the load operation. + */ errorret_t assetTilesetLoaderSync(assetloading_t *loading); + +/** + * Disposer for tileset assets. + * + * @param entry Asset entry containing the tileset to dispose. + * @return Error code indicating success or failure of the dispose operation. + */ errorret_t assetTilesetDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/json/assetjsonloader.c b/src/dusk/asset/loader/json/assetjsonloader.c index 2194622d..8ff8e501 100644 --- a/src/dusk/asset/loader/json/assetjsonloader.c +++ b/src/dusk/asset/loader/json/assetjsonloader.c @@ -11,40 +11,63 @@ #include "asset/loader/assetloading.h" #include "asset/loader/assetentry.h" -errorret_t assetJsonLoaderSync(assetloading_t *loading) { +errorret_t assetJsonLoaderAsync(assetloading_t *loading) { assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_JSON, "Invalid type."); + + if(loading->loading.json.state != ASSET_JSON_LOADING_STATE_READ_FILE) { + errorOk(); + } + + assertNull(loading->loading.json.buffer, "Buffer already defined?"); assetfile_t *file = &loading->loading.json.file; - assetLoaderErrorChain( - loading, assetFileInit(file, loading->entry->name, NULL, NULL) - ); + assetLoaderErrorChain(loading, assetFileInit(file, loading->entry->name, NULL, NULL)); if(file->size > ASSET_JSON_FILE_SIZE_MAX) { assetLoaderErrorThrow(loading, "JSON exceeds maximum allowed size"); } uint8_t *buffer = memoryAllocate(file->size); - assetLoaderErrorChain( - loading, assetFileOpen(file) - ); - assetLoaderErrorChain( - loading, 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."); - assetLoaderErrorChain( - loading, assetFileClose(file) - ); - assetLoaderErrorChain( - loading, assetFileDispose(file) - ); + assetLoaderErrorChain(loading, assetFileClose(file)); + assetLoaderErrorChain(loading, assetFileDispose(file)); + + loading->loading.json.buffer = buffer; + loading->loading.json.state = ASSET_JSON_LOADING_STATE_PARSE; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; + errorOk(); +} + +errorret_t assetJsonLoaderSync(assetloading_t *loading) { + assertNotNull(loading, "Loading cannot be NULL"); + assertTrue(loading->type == ASSET_LOADER_TYPE_JSON, "Invalid type."); + + switch(loading->loading.json.state) { + case ASSET_JSON_LOADING_STATE_INITIAL: + loading->loading.json.state = ASSET_JSON_LOADING_STATE_READ_FILE; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; + errorOk(); + break; + + case ASSET_JSON_LOADING_STATE_PARSE: + break; + + default: + errorOk(); + } + + uint8_t *buffer = loading->loading.json.buffer; + assertNotNull(buffer, "JSON buffer should have been loaded by now."); loading->entry->data.json = yyjson_read( (char *)buffer, - file->size, + loading->loading.json.file.size, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS ); memoryFree(buffer); + loading->loading.json.buffer = NULL; if(!loading->entry->data.json) { assetLoaderErrorThrow(loading, "Failed to parse JSON"); diff --git a/src/dusk/asset/loader/json/assetjsonloader.h b/src/dusk/asset/loader/json/assetjsonloader.h index af3be98d..cb0f5d73 100644 --- a/src/dusk/asset/loader/json/assetjsonloader.h +++ b/src/dusk/asset/loader/json/assetjsonloader.h @@ -15,10 +15,22 @@ typedef struct assetloading_s assetloading_t; typedef struct assetentry_s assetentry_t; typedef struct { void *nothing; } assetjsonloaderinput_t; + +typedef enum { + ASSET_JSON_LOADING_STATE_INITIAL, + ASSET_JSON_LOADING_STATE_READ_FILE, + ASSET_JSON_LOADING_STATE_PARSE, + ASSET_JSON_LOADING_STATE_DONE +} assetjsonloadingstate_t; + typedef struct { assetfile_t file; + assetjsonloadingstate_t state; + uint8_t *buffer; } assetjsonloaderloading_t; + typedef yyjson_doc * assetjsonoutput_t; +errorret_t assetJsonLoaderAsync(assetloading_t *loading); errorret_t assetJsonLoaderSync(assetloading_t *loading); errorret_t assetJsonDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.c b/src/dusk/asset/loader/locale/assetlocaleloader.c index e4ba51ef..d6840df5 100644 --- a/src/dusk/asset/loader/locale/assetlocaleloader.c +++ b/src/dusk/asset/loader/locale/assetlocaleloader.c @@ -14,9 +14,12 @@ #include "asset/loader/assetloading.h" #include "asset/loader/assetentry.h" -errorret_t assetLocaleLoaderSync(assetloading_t *loading) { +errorret_t assetLocaleLoaderAsync(assetloading_t *loading) { assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_LOCALE, "Invalid type."); + + if(loading->loading.locale.state != ASSET_LOCALE_LOADER_STATE_LOAD_HEADER) { + errorOk(); + } assetlocalefile_t *localeFile = &loading->entry->data.locale; memoryZero(localeFile, sizeof(assetlocalefile_t)); @@ -27,6 +30,29 @@ errorret_t assetLocaleLoaderSync(assetloading_t *loading) { assetLoaderErrorChain(loading, assetLocaleGetString(localeFile, "", 0, buffer, sizeof(buffer))); assetLoaderErrorChain(loading, assetLocaleParseHeader(localeFile, buffer, sizeof(buffer))); + loading->loading.locale.state = ASSET_LOCALE_LOADER_STATE_DONE; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; + errorOk(); +} + +errorret_t assetLocaleLoaderSync(assetloading_t *loading) { + assertNotNull(loading, "Loading cannot be NULL"); + assertTrue(loading->type == ASSET_LOADER_TYPE_LOCALE, "Invalid type."); + + switch(loading->loading.locale.state) { + case ASSET_LOCALE_LOADER_STATE_INITIAL: + loading->loading.locale.state = ASSET_LOCALE_LOADER_STATE_LOAD_HEADER; + loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; + errorOk(); + break; + + case ASSET_LOCALE_LOADER_STATE_DONE: + break; + + default: + errorOk(); + } + loading->entry->state = ASSET_ENTRY_STATE_LOADED; errorOk(); } diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.h b/src/dusk/asset/loader/locale/assetlocaleloader.h index 34079393..abd51b03 100644 --- a/src/dusk/asset/loader/locale/assetlocaleloader.h +++ b/src/dusk/asset/loader/locale/assetlocaleloader.h @@ -14,8 +14,16 @@ typedef struct assetentry_s assetentry_t; /** Input passed to the locale loader — currently unused. */ typedef struct { void *nothing; } assetlocaleloaderinput_t; +typedef enum { + ASSET_LOCALE_LOADER_STATE_INITIAL, + ASSET_LOCALE_LOADER_STATE_LOAD_HEADER, + ASSET_LOCALE_LOADER_STATE_DONE +} assetlocaleloaderstate_t; + /** Per-slot scratch data used while the locale file is loading. */ -typedef struct { void *nothing; } assetlocaleloaderloading_t; +typedef struct { + assetlocaleloaderstate_t state; +} assetlocaleloaderloading_t; /** Maximum number of distinct plural forms a locale file may declare. */ #define ASSET_LOCALE_FILE_PLURAL_FORM_COUNT 6 @@ -96,10 +104,19 @@ typedef struct { typedef assetlocalefile_t assetlocaleoutput_t; /** - * Synchronous loader callback. Opens and validates the locale file, reads the - * PO header, and parses plural-form rules. Sets entry state to - * `ASSET_ENTRY_STATE_LOADED` on success or `ASSET_ENTRY_STATE_ERROR` on - * failure. + * Asynchronous loader callback. Opens the locale file, reads the PO header, + * and parses plural-form rules. All I/O happens here so the main thread is + * not blocked. Sets entry state to `ASSET_ENTRY_STATE_PENDING_SYNC` on + * success or `ASSET_ENTRY_STATE_ERROR` on failure. + * + * @param loading The loading slot for this asset entry. + * @return OK on success, error otherwise. + */ +errorret_t assetLocaleLoaderAsync(assetloading_t *loading); + +/** + * Synchronous loader callback. Confirms the async phase completed and marks + * the entry as `ASSET_ENTRY_STATE_LOADED`. * * @param loading The loading slot for this asset entry. * @return OK on success, error otherwise.