diff options
Diffstat (limited to 'doc/ASSET_SYSTEM.md')
| -rw-r--r-- | doc/ASSET_SYSTEM.md | 317 |
1 files changed, 77 insertions, 240 deletions
diff --git a/doc/ASSET_SYSTEM.md b/doc/ASSET_SYSTEM.md index aea99a2..4e34378 100644 --- a/doc/ASSET_SYSTEM.md +++ b/doc/ASSET_SYSTEM.md @@ -1,271 +1,108 @@ -# compact asset system for the 64k demo +# Compact Asset System for the 64k Demo -This file describe the features of a compact asset system used in the demo. +This file describes the asset system architecture and runtime API. -The idea is the following: +## Core Concept -# run-time asset retrieval: - All assets are const byte arrays. We need a 'const uint8_t* GetAsset(uint16 asset_id)' - The asset ID is defined in a 'assets.h' header, generated during the final - compile by our own assembling tool. assets.h needs to be re-generated - infrequently. +All assets are const byte arrays embedded in the binary. Runtime retrieval via `GetAsset(AssetId)` returns direct pointers with O(1) lookup. -# assembling the assets: +## Runtime Asset Retrieval -## description file 'assets.txt' - All assets are just files in the assets/final/ directory - This directory needs a assets.txt text file to describe the asset files. - Each line of the assets.txt file contain, comma-separated: - * the name of the asset (that will be used by the #define in assets.h), - * the name of the file associated - * the compression to use for this asset (default NONE. More options later) - * and optionally the type of assets. - -## example For instance, a line in assets.txt will read: - -SAMPLE_142, sample_142.spec, NONE, "this is a drum kick sample" - -This instructs the final assembled file assets.h to have a code line: - #define ASSET_SAMPLE_142 6323 +```cpp +#include "assets.h" +const uint8_t* mysample = GetAsset(ASSET_SAMPLE_142, &size); +// ... use asset +DropAsset(ASSET_SAMPLE_142, mysample); // For lazy decompression cleanup +``` -(6323 is just an associated id) +## Asset Manifest Format -(or an enum instead of #define's) +File: `assets/final/demo_assets.txt` -so that we can call +Format (CSV): ``` -#include "asset.h" -const uint8_t* mysample = GetAsset(ASSET_SAMPLE_142); -...etc +ASSET_NAME, filename.ext, COMPRESSION, "Description" ``` -(if we use enums, GetAssert() signature needs to be changed) - -### Lazy decompression -to save memory some assets can be decompressed 'at retrieval time' but kept -compressed in memory until then. -This means that we need a 'void DropAsset(uint16 asset_id, const uint8* asset)' -method to handle memory disallocation depending on the asset type. - -### assembling tool - -we need a simple tool that: - * takes the assets.txt file and parse it - * generates the assets.h file with asset enums - * generates the assets_data.cc file with all the data - * put these in the source tree - * this process needs a script for automation - -## Technical Details - -To support diverse usage (binary data, strings, typed structs), the asset packer enforces the following: -1. **Alignment**: All static asset arrays are declared with `alignas(16)`. This ensures that `reinterpret_cast` to types like `float*` or SIMD vectors is safe (provided the data layout matches). -2. **String Safety**: Every static asset is appended with a null-terminator (`0x00`). This allows the raw pointer to be safely used as a `const char*` C-string. -3. **Size Reporting**: The `size` field in `AssetRecord` reflects the *original* file size. However, the underlying buffer is guaranteed to be at least `size + 1` bytes long (due to the null terminator). - -# Shader Snippets - -Shader code (WGSL) can also be managed as assets. -1. Create `.wgsl` files in `assets/final/shaders/` (or similar). -2. Add them to `assets/final/demo_assets.txt` with compression `NONE`. - Example: `SHADER_COMMON, shaders/common.wgsl, NONE` -3. In C++, retrieve them using `GetAsset()`. Since they are binary blobs, - construct a `std::string` or `std::string_view` using the returned pointer - and size. -4. Register them with the `ShaderComposer`. - ---- - -# Current Implementation Status - -## What's Working ✅ -- **Build-Time Packer** (`tools/asset_packer.cc`): Parses `demo_assets.txt`, embeds binary files as C++ arrays -- **Runtime Manager** (`src/util/asset_manager.{h,cc}`): O(1) retrieval with caching, SIOF fix via singleton -- **Asset Types**: Static binaries (`.spec`, `.wgsl`), procedural textures (`PROC(func, params)`) -- **Safety Guarantees**: 16-byte alignment, null-terminator for strings, size reporting -- **Testing**: `test_assets.cc` validates retrieval, caching, procedural generation -- **Current Manifest**: 33 assets (15 audio samples, 17 WGSL shaders, 1 procedural texture) - -## Design Strengths -- Enum-based type safety (no magic numbers or strings at runtime) -- Zero runtime cost for static assets (direct pointer return) -- Clean separation of build-time and runtime concerns -- Procedural generation support (saves binary size) -- Thread-safe for immutable assets (cache is write-once) - -## Design Weaknesses -- **No compression**: Only `NONE` supported, critical blocker for 64k goal -- **STL dependencies**: `std::map`, `std::string` in runtime code (conflicts with CRT replacement) -- **Hardcoded procedural dimensions**: Assumes 256×256 RGBA8 (lines 85-86 in `asset_manager.cc`) -- **No integrity checks**: No CRC/hash validation for asset correctness -- **Limited asset types**: No mesh/scene format yet (needed for Task #18) - ---- - -# Asset System Improvement Tasks - -## High Priority (Critical for 64k Goal) - -### Task #27: Asset Compression Layer -**Goal**: Implement runtime decompression to reduce binary size by 30-50%. - -**Attack Plan**: -- [ ] **27.1**: Add compression type enum (`NONE`, `ZLIB`, `RLE`, `CUSTOM`) -- [ ] **27.2**: Update `asset_packer.cc` to compress assets during build - - Integrate a lightweight compression library (e.g., miniz for zlib) - - Store compressed size + original size in `AssetRecord` -- [ ] **27.3**: Implement runtime decompressor in `asset_manager.cc` - - Allocate decompressed buffer on first `GetAsset()` call - - Cache decompressed data, allow `DropAsset()` to free memory -- [ ] **27.4**: Update `assets.txt` format to specify compression per-asset - - Example: `KICK_1, ZLIB, kick1.spec, "Compressed kick sample"` -- [ ] **27.5**: Add tests for compressed asset retrieval and DropAsset - -**Size Impact**: Estimated 30-50% reduction on asset blob (shaders compress well, spectrograms moderately). - -**Dependency**: None (standalone improvement). - ---- - -### Task #28: Spectrogram Quantization -**Goal**: Compress audio assets from `float32` to `uint16_t` with logarithmic frequency binning. - -**Attack Plan**: -- [ ] **28.1**: Research optimal frequency bin distribution (logarithmic vs linear) -- [ ] **28.2**: Update `spectool` to export quantized `.spec` format - - Add `--quantize` flag to `spectool analyze` - - Map spectral values to `uint16_t` range with configurable dynamic range -- [ ] **28.3**: Update audio synthesis engine to decode quantized spectrograms - - Add dequantization step in `src/audio/synth.cc` -- [ ] **28.4**: Re-generate all `.spec` assets with quantization enabled -- [ ] **28.5**: Add tests to verify audio quality degradation is acceptable - -**Size Impact**: 2-4× reduction on `.spec` files (currently 15 audio assets in manifest). - -**Dependency**: Task #27 (compression) for maximum size savings. - ---- - -### Task #29: WGSL Shader Minification -**Goal**: Minify WGSL shaders to reduce text asset size. - -**Attack Plan**: -- [ ] **29.1**: Implement WGSL minifier in `tools/wgsl_minify.py` - - Remove comments, unnecessary whitespace - - Rename variables to single-letter identifiers (preserve entry points) - - Strip debug labels and unused code -- [ ] **29.2**: Integrate minifier into build pipeline - - Add `MINIFY` compression type to `asset_packer.cc` - - Auto-minify all `SHADER_*` assets during build -- [ ] **29.3**: Validate minified shaders compile correctly - - Add shader compilation test to CI/CD - -**Size Impact**: 40-60% reduction on shader text (currently 17 WGSL shaders in manifest). - -**Dependency**: None (standalone improvement). - ---- - -## Medium Priority (Code Hygiene & Maintainability) - -### Task #20.1: Remove STL from Asset Manager (Part of Task #20) - COMPLETED -**Goal**: Eliminate `std::map` and `std::string` to prepare for CRT replacement. -**Status**: Initial removal for `AssetManager` hot paths completed. Full removal across the codebase deferred to Phase 2. - ---- - -### Task #30: Procedural Asset Generalization -**Goal**: Support variable dimensions and formats for procedural textures. - -**Attack Plan**: -- [ ] **30.1**: Update `assets.txt` format to encode dimensions in `proc_params` - - Example: `NOISE_TEX, PROC(gen_noise, 256, 256, 4, 1234, 16), _, "Width, Height, Channels, Seed, Freq"` -- [ ] **30.2**: Update `asset_packer.cc` to parse dimension parameters -- [ ] **30.3**: Update `GetAsset()` to read dimensions from `proc_params` - - Allocate buffer dynamically based on `width * height * channels` -- [ ] **30.4**: Add support for grayscale (1 channel) and RGB (3 channels) formats - -**Size Impact**: Negligible (unlocks new use cases, no binary size change). - -**Dependency**: None. - ---- - -### Task #31: Asset Integrity Validation -**Goal**: Add CRC32/hash checks to detect corrupted or incorrect assets. - -**Attack Plan**: -- [ ] **31.1**: Compute CRC32 for each asset during build (`asset_packer.cc`) - - Store CRC in `AssetRecord` struct -- [ ] **31.2**: Add optional validation in `GetAsset()` (debug builds only) - - Compare computed CRC against stored value - - Log warnings if mismatch detected -- [ ] **31.3**: Add `--verify-assets` flag to test suite - - Run full integrity check on all assets - -**Size Impact**: +4 bytes per asset (CRC32), negligible overhead. +Example: +``` +SAMPLE_142, sample_142.spec, NONE, "This is a drum kick sample" +SHADER_COMMON, shaders/common.wgsl, NONE, "Common utilities" +``` -**Dependency**: None. +This generates: +```cpp +enum class AssetId : uint16_t { + ASSET_SAMPLE_142 = 6323, + SHADER_COMMON = 6324, + // ... +}; +``` ---- +## Build Pipeline -## Lower Priority (Future Enhancements) +Tool: `tools/asset_packer.cc` -### Task #18.1: 3D Asset Format Support (Part of Task #18) -**Goal**: Extend asset system to handle binary mesh/scene data from Blender. +1. Parse `demo_assets.txt` +2. Read binary files from `assets/final/` +3. Generate `assets.h` (enum definitions) +4. Generate `assets_data.cc` (embedded byte arrays) +5. Auto-triggered by CMake on manifest changes -**Attack Plan**: -- [ ] **18.1.1**: Define binary mesh format (vertices, indices, normals, UVs) - - Consider alignment requirements for GPU upload -- [ ] **18.1.2**: Update `asset_packer.cc` to handle `.mesh` files - - Add `MESH3D` asset type -- [ ] **18.1.3**: Implement runtime parser in `src/3d/mesh_loader.cc` - - Parse binary blob into `Mesh` struct -- [ ] **18.1.4**: Create Blender export script (`tools/blender_export.py`) +## Technical Guarantees -**Size Impact**: Depends on scene complexity (meshes can be large, consider compression). +1. **Alignment**: All arrays declared `alignas(16)` for safe `reinterpret_cast` +2. **String Safety**: Null-terminator appended (safe as C-string) +3. **Size Reporting**: `size` reflects original file size (buffer is `size + 1`) -**Dependency**: Task #27 (compression) for mesh data. +## Shader Asset Usage ---- +```cpp +size_t size; +const uint8_t* shader_src = GetAsset(AssetId::SHADER_COMMON, &size); +std::string_view code(reinterpret_cast<const char*>(shader_src), size); +ShaderComposer::register_snippet("common", code); +``` -### Task #32: Asset Hot-Reloading (Development Only) -**Goal**: Enable live asset updates for faster iteration (under `!STRIP_ALL`). +## Procedural Assets -**Attack Plan**: -- [ ] **32.1**: Add file watcher for `assets/final/` directory - - Monitor `.spec`, `.wgsl` files for changes -- [ ] **32.2**: Trigger rebuild of `assets_data.cc` on file change - - Invoke `asset_packer` and recompile -- [ ] **32.3**: Add API to reload specific assets at runtime - - `ReloadAsset(AssetId)` for development use +Format: `PROC(function_name, params...)` -**Size Impact**: None (stripped from final build). +Example: +``` +NOISE_TEX, PROC(gen_noise, 1234, 16), NONE, "Noise texture seed=1234 freq=16" +``` -**Dependency**: None (development-only feature). +Runtime: First `GetAsset()` call invokes generator, caches result. ---- +## Design Strengths -### Task #33: Asset Usage Tracking & Dead Asset Elimination -**Goal**: Identify and remove unused assets to reduce binary size. +- Enum-based type safety (no magic numbers) +- Zero runtime cost for static assets +- Procedural generation support (size savings) +- Thread-safe for immutable assets -**Attack Plan**: -- [ ] **33.1**: Add instrumentation to track `GetAsset()` calls - - Log which assets are accessed during a full demo run -- [ ] **33.2**: Generate report of unused assets -- [ ] **33.3**: Remove unused entries from `demo_assets.txt` +## Design Weaknesses -**Size Impact**: Depends on dead assets (could save 5-15% if unused assets exist). +- **No compression**: Only `NONE` supported (critical blocker for 64k) +- **STL dependencies**: `std::map` in runtime code (conflicts with CRT removal) +- **Hardcoded procedural dimensions**: Assumes 256×256 RGBA8 +- **No integrity checks**: No CRC/hash validation -**Dependency**: None. +## Planned Improvements ---- +See **TODO.md** for detailed task breakdown: +- **Task #27**: Asset compression (zlib, RLE) +- **Task #28**: Spectrogram quantization (float32 → uint16_t) +- **Task #29**: WGSL shader minification +- **Task #30**: Procedural asset generalization +- **Task #31**: Asset integrity validation (CRC32) +- **Task #32**: Hot-reloading (dev builds) +- **Task #33**: Dead asset elimination -# Priority Recommendation +## Current Manifest -For the **64k goal**, prioritize in this order: -1. **Task #27** (Compression) - Biggest size win -2. **Task #29** (Shader Minification) - Easy win, low risk -3. **Task #28** (Spectrogram Quantization) - Medium effort, high impact -4. **Task #18.1** (3D Assets) - Once size budget allows -5. **Task #34: Full STL Removal** - Deferred to final optimization phase. +33 assets total: +- 15 audio samples (`.spec`) +- 17 WGSL shaders +- 1 procedural texture |
