diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-11 11:52:49 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-11 11:52:49 +0100 |
| commit | 58c595810d0346f0c2b32dbc70b7385aee545e11 (patch) | |
| tree | 09db51ba3d849a1f3ac3da715d038ddbbece1122 /doc | |
| parent | 0b756c0d0c9e968b2182ea720a8a24afd5c4bc0a (diff) | |
docs: Add auxiliary texture initialization tech doc
Documents the "half resolution" bug, root cause analysis, and
solution decision (resize before init vs lazy initialization).
Key points:
- Problem: Auxiliary textures created with default dimensions
- Root cause: init() called before resize()
- Solution: Swap order (resize → init) for 2-line fix
- Rejected: Lazy initialization (too complex, cascade effects)
Includes implementation details and guidelines for new effects.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/AUXILIARY_TEXTURE_INIT.md | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/doc/AUXILIARY_TEXTURE_INIT.md b/doc/AUXILIARY_TEXTURE_INIT.md new file mode 100644 index 0000000..e4200d1 --- /dev/null +++ b/doc/AUXILIARY_TEXTURE_INIT.md @@ -0,0 +1,166 @@ +# Auxiliary Texture Initialization + +**Technical decision document for auxiliary texture initialization order** + +## Problem + +Auxiliary textures (inter-effect shared textures like masks) were created with incorrect dimensions, causing "half resolution" rendering bugs. + +### Root Cause + +```cpp +// Before fix (effect.cc:180-181) +entry.seq->init(this); // width_/height_ = 1280x720 (defaults) +entry.seq->resize(width, height); // THEN set actual dimensions (e.g., 2560x1440) +``` + +Effects called `register_auxiliary_texture()` during `init()` using default dimensions (1280×720) before `resize()` set actual window size. Compute shaders received uniforms with correct resolution but rendered to wrong-sized textures. + +### Affected Code + +- `CircleMaskEffect` - registers `"circle_mask"` texture +- `CNNEffect` - registers `"captured_frame"` texture +- `RotatingCubeEffect` - consumes `"circle_mask"` texture +- Any effect that calls `register_auxiliary_texture()` in `init()` + +## Solutions Considered + +### Option 1: Lazy Initialization ❌ + +**Approach:** Defer texture creation until first use (compute/render). + +**Pros:** +- Textures created with correct dimensions +- No reallocation on resize if dimensions unchanged +- Memory saved if effect never renders + +**Cons:** +- Requires `ensure_texture()` guards at every access point +- Cascade complexity: consumers (like RotatingCubeEffect) also need lazy bind group creation +- Multiple code paths, harder to debug +- Violates "initialize once" principle + +**Outcome:** Attempted but rejected due to complexity cascade. + +### Option 2: Reorder init/resize ✅ + +**Approach:** Call `resize()` before `init()` to set dimensions first. + +```cpp +// After fix (effect.cc:179-180) +entry.seq->resize(width, height); // Set dimensions FIRST +entry.seq->init(this); // Then init with correct dimensions +``` + +**Pros:** +- **Simple:** 2-line change in MainSequence +- Dimensions available when needed during init() +- No lazy initialization complexity +- Clear, predictable initialization order + +**Cons:** +- Effects see resize() before init() (unusual but harmless) +- Base `Effect::resize()` just updates width_/height_ members + +**Outcome:** Implemented. Clean, maintainable solution. + +## Implementation + +### Changes Made + +**File:** `src/gpu/effect.cc` + +**Lines 179-180:** +```cpp +entry.seq->resize(width, height); // Set dimensions FIRST +entry.seq->init(this); // Then init with correct dimensions +``` + +**Lines 189-190:** +```cpp +seq->resize(width_, height_); // Set dimensions FIRST +seq->init(this); // Then init with correct dimensions +``` + +### Resize Optimization + +Added early return in effects to avoid unnecessary GPU reallocation: + +```cpp +void CircleMaskEffect::resize(int width, int height) { + if (width == width_ && height == height_) + return; // No-op if dimensions unchanged + + Effect::resize(width, height); + + if (!demo_ || !texture_initialized_) + return; + + demo_->resize_auxiliary_texture("circle_mask", width, height); + // Recreate bind groups with new texture view... +} +``` + +## Guidelines for New Effects + +### Creating Auxiliary Textures + +```cpp +void MyEffect::init(MainSequence* demo) { + demo_ = demo; + + // width_/height_ are VALID here (set by resize() before init()) + demo_->register_auxiliary_texture("my_texture", width_, height_); + + // Create pipelines and bind groups referencing the texture +} +``` + +### Consuming Auxiliary Textures + +```cpp +void MyEffect::init(MainSequence* demo) { + demo_ = demo; + + // Safe to get_auxiliary_view() here - producer effects + // initialized before consumers (order in timeline) + WGPUTextureView view = demo_->get_auxiliary_view("circle_mask"); +} +``` + +### Handling Resize + +```cpp +void MyEffect::resize(int width, int height) { + if (width == width_ && height == height_) + return; // Early exit if unchanged + + Effect::resize(width, height); + + // If you registered auxiliary texture, resize it: + if (demo_) { + demo_->resize_auxiliary_texture("my_texture", width, height); + // Recreate bind groups with new texture view + } +} +``` + +## Testing + +All 36 tests pass. Verified: +- CircleMaskEffect renders at correct resolution +- CNNEffect captured_frame has correct dimensions +- No artifacts or half-resolution bugs +- Window resize works correctly + +## Related Documentation + +- `doc/MASKING_SYSTEM.md` - Auxiliary texture usage patterns +- `doc/EFFECT_WORKFLOW.md` - Effect creation workflow +- `src/gpu/effect.h` - MainSequence API reference + +--- + +**Decision made:** 2026-02-11 +**Implemented by:** Claude Sonnet 4.5 +**Status:** Active |
