126 lines
4.1 KiB
Markdown
126 lines
4.1 KiB
Markdown
# Asset System
|
|
|
|
Source: `src/dusk/asset/`
|
|
|
|
## Overview
|
|
|
|
All game assets are packed into a single ZIP archive named `dusk.dsk`
|
|
(`ASSET_FILE_NAME`). The asset system loads entries from this archive
|
|
asynchronously on a background thread, caches them, and provides
|
|
synchronous blocking access when an asset is required immediately.
|
|
|
|
## Key limits
|
|
|
|
| Constant | Value | Meaning |
|
|
|----------|-------|---------|
|
|
| `ASSET_LOADING_COUNT_MAX` | 4 | Concurrent in-flight loads |
|
|
| `ASSET_ENTRY_COUNT_MAX` | 128 | Cached entries |
|
|
|
|
## Top-level API (`asset.h`)
|
|
|
|
```c
|
|
errorret_t assetInit(); // Open dusk.dsk, start background thread
|
|
void assetUpdate(); // Dispatch completed-load callbacks (main thread)
|
|
errorret_t assetDispose(); // Wait for loads, close archive
|
|
|
|
assetentry_t *assetGetEntry(
|
|
const char_t *path,
|
|
assetloadertype_t type,
|
|
assetloaderinput_t *input
|
|
); // Get (or create) a cache entry; does NOT start loading
|
|
|
|
errorret_t assetRequireLoaded(assetentry_t *entry);
|
|
// Block the calling thread until this entry is fully loaded.
|
|
// Only safe to call from the main thread.
|
|
|
|
void assetLock(assetentry_t *entry);
|
|
void assetUnlock(assetentry_t *entry);
|
|
// Reference counting. Lock before using loaded data; unlock when done.
|
|
// The entry will not be evicted while locked.
|
|
```
|
|
|
|
## Asset entry states
|
|
|
|
Each cache entry goes through a state machine:
|
|
|
|
```
|
|
IDLE -> QUEUED -> READING (async) -> PROCESSING (sync, main thread) -> LOADED
|
|
-> ERROR
|
|
```
|
|
|
|
- **READING** runs on the background loader thread (file I/O).
|
|
- **PROCESSING** runs on the main thread (GPU uploads, parsing finalization).
|
|
- Once LOADED, data is available in `entry->data`.
|
|
|
|
## Loader types
|
|
|
|
Loader types are registered in the `ASSET_LOADER_CALLBACKS[]` table.
|
|
Each type implements three callbacks: `loadSync`, `loadAsync`, `dispose`.
|
|
|
|
| `assetloadertype_t` | Data read | Description |
|
|
|---------------------|-----------|-------------|
|
|
| `ASSET_LOADER_TYPE_TEXTURE` | STB image | Loads image bytes async, creates GPU texture sync |
|
|
| `ASSET_LOADER_TYPE_TILESET` | `.dtf` binary | Custom tile format (magic, version, grid, UVs) |
|
|
| `ASSET_LOADER_TYPE_MESH` | `.stl` | STL mesh with configurable axis orientation |
|
|
| `ASSET_LOADER_TYPE_JSON` | yyjson | Up to 256 KB; parsed async |
|
|
| `ASSET_LOADER_TYPE_LOCALE` | Gettext `.po` | PO parser with plural-form expression evaluation |
|
|
| `ASSET_LOADER_TYPE_SCRIPT` | JS source | JerryScript module |
|
|
|
|
## Adding a new loader type
|
|
|
|
1. Add an enum value before `_COUNT` in `assetloadertype_t`
|
|
(`src/dusk/asset/loader/assetloader.h`).
|
|
2. Add fields to the input/loading/output unions in `assetloader.h`.
|
|
3. Implement `assetXxxLoaderSync`, `assetXxxLoaderAsync`, and
|
|
`assetXxxDispose` in `src/dusk/asset/loader/xxx/`.
|
|
4. Register the three callbacks in `ASSET_LOADER_CALLBACKS[]` in
|
|
`src/dusk/asset/loader/assetloader.c`.
|
|
5. If user-facing, create a JS module and a `.d.ts` file (see `CLAUDE.md`).
|
|
|
|
## Asset batch (`assetbatch.h`)
|
|
|
|
`assetbatch_t` groups multiple asset requests into a single logical
|
|
load. All entries in the batch start loading concurrently. The batch
|
|
fires a completion callback once every entry has reached LOADED (or
|
|
ERROR).
|
|
|
|
```c
|
|
assetbatch_t batch;
|
|
assetBatchInit(&batch, entries, count, onComplete, user);
|
|
assetBatchStart(&batch);
|
|
// ... later, after assetUpdate() fires the callback ...
|
|
assetBatchDispose(&batch);
|
|
```
|
|
|
|
## Usage pattern
|
|
|
|
```c
|
|
// 1. Get or create the cache entry (no I/O yet).
|
|
assetentry_t *tex = assetGetEntry(
|
|
"textures/hero.png",
|
|
ASSET_LOADER_TYPE_TEXTURE,
|
|
NULL
|
|
);
|
|
assetLock(tex);
|
|
|
|
// 2. Option A -- non-blocking: check tex->state each frame.
|
|
// Option B -- blocking (main thread only):
|
|
errorChain(assetRequireLoaded(tex));
|
|
|
|
// 3. Use the loaded data.
|
|
texture_t *t = &tex->data.texture;
|
|
|
|
// 4. Release when done.
|
|
assetUnlock(tex);
|
|
```
|
|
|
|
## Error macros (inside loader implementations)
|
|
|
|
```c
|
|
assetLoaderErrorThrow("msg %d", val); // errorThrow equivalent
|
|
assetLoaderErrorChain(someCall()); // errorChain equivalent
|
|
```
|
|
|
|
Use these instead of the bare error macros inside loader callbacks so
|
|
that failures include the loader context in the stack trace.
|