summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-11 11:52:49 +0100
committerskal <pascal.massimino@gmail.com>2026-02-11 11:52:49 +0100
commit58c595810d0346f0c2b32dbc70b7385aee545e11 (patch)
tree09db51ba3d849a1f3ac3da715d038ddbbece1122
parent0b756c0d0c9e968b2182ea720a8a24afd5c4bc0a (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>
-rw-r--r--doc/AUXILIARY_TEXTURE_INIT.md166
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