Example scene working

This commit is contained in:
2026-06-06 18:46:08 -05:00
parent 003b647d83
commit 9edb2aa0c1
13 changed files with 342 additions and 81 deletions
+41
View File
@@ -124,9 +124,11 @@ static void test_update_entry_reaches_loaded(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assert_int_equal(entry->state, ASSET_ENTRY_STATE_NOT_STARTED);
assetEntryLock(entry);
errorret_t ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(entry);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -134,11 +136,13 @@ static void test_update_entry_reaches_loaded(void **state) {
static void test_update_slot_occupied_after_first_update(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(entry);
assetUpdate();
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
// Slot not cleared until the second update processes the LOADED case.
assert_true(loading_slot_has_entry(entry));
assetEntryUnlock(entry);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -146,11 +150,13 @@ static void test_update_slot_occupied_after_first_update(void **state) {
static void test_update_slot_cleared_after_second_update(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(entry);
assetUpdate();
assetUpdate();
assert_false(loading_slot_has_entry(entry));
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(entry);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -162,6 +168,7 @@ static void test_update_four_slots_fill_independently(void **state) {
char_t name[ASSET_FILE_NAME_MAX];
snprintf(name, sizeof(name), "asset%d.locale", i);
entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(entries[i]);
}
errorret_t ret = assetUpdate();
@@ -169,6 +176,7 @@ static void test_update_four_slots_fill_independently(void **state) {
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
assert_int_equal(entries[i]->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(entries[i]);
}
assert_int_equal(memoryGetAllocatedCount(), 0);
@@ -201,6 +209,8 @@ static void test_update_noop_on_empty_table(void **state) {
static void test_update_loaded_entry_not_redispatched(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(entry);
assetUpdate();
assetUpdate(); // slot freed
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
@@ -212,6 +222,8 @@ static void test_update_loaded_entry_not_redispatched(void **state) {
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
assert_false(loading_slot_has_entry(entry));
assetEntryUnlock(entry);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -226,6 +238,11 @@ static void test_update_overflow_queues_entries(void **state) {
entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL);
}
// Lock first batch so the reaper won't collect them before we check.
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
assetEntryLock(entries[i]);
}
// Update 1: fills all slots, first ASSET_LOADING_COUNT_MAX entries reach LOADED.
// The overflow entry has no slot yet and stays NOT_STARTED.
errorret_t ret = assetUpdate();
@@ -235,17 +252,27 @@ static void test_update_overflow_queues_entries(void **state) {
}
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
// Unlock first batch — the reaper can collect them in update 2.
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
assetEntryUnlock(entries[i]);
}
// Update 2: LOADED slots are freed. Overflow entry still NOT_STARTED because
// the dispatch phase ran before the slots were cleared this turn.
ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
// Lock overflow entry so it stays LOADED after update 3.
assetEntryLock(entries[ASSET_LOADING_COUNT_MAX]);
// Update 3: now a slot is available; the overflow entry is dispatched and loaded.
ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(entries[ASSET_LOADING_COUNT_MAX]);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -340,6 +367,8 @@ static void test_update_reentrant_sync_loader(void **state) {
static void test_getEntry_returns_loaded_entry(void **state) {
assetentry_t *a = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(a);
assetUpdate();
assert_int_equal(a->state, ASSET_ENTRY_STATE_LOADED);
@@ -348,6 +377,7 @@ static void test_getEntry_returns_loaded_entry(void **state) {
assetentry_t *b = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assert_ptr_equal(a, b);
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(a);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -358,9 +388,12 @@ static void test_getEntry_returns_loaded_entry(void **state) {
static void test_entry_dispose_clears_entry(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(entry);
assetUpdate();
assetUpdate(); // ensure loading slot is freed before disposing
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(entry);
errorret_t ret = assetEntryDispose(entry);
assert_true(errorIsOk(ret));
@@ -371,8 +404,11 @@ static void test_entry_dispose_clears_entry(void **state) {
static void test_entry_dispose_slot_reusable(void **state) {
assetentry_t *a = assetGetEntry("a.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(a);
assetUpdate();
assetUpdate();
assetEntryUnlock(a);
assert_false(loading_slot_has_entry(a));
assetEntryDispose(a);
@@ -383,9 +419,11 @@ static void test_entry_dispose_slot_reusable(void **state) {
assert_non_null(b);
assert_int_equal(b->state, ASSET_ENTRY_STATE_NOT_STARTED);
assetEntryLock(b);
errorret_t ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(b);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
@@ -396,8 +434,11 @@ static void test_entry_dispose_slot_reusable(void **state) {
static void test_requireLoaded_already_loaded(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetEntryLock(entry);
assetUpdate();
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
assetEntryUnlock(entry);
// Should return immediately without calling assetUpdate again.
errorret_t ret = assetRequireLoaded(entry);
+33 -7
View File
@@ -10,6 +10,7 @@
#include "asset/loader/assetloader.h"
#include "asset/loader/assetentry.h"
#include "asset/loader/json/assetjsonloader.h"
#include "thread/thread.h"
#include "util/memory.h"
#include <zip.h>
@@ -20,6 +21,32 @@
static const char_t *JSON_VALID = "{\"hello\":\"world\",\"count\":42}";
static const char_t *JSON_INVALID = "{ this is definitely not valid json !!!";
// ============================================================
// Async thread helper
// ============================================================
typedef struct {
assetloading_t *loading;
bool_t ok;
} json_async_run_t;
static void json_async_thread_cb(thread_t *thread) {
json_async_run_t *run = (json_async_run_t *)thread->data;
errorret_t ret = assetJsonLoaderAsync(run->loading);
run->ok = errorIsOk(ret);
if(errorIsNotOk(ret)) errorCatch(ret);
}
static bool_t run_json_async(assetloading_t *loading) {
json_async_run_t run = { .loading = loading, .ok = false };
thread_t thread;
threadInit(&thread, json_async_thread_cb);
thread.data = &run;
threadStart(&thread);
threadStop(&thread);
return run.ok;
}
// ============================================================
// In-memory ZIP
// ============================================================
@@ -110,8 +137,10 @@ static errorret_t loader_ctx_run(loader_ctx_t *ctx) {
errorret_t ret = assetJsonLoaderSync(&ctx->loading);
if(errorIsNotOk(ret)) return ret;
ret = assetJsonLoaderAsync(&ctx->loading);
if(errorIsNotOk(ret)) return ret;
if(!run_json_async(&ctx->loading)) {
ctx->entry.state = ASSET_ENTRY_STATE_ERROR;
errorThrow("Async JSON load failed");
}
return assetJsonLoaderSync(&ctx->loading);
}
@@ -162,8 +191,7 @@ static void test_json_parse_error(void **state) {
errorret_t ret = assetJsonLoaderSync(&ctx.loading);
assert_true(errorIsOk(ret));
ret = assetJsonLoaderAsync(&ctx.loading);
assert_true(errorIsOk(ret));
assert_true(run_json_async(&ctx.loading));
ret = assetJsonLoaderSync(&ctx.loading);
assert_true(errorIsNotOk(ret));
@@ -184,9 +212,7 @@ static void test_json_missing_file(void **state) {
assert_true(errorIsOk(ret));
// Async phase stat-fails because the file isn't in the ZIP.
ret = assetJsonLoaderAsync(&ctx.loading);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_false(run_json_async(&ctx.loading));
assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR);
+34 -9
View File
@@ -10,6 +10,7 @@
#include "asset/loader/assetloader.h"
#include "asset/loader/assetentry.h"
#include "asset/loader/display/assettilesetloader.h"
#include "thread/thread.h"
#include "util/memory.h"
#include <zip.h>
@@ -91,6 +92,32 @@ static const uint8_t DTF_INVALID_UV[] = {
0x00, 0x00, 0x00, 0x40
};
// ============================================================
// Async thread helper
// ============================================================
typedef struct {
assetloading_t *loading;
bool_t ok;
} tileset_async_run_t;
static void tileset_async_thread_cb(thread_t *thread) {
tileset_async_run_t *run = (tileset_async_run_t *)thread->data;
errorret_t ret = assetTilesetLoaderAsync(run->loading);
run->ok = errorIsOk(ret);
if(errorIsNotOk(ret)) errorCatch(ret);
}
static bool_t run_tileset_async(assetloading_t *loading) {
tileset_async_run_t run = { .loading = loading, .ok = false };
thread_t thread;
threadInit(&thread, tileset_async_thread_cb);
thread.data = &run;
threadStart(&thread);
threadStop(&thread);
return run.ok;
}
// ============================================================
// In-memory ZIP
// ============================================================
@@ -183,8 +210,10 @@ static errorret_t loader_ctx_run(loader_ctx_t *ctx) {
errorret_t ret = assetTilesetLoaderSync(&ctx->loading);
if(errorIsNotOk(ret)) return ret;
ret = assetTilesetLoaderAsync(&ctx->loading);
if(errorIsNotOk(ret)) return ret;
if(!run_tileset_async(&ctx->loading)) {
ctx->entry.state = ASSET_ENTRY_STATE_ERROR;
errorThrow("Async tileset load failed");
}
return assetTilesetLoaderSync(&ctx->loading);
}
@@ -238,8 +267,7 @@ static void test_tileset_bad_magic(void **state) {
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
assert_true(errorIsOk(ret));
ret = assetTilesetLoaderAsync(&ctx.loading);
assert_true(errorIsOk(ret));
assert_true(run_tileset_async(&ctx.loading));
ret = assetTilesetLoaderSync(&ctx.loading);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
@@ -255,8 +283,7 @@ static void test_tileset_bad_version(void **state) {
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
assert_true(errorIsOk(ret));
ret = assetTilesetLoaderAsync(&ctx.loading);
assert_true(errorIsOk(ret));
assert_true(run_tileset_async(&ctx.loading));
ret = assetTilesetLoaderSync(&ctx.loading);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
@@ -328,9 +355,7 @@ static void test_tileset_missing_file(void **state) {
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
assert_true(errorIsOk(ret));
ret = assetTilesetLoaderAsync(&ctx.loading);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_false(run_tileset_async(&ctx.loading));
assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR);
loader_ctx_dispose(&ctx);