summaryrefslogtreecommitdiff
path: root/doc/MASKING_SYSTEM.md
blob: 86d6a74bc63a5bc6be5d14b48c400125537060d8 (plain)
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)