@aardworx/wombat.rendering 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,63 +2,217 @@
2
2
  // RenderObject. Vertex layout, blending, depth/stencil, raster.
3
3
  // Combined with a CompiledEffect + FramebufferSignature this is
4
4
  // enough to bake a GPURenderPipeline.
5
+ //
6
+ // All fields are `aval<T>` so wombat.dom's render-state scopes can
7
+ // flow through without forcing. The shader (`Effect`) stays a plain
8
+ // non-aval value; only uniforms and pipeline state are reactive.
5
9
 
6
- import type { HashMap } from "@aardworx/wombat.adaptive";
10
+ import { AVal, HashMap, type aval } from "@aardworx/wombat.adaptive";
7
11
 
8
12
  export type CullMode = "none" | "front" | "back";
9
13
  export type FrontFace = "ccw" | "cw";
10
14
  export type Topology = GPUPrimitiveTopology;
11
15
 
16
+ export interface DepthBiasState {
17
+ readonly constant: number;
18
+ readonly slopeScale: number;
19
+ readonly clamp: number;
20
+ }
21
+
12
22
  export interface RasterizerState {
23
+ readonly topology: aval<Topology>;
24
+ readonly cullMode: aval<CullMode>;
25
+ readonly frontFace: aval<FrontFace>;
26
+ /** `undefined` value disables polygon offset. */
27
+ readonly depthBias?: aval<DepthBiasState | undefined>;
28
+ }
29
+
30
+ export interface DepthState {
31
+ readonly write: aval<boolean>;
32
+ readonly compare: aval<GPUCompareFunction>;
33
+ /** unclippedDepth — requires the WebGPU adapter feature. */
34
+ readonly clamp?: aval<boolean>;
35
+ }
36
+
37
+ export interface StencilFaceState {
38
+ readonly compare: aval<GPUCompareFunction>;
39
+ readonly failOp: aval<GPUStencilOperation>;
40
+ readonly depthFailOp: aval<GPUStencilOperation>;
41
+ readonly passOp: aval<GPUStencilOperation>;
42
+ }
43
+
44
+ export interface StencilState {
45
+ readonly enabled: aval<boolean>;
46
+ /** Per-frame, NOT pipeline rebuild. */
47
+ readonly reference: aval<number>;
48
+ readonly readMask: aval<number>;
49
+ readonly writeMask: aval<number>;
50
+ readonly front: StencilFaceState;
51
+ readonly back: StencilFaceState;
52
+ }
53
+
54
+ export interface BlendComponentState {
55
+ readonly operation: aval<GPUBlendOperation>;
56
+ readonly srcFactor: aval<GPUBlendFactor>;
57
+ readonly dstFactor: aval<GPUBlendFactor>;
58
+ }
59
+
60
+ export interface BlendState {
61
+ readonly color: BlendComponentState;
62
+ readonly alpha: BlendComponentState;
63
+ /** RGBA channel write mask (bitmask: R=1, G=2, B=4, A=8). */
64
+ readonly writeMask: aval<number>;
65
+ }
66
+
67
+ export interface PipelineState {
68
+ readonly rasterizer: RasterizerState;
69
+ readonly depth?: DepthState;
70
+ readonly stencil?: StencilState;
71
+ /**
72
+ * Per-color-attachment blend state, keyed by the attachment name
73
+ * from the `FramebufferSignature`. Missing entries default to
74
+ * "no blending, write all channels".
75
+ */
76
+ readonly blends?: aval<HashMap<string, BlendState>>;
77
+ /** Multi-sample alpha-to-coverage. Default `false`. */
78
+ readonly alphaToCoverage?: aval<boolean>;
79
+ /** Per-frame, not pipeline-rebuild. */
80
+ readonly blendConstant?: aval<{ r: number; g: number; b: number; a: number }>;
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Plain (non-aval) mirror types — handy for the `PipelineState.constant`
85
+ // helper. Callers that already hold avals construct `PipelineState`
86
+ // directly; callers that have plain values use the helper to wrap.
87
+ // ---------------------------------------------------------------------------
88
+
89
+ export interface PlainRasterizerState {
13
90
  readonly topology: Topology;
14
91
  readonly cullMode: CullMode;
15
92
  readonly frontFace: FrontFace;
16
- /** "none" disables polygon offset. */
17
- readonly depthBias?: { readonly constant: number; readonly slopeScale: number; readonly clamp: number };
93
+ readonly depthBias?: DepthBiasState;
18
94
  }
19
95
 
20
- export interface DepthState {
96
+ export interface PlainDepthState {
21
97
  readonly write: boolean;
22
98
  readonly compare: GPUCompareFunction;
99
+ readonly clamp?: boolean;
23
100
  }
24
101
 
25
- export interface StencilFaceState {
102
+ export interface PlainStencilFaceState {
26
103
  readonly compare: GPUCompareFunction;
27
104
  readonly failOp: GPUStencilOperation;
28
105
  readonly depthFailOp: GPUStencilOperation;
29
106
  readonly passOp: GPUStencilOperation;
30
107
  }
31
108
 
32
- export interface StencilState {
109
+ export interface PlainStencilState {
110
+ readonly enabled: boolean;
111
+ readonly reference: number;
33
112
  readonly readMask: number;
34
113
  readonly writeMask: number;
35
- readonly front: StencilFaceState;
36
- readonly back: StencilFaceState;
114
+ readonly front: PlainStencilFaceState;
115
+ readonly back: PlainStencilFaceState;
37
116
  }
38
117
 
39
- export interface BlendComponentState {
118
+ export interface PlainBlendComponentState {
40
119
  readonly operation: GPUBlendOperation;
41
120
  readonly srcFactor: GPUBlendFactor;
42
121
  readonly dstFactor: GPUBlendFactor;
43
122
  }
44
123
 
45
- export interface BlendState {
46
- readonly color: BlendComponentState;
47
- readonly alpha: BlendComponentState;
48
- /** RGBA channel write mask (bitmask: R=1, G=2, B=4, A=8). */
124
+ export interface PlainBlendState {
125
+ readonly color: PlainBlendComponentState;
126
+ readonly alpha: PlainBlendComponentState;
49
127
  readonly writeMask: number;
50
128
  }
51
129
 
52
- export interface PipelineState {
53
- readonly rasterizer: RasterizerState;
54
- readonly depth?: DepthState;
55
- readonly stencil?: StencilState;
56
- /**
57
- * Per-color-attachment blend state, keyed by the attachment name
58
- * from the `FramebufferSignature`. Missing entries default to
59
- * "no blending, write all channels".
60
- */
61
- readonly blends?: HashMap<string, BlendState>;
62
- /** Multi-sample alpha-to-coverage. Default `false`. */
130
+ export interface PlainPipelineState {
131
+ readonly rasterizer: PlainRasterizerState;
132
+ readonly depth?: PlainDepthState;
133
+ readonly stencil?: PlainStencilState;
134
+ readonly blends?: HashMap<string, PlainBlendState>;
63
135
  readonly alphaToCoverage?: boolean;
136
+ readonly blendConstant?: { r: number; g: number; b: number; a: number };
64
137
  }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Helpers
141
+ // ---------------------------------------------------------------------------
142
+
143
+ function blendStateFromPlain(b: PlainBlendState): BlendState {
144
+ return {
145
+ color: {
146
+ operation: AVal.constant(b.color.operation),
147
+ srcFactor: AVal.constant(b.color.srcFactor),
148
+ dstFactor: AVal.constant(b.color.dstFactor),
149
+ },
150
+ alpha: {
151
+ operation: AVal.constant(b.alpha.operation),
152
+ srcFactor: AVal.constant(b.alpha.srcFactor),
153
+ dstFactor: AVal.constant(b.alpha.dstFactor),
154
+ },
155
+ writeMask: AVal.constant(b.writeMask),
156
+ };
157
+ }
158
+
159
+ function stencilFaceFromPlain(f: PlainStencilFaceState): StencilFaceState {
160
+ return {
161
+ compare: AVal.constant(f.compare),
162
+ failOp: AVal.constant(f.failOp),
163
+ depthFailOp: AVal.constant(f.depthFailOp),
164
+ passOp: AVal.constant(f.passOp),
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Wrap a plain `PlainPipelineState` into the aval-shaped
170
+ * `PipelineState` by lifting every leaf with `AVal.constant`. Useful
171
+ * for tests and for callers that don't carry avals yet — wombat.dom's
172
+ * `derivePipelineState` constructs the aval-shape directly.
173
+ */
174
+ export const PipelineState = {
175
+ constant(plain: PlainPipelineState): PipelineState {
176
+ const rast: RasterizerState = {
177
+ topology: AVal.constant(plain.rasterizer.topology),
178
+ cullMode: AVal.constant(plain.rasterizer.cullMode),
179
+ frontFace: AVal.constant(plain.rasterizer.frontFace),
180
+ ...(plain.rasterizer.depthBias !== undefined
181
+ ? { depthBias: AVal.constant<DepthBiasState | undefined>(plain.rasterizer.depthBias) }
182
+ : {}),
183
+ };
184
+ let blends: aval<HashMap<string, BlendState>> | undefined;
185
+ if (plain.blends !== undefined) {
186
+ let m = HashMap.empty<string, BlendState>();
187
+ for (const [k, v] of plain.blends) m = m.add(k, blendStateFromPlain(v));
188
+ blends = AVal.constant(m);
189
+ }
190
+ return {
191
+ rasterizer: rast,
192
+ ...(plain.depth !== undefined
193
+ ? {
194
+ depth: {
195
+ write: AVal.constant(plain.depth.write),
196
+ compare: AVal.constant(plain.depth.compare),
197
+ ...(plain.depth.clamp !== undefined ? { clamp: AVal.constant(plain.depth.clamp) } : {}),
198
+ },
199
+ }
200
+ : {}),
201
+ ...(plain.stencil !== undefined
202
+ ? {
203
+ stencil: {
204
+ enabled: AVal.constant(plain.stencil.enabled),
205
+ reference: AVal.constant(plain.stencil.reference),
206
+ readMask: AVal.constant(plain.stencil.readMask),
207
+ writeMask: AVal.constant(plain.stencil.writeMask),
208
+ front: stencilFaceFromPlain(plain.stencil.front),
209
+ back: stencilFaceFromPlain(plain.stencil.back),
210
+ },
211
+ }
212
+ : {}),
213
+ ...(blends !== undefined ? { blends } : {}),
214
+ ...(plain.alphaToCoverage !== undefined ? { alphaToCoverage: AVal.constant(plain.alphaToCoverage) } : {}),
215
+ ...(plain.blendConstant !== undefined ? { blendConstant: AVal.constant(plain.blendConstant) } : {}),
216
+ };
217
+ },
218
+ };