1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
# Auxiliary Texture Masking System
## Purpose
Share textures between effects within a single frame for **screen-space partitioning** (split-screen, portals, picture-in-picture).
## Concept
- Effect1: Generate mask (1 = region A, 0 = region B)
- Effect1: Render scene A where mask = 1
- Effect2: Reuse mask, render scene B where mask = 0
- Both render to same framebuffer
## Design Choice: Mask Texture vs Stencil Buffer
**Chosen: Mask Texture**
Pros:
- Flexible (soft edges, gradients)
- Debuggable (visualize as texture)
- Reusable (multiple effects read same mask)
- Simple pipeline setup
Cons:
- Memory cost (~4 MB for 1280x720 RGBA8)
- Fragment shader discard (slightly slower than stencil)
**Not Chosen: Stencil Buffer**
Pros: Hardware-accelerated, fast early-rejection
Cons: 8-bit limitation, complex pipeline, hard to debug
## API Reference
```cpp
class MainSequence {
public:
// Register once in effect init
void register_auxiliary_texture(const char* name, int width, int height);
// Get view every frame
WGPUTextureView get_auxiliary_view(const char* name);
};
```
## Usage Pattern
```cpp
// Effect1 (mask generator)
void init(MainSequence* demo) {
demo->register_auxiliary_texture("portal_mask", width, height);
}
void compute(WGPUCommandEncoder encoder, ...) {
WGPUTextureView mask = demo_->get_auxiliary_view("portal_mask");
// Generate mask to texture
}
void render(WGPURenderPassEncoder pass, ...) {
WGPUTextureView mask = demo_->get_auxiliary_view("portal_mask");
// Sample mask, discard where mask < 0.5, render scene A
}
// Effect2 (mask consumer)
void render(WGPURenderPassEncoder pass, ...) {
WGPUTextureView mask = demo_->get_auxiliary_view("portal_mask");
// Sample mask, discard where mask > 0.5, render scene B
}
```
## Shader Example
**Mask Generation:**
```wgsl
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
let dist = length(uv - vec2(0.5));
let mask = f32(dist < 0.3); // Circular portal
return vec4<f32>(mask);
}
```
**Masked Rendering:**
```wgsl
@group(0) @binding(3) var mask_texture: texture_2d<f32>;
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let uv = in.position.xy / uniforms.resolution;
let mask = textureSample(mask_texture, mask_sampler, uv).r;
if (mask < 0.5) { discard; } // Effect1: discard outside
// if (mask > 0.5) { discard; } // Effect2: discard inside
return compute_scene_color(in);
}
```
## Memory Impact
Per texture: **width × height × 4 bytes**
- 1280×720: ~3.7 MB
- 1920×1080: ~8.3 MB
Typical usage: 2-3 masks = 10-25 MB (acceptable overhead)
## Use Cases
1. Split-screen (vertical/horizontal)
2. Portals (arbitrary shape windows)
3. Picture-in-picture
4. Masked transitions (wipe effects)
5. Shadow maps (pre-generated in compute)
6. Reflection probes
## Size Impact
- Code: ~100 lines (~500 bytes)
- Runtime memory: 4-8 MB per mask
## Future Extensions
- Multi-channel masks (RGBA = 4 regions)
- Mipmap support
- R8 compression (1 byte/pixel)
- Enum-based lookup (replace string keys)
|