longpipe 0.0.14 → 0.0.16

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.
package/dist/model.js ADDED
@@ -0,0 +1,2085 @@
1
+ import {a,b,f as f$1,g,c as c$1,e,d}from'./chunk-NYCCJS3G.js';export{h as WebGPUBackend}from'./chunk-NYCCJS3G.js';var Ct=`#version 300 es
2
+ const vec2 VERTS[6] = vec2[6](
3
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
4
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
5
+ );
6
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,c=class{constructor(e){this.backend=e;}backend;shader="";program;samplers=[];uniformInts={};uniformFloats={};makeTexture(e,t,r){let o=this.backend.gl,i=this.backend.textureFormat,s=this.backend.toTextureView(e),n=o.createTexture();return o.bindTexture(o.TEXTURE_2D,n),o.texImage2D(o.TEXTURE_2D,0,i.internalFormat,t,r,0,i.format,i.type,s),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.NEAREST),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.NEAREST),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),n}defaultSetup(){let e=this.backend.gl,t=e.createShader(e.VERTEX_SHADER);e.shaderSource(t,Ct),e.compileShader(t);let r=e.createShader(e.FRAGMENT_SHADER);if(e.shaderSource(r,this.shader),e.compileShader(r),!e.getShaderParameter(r,e.COMPILE_STATUS))throw new Error(`GLSL compile error: ${e.getShaderInfoLog(r)}`);if(this.program=e.createProgram(),e.attachShader(this.program,t),e.attachShader(this.program,r),e.linkProgram(this.program),!e.getProgramParameter(this.program,e.LINK_STATUS))throw new Error(`GLSL link error: ${e.getProgramInfoLog(this.program)}`)}run(){let e=this.backend.gl;e.useProgram(this.program),this.samplers.forEach(({name:t,texture:r},o)=>{e.activeTexture(e.TEXTURE0+o),e.bindTexture(e.TEXTURE_2D,r),e.uniform1i(e.getUniformLocation(this.program,t),o);});for(let[t,r]of Object.entries(this.uniformInts))e.uniform1i(e.getUniformLocation(this.program,t),r);for(let[t,r]of Object.entries(this.uniformFloats))e.uniform1f(e.getUniformLocation(this.program,t),r);e.bindFramebuffer(e.FRAMEBUFFER,this.backend.fbo),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,this.output.texture,0),e.viewport(0,0,this.dispatch[0],this.dispatch[1]),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6);}};var Re=`#version 300 es
7
+ // Conv2d \u2014 handles all variants: 1\xD71 (pointwise), 3\xD73, 5\xD75, strided, BN-fused.
8
+ //
9
+ // Tensor layout: NHWC vec4 \u2014 texture dimensions (W * C/4, H).
10
+ // texel (x * C_groups + c_group, y) = channels [c_group*4 .. c_group*4+3] at pixel (x,y).
11
+ //
12
+ // Weight layout: (I_groups * 4, K\xB2 * O_groups) RGBA32F texture.
13
+ // Row = z * u_out_c_groups + o_group (kernel_pos outer, out_group inner)
14
+ // Col = i_group * 4 + mat_col (4 texels per mat4)
15
+ // mat4[col][row] = weight(in_channel=col, out_channel=row)
16
+ // Operation: (mat4 * in_val)[r] = \u03A3_c weight(in=c, out=r) * in_val[c] \u2014 same as WGSL.
17
+ //
18
+ // Bias layout: (O_groups, 1).
19
+ //
20
+ // Padding model: pad_top / pad_left applied to input offset; right/bottom asymmetry
21
+ // is handled implicitly by the bounds check, same as the WGSL implementation.
22
+
23
+ precision highp float;
24
+ precision highp int;
25
+
26
+ uniform sampler2D u_input; // (in_W * in_C_groups, in_H)
27
+ uniform sampler2D u_weights; // (in_C_groups * 4, K\xB2 * out_C_groups)
28
+ uniform sampler2D u_bias; // (out_C_groups, 1)
29
+
30
+ uniform int u_in_w;
31
+ uniform int u_in_h;
32
+ uniform int u_in_c_groups;
33
+ uniform int u_out_c_groups;
34
+ uniform int u_kernel_h;
35
+ uniform int u_kernel_w;
36
+ uniform int u_stride;
37
+ uniform int u_pad_top;
38
+ uniform int u_pad_left;
39
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu, 3 = leaky(0.1)
40
+
41
+ out vec4 fragColor;
42
+
43
+ void main() {
44
+ int fx = int(gl_FragCoord.x);
45
+ int fy = int(gl_FragCoord.y);
46
+ int x_out = fx / u_out_c_groups;
47
+ int y_out = fy;
48
+ int o_group = fx - x_out * u_out_c_groups;
49
+
50
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
51
+
52
+ for (int ky = 0; ky < u_kernel_h; ky++) {
53
+ for (int kx = 0; kx < u_kernel_w; kx++) {
54
+ int in_y = y_out * u_stride + ky - u_pad_top;
55
+ int in_x = x_out * u_stride + kx - u_pad_left;
56
+
57
+ if (in_y < 0 || in_y >= u_in_h || in_x < 0 || in_x >= u_in_w) continue;
58
+
59
+ int z = ky * u_kernel_w + kx;
60
+ int w_row = z * u_out_c_groups + o_group;
61
+
62
+ for (int i = 0; i < u_in_c_groups; i++) {
63
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_in_c_groups + i, in_y), 0);
64
+
65
+ int base = i * 4;
66
+ mat4 w = mat4(
67
+ texelFetch(u_weights, ivec2(base, w_row), 0),
68
+ texelFetch(u_weights, ivec2(base + 1, w_row), 0),
69
+ texelFetch(u_weights, ivec2(base + 2, w_row), 0),
70
+ texelFetch(u_weights, ivec2(base + 3, w_row), 0)
71
+ );
72
+ result += w * in_val;
73
+ }
74
+ }
75
+ }
76
+
77
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
78
+ else if (u_activation == 2) result = max(result, vec4(0.0));
79
+ else if (u_activation == 3) result = max(result, 0.1 * result);
80
+
81
+ fragColor = result;
82
+ }
83
+ `;var E=class extends c{inputs;weights;output;dispatch;shader=Re;constructor(e$1,t,r,o){super(e$1);let i=c$1(t.h,o.kernel,o.stride,o.padding),s=c$1(t.w,o.kernel,o.stride,o.padding),n=t.c/4,u=o.outChannels/4,l=e(o.padding,t.h,i,o.kernel,o.stride),p=e(o.padding,t.w,s,o.kernel,o.stride),h=f$1(r.weights),m=f$1(r.bias),x=this.makeTexture(h,n*4,o.kernel*o.kernel*u),_=this.makeTexture(m,u,1),g=s*u,b=this.makeTexture(null,g,i);this.output={h:i,w:s,c:o.outChannels,texture:b,texW:g,texH:i},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:x},{name:"u_bias",texture:_}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_in_c_groups:n,u_out_c_groups:u,u_kernel_h:o.kernel,u_kernel_w:o.kernel,u_stride:o.stride,u_pad_top:l,u_pad_left:p,u_activation:o.activation==="relu6"?1:o.activation==="relu"?2:o.activation==="leaky"?3:0},this.defaultSetup(),this.dispatch=[g,i];}};var De=`#version 300 es
84
+ // ConvTranspose2d \u2014 gather form. Identical texture layouts + mat4 weight
85
+ // semantics as conv2d.glsl; only the spatial mapping differs:
86
+ // in_y = (y_out + pad - ky) / stride (must divide evenly + be in bounds)
87
+ // No explicit kernel flip \u2014 the (y_out + pad - ky) indexing carries it.
88
+
89
+ precision highp float;
90
+ precision highp int;
91
+
92
+ uniform sampler2D u_input; // (in_W * in_C_groups, in_H)
93
+ uniform sampler2D u_weights; // (in_C_groups * 4, K\xB2 * out_C_groups)
94
+ uniform sampler2D u_bias; // (out_C_groups, 1)
95
+
96
+ uniform int u_in_w;
97
+ uniform int u_in_h;
98
+ uniform int u_in_c_groups;
99
+ uniform int u_out_c_groups;
100
+ uniform int u_kernel_h;
101
+ uniform int u_kernel_w;
102
+ uniform int u_stride;
103
+ uniform int u_pad_top;
104
+ uniform int u_pad_left;
105
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu, 3 = leaky(0.1)
106
+
107
+ out vec4 fragColor;
108
+
109
+ void main() {
110
+ int fx = int(gl_FragCoord.x);
111
+ int fy = int(gl_FragCoord.y);
112
+ int x_out = fx / u_out_c_groups;
113
+ int y_out = fy;
114
+ int o_group = fx - x_out * u_out_c_groups;
115
+
116
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
117
+
118
+ for (int ky = 0; ky < u_kernel_h; ky++) {
119
+ for (int kx = 0; kx < u_kernel_w; kx++) {
120
+ int iy_num = y_out + u_pad_top - ky;
121
+ int ix_num = x_out + u_pad_left - kx;
122
+ if (iy_num < 0 || ix_num < 0) continue;
123
+ if ((iy_num % u_stride) != 0 || (ix_num % u_stride) != 0) continue;
124
+ int in_y = iy_num / u_stride;
125
+ int in_x = ix_num / u_stride;
126
+ if (in_y >= u_in_h || in_x >= u_in_w) continue;
127
+
128
+ int z = ky * u_kernel_w + kx;
129
+ int w_row = z * u_out_c_groups + o_group;
130
+
131
+ for (int i = 0; i < u_in_c_groups; i++) {
132
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_in_c_groups + i, in_y), 0);
133
+
134
+ int base = i * 4;
135
+ mat4 w = mat4(
136
+ texelFetch(u_weights, ivec2(base, w_row), 0),
137
+ texelFetch(u_weights, ivec2(base + 1, w_row), 0),
138
+ texelFetch(u_weights, ivec2(base + 2, w_row), 0),
139
+ texelFetch(u_weights, ivec2(base + 3, w_row), 0)
140
+ );
141
+ result += w * in_val;
142
+ }
143
+ }
144
+ }
145
+
146
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
147
+ else if (u_activation == 2) result = max(result, vec4(0.0));
148
+ else if (u_activation == 3) result = max(result, 0.1 * result);
149
+
150
+ fragColor = result;
151
+ }
152
+ `;var G=class extends c{inputs;weights;output;dispatch;shader=De;constructor(e,t,r,o){super(e);let i=d(t.h,o.kernel,o.stride,o.padding),s=d(t.w,o.kernel,o.stride,o.padding),n=t.c/4,u=o.outChannels/4,l=f$1(r.weights),p=f$1(r.bias),h=this.makeTexture(l,n*4,o.kernel*o.kernel*u),m=this.makeTexture(p,u,1),x=s*u,_=this.makeTexture(null,x,i);this.output={h:i,w:s,c:o.outChannels,texture:_,texW:x,texH:i},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:h},{name:"u_bias",texture:m}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_in_c_groups:n,u_out_c_groups:u,u_kernel_h:o.kernel,u_kernel_w:o.kernel,u_stride:o.stride,u_pad_top:o.padding,u_pad_left:o.padding,u_activation:o.activation==="relu6"?1:o.activation==="relu"?2:o.activation==="leaky"?3:0},this.defaultSetup(),this.dispatch=[x,i];}};var Be=`#version 300 es
153
+ // Depthwise Conv2d \u2014 groups = in_channels (each channel convolved independently).
154
+ //
155
+ // Weight layout: (C_groups, K\xB2) RGBA32F texture \u2014 contrast with conv2d which uses mat4.
156
+ // texel (c_group, z) = vec4 kernel weights for 4 channels at kernel position z.
157
+ // Operation: element-wise multiply (not matmul). 4\xD7 smaller than a mat4 approach.
158
+ //
159
+ // Padding model: same asymmetric SAME-pad handling as conv2d.glsl.
160
+
161
+ precision highp float;
162
+ precision highp int;
163
+
164
+ uniform sampler2D u_input; // (in_W * C_groups, in_H)
165
+ uniform sampler2D u_weights; // (C_groups, K\xB2)
166
+ uniform sampler2D u_bias; // (C_groups, 1)
167
+
168
+ uniform int u_in_w;
169
+ uniform int u_in_h;
170
+ uniform int u_c_groups;
171
+ uniform int u_kernel_h;
172
+ uniform int u_kernel_w;
173
+ uniform int u_stride;
174
+ uniform int u_pad_top;
175
+ uniform int u_pad_left;
176
+ uniform int u_apply_relu6; // 0 = none, 1 = relu6
177
+
178
+ out vec4 fragColor;
179
+
180
+ void main() {
181
+ int fx = int(gl_FragCoord.x);
182
+ int fy = int(gl_FragCoord.y);
183
+ int x_out = fx / u_c_groups;
184
+ int y_out = fy;
185
+ int c_group = fx - x_out * u_c_groups;
186
+
187
+ vec4 result = texelFetch(u_bias, ivec2(c_group, 0), 0);
188
+
189
+ for (int ky = 0; ky < u_kernel_h; ky++) {
190
+ for (int kx = 0; kx < u_kernel_w; kx++) {
191
+ int in_y = y_out * u_stride + ky - u_pad_top;
192
+ int in_x = x_out * u_stride + kx - u_pad_left;
193
+
194
+ if (in_y < 0 || in_y >= u_in_h || in_x < 0 || in_x >= u_in_w) continue;
195
+
196
+ int z = ky * u_kernel_w + kx;
197
+
198
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_c_groups + c_group, in_y), 0);
199
+ vec4 w = texelFetch(u_weights, ivec2(c_group, z), 0);
200
+
201
+ result += w * in_val; // element-wise multiply, not matmul
202
+ }
203
+ }
204
+
205
+ if (u_apply_relu6 == 1) result = clamp(result, 0.0, 6.0);
206
+
207
+ fragColor = result;
208
+ }
209
+ `;var F=class extends c{inputs;weights;output;dispatch;shader=Be;constructor(e$1,t,r,o){super(e$1);let i=c$1(t.h,o.kernel,o.stride,o.padding),s=c$1(t.w,o.kernel,o.stride,o.padding),n=t.c/4,u=e(o.padding,t.h,i,o.kernel,o.stride),l=e(o.padding,t.w,s,o.kernel,o.stride),p=f$1(r.weights),h=f$1(r.bias),m=this.makeTexture(p,n,o.kernel*o.kernel),x=this.makeTexture(h,n,1),_=s*n,g=this.makeTexture(null,_,i);this.output={h:i,w:s,c:t.c,texture:g,texW:_,texH:i},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:m},{name:"u_bias",texture:x}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_c_groups:n,u_kernel_h:o.kernel,u_kernel_w:o.kernel,u_stride:o.stride,u_pad_top:u,u_pad_left:l,u_apply_relu6:o.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[_,i];}};var Ue=`#version 300 es
210
+ // Element-wise add \u2014 both inputs and output share the same texture dimensions.
211
+ // Works for any 2D layout; used here with a flat (nVec4, 1) texture.
212
+
213
+ precision highp float;
214
+
215
+ uniform sampler2D u_input_a;
216
+ uniform sampler2D u_input_b;
217
+
218
+ out vec4 fragColor;
219
+
220
+ void main() {
221
+ ivec2 fc = ivec2(gl_FragCoord.xy);
222
+ fragColor = texelFetch(u_input_a, fc, 0) + texelFetch(u_input_b, fc, 0);
223
+ }
224
+ `;var A=class extends c{inputs;weights=[];output;dispatch;shader=Ue;constructor(e,t,r){super(e);let o=t,i=this.makeTexture(null,o.texW,o.texH);this.output={h:t.h,w:t.w,c:t.c,texture:i,texW:o.texW,texH:o.texH},this.inputs=[t,r],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:r.texture}],this.defaultSetup(),this.dispatch=[o.texW,o.texH];}};var Pe=`#version 300 es
225
+ // Element-wise sigmoid \u2014 works on any texture layout (flat or spatial).
226
+
227
+ precision highp float;
228
+
229
+ uniform sampler2D u_input;
230
+
231
+ out vec4 fragColor;
232
+
233
+ void main() {
234
+ vec4 x = texelFetch(u_input, ivec2(gl_FragCoord.xy), 0);
235
+ fragColor = 1.0 / (1.0 + exp(-x));
236
+ }
237
+ `;var S=class extends c{inputs;weights=[];output;dispatch;shader=Pe;constructor(e,t){super(e);let r=t,o=this.makeTexture(null,r.texW,r.texH);this.output={h:t.h,w:t.w,c:t.c,texture:o,texW:r.texW,texH:r.texH},this.inputs=[t],this.samplers=[{name:"u_input",texture:r.texture}],this.defaultSetup(),this.dispatch=[r.texW,r.texH];}};var Me=`#version 300 es
238
+ // Element-wise tanh \u2014 used by ConvGRU candidate activation.
239
+ // Layout-agnostic (flat or spatial); GLSL tanh is element-wise on vec4.
240
+
241
+ precision highp float;
242
+
243
+ uniform sampler2D u_input;
244
+
245
+ out vec4 fragColor;
246
+
247
+ void main() {
248
+ fragColor = tanh(texelFetch(u_input, ivec2(gl_FragCoord.xy), 0));
249
+ }
250
+ `;var R=class extends c{inputs;weights=[];output;dispatch;shader=Me;constructor(e,t){super(e);let r=t,o=this.makeTexture(null,r.texW,r.texH);this.output={h:t.h,w:t.w,c:t.c,texture:o,texW:r.texW,texH:r.texH},this.inputs=[t],this.samplers=[{name:"u_input",texture:r.texture}],this.defaultSetup(),this.dispatch=[r.texW,r.texH];}};var Oe=`#version 300 es
251
+ // Element-wise multiply \u2014 used in ConvGRU for r \u2299 h_prev.
252
+ // Same shape constraint as add.glsl.
253
+
254
+ precision highp float;
255
+
256
+ uniform sampler2D u_input_a;
257
+ uniform sampler2D u_input_b;
258
+
259
+ out vec4 fragColor;
260
+
261
+ void main() {
262
+ ivec2 fc = ivec2(gl_FragCoord.xy);
263
+ fragColor = texelFetch(u_input_a, fc, 0) * texelFetch(u_input_b, fc, 0);
264
+ }
265
+ `;var D=class extends c{inputs;weights=[];output;dispatch;shader=Oe;constructor(e,t,r){super(e);let o=t,i=this.makeTexture(null,o.texW,o.texH);this.output={h:t.h,w:t.w,c:t.c,texture:i,texW:o.texW,texH:o.texH},this.inputs=[t,r],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:r.texture}],this.defaultSetup(),this.dispatch=[o.texW,o.texH];}};var He=`#version 300 es
266
+ // Bilinear gather-warp. out[p] = sample(source, p + flow_scale\xB7flow[p].xy),
267
+ // edge-clamped. Source/output are (W\xB7G, H) with G = c/4 channel groups
268
+ // (fragment x decomposes to pixel + group, like conv); flow is 4-ch, same
269
+ // spatial resolution, vector in .xy.
270
+
271
+ precision highp float;
272
+ precision highp int;
273
+
274
+ uniform sampler2D u_source; // (W * G, H)
275
+ uniform sampler2D u_flow; // (W, H), flow in .xy
276
+ uniform int u_w;
277
+ uniform int u_h;
278
+ uniform int u_groups;
279
+ uniform float u_flow_scale;
280
+
281
+ out vec4 fragColor;
282
+
283
+ vec4 samp(int x, int y, int g) {
284
+ int cx = clamp(x, 0, u_w - 1);
285
+ int cy = clamp(y, 0, u_h - 1);
286
+ return texelFetch(u_source, ivec2(cx * u_groups + g, cy), 0);
287
+ }
288
+
289
+ void main() {
290
+ int fx = int(gl_FragCoord.x);
291
+ int y = int(gl_FragCoord.y);
292
+ int x = fx / u_groups;
293
+ int g = fx - x * u_groups;
294
+
295
+ vec2 f = texelFetch(u_flow, ivec2(x, y), 0).xy;
296
+ float sx = clamp(float(x) + u_flow_scale * f.x, 0.0, float(u_w - 1));
297
+ float sy = clamp(float(y) + u_flow_scale * f.y, 0.0, float(u_h - 1));
298
+
299
+ int x0 = int(floor(sx));
300
+ int y0 = int(floor(sy));
301
+ float tx = sx - float(x0);
302
+ float ty = sy - float(y0);
303
+
304
+ vec4 top = mix(samp(x0, y0, g), samp(x0 + 1, y0, g), tx);
305
+ vec4 bot = mix(samp(x0, y0 + 1, g), samp(x0 + 1, y0 + 1, g), tx);
306
+ fragColor = mix(top, bot, ty);
307
+ }
308
+ `;var B=class extends c{inputs;weights=[];output;dispatch;shader=He;constructor(e,t,r,o){super(e);let i=t.w,s=t.h,n=t.c/4,u=this.makeTexture(null,i*n,s);this.output={h:s,w:i,c:t.c,texture:u,texW:i*n,texH:s},this.inputs=[t,r],this.samplers=[{name:"u_source",texture:t.texture},{name:"u_flow",texture:r.texture}],this.uniformInts={u_w:i,u_h:s,u_groups:n},this.uniformFloats={u_flow_scale:o.flowScale},this.defaultSetup(),this.dispatch=[i*n,s];}};var Ie=`#version 300 es
309
+ // Face-box decode from 5-keypoint heatmaps. Output is a 1\xD71 texture:
310
+ // (cx, cy, halfSide, score) in FRAME FRACTIONS (halfSide as a fraction of
311
+ // frame WIDTH; the heatmap grid shares the frame's aspect, so squaring in
312
+ // grid px squares in frame px). Peaks refined with a windowed soft-argmax
313
+ // centroid \u2014 NEVER hard argmax (whole-cell snapping jitters the crop).
314
+ //
315
+ // Single fragment does all 5 channels serially \u2014 the grid is tiny (\u2264 ~48\xD728).
316
+ // Heatmap texture: (w * 2, h) \u2014 8 channels = 2 vec4 groups per pixel.
317
+
318
+ precision highp float;
319
+ precision highp int;
320
+
321
+ uniform sampler2D u_hm;
322
+ uniform int u_h;
323
+ uniform int u_w;
324
+ uniform int u_win;
325
+ uniform float u_thresh;
326
+ uniform float u_box_scale;
327
+
328
+ out vec4 fragColor;
329
+
330
+ float hmAt(int x, int y, int k) {
331
+ vec4 v = texelFetch(u_hm, ivec2(x * 2 + k / 4, y), 0);
332
+ return v[k % 4];
333
+ }
334
+
335
+ void main() {
336
+ float x0 = 1e9, x1 = -1e9, y0 = 1e9, y1 = -1e9, score = 1e9;
337
+
338
+ for (int k = 0; k < 5; k++) {
339
+ // Peak.
340
+ float peak = -1.0;
341
+ int pr = 0, pc = 0;
342
+ for (int y = 0; y < u_h; y++) {
343
+ for (int x = 0; x < u_w; x++) {
344
+ float v = hmAt(x, y, k);
345
+ if (v > peak) { peak = v; pr = y; pc = x; }
346
+ }
347
+ }
348
+ // Windowed soft-argmax centroid.
349
+ int r0 = max(0, pr - u_win), r1 = min(u_h, pr + u_win + 1);
350
+ int c0 = max(0, pc - u_win), c1 = min(u_w, pc + u_win + 1);
351
+ float wsum = 0.0, sy = 0.0, sx = 0.0;
352
+ for (int y = r0; y < r1; y++) {
353
+ for (int x = c0; x < c1; x++) {
354
+ float v = hmAt(x, y, k);
355
+ wsum += v; sy += v * float(y); sx += v * float(x);
356
+ }
357
+ }
358
+ wsum = max(wsum, 1e-6);
359
+ float kx = sx / wsum, ky = sy / wsum;
360
+ x0 = min(x0, kx); x1 = max(x1, kx);
361
+ y0 = min(y0, ky); y1 = max(y1, ky);
362
+ score = min(score, peak);
363
+ }
364
+ if (score < u_thresh) score = 0.0;
365
+
366
+ float halfPx = 0.5 * u_box_scale * max(x1 - x0, y1 - y0);
367
+ float cx = ((x0 + x1) * 0.5 + 0.5) / float(u_w);
368
+ float cy = ((y0 + y1) * 0.5 + 0.5) / float(u_h);
369
+ fragColor = vec4(cx, cy, halfPx / float(u_w), score);
370
+ }
371
+ `;var U=class extends c{inputs;weights=[];output;dispatch=[1,1];shader=Ie;constructor(e,t,r){if(super(e),t.c!==8)throw new Error(`FaceBoxFromHeatmaps: expected 8-ch heatmaps (5 real), got ${t.c}`);let o=this.makeTexture(null,1,1);this.output={h:1,w:1,c:4,texture:o,texW:1,texH:1},this.inputs=[t],this.samplers=[{name:"u_hm",texture:t.texture}],this.uniformInts={u_h:t.h,u_w:t.w,u_win:r.win},this.uniformFloats={u_thresh:r.thresh,u_box_scale:r.boxScale},this.defaultSetup();}};var Ne=`#version 300 es
372
+ // MULTI-FACE box decode from 5-keypoint heatmaps \u2192 1\xD7K texture, slot i =
373
+ // (cx, cy, halfSide, score) in FRAME FRACTIONS (halfSide a fraction of frame
374
+ // WIDTH). The multi-face sibling of face_box.glsl. See face_boxes.wgsl for the
375
+ // full algorithm commentary \u2014 this mirrors it exactly.
376
+ //
377
+ // WebGL has no compute and no shared memory, so each fragment (= one face slot)
378
+ // independently redoes the ENTIRE decode \u2014 candidates for all 5 channels,
379
+ // hypotheses, NMS \u2014 and emits the slot matching its own x. That's K\xD7 redundant
380
+ // work, which is fine here: the grid is \u2264 ~48\xD728 and K \u2264 6, and it keeps the
381
+ // WGSL and GLSL paths line-for-line comparable.
382
+ //
383
+ // Heatmap texture: (w * 2, h) \u2014 8 channels = 2 vec4 groups per pixel.
384
+
385
+ precision highp float;
386
+ precision highp int;
387
+
388
+ uniform sampler2D u_hm;
389
+ uniform int u_h;
390
+ uniform int u_w;
391
+ uniform int u_win;
392
+ uniform float u_thresh;
393
+ uniform float u_box_scale;
394
+ uniform int u_max_faces;
395
+ uniform float u_tol;
396
+
397
+ out vec4 fragColor;
398
+
399
+ const int N_KP = 5;
400
+ const int MAX_CAND = 5;
401
+ const float LAMBDA = 0.5;
402
+ const float MIN_SEP = 1.0;
403
+ const float MIN_EYE = 1.0;
404
+
405
+ float c_x[25];
406
+ float c_y[25];
407
+ float c_s[25];
408
+ int c_n[5];
409
+
410
+ float hmAt(int x, int y, int k) {
411
+ vec4 v = texelFetch(u_hm, ivec2(x * 2 + k / 4, y), 0);
412
+ return v[k % 4];
413
+ }
414
+
415
+ // ArcFace canonical template in the eye frame (origin = L-eye, +u along the
416
+ // L\u2192R eye vector, +w perpendicular), normalized by interocular distance.
417
+ float tmplU(int t) { return t == 0 ? 0.50002 : (t == 1 ? 0.08598 : 0.91410); }
418
+ float tmplW(int t) { return t == 0 ? 0.57150 : 1.15462; }
419
+
420
+ void main() {
421
+ int myFace = int(gl_FragCoord.x);
422
+
423
+ // \u2500\u2500 stage 1: local-maximum candidates, per channel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
424
+ for (int k = 0; k < N_KP; k++) {
425
+ int pr[5];
426
+ int pc[5];
427
+ float ps[5];
428
+ int n = 0;
429
+
430
+ for (int y = 0; y < u_h; y++) {
431
+ for (int x = 0; x < u_w; x++) {
432
+ float v = hmAt(x, y, k);
433
+ if (v < u_thresh) continue;
434
+
435
+ bool isMax = true;
436
+ for (int dy = -1; dy <= 1; dy++) {
437
+ for (int dx = -1; dx <= 1; dx++) {
438
+ if (dx == 0 && dy == 0) continue;
439
+ int rr = y + dy, cc = x + dx;
440
+ if (rr < 0 || rr >= u_h || cc < 0 || cc >= u_w) continue;
441
+ float u = hmAt(cc, rr, k);
442
+ if (u > v || (u == v && rr * u_w + cc < y * u_w + x)) isMax = false;
443
+ }
444
+ }
445
+ if (!isMax) continue;
446
+
447
+ int slot = n;
448
+ if (n == MAX_CAND) {
449
+ if (v <= ps[MAX_CAND - 1]) continue;
450
+ slot = MAX_CAND - 1;
451
+ } else {
452
+ n++;
453
+ }
454
+ while (slot > 0 && ps[slot - 1] < v) {
455
+ ps[slot] = ps[slot - 1]; pr[slot] = pr[slot - 1]; pc[slot] = pc[slot - 1];
456
+ slot--;
457
+ }
458
+ ps[slot] = v; pr[slot] = y; pc[slot] = x;
459
+ }
460
+ }
461
+
462
+ int m = 0;
463
+ for (int i = 0; i < n; i++) {
464
+ float rx = float(pc[i]);
465
+ float ry = float(pr[i]);
466
+ if (u_win > 0) {
467
+ int r0 = max(0, pr[i] - u_win), r1 = min(u_h, pr[i] + u_win + 1);
468
+ int c0 = max(0, pc[i] - u_win), c1 = min(u_w, pc[i] + u_win + 1);
469
+ float wsum = 0.0, sy = 0.0, sx = 0.0;
470
+ for (int y = r0; y < r1; y++) {
471
+ for (int x = c0; x < c1; x++) {
472
+ float v = hmAt(x, y, k);
473
+ wsum += v; sy += v * float(y); sx += v * float(x);
474
+ }
475
+ }
476
+ wsum = max(wsum, 1e-6);
477
+ rx = sx / wsum; ry = sy / wsum;
478
+ }
479
+ bool dup = false;
480
+ for (int j = 0; j < m; j++) {
481
+ float dx = c_x[k * MAX_CAND + j] - rx;
482
+ float dy = c_y[k * MAX_CAND + j] - ry;
483
+ if (sqrt(dx * dx + dy * dy) < MIN_SEP) dup = true;
484
+ }
485
+ if (dup) continue;
486
+ c_x[k * MAX_CAND + m] = rx;
487
+ c_y[k * MAX_CAND + m] = ry;
488
+ c_s[k * MAX_CAND + m] = ps[i];
489
+ m++;
490
+ }
491
+ c_n[k] = m;
492
+ }
493
+
494
+ // \u2500\u2500 stage 2: eye-pair hypotheses \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
495
+ float h_score[25];
496
+ int h_kp[125];
497
+ int nh = 0;
498
+ float maxEye = float(u_w) * 0.45;
499
+
500
+ for (int i = 0; i < c_n[0]; i++) {
501
+ for (int j = 0; j < c_n[1]; j++) {
502
+ float ax = c_x[i], ay = c_y[i];
503
+ float bx = c_x[MAX_CAND + j], by = c_y[MAX_CAND + j];
504
+ float evx = bx - ax, evy = by - ay;
505
+ float len = sqrt(evx * evx + evy * evy);
506
+ if (len < MIN_EYE || len > maxEye) continue;
507
+ vec2 ex = vec2(evx / len, evy / len);
508
+ vec2 ey = vec2(-ex.y, ex.x);
509
+
510
+ float sc = c_s[i] + c_s[MAX_CAND + j];
511
+ float resid = 0.0;
512
+ int nMatch = 2, mouths = 0, nose = -1;
513
+ int mo[2];
514
+ mo[0] = -1; mo[1] = -1;
515
+
516
+ for (int t = 0; t < 3; t++) {
517
+ int kk = t + 2;
518
+ float px = ax + len * (tmplU(t) * ex.x + tmplW(t) * ey.x);
519
+ float py = ay + len * (tmplU(t) * ex.y + tmplW(t) * ey.y);
520
+ int best = -1;
521
+ float bestD = u_tol * len;
522
+ for (int q = 0; q < c_n[kk]; q++) {
523
+ float dx = c_x[kk * MAX_CAND + q] - px;
524
+ float dy = c_y[kk * MAX_CAND + q] - py;
525
+ float d = sqrt(dx * dx + dy * dy);
526
+ if (d < bestD) { bestD = d; best = q; }
527
+ }
528
+ if (best < 0) continue;
529
+ sc += c_s[kk * MAX_CAND + best];
530
+ resid += bestD / len;
531
+ nMatch++;
532
+ if (t == 0) nose = best; else { mo[t - 1] = best; mouths++; }
533
+ }
534
+ if (nose < 0 || mouths < 1) continue;
535
+
536
+ h_score[nh] = sc / float(nMatch) - LAMBDA * (resid / float(nMatch - 2));
537
+ h_kp[nh * N_KP + 0] = i;
538
+ h_kp[nh * N_KP + 1] = j;
539
+ h_kp[nh * N_KP + 2] = nose;
540
+ h_kp[nh * N_KP + 3] = mo[0];
541
+ h_kp[nh * N_KP + 4] = mo[1];
542
+ nh++;
543
+ }
544
+ }
545
+
546
+ // \u2500\u2500 stage 3/4: greedy NMS \u2192 the box for slot myFace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
547
+ bool used[25];
548
+ for (int i = 0; i < N_KP * MAX_CAND; i++) used[i] = false;
549
+ int outN = 0;
550
+
551
+ for (int iter = 0; iter < 25; iter++) {
552
+ if (outN >= u_max_faces) break;
553
+
554
+ int best = -1;
555
+ float bestS = -1e8;
556
+ for (int q = 0; q < nh; q++) {
557
+ if (h_score[q] > bestS) { bestS = h_score[q]; best = q; }
558
+ }
559
+ if (best < 0) break;
560
+ h_score[best] = -1e9;
561
+
562
+ bool clash = false;
563
+ for (int t = 0; t < N_KP; t++) {
564
+ int ci = h_kp[best * N_KP + t];
565
+ if (ci < 0) continue;
566
+ if (used[t * MAX_CAND + ci]) clash = true;
567
+ }
568
+ if (clash) continue;
569
+
570
+ float x0 = 1e9, x1 = -1e9, y0 = 1e9, y1 = -1e9, score = 1e9;
571
+ for (int t = 0; t < N_KP; t++) {
572
+ int ci = h_kp[best * N_KP + t];
573
+ if (ci < 0) continue;
574
+ int e = t * MAX_CAND + ci;
575
+ used[e] = true;
576
+ x0 = min(x0, c_x[e]); x1 = max(x1, c_x[e]);
577
+ y0 = min(y0, c_y[e]); y1 = max(y1, c_y[e]);
578
+ score = min(score, c_s[e]);
579
+ }
580
+
581
+ if (outN == myFace) {
582
+ float halfPx = 0.5 * u_box_scale * max(x1 - x0, y1 - y0);
583
+ float cx = ((x0 + x1) * 0.5 + 0.5) / float(u_w);
584
+ float cy = ((y0 + y1) * 0.5 + 0.5) / float(u_h);
585
+ fragColor = vec4(cx, cy, halfPx / float(u_w), score);
586
+ return;
587
+ }
588
+ outN++;
589
+ }
590
+
591
+ fragColor = vec4(0.0); // no face in this slot
592
+ }
593
+ `;var P=class extends c{inputs;weights=[];output;dispatch;shader=Ne;constructor(e,t,r){if(super(e),t.c!==8)throw new Error(`FaceBoxesFromHeatmaps: expected 8-ch heatmaps (5 real), got ${t.c}`);if(r.maxFaces<1||r.maxFaces>6)throw new Error(`FaceBoxesFromHeatmaps: maxFaces must be 1..6, got ${r.maxFaces}`);let o=r.maxFaces;this.dispatch=[o,1];let i=this.makeTexture(null,o,1);this.output={h:1,w:o,c:4,texture:i,texW:o,texH:1},this.inputs=[t],this.samplers=[{name:"u_hm",texture:t.texture}],this.uniformInts={u_h:t.h,u_w:t.w,u_win:r.win,u_max_faces:o},this.uniformFloats={u_thresh:r.thresh,u_box_scale:r.boxScale,u_tol:r.tol},this.defaultSetup();}};var Ve=`#version 300 es
594
+ // Auto-reframe state update \u2192 1\xD71\xD78 tensor, i.e. a 2\xD71 texture of vec4 groups:
595
+ // x=0 (cx, cy, size, moving) \u2014 the view rect
596
+ // x=1 (subjX, subjY, subjHs, valid) \u2014 the incumbent subject, for hysteresis
597
+ // See reframe_state.wgsl for the full commentary \u2014 this mirrors it.
598
+ //
599
+ // A fragment shader writes ONE vec4, so both fragments redo the whole (tiny)
600
+ // computation and each emits its own group \u2014 same shape as face_boxes.glsl.
601
+
602
+ precision highp float;
603
+ precision highp int;
604
+
605
+ uniform sampler2D u_box; // 1\xD7K \u2014 (cx, cy, halfSide/W, score)
606
+ uniform sampler2D u_prev; // 1\xD71 \u2014 previous state
607
+ uniform sampler2D u_cmd; // 1\xD71 \u2014 .x = mode
608
+ uniform int u_k;
609
+ uniform float u_zoom;
610
+ uniform float u_gravity;
611
+ uniform float u_margin;
612
+ uniform float u_deadband;
613
+ uniform float u_ease;
614
+ uniform float u_aspect; // frame w/h
615
+
616
+ out vec4 fragColor;
617
+
618
+ const float MODE_HOLD = 1.0;
619
+ const float MODE_SOLVE = 2.0;
620
+ const float SWITCH_RATIO = 1.25;
621
+ const float TRACK_RADIUS_MIN = 0.1;
622
+
623
+ void main() {
624
+ int group = int(gl_FragCoord.x); // 0 = view rect, 1 = subject memory
625
+ vec4 prev = texelFetch(u_prev, ivec2(0, 0), 0);
626
+ vec4 prevS = texelFetch(u_prev, ivec2(1, 0), 0);
627
+ float mode = texelFetch(u_cmd, ivec2(0, 0), 0).x;
628
+
629
+ // Challenger = largest live face.
630
+ int chal = -1;
631
+ float chalHs = 0.0;
632
+ for (int i = 0; i < u_k; i++) {
633
+ vec4 b = texelFetch(u_box, ivec2(i, 0), 0);
634
+ if (b.w > 0.0 && b.z > chalHs) { chalHs = b.z; chal = i; }
635
+ }
636
+ if (chal < 0) {
637
+ // Hold, and keep the subject memory \u2014 a one-frame detection miss must not
638
+ // hand the camera to someone else.
639
+ fragColor = (group == 0) ? prev : prevS;
640
+ return;
641
+ }
642
+
643
+ // Incumbent = the live face nearest where the subject was (slot indices are
644
+ // not stable identities, so it's tracked by position).
645
+ int inc = -1;
646
+ float incD = 1e9;
647
+ if (prevS.w > 0.5) {
648
+ float radius = max(TRACK_RADIUS_MIN, prevS.z * 2.0);
649
+ for (int i = 0; i < u_k; i++) {
650
+ vec4 b = texelFetch(u_box, ivec2(i, 0), 0);
651
+ if (b.w <= 0.0) continue;
652
+ float d = length(vec2(b.x - prevS.x, b.y - prevS.y));
653
+ if (d < radius && d < incD) { incD = d; inc = i; }
654
+ }
655
+ }
656
+
657
+ int pick = chal;
658
+ if (inc >= 0 && chalHs <= texelFetch(u_box, ivec2(inc, 0), 0).z * SWITCH_RATIO) pick = inc;
659
+ vec4 subj = texelFetch(u_box, ivec2(pick, 0), 0);
660
+ if (group == 1) { fragColor = vec4(subj.x, subj.y, subj.z, 1.0); return; }
661
+
662
+ float halfX = subj.z;
663
+ float halfY = subj.z * u_aspect;
664
+ // Fallback = the full frame (no reframe) \u2014 see reframe_state.wgsl.
665
+ float wcx = 0.5, wcy = 0.5, ws = 1.0;
666
+ for (int t = 0; t <= 25; t++) {
667
+ float z = u_zoom + (1.0 - u_zoom) * (float(t) / 25.0);
668
+ float s = 1.0 / z;
669
+ float cx = clamp(0.5 + u_gravity * (subj.x - 0.5), s * 0.5, 1.0 - s * 0.5);
670
+ float cy = clamp(0.5 + u_gravity * (subj.y - 0.5), s * 0.5, 1.0 - s * 0.5);
671
+ if (abs(subj.x - cx) + halfX + u_margin <= s * 0.5 &&
672
+ abs(subj.y - cy) + halfY + u_margin <= s * 0.5) {
673
+ wcx = cx; wcy = cy; ws = s;
674
+ break;
675
+ }
676
+ }
677
+ if (prev.z <= 0.0 || mode == MODE_SOLVE) { fragColor = vec4(wcx, wcy, ws, 0.0); return; }
678
+ if (mode == MODE_HOLD) { fragColor = prev; return; }
679
+
680
+ float d = length(vec2(wcx - prev.x, wcy - prev.y)) + abs(ws - prev.z);
681
+ float moving = prev.w;
682
+ if (d > u_deadband) moving = 1.0;
683
+ else if (d < u_deadband * 0.3) moving = 0.0;
684
+ if (moving < 0.5) { fragColor = vec4(prev.xyz, 0.0); return; }
685
+
686
+ fragColor = vec4(
687
+ mix(prev.x, wcx, u_ease),
688
+ mix(prev.y, wcy, u_ease),
689
+ mix(prev.z, ws, u_ease),
690
+ 1.0
691
+ );
692
+ }
693
+ `;var M=class extends c{inputs;weights=[];output;dispatch=[2,1];shader=Ve;constructor(e,t,r,o,i){super(e);let s=this.makeTexture(null,2,1);this.output={h:1,w:1,c:8,texture:s,texW:2,texH:1},this.inputs=[t,r,o],this.samplers=[{name:"u_box",texture:t.texture},{name:"u_prev",texture:r.texture},{name:"u_cmd",texture:o.texture}],this.uniformInts={u_k:t.w*t.h},this.uniformFloats={u_zoom:i.zoom,u_gravity:i.gravity,u_margin:i.margin,u_deadband:i.deadband,u_ease:i.ease,u_aspect:i.aspect},this.defaultSetup();}};var Xe=`#version 300 es
694
+ // Apply the reframe crop: sample \`src\` through the view rect. Same shape in and
695
+ // out. See reframe.wgsl for why this sits after the effect chain and touches
696
+ // only the foreground + alpha (never the background).
697
+ //
698
+ // rect = (cx, cy, size, moving), frame fractions; size \u2264 0 \u2192 identity.
699
+
700
+ precision highp float;
701
+ precision highp int;
702
+
703
+ uniform sampler2D u_src;
704
+ uniform sampler2D u_rect; // 1\xD71
705
+ uniform int u_h;
706
+ uniform int u_w;
707
+
708
+ out vec4 fragColor;
709
+
710
+ vec4 samp(int x, int y) {
711
+ return texelFetch(u_src, ivec2(clamp(x, 0, u_w - 1), clamp(y, 0, u_h - 1)), 0);
712
+ }
713
+
714
+ void main() {
715
+ int x = int(gl_FragCoord.x);
716
+ int y = int(gl_FragCoord.y);
717
+
718
+ vec4 r = texelFetch(u_rect, ivec2(0, 0), 0);
719
+ float s = r.z, rcx = r.x, rcy = r.y;
720
+ if (s <= 0.0) { s = 1.0; rcx = 0.5; rcy = 0.5; }
721
+
722
+ float fx = (rcx - s * 0.5) + ((float(x) + 0.5) / float(u_w)) * s;
723
+ float fy = (rcy - s * 0.5) + ((float(y) + 0.5) / float(u_h)) * s;
724
+ // fraction \u2192 texel index; an identity rect lands exactly on texel centres,
725
+ // making the op a bit-exact copy.
726
+ float sx = fx * float(u_w) - 0.5;
727
+ float sy = fy * float(u_h) - 0.5;
728
+
729
+ int x0 = int(floor(sx));
730
+ int y0 = int(floor(sy));
731
+ float tx = sx - float(x0);
732
+ float ty = sy - float(y0);
733
+
734
+ vec4 top = mix(samp(x0, y0), samp(x0 + 1, y0), tx);
735
+ vec4 bot = mix(samp(x0, y0 + 1), samp(x0 + 1, y0 + 1), tx);
736
+ fragColor = mix(top, bot, ty);
737
+ }
738
+ `;var O=class extends c{inputs;weights=[];output;dispatch;shader=Xe;constructor(e,t,r){super(e);let o=this.makeTexture(null,t.w,t.h);this.output={h:t.h,w:t.w,c:4,texture:o,texW:t.w,texH:t.h},this.inputs=[t,r],this.samplers=[{name:"u_src",texture:t.texture},{name:"u_rect",texture:r.texture}],this.uniformInts={u_h:t.h,u_w:t.w},this.defaultSetup(),this.dispatch=[t.w,t.h];}};var ze=`#version 300 es
739
+ // Box-driven square crop + bilinear resample + ((rgb - mean)/std) normalize.
740
+ // Reads the box tensor (face_box output: cx, cy, halfSide, score in frame
741
+ // fractions; halfSide as a fraction of frame WIDTH) and emits the landmark
742
+ // model's input, .w = 0. Sampling mirrors landmark training's warpAffine:
743
+ // src = center + (u - out/2) \xB7 side/out.
744
+ //
745
+ // frame texture: (w, h) \u2014 4-ch, 1 group. box texture: 1\xD71.
746
+
747
+ precision highp float;
748
+ precision highp int;
749
+
750
+ uniform sampler2D u_frame;
751
+ uniform sampler2D u_box;
752
+ uniform int u_in_h;
753
+ uniform int u_in_w;
754
+ uniform int u_out_h;
755
+ uniform int u_out_w;
756
+ uniform int u_slot; // box-tensor slot (multi-face); 0 for the single-face path
757
+ uniform float u_mean_r;
758
+ uniform float u_mean_g;
759
+ uniform float u_mean_b;
760
+ uniform float u_std_r;
761
+ uniform float u_std_g;
762
+ uniform float u_std_b;
763
+
764
+ out vec4 fragColor;
765
+
766
+ vec4 samp(int x, int y) {
767
+ return texelFetch(u_frame, ivec2(clamp(x, 0, u_in_w - 1), clamp(y, 0, u_in_h - 1)), 0);
768
+ }
769
+
770
+ void main() {
771
+ int x = int(gl_FragCoord.x);
772
+ int y = int(gl_FragCoord.y);
773
+
774
+ vec4 box = texelFetch(u_box, ivec2(u_slot, 0), 0);
775
+ float cx = box.x * float(u_in_w);
776
+ float cy = box.y * float(u_in_h);
777
+ float side = 2.0 * box.z * float(u_in_w);
778
+
779
+ float sx = clamp(cx + (float(x) - float(u_out_w) * 0.5) * side / float(u_out_w), 0.0, float(u_in_w - 1));
780
+ float sy = clamp(cy + (float(y) - float(u_out_h) * 0.5) * side / float(u_out_h), 0.0, float(u_in_h - 1));
781
+
782
+ int x0 = int(floor(sx));
783
+ int y0 = int(floor(sy));
784
+ float tx = sx - float(x0);
785
+ float ty = sy - float(y0);
786
+
787
+ vec4 top = mix(samp(x0, y0), samp(x0 + 1, y0), tx);
788
+ vec4 bot = mix(samp(x0, y0 + 1), samp(x0 + 1, y0 + 1), tx);
789
+ vec4 v = mix(top, bot, ty);
790
+
791
+ vec3 mean = vec3(u_mean_r, u_mean_g, u_mean_b);
792
+ vec3 std = vec3(u_std_r, u_std_g, u_std_b);
793
+ fragColor = vec4((v.rgb - mean) / std, 0.0);
794
+ }
795
+ `;var H=class extends c{inputs;weights=[];output;dispatch;shader=ze;constructor(e,t,r,o){super(e);let i=o.slot??0;if(i>=r.w*r.h)throw new Error(`CropResample: slot ${i} out of range for a ${r.h}\xD7${r.w} box tensor`);let s=this.makeTexture(null,o.outW,o.outH);this.output={h:o.outH,w:o.outW,c:4,texture:s,texW:o.outW,texH:o.outH},this.inputs=[t,r],this.samplers=[{name:"u_frame",texture:t.texture},{name:"u_box",texture:r.texture}],this.uniformInts={u_in_h:t.h,u_in_w:t.w,u_out_h:o.outH,u_out_w:o.outW,u_slot:i},this.uniformFloats={u_mean_r:o.mean[0],u_mean_g:o.mean[1],u_mean_b:o.mean[2],u_std_r:o.std[0],u_std_g:o.std[1],u_std_b:o.std[2]},this.defaultSetup(),this.dispatch=[o.outW,o.outH];}};var I=`#version 300 es
796
+ // Passthrough "compositor": writes the image directly to the canvas
797
+ // (default framebuffer). No alpha math, no background. Used by RenderOp
798
+ // when the renderer is in disabled state.
799
+ //
800
+ // Caller invariants:
801
+ // - canvas (viewport) === image h \xD7 w (no resampling here)
802
+
803
+ precision highp float;
804
+
805
+ uniform sampler2D u_image; // image as NHWC vec4 (RGBA)
806
+
807
+ out vec4 fragColor;
808
+
809
+ void main() {
810
+ // WebGL gl_FragCoord origin is bottom-left; tensor textures are stored
811
+ // top-down. Flip y so the displayed image is upright. (Matches what the
812
+ // existing composite_solid.glsl does.)
813
+ int H = textureSize(u_image, 0).y;
814
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
815
+ vec3 rgb = texelFetch(u_image, px, 0).rgb;
816
+ fragColor = vec4(rgb, 1.0);
817
+ }
818
+ `;var Nt=`#version 300 es
819
+ const vec2 VERTS[6] = vec2[6](
820
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
821
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
822
+ );
823
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,Vt=`#version 300 es
824
+ precision highp float;
825
+ uniform sampler2D u_lm;
826
+ uniform sampler2D u_box;
827
+ uniform int u_slot;
828
+ uniform float u_thresh;
829
+ uniform float u_point_size;
830
+ uniform float u_canvas_w;
831
+ uniform float u_canvas_h;
832
+
833
+ void main() {
834
+ int i = gl_VertexID;
835
+ vec4 box = texelFetch(u_box, ivec2(u_slot, 0), 0);
836
+ if (box.w < u_thresh) {
837
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0); // clipped away
838
+ gl_PointSize = 0.0;
839
+ return;
840
+ }
841
+ vec4 g = texelFetch(u_lm, ivec2(i / 2, 0), 0);
842
+ float lx = g[(i % 2) * 2];
843
+ float ly = g[(i % 2) * 2 + 1];
844
+
845
+ float hsx = box.z;
846
+ float hsy = box.z * u_canvas_w / u_canvas_h;
847
+ float px = (box.x - hsx) + lx * 2.0 * hsx;
848
+ float py = (box.y - hsy) + ly * 2.0 * hsy;
849
+
850
+ gl_Position = vec4(px * 2.0 - 1.0, 1.0 - 2.0 * py, 0.0, 1.0);
851
+ gl_PointSize = u_point_size;
852
+ }`,Xt=`#version 300 es
853
+ precision highp float;
854
+ uniform vec3 u_color;
855
+ out vec4 fragColor;
856
+ void main() { fragColor = vec4(u_color, 1.0); }`;function $e(a,e,t,r){let o=a.createShader(a.VERTEX_SHADER);if(a.shaderSource(o,e),a.compileShader(o),!a.getShaderParameter(o,a.COMPILE_STATUS))throw new Error(`${r} vert compile error: ${a.getShaderInfoLog(o)}`);let i=a.createShader(a.FRAGMENT_SHADER);if(a.shaderSource(i,t),a.compileShader(i),!a.getShaderParameter(i,a.COMPILE_STATUS))throw new Error(`${r} frag compile error: ${a.getShaderInfoLog(i)}`);let s=a.createProgram();if(a.attachShader(s,o),a.attachShader(s,i),a.linkProgram(s),!a.getProgramParameter(s,a.LINK_STATUS))throw new Error(`${r} link error: ${a.getProgramInfoLog(s)}`);return s}var N=class{constructor(e,t,r,o,i){this.backend=e;if(r.c<i.count*2)throw new Error(`LandmarkOverlay: landmarks tensor holds ${r.c/2} points < count ${i.count}`);if(this.slot=i.slot??0,this.slot>=o.w*o.h)throw new Error(`LandmarkOverlay: slot ${this.slot} out of range for a ${o.h}\xD7${o.w} box tensor`);this.imageTex=t.texture,this.lmTex=r.texture,this.boxTex=o.texture,this.params=i,this.canvasW=t.w,this.canvasH=t.h;let s=e.gl;this.imgProgram=$e(s,Nt,I,"landmark_overlay img"),this.ptsProgram=$e(s,Vt,Xt,"landmark_overlay pts");}backend;imgProgram;ptsProgram;imageTex;lmTex;boxTex;params;canvasW;canvasH;slot;run(){let e=this.backend.gl;this.backend.bindDisplayFramebuffer(),e.bindVertexArray(null),(this.params.drawImage??true)&&(e.useProgram(this.imgProgram),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.imageTex),e.uniform1i(e.getUniformLocation(this.imgProgram,"u_image"),0),e.drawArrays(e.TRIANGLES,0,6)),e.useProgram(this.ptsProgram),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.lmTex),e.uniform1i(e.getUniformLocation(this.ptsProgram,"u_lm"),0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.boxTex),e.uniform1i(e.getUniformLocation(this.ptsProgram,"u_box"),1),e.uniform1i(e.getUniformLocation(this.ptsProgram,"u_slot"),this.slot),e.uniform1f(e.getUniformLocation(this.ptsProgram,"u_thresh"),this.params.thresh),e.uniform1f(e.getUniformLocation(this.ptsProgram,"u_point_size"),this.params.pointSize),e.uniform1f(e.getUniformLocation(this.ptsProgram,"u_canvas_w"),this.canvasW),e.uniform1f(e.getUniformLocation(this.ptsProgram,"u_canvas_h"),this.canvasH),e.uniform3f(e.getUniformLocation(this.ptsProgram,"u_color"),this.params.color[0],this.params.color[1],this.params.color[2]),e.drawArrays(e.POINTS,0,this.params.count);}};var W=`#version 300 es
857
+ const vec2 VERTS[6] = vec2[6](
858
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
859
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
860
+ );
861
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,Le=`
862
+ uniform sampler2D u_lm;
863
+ uniform sampler2D u_box;
864
+ uniform float u_thresh;
865
+ uniform float u_canvas_w;
866
+ uniform float u_canvas_h;
867
+ uniform int u_grid;
868
+
869
+ vec3 lmFrame(int i, int face) {
870
+ vec4 box = texelFetch(u_box, ivec2(face, 0), 0);
871
+ int gi = face * 478 + i;
872
+ vec4 g = texelFetch(u_lm, ivec2(gi / 2, 0), 0);
873
+ float lx = g[(gi % 2) * 2];
874
+ float ly = g[(gi % 2) * 2 + 1];
875
+ float hsx = box.z;
876
+ float hsy = box.z * u_canvas_w / u_canvas_h;
877
+ return vec3((box.x - hsx) + lx * 2.0 * hsx, (box.y - hsy) + ly * 2.0 * hsy, box.w);
878
+ }
879
+
880
+ // Face 'face' owns tile (face % grid, face / grid). grid=1 (K=1) -> the whole
881
+ // atlas, i.e. the single-face layout unchanged.
882
+ vec2 tileUv(vec2 uv, int face) {
883
+ float g = float(u_grid);
884
+ return (uv + vec2(float(face % u_grid), float(face / u_grid))) / g;
885
+ }`,je=`#version 300 es
886
+ precision highp float;
887
+ layout(location = 0) in vec2 a_uv;
888
+ layout(location = 1) in float a_idx;
889
+ ${Le}
890
+ out vec2 v_src;
891
+ void main() {
892
+ vec3 l = lmFrame(int(a_idx), gl_InstanceID);
893
+ if (l.z < u_thresh) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); return; }
894
+ v_src = l.xy;
895
+ gl_Position = vec4(tileUv(a_uv, gl_InstanceID) * 2.0 - 1.0, 0.0, 1.0);
896
+ }`,Ke=`
897
+ uniform sampler2D u_frame;
898
+ uniform float u_frame_w;
899
+ uniform float u_frame_h;
900
+
901
+ vec3 frameAt(int x, int y) {
902
+ return texelFetch(u_frame, ivec2(clamp(x, 0, int(u_frame_w) - 1), clamp(y, 0, int(u_frame_h) - 1)), 0).rgb;
903
+ }
904
+ vec3 frameBilinear(vec2 f) {
905
+ float sx = clamp(f.x * u_frame_w - 0.5, 0.0, u_frame_w - 1.0);
906
+ float sy = clamp(f.y * u_frame_h - 0.5, 0.0, u_frame_h - 1.0);
907
+ int x0 = int(floor(sx));
908
+ int y0 = int(floor(sy));
909
+ float tx = sx - float(x0);
910
+ float ty = sy - float(y0);
911
+ vec3 top = mix(frameAt(x0, y0), frameAt(x0 + 1, y0), tx);
912
+ vec3 bot = mix(frameAt(x0, y0 + 1), frameAt(x0 + 1, y0 + 1), tx);
913
+ return mix(top, bot, ty);
914
+ }`,qe=`#version 300 es
915
+ precision highp float;
916
+ precision highp int;
917
+ in vec2 v_src;
918
+ ${Ke}
919
+ out vec4 fragColor;
920
+ void main() { fragColor = vec4(frameBilinear(v_src), 1.0); }`,Ye=`#version 300 es
921
+ precision highp float;
922
+ uniform sampler2D u_tex;
923
+ uniform vec2 u_dir;
924
+ uniform float u_sigma;
925
+ uniform int u_grid;
926
+ // The tile containing 'uv', as (lo.xy, hi.zw) - filter taps clamp to it so one
927
+ // face's skin can't bleed into a neighbour's tile.
928
+ vec4 tileBounds(vec2 uv) {
929
+ float g = float(u_grid);
930
+ vec2 t = floor(clamp(uv, vec2(0.0), vec2(0.99999)) * g);
931
+ return vec4(t / g, (t + vec2(1.0)) / g);
932
+ }
933
+ out vec4 fragColor;
934
+ void main() {
935
+ vec2 uv = gl_FragCoord.xy / 512.0;
936
+ vec4 tb = tileBounds(uv);
937
+ float ht = 0.5 / 512.0;
938
+ vec2 lo = tb.xy + ht, hi = tb.zw - ht;
939
+ float s = max(u_sigma, 0.001);
940
+ int R = int(clamp(ceil(s * 2.5), 1.0, 48.0));
941
+ vec4 sum = vec4(0.0);
942
+ float wsum = 0.0;
943
+ for (int i = -48; i <= 48; i++) {
944
+ if (i < -R || i > R) continue;
945
+ float w = exp(-float(i * i) / (2.0 * s * s));
946
+ sum += texture(u_tex, clamp(uv + u_dir * float(i), lo, hi)) * w;
947
+ wsum += w;
948
+ }
949
+ fragColor = sum / wsum;
950
+ }`,Qe=`#version 300 es
951
+ precision highp float;
952
+ uniform sampler2D u_atlas;
953
+ uniform sampler2D u_low;
954
+ uniform float u_detail;
955
+ out vec4 fragColor;
956
+ void main() {
957
+ vec2 uv = gl_FragCoord.xy / 512.0;
958
+ vec4 a = texture(u_atlas, uv);
959
+ vec4 lo = texture(u_low, uv);
960
+ fragColor = clamp(lo + (a - lo) * u_detail, 0.0, 1.0);
961
+ }`,Je=`#version 300 es
962
+ precision highp float;
963
+ uniform sampler2D u_tex;
964
+ uniform float u_sigma;
965
+ uniform int u_grid;
966
+ vec4 tileBounds(vec2 uv) {
967
+ float g = float(u_grid);
968
+ vec2 t = floor(clamp(uv, vec2(0.0), vec2(0.99999)) * g);
969
+ return vec4(t / g, (t + vec2(1.0)) / g);
970
+ }
971
+ out vec4 fragColor;
972
+ void main() {
973
+ vec2 uv = gl_FragCoord.xy / 512.0;
974
+ vec2 texel = vec2(1.0 / 512.0);
975
+ vec4 tb = tileBounds(uv);
976
+ vec2 blo = tb.xy + texel * 0.5, bhi = tb.zw - texel * 0.5;
977
+ float ss = max(u_sigma, 0.001);
978
+ int R = int(clamp(ceil(ss), 1.0, 12.0));
979
+ float sr = 0.15;
980
+ vec3 c = texture(u_tex, uv).rgb;
981
+ vec3 sum = vec3(0.0);
982
+ float wsum = 0.0;
983
+ for (int y = -12; y <= 12; y++) {
984
+ if (y < -R || y > R) continue;
985
+ for (int x = -12; x <= 12; x++) {
986
+ if (x < -R || x > R) continue;
987
+ vec3 sc = texture(u_tex, clamp(uv + texel * vec2(float(x), float(y)), blo, bhi)).rgb;
988
+ float ws = exp(-float(x * x + y * y) / (2.0 * ss * ss));
989
+ vec3 d = sc - c;
990
+ float wr = exp(-dot(d, d) / (2.0 * sr * sr));
991
+ sum += sc * ws * wr;
992
+ wsum += ws * wr;
993
+ }
994
+ }
995
+ fragColor = vec4(sum / wsum, 1.0);
996
+ }`,zt=`#version 300 es
997
+ precision highp float;
998
+ uniform sampler2D u_image;
999
+ out vec4 fragColor;
1000
+ void main() {
1001
+ int H = textureSize(u_image, 0).y;
1002
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
1003
+ fragColor = vec4(texelFetch(u_image, px, 0).rgb, 1.0);
1004
+ }`,$t=`#version 300 es
1005
+ precision highp float;
1006
+ layout(location = 0) in vec2 a_uv;
1007
+ layout(location = 1) in float a_idx;
1008
+ ${Le}
1009
+ out vec2 v_uv; // TILED atlas uv \u2014 the atlas is per-face
1010
+ out vec2 v_uv_mask; // UNTILED canonical uv \u2014 the weight mask is one shared asset
1011
+ out vec2 v_src;
1012
+ void main() {
1013
+ vec3 l = lmFrame(int(a_idx), gl_InstanceID);
1014
+ if (l.z < u_thresh) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); return; }
1015
+ v_uv = tileUv(a_uv, gl_InstanceID);
1016
+ v_uv_mask = a_uv;
1017
+ v_src = l.xy;
1018
+ gl_Position = vec4(l.x * 2.0 - 1.0, 1.0 - 2.0 * l.y, 0.0, 1.0);
1019
+ }`,Ze=`#version 300 es
1020
+ precision highp float;
1021
+ precision highp int;
1022
+ in vec2 v_uv;
1023
+ in vec2 v_uv_mask;
1024
+ in vec2 v_src;
1025
+ uniform sampler2D u_smoothed;
1026
+ uniform sampler2D u_weight;
1027
+ uniform float u_strength;
1028
+ ${Ke}
1029
+ out vec4 fragColor;
1030
+ void main() {
1031
+ vec3 orig = frameBilinear(v_src);
1032
+ vec3 sm = texture(u_smoothed, v_uv).rgb;
1033
+ // v_uv_mask, not v_uv: the atlas is tiled per face, the weight mask is one
1034
+ // shared canonical asset. See face_touchup.wgsl fs_comp.
1035
+ float w = texture(u_weight, v_uv_mask).r * u_strength;
1036
+ fragColor = vec4(mix(orig, sm, w), 1.0);
1037
+ }`;function T(a,e,t,r){let o=(s,n,u)=>{let l=a.createShader(s);if(a.shaderSource(l,n),a.compileShader(l),!a.getShaderParameter(l,a.COMPILE_STATUS))throw new Error(`${r} ${u} compile error: ${a.getShaderInfoLog(l)}`);return l},i=a.createProgram();if(a.attachShader(i,o(a.VERTEX_SHADER,e,"vert")),a.attachShader(i,o(a.FRAGMENT_SHADER,t,"frag")),a.linkProgram(i),!a.getProgramParameter(i,a.LINK_STATUS))throw new Error(`${r} link error: ${a.getProgramInfoLog(i)}`);return i}var V=class{constructor(e,t,r,o,i,s){this.backend=e;let n=e.gl;if(this.frame=t,this.lm=r,this.box=o,this.count=i.count,this.params=s,this.slots=s.slots??1,this.slots!==1&&this.slots!==4)throw new Error(`FaceTouchup: slots must be 1 or 4, got ${this.slots}`);if(this.grid=this.slots===4?2:1,this.activeSlots=this.slots,o.w*o.h<this.slots)throw new Error(`FaceTouchup: slots ${this.slots} exceeds the ${o.h}\xD7${o.w} box tensor`);if(r.c<this.slots*956)throw new Error(`FaceTouchup: landmarks tensor holds ${r.c/956} faces < slots ${this.slots}`);this.progs={unwrap:T(n,je,qe,"touchup unwrap"),blur:T(n,W,Ye,"touchup blur"),combine:T(n,W,Qe,"touchup combine"),bilateral:T(n,W,Je,"touchup bilateral"),blit:T(n,W,zt,"touchup blit"),comp:T(n,$t,Ze,"touchup comp")},this.meshVao=n.createVertexArray(),n.bindVertexArray(this.meshVao);let u=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,u),n.bufferData(n.ARRAY_BUFFER,i.uv,n.STATIC_DRAW),n.enableVertexAttribArray(0),n.vertexAttribPointer(0,2,n.FLOAT,false,0,0);let l=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,l),n.bufferData(n.ARRAY_BUFFER,i.idx,n.STATIC_DRAW),n.enableVertexAttribArray(1),n.vertexAttribPointer(1,1,n.FLOAT,false,0,0),n.bindVertexArray(null);let p=h=>{let m=n.createTexture();return n.bindTexture(n.TEXTURE_2D,m),h?n.texImage2D(n.TEXTURE_2D,0,n.RGBA8,n.RGBA,n.UNSIGNED_BYTE,h):n.texImage2D(n.TEXTURE_2D,0,n.RGBA8,512,512,0,n.RGBA,n.UNSIGNED_BYTE,null),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),m};this.atlas=p(null),this.ping=p(null),this.low=p(null),this.smoothed=p(null),this.weight=p(i.weightMask),this.fbo=n.createFramebuffer();}backend;progs;meshVao;fbo;atlas;ping;low;smoothed;weight;frame;lm;box;count;params;slots;grid;activeSlots;bindTex(e,t,r,o){let i=this.backend.gl;i.activeTexture(i.TEXTURE0+t),i.bindTexture(i.TEXTURE_2D,o),i.uniform1i(i.getUniformLocation(e,r),t);}lmUniforms(e){let t=this.backend.gl;this.bindTex(e,4,"u_lm",this.lm.texture),this.bindTex(e,5,"u_box",this.box.texture),t.uniform1f(t.getUniformLocation(e,"u_thresh"),this.params.thresh),t.uniform1f(t.getUniformLocation(e,"u_canvas_w"),this.frame.w),t.uniform1f(t.getUniformLocation(e,"u_canvas_h"),this.frame.h),t.uniform1i(t.getUniformLocation(e,"u_grid"),this.grid);}frameUniforms(e){let t=this.backend.gl;this.bindTex(e,0,"u_frame",this.frame.texture),t.uniform1f(t.getUniformLocation(e,"u_frame_w"),this.frame.w),t.uniform1f(t.getUniformLocation(e,"u_frame_h"),this.frame.h);}atlasPass(e,t){let r=this.backend.gl;r.bindFramebuffer(r.FRAMEBUFFER,this.fbo),r.framebufferTexture2D(r.FRAMEBUFFER,r.COLOR_ATTACHMENT0,r.TEXTURE_2D,e,0),r.viewport(0,0,512,512),t();}run(){let e=this.backend.gl;if(this.atlasPass(this.atlas,()=>{e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),e.useProgram(this.progs.unwrap),this.frameUniforms(this.progs.unwrap),this.lmUniforms(this.progs.unwrap),e.bindVertexArray(this.meshVao),e.drawArraysInstanced(e.TRIANGLES,0,this.count,this.activeSlots),e.bindVertexArray(null);}),(this.params.style??"freq-sep")==="bilateral")this.atlasPass(this.smoothed,()=>{e.useProgram(this.progs.bilateral),this.bindTex(this.progs.bilateral,0,"u_tex",this.atlas),e.uniform1f(e.getUniformLocation(this.progs.bilateral,"u_sigma"),this.params.amount),e.uniform1i(e.getUniformLocation(this.progs.bilateral,"u_grid"),this.grid),e.drawArrays(e.TRIANGLES,0,6);});else {let t=(r,o,i,s)=>this.atlasPass(o,()=>{e.useProgram(this.progs.blur),this.bindTex(this.progs.blur,0,"u_tex",r),e.uniform2f(e.getUniformLocation(this.progs.blur,"u_dir"),i,s),e.uniform1f(e.getUniformLocation(this.progs.blur,"u_sigma"),this.params.amount),e.uniform1i(e.getUniformLocation(this.progs.blur,"u_grid"),this.grid),e.drawArrays(e.TRIANGLES,0,6);});t(this.atlas,this.ping,1/512,0),t(this.ping,this.low,0,1/512),this.atlasPass(this.smoothed,()=>{e.useProgram(this.progs.combine),this.bindTex(this.progs.combine,0,"u_atlas",this.atlas),this.bindTex(this.progs.combine,1,"u_low",this.low),e.uniform1f(e.getUniformLocation(this.progs.combine,"u_detail"),this.params.detail),e.drawArrays(e.TRIANGLES,0,6);});}this.backend.bindDisplayFramebuffer(),e.viewport(0,0,this.frame.w,this.frame.h),e.useProgram(this.progs.blit),this.bindTex(this.progs.blit,0,"u_image",this.frame.texture),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6),e.useProgram(this.progs.comp),this.frameUniforms(this.progs.comp),this.lmUniforms(this.progs.comp),this.bindTex(this.progs.comp,1,"u_smoothed",this.smoothed),this.bindTex(this.progs.comp,2,"u_weight",this.weight),e.uniform1f(e.getUniformLocation(this.progs.comp,"u_strength"),this.params.strength),e.bindVertexArray(this.meshVao),e.drawArraysInstanced(e.TRIANGLES,0,this.count,this.activeSlots),e.bindVertexArray(null);}},jt=`#version 300 es
1038
+ precision highp float;
1039
+ uniform sampler2D u_image;
1040
+ out vec4 fragColor;
1041
+ void main() {
1042
+ ivec2 px = ivec2(gl_FragCoord.xy);
1043
+ fragColor = vec4(texelFetch(u_image, px, 0).rgb, 1.0);
1044
+ }`,Kt=`#version 300 es
1045
+ precision highp float;
1046
+ layout(location = 0) in vec2 a_uv;
1047
+ layout(location = 1) in float a_idx;
1048
+ ${Le}
1049
+ out vec2 v_uv; // TILED atlas uv \u2014 the atlas is per-face
1050
+ out vec2 v_uv_mask; // UNTILED canonical uv \u2014 the weight mask is one shared asset
1051
+ out vec2 v_src;
1052
+ void main() {
1053
+ vec3 l = lmFrame(int(a_idx), gl_InstanceID);
1054
+ if (l.z < u_thresh) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); return; }
1055
+ v_uv = tileUv(a_uv, gl_InstanceID);
1056
+ v_uv_mask = a_uv;
1057
+ v_src = l.xy;
1058
+ gl_Position = vec4(l.x * 2.0 - 1.0, l.y * 2.0 - 1.0, 0.0, 1.0);
1059
+ }`,X=class{constructor(e,t,r,o,i,s){this.backend=e;let n=e.gl;if(this.inputs=[t,r,o],this.frame=t,this.lm=r,this.box=o,this.count=i.count,this.params=s,this.slots=s.slots??1,this.slots!==1&&this.slots!==4)throw new Error(`FaceTouchup: slots must be 1 or 4, got ${this.slots}`);if(this.grid=this.slots===4?2:1,this.activeSlots=this.slots,o.w*o.h<this.slots)throw new Error(`FaceTouchup: slots ${this.slots} exceeds the ${o.h}\xD7${o.w} box tensor`);if(r.c<this.slots*956)throw new Error(`FaceTouchup: landmarks tensor holds ${r.c/956} faces < slots ${this.slots}`);this.output=e.tensor(t.h,t.w,4),this.progs={unwrap:T(n,je,qe,"touchup-stage unwrap"),blur:T(n,W,Ye,"touchup-stage blur"),combine:T(n,W,Qe,"touchup-stage combine"),bilateral:T(n,W,Je,"touchup-stage bilateral"),blitT:T(n,W,jt,"touchup-stage blitT"),compT:T(n,Kt,Ze,"touchup-stage compT")},this.meshVao=n.createVertexArray(),n.bindVertexArray(this.meshVao);let u=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,u),n.bufferData(n.ARRAY_BUFFER,i.uv,n.STATIC_DRAW),n.enableVertexAttribArray(0),n.vertexAttribPointer(0,2,n.FLOAT,false,0,0);let l=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,l),n.bufferData(n.ARRAY_BUFFER,i.idx,n.STATIC_DRAW),n.enableVertexAttribArray(1),n.vertexAttribPointer(1,1,n.FLOAT,false,0,0),n.bindVertexArray(null);let p=h=>{let m=n.createTexture();return n.bindTexture(n.TEXTURE_2D,m),h?n.texImage2D(n.TEXTURE_2D,0,n.RGBA8,n.RGBA,n.UNSIGNED_BYTE,h):n.texImage2D(n.TEXTURE_2D,0,n.RGBA8,512,512,0,n.RGBA,n.UNSIGNED_BYTE,null),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),m};this.atlas=p(null),this.ping=p(null),this.low=p(null),this.smoothed=p(null),this.weightTex=p(i.weightMask),this.fbo=n.createFramebuffer();}backend;inputs;weights=[];output;progs;meshVao;fbo;atlas;ping;low;smoothed;weightTex;frame;lm;box;count;params;slots;grid;activeSlots;bindTex(e,t,r,o){let i=this.backend.gl;i.activeTexture(i.TEXTURE0+t),i.bindTexture(i.TEXTURE_2D,o),i.uniform1i(i.getUniformLocation(e,r),t);}lmUniforms(e){let t=this.backend.gl;this.bindTex(e,4,"u_lm",this.lm.texture),this.bindTex(e,5,"u_box",this.box.texture),t.uniform1f(t.getUniformLocation(e,"u_thresh"),this.params.thresh),t.uniform1f(t.getUniformLocation(e,"u_canvas_w"),this.frame.w),t.uniform1f(t.getUniformLocation(e,"u_canvas_h"),this.frame.h),t.uniform1i(t.getUniformLocation(e,"u_grid"),this.grid);}frameUniforms(e){let t=this.backend.gl;this.bindTex(e,0,"u_frame",this.frame.texture),t.uniform1f(t.getUniformLocation(e,"u_frame_w"),this.frame.w),t.uniform1f(t.getUniformLocation(e,"u_frame_h"),this.frame.h);}pass(e,t,r,o){let i=this.backend.gl;i.bindFramebuffer(i.FRAMEBUFFER,this.fbo),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,e,0),i.viewport(0,0,t,r),o();}setActiveSlots(e){this.activeSlots=Math.max(0,Math.min(e,this.slots));}run(){let e=this.backend.gl;if(this.pass(this.atlas,512,512,()=>{e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),e.useProgram(this.progs.unwrap),this.frameUniforms(this.progs.unwrap),this.lmUniforms(this.progs.unwrap),e.bindVertexArray(this.meshVao),e.drawArraysInstanced(e.TRIANGLES,0,this.count,this.activeSlots),e.bindVertexArray(null);}),(this.params.style??"freq-sep")==="bilateral")this.pass(this.smoothed,512,512,()=>{e.useProgram(this.progs.bilateral),this.bindTex(this.progs.bilateral,0,"u_tex",this.atlas),e.uniform1f(e.getUniformLocation(this.progs.bilateral,"u_sigma"),this.params.amount),e.uniform1i(e.getUniformLocation(this.progs.bilateral,"u_grid"),this.grid),e.drawArrays(e.TRIANGLES,0,6);});else {let t=(r,o,i,s)=>this.pass(o,512,512,()=>{e.useProgram(this.progs.blur),this.bindTex(this.progs.blur,0,"u_tex",r),e.uniform2f(e.getUniformLocation(this.progs.blur,"u_dir"),i,s),e.uniform1f(e.getUniformLocation(this.progs.blur,"u_sigma"),this.params.amount),e.uniform1i(e.getUniformLocation(this.progs.blur,"u_grid"),this.grid),e.drawArrays(e.TRIANGLES,0,6);});t(this.atlas,this.ping,1/512,0),t(this.ping,this.low,0,1/512),this.pass(this.smoothed,512,512,()=>{e.useProgram(this.progs.combine),this.bindTex(this.progs.combine,0,"u_atlas",this.atlas),this.bindTex(this.progs.combine,1,"u_low",this.low),e.uniform1f(e.getUniformLocation(this.progs.combine,"u_detail"),this.params.detail),e.drawArrays(e.TRIANGLES,0,6);});}this.pass(this.output.texture,this.frame.w,this.frame.h,()=>{e.useProgram(this.progs.blitT),this.bindTex(this.progs.blitT,0,"u_image",this.frame.texture),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6),e.useProgram(this.progs.compT),this.frameUniforms(this.progs.compT),this.lmUniforms(this.progs.compT),this.bindTex(this.progs.compT,1,"u_smoothed",this.smoothed),this.bindTex(this.progs.compT,2,"u_weight",this.weightTex),e.uniform1f(e.getUniformLocation(this.progs.compT,"u_strength"),this.params.strength),e.bindVertexArray(this.meshVao),e.drawArraysInstanced(e.TRIANGLES,0,this.count,this.activeSlots),e.bindVertexArray(null);});}};var et=`#version 300 es
1060
+ // Flow-gated temporal stabilizer. Per pixel:
1061
+ // env = max(|flow.xy|, release\xB7envPrev.y) peak-hold
1062
+ // g = max(clamp((env - tLo)/(tHi - tLo), 0, 1), leak)
1063
+ // out = vec4((g\xB7pred + (1-g)\xB7ref).x, env, 0, 0)
1064
+
1065
+ precision highp float;
1066
+ precision highp int;
1067
+
1068
+ uniform sampler2D u_flow;
1069
+ uniform sampler2D u_pred;
1070
+ uniform sampler2D u_ref;
1071
+ uniform sampler2D u_env_prev;
1072
+ uniform int u_w;
1073
+ uniform int u_h;
1074
+ uniform float u_t_lo;
1075
+ uniform float u_t_hi;
1076
+ uniform float u_leak;
1077
+ uniform float u_release;
1078
+ uniform float u_t_div;
1079
+ uniform float u_div_scale;
1080
+ uniform int u_step_x;
1081
+ uniform int u_step_y;
1082
+
1083
+ out vec4 fragColor;
1084
+
1085
+ void main() {
1086
+ int x = int(gl_FragCoord.x);
1087
+ int y = int(gl_FragCoord.y);
1088
+
1089
+ float mag = length(texelFetch(u_flow, ivec2(x, y), 0).xy);
1090
+ float envPrev = texelFetch(u_env_prev, ivec2(x, y), 0).y;
1091
+ float env = max(mag, u_release * envPrev);
1092
+
1093
+ // Flow divergence over a \xB1step finite-difference (clamped to the edges).
1094
+ int xr = min(x + u_step_x, u_w - 1);
1095
+ int xl = max(x - u_step_x, 0);
1096
+ int yd = min(y + u_step_y, u_h - 1);
1097
+ int yu = max(y - u_step_y, 0);
1098
+ float dfx = texelFetch(u_flow, ivec2(xr, y), 0).x - texelFetch(u_flow, ivec2(xl, y), 0).x;
1099
+ float dfy = texelFetch(u_flow, ivec2(x, yd), 0).y - texelFetch(u_flow, ivec2(x, yu), 0).y;
1100
+ float divg = abs(dfx + dfy);
1101
+
1102
+ float gMag = clamp((env - u_t_lo) / max(u_t_hi - u_t_lo, 1e-3), 0.0, 1.0);
1103
+ float gDiv = clamp((divg - u_t_div) / max(u_div_scale, 1e-3), 0.0, 1.0);
1104
+ float g = max(max(gMag, gDiv), u_leak);
1105
+
1106
+ float pred = texelFetch(u_pred, ivec2(x, y), 0).x;
1107
+ float refv = texelFetch(u_ref, ivec2(x, y), 0).x;
1108
+ float stab = g * pred + (1.0 - g) * refv;
1109
+
1110
+ fragColor = vec4(stab, env, 0.0, 0.0);
1111
+ }
1112
+ `;var z=class extends c{inputs;weights=[];output;dispatch;shader=et;constructor(e,t,r,o,i,s){super(e);let n=t.w,u=t.h,l=this.makeTexture(null,n,u);this.output={h:u,w:n,c:4,texture:l,texW:n,texH:u},this.inputs=[t,r,o,i],this.samplers=[{name:"u_flow",texture:t.texture},{name:"u_pred",texture:r.texture},{name:"u_ref",texture:o.texture},{name:"u_env_prev",texture:i.texture}],this.uniformInts={u_w:n,u_h:u,u_step_x:s.stepX,u_step_y:s.stepY},this.uniformFloats={u_t_lo:s.tLo,u_t_hi:s.tHi,u_leak:s.leak,u_release:s.release,u_t_div:s.tDiv,u_div_scale:s.divScale},this.defaultSetup(),this.dispatch=[n,u];}};var tt=`#version 300 es
1113
+ // Bilinear upsample (arbitrary scale) \u2014 align_corners=False.
1114
+ //
1115
+ // Hardware texture filtering can't be used: adjacent texels in x belong to
1116
+ // different channel groups, not spatial neighbours. Manual 4-tap blend.
1117
+ //
1118
+ // Input: (in_W * c_groups, in_H)
1119
+ // Output: (out_W * c_groups, out_H)
1120
+
1121
+ precision highp float;
1122
+ precision highp int;
1123
+
1124
+ uniform sampler2D u_input;
1125
+ uniform int u_in_w;
1126
+ uniform int u_in_h;
1127
+ uniform int u_out_w;
1128
+ uniform int u_out_h;
1129
+ uniform int u_c_groups;
1130
+
1131
+ out vec4 fragColor;
1132
+
1133
+ void main() {
1134
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1135
+ int x_out = fc.x / u_c_groups;
1136
+ int y_out = fc.y;
1137
+ int c_group = fc.x - x_out * u_c_groups;
1138
+
1139
+ float scale_x = float(u_in_w) / float(u_out_w);
1140
+ float scale_y = float(u_in_h) / float(u_out_h);
1141
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
1142
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
1143
+
1144
+ int x0 = int(floor(src_x));
1145
+ int y0 = int(floor(src_y));
1146
+ int x1 = x0 + 1;
1147
+ int y1 = y0 + 1;
1148
+ float fx = src_x - float(x0);
1149
+ float fy = src_y - float(y0);
1150
+
1151
+ x0 = clamp(x0, 0, u_in_w - 1);
1152
+ y0 = clamp(y0, 0, u_in_h - 1);
1153
+ x1 = clamp(x1, 0, u_in_w - 1);
1154
+ y1 = clamp(y1, 0, u_in_h - 1);
1155
+
1156
+ vec4 v00 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y0), 0);
1157
+ vec4 v10 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y0), 0);
1158
+ vec4 v01 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y1), 0);
1159
+ vec4 v11 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y1), 0);
1160
+
1161
+ fragColor = mix(mix(v00, v10, fx), mix(v01, v11, fx), fy);
1162
+ }
1163
+ `;var $=class extends c{inputs;weights=[];output;dispatch;shader=tt;constructor(e,t,r){super(e);let o=t.c/4,i=r.outW*o,s=this.makeTexture(null,i,r.outH);this.output={h:r.outH,w:r.outW,c:t.c,texture:s,texW:i,texH:r.outH},this.inputs=[t],this.samplers=[{name:"u_input",texture:t.texture}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:r.outW,u_out_h:r.outH,u_c_groups:o},this.defaultSetup(),this.dispatch=[i,r.outH];}};var ot=`#version 300 es
1164
+ // Top-left crop: the output viewport is smaller than the input texture, so reading
1165
+ // the same fragment coord yields the top-left subregion (channel groups packed in
1166
+ // the x axis line up because group count is unchanged).
1167
+
1168
+ precision highp float;
1169
+ precision highp int;
1170
+
1171
+ uniform sampler2D u_input;
1172
+ out vec4 fragColor;
1173
+
1174
+ void main() {
1175
+ fragColor = texelFetch(u_input, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);
1176
+ }
1177
+ `;var j=class extends c{inputs;weights=[];output;dispatch;shader=ot;constructor(e,t,r){super(e);let o=t.c/4,i=r.outW*o,s=this.makeTexture(null,i,r.outH);this.output={h:r.outH,w:r.outW,c:t.c,texture:s,texW:i,texH:r.outH},this.inputs=[t],this.samplers=[{name:"u_input",texture:t.texture}],this.defaultSetup(),this.dispatch=[i,r.outH];}};var rt=`#version 300 es
1178
+ // Bicubic upsample (arbitrary scale) \u2014 Keys cubic, a=-0.75 (PyTorch default
1179
+ // for mode='bicubic', align_corners=False).
1180
+ //
1181
+ // Direct 2D, 4\xD74 = 16 taps per output pixel. Hardware texture filtering
1182
+ // can't be used (NHWC vec4 layout means adjacent texels in x belong to
1183
+ // different channel groups, not spatial neighbours).
1184
+
1185
+ precision highp float;
1186
+ precision highp int;
1187
+
1188
+ uniform sampler2D u_input;
1189
+ uniform int u_in_w;
1190
+ uniform int u_in_h;
1191
+ uniform int u_out_w;
1192
+ uniform int u_out_h;
1193
+ uniform int u_c_groups;
1194
+
1195
+ const float A = -0.75;
1196
+
1197
+ float wcubic(float d) {
1198
+ float ad = abs(d);
1199
+ if (ad <= 1.0) return ((A + 2.0) * ad - (A + 3.0)) * ad * ad + 1.0;
1200
+ if (ad < 2.0) return ((A * ad - 5.0 * A) * ad + 8.0 * A) * ad - 4.0 * A;
1201
+ return 0.0;
1202
+ }
1203
+
1204
+ out vec4 fragColor;
1205
+
1206
+ void main() {
1207
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1208
+ int x_out = fc.x / u_c_groups;
1209
+ int y_out = fc.y;
1210
+ int c_group = fc.x - x_out * u_c_groups;
1211
+
1212
+ float src_x = (float(x_out) + 0.5) * float(u_in_w) / float(u_out_w) - 0.5;
1213
+ float src_y = (float(y_out) + 0.5) * float(u_in_h) / float(u_out_h) - 0.5;
1214
+
1215
+ int x0 = int(floor(src_x));
1216
+ int y0 = int(floor(src_y));
1217
+ float fx = src_x - float(x0);
1218
+ float fy = src_y - float(y0);
1219
+
1220
+ // Weights for offsets {-1, 0, 1, 2} from x0/y0.
1221
+ float wx[4];
1222
+ float wy[4];
1223
+ wx[0] = wcubic(1.0 + fx); wx[1] = wcubic(fx); wx[2] = wcubic(1.0 - fx); wx[3] = wcubic(2.0 - fx);
1224
+ wy[0] = wcubic(1.0 + fy); wy[1] = wcubic(fy); wy[2] = wcubic(1.0 - fy); wy[3] = wcubic(2.0 - fy);
1225
+
1226
+ vec4 acc = vec4(0.0);
1227
+ for (int j = 0; j < 4; j++) {
1228
+ int sy = clamp(y0 + j - 1, 0, u_in_h - 1);
1229
+ for (int i = 0; i < 4; i++) {
1230
+ int sx = clamp(x0 + i - 1, 0, u_in_w - 1);
1231
+ vec4 v = texelFetch(u_input, ivec2(sx * u_c_groups + c_group, sy), 0);
1232
+ acc += (wx[i] * wy[j]) * v;
1233
+ }
1234
+ }
1235
+ fragColor = acc;
1236
+ }
1237
+ `;var K=class extends c{inputs;weights=[];output;dispatch;shader=rt;constructor(e,t,r){super(e);let o=t.c/4,i=r.outW*o,s=this.makeTexture(null,i,r.outH);this.output={h:r.outH,w:r.outW,c:t.c,texture:s,texW:i,texH:r.outH},this.inputs=[t],this.samplers=[{name:"u_input",texture:t.texture}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:r.outW,u_out_h:r.outH,u_c_groups:o},this.defaultSetup(),this.dispatch=[i,r.outH];}};var it=`#version 300 es
1238
+ // Channel concat \u2014 concatenates two NHWC textures along the channel dimension.
1239
+ // Both inputs must share the same spatial dimensions (W, H). Channel counts are
1240
+ // multiples of 4 so each "group" is one vec4 texel.
1241
+ //
1242
+ // Input A: (W * a_c_groups, H)
1243
+ // Input B: (W * b_c_groups, H)
1244
+ // Output: (W * (a_c_groups + b_c_groups), H)
1245
+
1246
+ precision highp float;
1247
+ precision highp int;
1248
+
1249
+ uniform sampler2D u_input_a;
1250
+ uniform sampler2D u_input_b;
1251
+ uniform int u_a_c_groups;
1252
+ uniform int u_b_c_groups;
1253
+
1254
+ out vec4 fragColor;
1255
+
1256
+ void main() {
1257
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1258
+ int out_groups = u_a_c_groups + u_b_c_groups;
1259
+ int x_spatial = fc.x / out_groups;
1260
+ int c_out = fc.x - x_spatial * out_groups;
1261
+ int y = fc.y;
1262
+
1263
+ if (c_out < u_a_c_groups) {
1264
+ fragColor = texelFetch(u_input_a, ivec2(x_spatial * u_a_c_groups + c_out, y), 0);
1265
+ } else {
1266
+ int c_b = c_out - u_a_c_groups;
1267
+ fragColor = texelFetch(u_input_b, ivec2(x_spatial * u_b_c_groups + c_b, y), 0);
1268
+ }
1269
+ }
1270
+ `;var q=class extends c{inputs;weights=[];output;dispatch;shader=it;constructor(e,t,r){super(e);let o=t.c/4,i=r.c/4,s=o+i,n=t.c+r.c,u=t.w*s,l=this.makeTexture(null,u,t.h);this.output={h:t.h,w:t.w,c:n,texture:l,texW:u,texH:t.h},this.inputs=[t,r],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:r.texture}],this.uniformInts={u_a_c_groups:o,u_b_c_groups:i},this.defaultSetup(),this.dispatch=[u,t.h];}};var nt=`#version 300 es
1271
+ // Bilinear upsample + sigmoid fused \u2014 sigmoid applied at write time, no intermediate buffer.
1272
+ // See bilinear_upsample.glsl for the manual 4-tap blend rationale.
1273
+ //
1274
+ // Input: (in_W * c_groups, in_H)
1275
+ // Output: (out_W * c_groups, out_H)
1276
+
1277
+ precision highp float;
1278
+ precision highp int;
1279
+
1280
+ uniform sampler2D u_input;
1281
+ uniform int u_in_w;
1282
+ uniform int u_in_h;
1283
+ uniform int u_out_w;
1284
+ uniform int u_out_h;
1285
+ uniform int u_c_groups;
1286
+
1287
+ out vec4 fragColor;
1288
+
1289
+ void main() {
1290
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1291
+ int x_out = fc.x / u_c_groups;
1292
+ int y_out = fc.y;
1293
+ int c_group = fc.x - x_out * u_c_groups;
1294
+
1295
+ float scale_x = float(u_in_w) / float(u_out_w);
1296
+ float scale_y = float(u_in_h) / float(u_out_h);
1297
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
1298
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
1299
+
1300
+ int x0 = int(floor(src_x));
1301
+ int y0 = int(floor(src_y));
1302
+ int x1 = x0 + 1;
1303
+ int y1 = y0 + 1;
1304
+ float fx = src_x - float(x0);
1305
+ float fy = src_y - float(y0);
1306
+
1307
+ x0 = clamp(x0, 0, u_in_w - 1);
1308
+ y0 = clamp(y0, 0, u_in_h - 1);
1309
+ x1 = clamp(x1, 0, u_in_w - 1);
1310
+ y1 = clamp(y1, 0, u_in_h - 1);
1311
+
1312
+ vec4 v00 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y0), 0);
1313
+ vec4 v10 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y0), 0);
1314
+ vec4 v01 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y1), 0);
1315
+ vec4 v11 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y1), 0);
1316
+
1317
+ vec4 result = mix(mix(v00, v10, fx), mix(v01, v11, fx), fy);
1318
+ fragColor = 1.0 / (1.0 + exp(-result));
1319
+ }
1320
+ `;var Y=class extends c{inputs;weights=[];output;dispatch;shader=nt;constructor(e,t,r){super(e);let o=t.c/4,i=r.outW*o,s=this.makeTexture(null,i,r.outH);this.output={h:r.outH,w:r.outW,c:t.c,texture:s,texW:i,texH:r.outH},this.inputs=[t],this.samplers=[{name:"u_input",texture:t.texture}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:r.outW,u_out_h:r.outH,u_c_groups:o},this.defaultSetup(),this.dispatch=[i,r.outH];}};var st=`#version 300 es
1321
+ // Bilinear upsample (input_a) + channel concat (with input_b) fused.
1322
+ // input_a is the decoder tensor at small spatial res (in_h \xD7 in_w), bilinearly resized to (out_h \xD7 out_w).
1323
+ // input_b is the encoder skip feature already at output resolution.
1324
+ // Output channels [0..a_groups-1] come from upsampled input_a.
1325
+ // Output channels [a_groups..out_groups-1] come from input_b (passthrough).
1326
+
1327
+ precision highp float;
1328
+ precision highp int;
1329
+
1330
+ uniform sampler2D u_input_a;
1331
+ uniform sampler2D u_input_b;
1332
+ uniform int u_in_w;
1333
+ uniform int u_in_h;
1334
+ uniform int u_out_w;
1335
+ uniform int u_out_h;
1336
+ uniform int u_a_c_groups;
1337
+ uniform int u_b_c_groups;
1338
+
1339
+ out vec4 fragColor;
1340
+
1341
+ void main() {
1342
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1343
+ int out_groups = u_a_c_groups + u_b_c_groups;
1344
+ int x_out = fc.x / out_groups;
1345
+ int c_out = fc.x - x_out * out_groups;
1346
+ int y_out = fc.y;
1347
+
1348
+ if (c_out < u_a_c_groups) {
1349
+ // Bilinear sample from input_a at (x_out, y_out) for channel group c_out.
1350
+ float scale_x = float(u_in_w) / float(u_out_w);
1351
+ float scale_y = float(u_in_h) / float(u_out_h);
1352
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
1353
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
1354
+
1355
+ int x0 = int(floor(src_x));
1356
+ int y0 = int(floor(src_y));
1357
+ int x1 = x0 + 1;
1358
+ int y1 = y0 + 1;
1359
+ float fx = src_x - float(x0);
1360
+ float fy = src_y - float(y0);
1361
+
1362
+ x0 = clamp(x0, 0, u_in_w - 1);
1363
+ y0 = clamp(y0, 0, u_in_h - 1);
1364
+ x1 = clamp(x1, 0, u_in_w - 1);
1365
+ y1 = clamp(y1, 0, u_in_h - 1);
1366
+
1367
+ vec4 v00 = texelFetch(u_input_a, ivec2(x0 * u_a_c_groups + c_out, y0), 0);
1368
+ vec4 v10 = texelFetch(u_input_a, ivec2(x1 * u_a_c_groups + c_out, y0), 0);
1369
+ vec4 v01 = texelFetch(u_input_a, ivec2(x0 * u_a_c_groups + c_out, y1), 0);
1370
+ vec4 v11 = texelFetch(u_input_a, ivec2(x1 * u_a_c_groups + c_out, y1), 0);
1371
+
1372
+ fragColor = mix(mix(v00, v10, fx), mix(v01, v11, fx), fy);
1373
+ } else {
1374
+ int c_b = c_out - u_a_c_groups;
1375
+ fragColor = texelFetch(u_input_b, ivec2(x_out * u_b_c_groups + c_b, y_out), 0);
1376
+ }
1377
+ }
1378
+ `;var Q=class extends c{inputs;weights=[];output;dispatch;shader=st;constructor(e,t,r,o){super(e);let i=t.c/4,s=r.c/4,n=i+s,u=t.c+r.c,l=o.outW*n,p=this.makeTexture(null,l,o.outH);this.output={h:o.outH,w:o.outW,c:u,texture:p,texW:l,texH:o.outH},this.inputs=[t,r],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:r.texture}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:o.outW,u_out_h:o.outH,u_a_c_groups:i,u_b_c_groups:s},this.defaultSetup(),this.dispatch=[l,o.outH];}};var at=`#version 300 es
1379
+ // Bilinear upsample + 1\xD71 pointwise conv fused.
1380
+ // For each output pixel + out group, bilinearly samples each in_group from input,
1381
+ // applies the 1\xD71 conv weight, and writes the activated result.
1382
+ //
1383
+ // Input: (in_W * in_groups, in_H)
1384
+ // Output: (out_W * out_groups, out_H)
1385
+ // Weights: (in_groups * 4, out_groups) (K=1, so kernel-row dimension collapses)
1386
+ // mat4[col][row] = weight(in_channel=col, out_channel=row)
1387
+ // Bias: (out_groups, 1)
1388
+
1389
+ precision highp float;
1390
+ precision highp int;
1391
+
1392
+ uniform sampler2D u_input;
1393
+ uniform sampler2D u_weights;
1394
+ uniform sampler2D u_bias;
1395
+
1396
+ uniform int u_in_w;
1397
+ uniform int u_in_h;
1398
+ uniform int u_out_w;
1399
+ uniform int u_out_h;
1400
+ uniform int u_in_c_groups;
1401
+ uniform int u_out_c_groups;
1402
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu
1403
+
1404
+ out vec4 fragColor;
1405
+
1406
+ void main() {
1407
+ int fx = int(gl_FragCoord.x);
1408
+ int fy = int(gl_FragCoord.y);
1409
+ int x_out = fx / u_out_c_groups;
1410
+ int y_out = fy;
1411
+ int o_group = fx - x_out * u_out_c_groups;
1412
+
1413
+ float scale_x = float(u_in_w) / float(u_out_w);
1414
+ float scale_y = float(u_in_h) / float(u_out_h);
1415
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
1416
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
1417
+
1418
+ int x0 = int(floor(src_x));
1419
+ int y0 = int(floor(src_y));
1420
+ int x1 = x0 + 1;
1421
+ int y1 = y0 + 1;
1422
+ float fx_w = src_x - float(x0);
1423
+ float fy_w = src_y - float(y0);
1424
+
1425
+ x0 = clamp(x0, 0, u_in_w - 1);
1426
+ y0 = clamp(y0, 0, u_in_h - 1);
1427
+ x1 = clamp(x1, 0, u_in_w - 1);
1428
+ y1 = clamp(y1, 0, u_in_h - 1);
1429
+
1430
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
1431
+
1432
+ for (int i = 0; i < u_in_c_groups; i++) {
1433
+ vec4 v00 = texelFetch(u_input, ivec2(x0 * u_in_c_groups + i, y0), 0);
1434
+ vec4 v10 = texelFetch(u_input, ivec2(x1 * u_in_c_groups + i, y0), 0);
1435
+ vec4 v01 = texelFetch(u_input, ivec2(x0 * u_in_c_groups + i, y1), 0);
1436
+ vec4 v11 = texelFetch(u_input, ivec2(x1 * u_in_c_groups + i, y1), 0);
1437
+
1438
+ vec4 sampled = mix(mix(v00, v10, fx_w), mix(v01, v11, fx_w), fy_w);
1439
+
1440
+ int base = i * 4;
1441
+ mat4 w = mat4(
1442
+ texelFetch(u_weights, ivec2(base, o_group), 0),
1443
+ texelFetch(u_weights, ivec2(base + 1, o_group), 0),
1444
+ texelFetch(u_weights, ivec2(base + 2, o_group), 0),
1445
+ texelFetch(u_weights, ivec2(base + 3, o_group), 0)
1446
+ );
1447
+ result += w * sampled;
1448
+ }
1449
+
1450
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
1451
+ else if (u_activation == 2) result = max(result, vec4(0.0));
1452
+
1453
+ fragColor = result;
1454
+ }
1455
+ `;var J=class extends c{inputs;weights;output;dispatch;shader=at;constructor(e,t,r,o){super(e);let i=t.c/4,s=o.outChannels/4,n=f$1(r.weights),u=f$1(r.bias),l=this.makeTexture(n,i*4,s),p=this.makeTexture(u,s,1),h=o.outW*s,m=this.makeTexture(null,h,o.outH);this.output={h:o.outH,w:o.outW,c:o.outChannels,texture:m,texW:h,texH:o.outH},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:l},{name:"u_bias",texture:p}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:o.outW,u_out_h:o.outH,u_in_c_groups:i,u_out_c_groups:s,u_activation:o.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[h,o.outH];}};var ut=`#version 300 es
1456
+ // Conv2d + skip add fused \u2014 identical to conv2d.glsl with a skip texture added at write time.
1457
+ // See conv2d.glsl for tensor/weight layout details.
1458
+
1459
+ precision highp float;
1460
+ precision highp int;
1461
+
1462
+ uniform sampler2D u_input;
1463
+ uniform sampler2D u_skip;
1464
+ uniform sampler2D u_weights;
1465
+ uniform sampler2D u_bias;
1466
+
1467
+ uniform int u_in_w;
1468
+ uniform int u_in_h;
1469
+ uniform int u_in_c_groups;
1470
+ uniform int u_out_c_groups;
1471
+ uniform int u_kernel_h;
1472
+ uniform int u_kernel_w;
1473
+ uniform int u_stride;
1474
+ uniform int u_pad_top;
1475
+ uniform int u_pad_left;
1476
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu
1477
+
1478
+ out vec4 fragColor;
1479
+
1480
+ void main() {
1481
+ int fx = int(gl_FragCoord.x);
1482
+ int fy = int(gl_FragCoord.y);
1483
+ int x_out = fx / u_out_c_groups;
1484
+ int y_out = fy;
1485
+ int o_group = fx - x_out * u_out_c_groups;
1486
+
1487
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
1488
+
1489
+ for (int ky = 0; ky < u_kernel_h; ky++) {
1490
+ for (int kx = 0; kx < u_kernel_w; kx++) {
1491
+ int in_y = y_out * u_stride + ky - u_pad_top;
1492
+ int in_x = x_out * u_stride + kx - u_pad_left;
1493
+
1494
+ if (in_y < 0 || in_y >= u_in_h || in_x < 0 || in_x >= u_in_w) continue;
1495
+
1496
+ int z = ky * u_kernel_w + kx;
1497
+ int w_row = z * u_out_c_groups + o_group;
1498
+
1499
+ for (int i = 0; i < u_in_c_groups; i++) {
1500
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_in_c_groups + i, in_y), 0);
1501
+
1502
+ int base = i * 4;
1503
+ mat4 w = mat4(
1504
+ texelFetch(u_weights, ivec2(base, w_row), 0),
1505
+ texelFetch(u_weights, ivec2(base + 1, w_row), 0),
1506
+ texelFetch(u_weights, ivec2(base + 2, w_row), 0),
1507
+ texelFetch(u_weights, ivec2(base + 3, w_row), 0)
1508
+ );
1509
+ result += w * in_val;
1510
+ }
1511
+ }
1512
+ }
1513
+
1514
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
1515
+ else if (u_activation == 2) result = max(result, vec4(0.0));
1516
+
1517
+ fragColor = result + texelFetch(u_skip, ivec2(fx, fy), 0);
1518
+ }
1519
+ `;var Z=class extends c{inputs;weights;output;dispatch;shader=ut;constructor(e$1,t,r,o,i){super(e$1);let s=c$1(t.h,i.kernel,i.stride,i.padding),n=c$1(t.w,i.kernel,i.stride,i.padding),u=t.c/4,l=i.outChannels/4,p=e(i.padding,t.h,s,i.kernel,i.stride),h=e(i.padding,t.w,n,i.kernel,i.stride),m=f$1(o.weights),x=f$1(o.bias),_=this.makeTexture(m,u*4,i.kernel*i.kernel*l),g=this.makeTexture(x,l,1),b=n*l,L=this.makeTexture(null,b,s);this.output={h:s,w:n,c:i.outChannels,texture:L,texW:b,texH:s},this.inputs=[t,r],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_skip",texture:r.texture},{name:"u_weights",texture:_},{name:"u_bias",texture:g}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_in_c_groups:u,u_out_c_groups:l,u_kernel_h:i.kernel,u_kernel_w:i.kernel,u_stride:i.stride,u_pad_top:p,u_pad_left:h,u_activation:i.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[b,s];}};var lt=`#version 300 es
1520
+ // proj_residual: bespoke 1\xD71 conv (no activation) + residual add, fused.
1521
+ // Specializes conv2d_add to kernel=1 / stride=1 / pad=0 / no activation. Both
1522
+ // inputs share the same spatial resolution. See conv2d.glsl for the weight
1523
+ // texture layout (here K=1, so the weight texture is (inGroups*4, outGroups)).
1524
+
1525
+ precision highp float;
1526
+ precision highp int;
1527
+
1528
+ uniform sampler2D u_input;
1529
+ uniform sampler2D u_skip;
1530
+ uniform sampler2D u_weights;
1531
+ uniform sampler2D u_bias;
1532
+
1533
+ uniform int u_in_c_groups;
1534
+ uniform int u_out_c_groups;
1535
+
1536
+ out vec4 fragColor;
1537
+
1538
+ void main() {
1539
+ int fx = int(gl_FragCoord.x);
1540
+ int fy = int(gl_FragCoord.y);
1541
+ int x_out = fx / u_out_c_groups;
1542
+ int o_group = fx - x_out * u_out_c_groups;
1543
+
1544
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
1545
+
1546
+ for (int i = 0; i < u_in_c_groups; i++) {
1547
+ vec4 in_val = texelFetch(u_input, ivec2(x_out * u_in_c_groups + i, fy), 0);
1548
+
1549
+ int base = i * 4;
1550
+ mat4 w = mat4(
1551
+ texelFetch(u_weights, ivec2(base, o_group), 0),
1552
+ texelFetch(u_weights, ivec2(base + 1, o_group), 0),
1553
+ texelFetch(u_weights, ivec2(base + 2, o_group), 0),
1554
+ texelFetch(u_weights, ivec2(base + 3, o_group), 0)
1555
+ );
1556
+ result += w * in_val;
1557
+ }
1558
+
1559
+ fragColor = result + texelFetch(u_skip, ivec2(fx, fy), 0);
1560
+ }
1561
+ `;var ee=class extends c{inputs;weights;output;dispatch;shader=lt;constructor(e,t,r,o,i){super(e);let s=t.c/4,n=i.outChannels/4,u=f$1(o.weights),l=f$1(o.bias),p=this.makeTexture(u,s*4,n),h=this.makeTexture(l,n,1),m=t.w*n,x=this.makeTexture(null,m,t.h);this.output={h:t.h,w:t.w,c:i.outChannels,texture:x,texW:m,texH:t.h},this.inputs=[t,r],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_skip",texture:r.texture},{name:"u_weights",texture:p},{name:"u_bias",texture:h}],this.uniformInts={u_in_c_groups:s,u_out_c_groups:n},this.defaultSetup(),this.dispatch=[m,t.h];}};var ct=`#version 300 es
1562
+ // concat_conv2d: fuses [concat(a, b) \u2192 conv 3\xD73 (pad 1) \u2192 relu6] into one pass.
1563
+ // Both inputs already at output resolution. Weight cols [0, a_groups) read a,
1564
+ // [a_groups, I) read b. Weight texture layout matches conv2d.glsl
1565
+ // ((in_groups*4, 9*out_groups), in_groups = a_groups + b_groups).
1566
+
1567
+ precision highp float;
1568
+ precision highp int;
1569
+
1570
+ uniform sampler2D u_a;
1571
+ uniform sampler2D u_b;
1572
+ uniform sampler2D u_weights;
1573
+ uniform sampler2D u_bias;
1574
+
1575
+ uniform int u_w;
1576
+ uniform int u_h;
1577
+ uniform int u_a_groups;
1578
+ uniform int u_b_groups;
1579
+ uniform int u_out_c_groups;
1580
+
1581
+ out vec4 fragColor;
1582
+
1583
+ void main() {
1584
+ int fx = int(gl_FragCoord.x);
1585
+ int fy = int(gl_FragCoord.y);
1586
+ int x_out = fx / u_out_c_groups;
1587
+ int o_group = fx - x_out * u_out_c_groups;
1588
+ int A = u_a_groups;
1589
+ int B = u_b_groups;
1590
+ int I = A + B;
1591
+
1592
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
1593
+
1594
+ for (int ky = 0; ky < 3; ky++) {
1595
+ for (int kx = 0; kx < 3; kx++) {
1596
+ int in_y = fy + ky - 1;
1597
+ int in_x = x_out + kx - 1;
1598
+ if (in_x < 0 || in_y < 0 || in_x >= u_w || in_y >= u_h) continue;
1599
+
1600
+ int z = ky * 3 + kx;
1601
+ int w_row = z * u_out_c_groups + o_group;
1602
+
1603
+ for (int i = 0; i < I; i++) {
1604
+ vec4 in_val = (i < A)
1605
+ ? texelFetch(u_a, ivec2(in_x * A + i, in_y), 0)
1606
+ : texelFetch(u_b, ivec2(in_x * B + (i - A), in_y), 0);
1607
+
1608
+ int base = i * 4;
1609
+ mat4 w = mat4(
1610
+ texelFetch(u_weights, ivec2(base, w_row), 0),
1611
+ texelFetch(u_weights, ivec2(base + 1, w_row), 0),
1612
+ texelFetch(u_weights, ivec2(base + 2, w_row), 0),
1613
+ texelFetch(u_weights, ivec2(base + 3, w_row), 0)
1614
+ );
1615
+ result += w * in_val;
1616
+ }
1617
+ }
1618
+ }
1619
+
1620
+ fragColor = clamp(result, 0.0, 6.0);
1621
+ }
1622
+ `;var te=class extends c{inputs;weights;output;dispatch;shader=ct;constructor(e,t,r,o,i){super(e);let s=t.c/4,n=r.c/4,u=s+n,l=i.outChannels/4,p=f$1(o.weights),h=f$1(o.bias),m=this.makeTexture(p,u*4,9*l),x=this.makeTexture(h,l,1),_=t.w*l,g=this.makeTexture(null,_,t.h);this.output={h:t.h,w:t.w,c:i.outChannels,texture:g,texW:_,texH:t.h},this.inputs=[t,r],this.weights=[],this.samplers=[{name:"u_a",texture:t.texture},{name:"u_b",texture:r.texture},{name:"u_weights",texture:m},{name:"u_bias",texture:x}],this.uniformInts={u_w:t.w,u_h:t.h,u_a_groups:s,u_b_groups:n,u_out_c_groups:l},this.defaultSetup(),this.dispatch=[_,t.h];}};var pt=`#version 300 es
1623
+ // gates_fused: ConvGRU z + r gates, fused. Production config (c_up=2,
1624
+ // recurrent=1). u_in (.x=a, .y=b), h_prev (.x). weights 9 vec4 =
1625
+ // (z_w_b, z_w_h, r_w_b, r_w_h) in a (9,1) texture. bias .xy. output (z, r, 0, 0).
1626
+
1627
+ precision highp float;
1628
+ precision highp int;
1629
+
1630
+ uniform sampler2D u_u_in; // (W, H)
1631
+ uniform sampler2D u_h_prev; // (W, H)
1632
+ uniform sampler2D u_weights; // (9, 1)
1633
+ uniform sampler2D u_bias; // (1, 1)
1634
+ uniform int u_w;
1635
+ uniform int u_h;
1636
+
1637
+ out vec4 fragColor;
1638
+
1639
+ void main() {
1640
+ int x = int(gl_FragCoord.x);
1641
+ int y = int(gl_FragCoord.y);
1642
+
1643
+ vec2 b = texelFetch(u_bias, ivec2(0, 0), 0).xy;
1644
+ float z_pre = b.x;
1645
+ float r_pre = b.y;
1646
+
1647
+ for (int ky = 0; ky < 3; ky++) {
1648
+ for (int kx = 0; kx < 3; kx++) {
1649
+ int iy = y + ky - 1;
1650
+ int ix = x + kx - 1;
1651
+ if (iy < 0 || ix < 0 || iy >= u_h || ix >= u_w) continue;
1652
+ int kpos = ky * 3 + kx;
1653
+ float b_n = texelFetch(u_u_in, ivec2(ix, iy), 0).y;
1654
+ float h_n = texelFetch(u_h_prev, ivec2(ix, iy), 0).z;
1655
+ vec4 w = texelFetch(u_weights, ivec2(kpos, 0), 0);
1656
+ z_pre += w.x * b_n + w.y * h_n;
1657
+ r_pre += w.z * b_n + w.w * h_n;
1658
+ }
1659
+ }
1660
+
1661
+ float z = 1.0 / (1.0 + exp(-z_pre));
1662
+ float r = 1.0 / (1.0 + exp(-r_pre));
1663
+ fragColor = vec4(z, r, 0.0, 0.0);
1664
+ }
1665
+ `;var oe=class extends c{inputs;weights;output;dispatch;shader=pt;constructor(e,t,r,o){super(e);let i=this.makeTexture(f$1(o.weights),9,1),s=this.makeTexture(g(o.bias),1,1),n=t.w,u=this.makeTexture(null,n,t.h);this.output={h:t.h,w:t.w,c:4,texture:u,texW:n,texH:t.h},this.inputs=[t,r],this.weights=[],this.samplers=[{name:"u_u_in",texture:t.texture},{name:"u_h_prev",texture:r.texture},{name:"u_weights",texture:i},{name:"u_bias",texture:s}],this.uniformInts={u_w:t.w,u_h:t.h},this.defaultSetup(),this.dispatch=[n,t.h];}};var ht=`#version 300 es
1666
+ // cand_update_fused: ConvGRU candidate + state update + output, fused.
1667
+ // Production config (c_up=2, recurrent=1). u_in (.x=a, .y=b), h_prev (.x),
1668
+ // gates_out (.x=z, .y=r). weights 9 vec4 (.xy = b_w, rh_w) in a (9,1) texture.
1669
+ // bias .x, gamma .x. output (a, b_out, 0, 0).
1670
+
1671
+ precision highp float;
1672
+ precision highp int;
1673
+
1674
+ uniform sampler2D u_u_in; // (W, H)
1675
+ uniform sampler2D u_h_prev; // (W, H)
1676
+ uniform sampler2D u_gates_out; // (W, H)
1677
+ uniform sampler2D u_weights; // (9, 1)
1678
+ uniform sampler2D u_bias; // (1, 1)
1679
+ uniform sampler2D u_gamma; // (1, 1)
1680
+ uniform int u_w;
1681
+ uniform int u_h;
1682
+
1683
+ out vec4 fragColor;
1684
+
1685
+ void main() {
1686
+ int x = int(gl_FragCoord.x);
1687
+ int y = int(gl_FragCoord.y);
1688
+
1689
+ float cand_pre = texelFetch(u_bias, ivec2(0, 0), 0).x;
1690
+ for (int ky = 0; ky < 3; ky++) {
1691
+ for (int kx = 0; kx < 3; kx++) {
1692
+ int iy = y + ky - 1;
1693
+ int ix = x + kx - 1;
1694
+ if (iy < 0 || ix < 0 || iy >= u_h || ix >= u_w) continue;
1695
+ int kpos = ky * 3 + kx;
1696
+ float b_n = texelFetch(u_u_in, ivec2(ix, iy), 0).y;
1697
+ float h_n = texelFetch(u_h_prev, ivec2(ix, iy), 0).z;
1698
+ float r_n = texelFetch(u_gates_out, ivec2(ix, iy), 0).y;
1699
+ vec4 w = texelFetch(u_weights, ivec2(kpos, 0), 0);
1700
+ cand_pre += w.x * b_n + w.y * (r_n * h_n);
1701
+ }
1702
+ }
1703
+
1704
+ float h_til = tanh(cand_pre);
1705
+ vec4 u_cur = texelFetch(u_u_in, ivec2(x, y), 0);
1706
+ float z_cur = texelFetch(u_gates_out, ivec2(x, y), 0).x;
1707
+ float h_prev_cur = texelFetch(u_h_prev, ivec2(x, y), 0).z;
1708
+ float h_new = (1.0 - z_cur) * h_prev_cur + z_cur * h_til;
1709
+ float gamma = texelFetch(u_gamma, ivec2(0, 0), 0).x;
1710
+ float b_out = u_cur.y + gamma * h_new;
1711
+ fragColor = vec4(u_cur.x, b_out, h_new, 0.0);
1712
+ }
1713
+ `;var re=class extends c{inputs;weights;output;dispatch;shader=ht;constructor(e,t,r,o,i,s){super(e);let n=this.makeTexture(f$1(i.weights),9,1),u=this.makeTexture(g(i.bias),1,1),l=this.makeTexture(g(s),1,1),p=t.w,h=this.makeTexture(null,p,t.h);this.output={h:t.h,w:t.w,c:4,texture:h,texW:p,texH:t.h},this.inputs=[t,r,o],this.weights=[],this.samplers=[{name:"u_u_in",texture:t.texture},{name:"u_h_prev",texture:r.texture},{name:"u_gates_out",texture:o.texture},{name:"u_weights",texture:n},{name:"u_bias",texture:u},{name:"u_gamma",texture:l}],this.uniformInts={u_w:t.w,u_h:t.h},this.defaultSetup(),this.dispatch=[p,t.h];}};var mt=`#version 300 es
1714
+ // conv_expand: bespoke N\u21922 conv 3\xD73 (pad 1) + relu (wrapper expand_feat).
1715
+ // Input N ch (in_groups vec4); output 2ch in .xy (.zw = 0). Weights: 9 *
1716
+ // in_groups mat4x2 (8 floats each, col-major c0r0,c0r1,...,c3r0,c3r1) in a
1717
+ // 1-row texture \u2014 bounded small (feat_ch \u2264 32).
1718
+
1719
+ precision highp float;
1720
+ precision highp int;
1721
+
1722
+ uniform sampler2D u_input; // (W*in_groups, H)
1723
+ uniform sampler2D u_weights; // (ceil(9*in_groups*8/4), 1)
1724
+ uniform sampler2D u_bias; // (1, 1) \u2014 .xy
1725
+ uniform int u_w;
1726
+ uniform int u_h;
1727
+ uniform int u_in_groups;
1728
+
1729
+ out vec4 fragColor;
1730
+
1731
+ float wf(int i) { return texelFetch(u_weights, ivec2(i / 4, 0), 0)[i & 3]; }
1732
+
1733
+ void main() {
1734
+ int x = int(gl_FragCoord.x);
1735
+ int y = int(gl_FragCoord.y);
1736
+
1737
+ vec2 result = texelFetch(u_bias, ivec2(0, 0), 0).xy;
1738
+
1739
+ for (int ky = 0; ky < 3; ky++) {
1740
+ for (int kx = 0; kx < 3; kx++) {
1741
+ int iy = y + ky - 1;
1742
+ int ix = x + kx - 1;
1743
+ if (iy < 0 || ix < 0 || iy >= u_h || ix >= u_w) continue;
1744
+ int kpos = ky * 3 + kx;
1745
+ for (int ig = 0; ig < u_in_groups; ig++) {
1746
+ vec4 v = texelFetch(u_input, ivec2(ix * u_in_groups + ig, iy), 0);
1747
+ int base = (kpos * u_in_groups + ig) * 8; // mat4x2 = 8 floats
1748
+ result.x += wf(base + 0) * v.x + wf(base + 2) * v.y + wf(base + 4) * v.z + wf(base + 6) * v.w;
1749
+ result.y += wf(base + 1) * v.x + wf(base + 3) * v.y + wf(base + 5) * v.z + wf(base + 7) * v.w;
1750
+ }
1751
+ }
1752
+ }
1753
+
1754
+ fragColor = vec4(max(result, vec2(0.0)), 0.0, 0.0); // expand_feat is F.relu
1755
+ }
1756
+ `;var ie=class extends c{inputs;weights;output;dispatch;shader=mt;constructor(e,t,r){super(e);let o=t.c/4,i=this.makeTexture(f$1(r.weights),18*o,1),s=this.makeTexture(g(r.bias),1,1),n=t.w,u=this.makeTexture(null,n,t.h);this.output={h:t.h,w:t.w,c:4,texture:u,texW:n,texH:t.h},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:i},{name:"u_bias",texture:s}],this.uniformInts={u_w:t.w,u_h:t.h,u_in_groups:o},this.defaultSetup(),this.dispatch=[n,t.h];}};var dt=`#version 300 es
1757
+ // cat_conv_6to2: fused concat(u[2], d[4]) \u2192 6\u21922 conv 3\xD73 (pad 1) + relu
1758
+ // (E up1_combine). Channel order v3a=(u.x,u.y,d.x), v3b=d.yzw. weights = 9*2
1759
+ // mat3x2 (6 floats each, col-major) in a 1-row (27-texel) texture.
1760
+
1761
+ precision highp float;
1762
+ precision highp int;
1763
+
1764
+ uniform sampler2D u_u_in; // (W, H) \u2014 .xy
1765
+ uniform sampler2D u_d_in; // (W, H) \u2014 full vec4
1766
+ uniform sampler2D u_weights; // (27, 1)
1767
+ uniform sampler2D u_bias; // (1, 1) \u2014 .xy
1768
+ uniform int u_w;
1769
+ uniform int u_h;
1770
+
1771
+ out vec4 fragColor;
1772
+
1773
+ float wf(int i) { return texelFetch(u_weights, ivec2(i / 4, 0), 0)[i & 3]; }
1774
+
1775
+ void main() {
1776
+ int x = int(gl_FragCoord.x);
1777
+ int y = int(gl_FragCoord.y);
1778
+
1779
+ vec2 result = texelFetch(u_bias, ivec2(0, 0), 0).xy;
1780
+ for (int ky = 0; ky < 3; ky++) {
1781
+ for (int kx = 0; kx < 3; kx++) {
1782
+ int iy = y + ky - 1;
1783
+ int ix = x + kx - 1;
1784
+ if (iy < 0 || ix < 0 || iy >= u_h || ix >= u_w) continue;
1785
+ int kpos = ky * 3 + kx;
1786
+ vec4 u = texelFetch(u_u_in, ivec2(ix, iy), 0);
1787
+ vec4 d = texelFetch(u_d_in, ivec2(ix, iy), 0);
1788
+ vec3 v3a = vec3(u.x, u.y, d.x);
1789
+ vec3 v3b = d.yzw;
1790
+ for (int ig = 0; ig < 2; ig++) {
1791
+ int base = (kpos * 2 + ig) * 6;
1792
+ vec3 v3 = (ig == 0) ? v3a : v3b;
1793
+ for (int col = 0; col < 3; col++) {
1794
+ result.x += wf(base + col * 2 + 0) * v3[col];
1795
+ result.y += wf(base + col * 2 + 1) * v3[col];
1796
+ }
1797
+ }
1798
+ }
1799
+ }
1800
+
1801
+ fragColor = vec4(max(result, vec2(0.0)), 0.0, 0.0); // up1_combine is F.relu
1802
+ }
1803
+ `;var ne=class extends c{inputs;weights;output;dispatch;shader=dt;constructor(e,t,r,o){super(e);let i=this.makeTexture(f$1(o.weights),27,1),s=this.makeTexture(g(o.bias),1,1),n=t.w,u=this.makeTexture(null,n,t.h);this.output={h:t.h,w:t.w,c:4,texture:u,texW:n,texH:t.h},this.inputs=[t,r],this.weights=[],this.samplers=[{name:"u_u_in",texture:t.texture},{name:"u_d_in",texture:r.texture},{name:"u_weights",texture:i},{name:"u_bias",texture:s}],this.uniformInts={u_w:t.w,u_h:t.h},this.defaultSetup(),this.dispatch=[n,t.h];}};var _t=`#version 300 es
1804
+ // down_adapter: stride-N 3\xD73 conv (mat4x4, 4\u21924) + relu, then 1\xD71 adapter
1805
+ // (4\u21923, last row 0). down_w = 9 mat4x4 (1-row, 36 texels); adapt_w = 1 mat4x4
1806
+ // (4 texels). Output 3ch in .xyz (.w=0). Symmetric pad. Weight packing matches
1807
+ // conv2d (col-major mat4x4 via the flat accessor).
1808
+
1809
+ precision highp float;
1810
+ precision highp int;
1811
+
1812
+ uniform sampler2D u_input; // (in_W, in_H) \u2014 in_c padded to 1 vec4
1813
+ uniform sampler2D u_down_w; // (36, 1)
1814
+ uniform sampler2D u_down_b; // (1, 1)
1815
+ uniform sampler2D u_adapt_w; // (4, 1)
1816
+ uniform sampler2D u_adapt_b; // (1, 1) \u2014 .xyz
1817
+ uniform int u_in_w;
1818
+ uniform int u_in_h;
1819
+ uniform int u_stride;
1820
+ uniform int u_pad;
1821
+
1822
+ out vec4 fragColor;
1823
+
1824
+ float dwf(int i) { return texelFetch(u_down_w, ivec2(i / 4, 0), 0)[i & 3]; }
1825
+ float awf(int i) { return texelFetch(u_adapt_w, ivec2(i / 4, 0), 0)[i & 3]; }
1826
+
1827
+ void main() {
1828
+ int x = int(gl_FragCoord.x);
1829
+ int y = int(gl_FragCoord.y);
1830
+
1831
+ vec4 d = texelFetch(u_down_b, ivec2(0, 0), 0);
1832
+ for (int ky = 0; ky < 3; ky++) {
1833
+ for (int kx = 0; kx < 3; kx++) {
1834
+ int iy = y * u_stride + ky - u_pad;
1835
+ int ix = x * u_stride + kx - u_pad;
1836
+ if (iy < 0 || ix < 0 || iy >= u_in_h || ix >= u_in_w) continue;
1837
+ int kpos = ky * 3 + kx;
1838
+ vec4 v = texelFetch(u_input, ivec2(ix, iy), 0);
1839
+ int m = kpos * 16;
1840
+ d.x += dwf(m + 0) * v.x + dwf(m + 4) * v.y + dwf(m + 8) * v.z + dwf(m + 12) * v.w;
1841
+ d.y += dwf(m + 1) * v.x + dwf(m + 5) * v.y + dwf(m + 9) * v.z + dwf(m + 13) * v.w;
1842
+ d.z += dwf(m + 2) * v.x + dwf(m + 6) * v.y + dwf(m + 10) * v.z + dwf(m + 14) * v.w;
1843
+ d.w += dwf(m + 3) * v.x + dwf(m + 7) * v.y + dwf(m + 11) * v.z + dwf(m + 15) * v.w;
1844
+ }
1845
+ }
1846
+ d = max(d, vec4(0.0)); // relu
1847
+
1848
+ vec4 ab = texelFetch(u_adapt_b, ivec2(0, 0), 0);
1849
+ vec4 a;
1850
+ a.x = awf(0) * d.x + awf(4) * d.y + awf(8) * d.z + awf(12) * d.w + ab.x;
1851
+ a.y = awf(1) * d.x + awf(5) * d.y + awf(9) * d.z + awf(13) * d.w + ab.y;
1852
+ a.z = awf(2) * d.x + awf(6) * d.y + awf(10) * d.z + awf(14) * d.w + ab.z;
1853
+ fragColor = vec4(a.xyz, 0.0);
1854
+ }
1855
+ `;var se=class extends c{inputs;weights;output;dispatch;shader=_t;constructor(e$1,t,r,o,i){super(e$1);let s=c$1(t.h,3,i.stride,1),n=c$1(t.w,3,i.stride,1),u=e(1,t.h,s,3,i.stride),l=this.makeTexture(f$1(r.weights),36,1),p=this.makeTexture(g(r.bias),1,1),h=this.makeTexture(f$1(o.weights),4,1),m=this.makeTexture(g(o.bias),1,1),x=n,_=this.makeTexture(null,x,s);this.output={h:s,w:n,c:4,texture:_,texW:x,texH:s},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_down_w",texture:l},{name:"u_down_b",texture:p},{name:"u_adapt_w",texture:h},{name:"u_adapt_b",texture:m}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_stride:i.stride,u_pad:u},this.defaultSetup(),this.dispatch=[x,s];}};var ft=`#version 300 es
1856
+ // up_final: cat(u[2], rgb[3]) \u2192 conv 3\xD73 5\u21921 \u2192 sigmoid (A/B alpha head).
1857
+ // weights 18 vec4 (1-row): [kpos]=(w0,w1,0,0) for u, [9+kpos]=(w2,w3,w4,0) rgb.
1858
+
1859
+ precision highp float;
1860
+ precision highp int;
1861
+
1862
+ uniform sampler2D u_u_gru; // (W, H) \u2014 .xy
1863
+ uniform sampler2D u_rgb; // (W, H) \u2014 .xyz
1864
+ uniform sampler2D u_weights; // (18, 1)
1865
+ uniform sampler2D u_bias; // (1, 1) \u2014 .x
1866
+ uniform int u_w;
1867
+ uniform int u_h;
1868
+
1869
+ out vec4 fragColor;
1870
+
1871
+ void main() {
1872
+ int x = int(gl_FragCoord.x);
1873
+ int y = int(gl_FragCoord.y);
1874
+
1875
+ float acc = texelFetch(u_bias, ivec2(0, 0), 0).x;
1876
+ for (int ky = 0; ky < 3; ky++) {
1877
+ for (int kx = 0; kx < 3; kx++) {
1878
+ int iy = y + ky - 1;
1879
+ int ix = x + kx - 1;
1880
+ if (iy < 0 || ix < 0 || iy >= u_h || ix >= u_w) continue;
1881
+ int kpos = ky * 3 + kx;
1882
+ vec4 u = texelFetch(u_u_gru, ivec2(ix, iy), 0);
1883
+ vec4 r = texelFetch(u_rgb, ivec2(ix, iy), 0);
1884
+ vec4 wu = texelFetch(u_weights, ivec2(kpos, 0), 0);
1885
+ vec4 wr = texelFetch(u_weights, ivec2(9 + kpos, 0), 0);
1886
+ acc += dot(wu.xy, u.xy);
1887
+ acc += dot(wr.xyz, r.xyz);
1888
+ }
1889
+ }
1890
+
1891
+ fragColor = vec4(1.0 / (1.0 + exp(-acc)), 0.0, 0.0, 0.0);
1892
+ }
1893
+ `;var ae=class extends c{inputs;weights;output;dispatch;shader=ft;constructor(e,t,r,o){super(e);let i=this.makeTexture(f$1(o.weights),18,1),s=this.makeTexture(g(o.bias),1,1),n=t.w,u=this.makeTexture(null,n,t.h);this.output={h:t.h,w:t.w,c:4,texture:u,texW:n,texH:t.h},this.inputs=[t,r],this.weights=[],this.samplers=[{name:"u_u_gru",texture:t.texture},{name:"u_rgb",texture:r.texture},{name:"u_weights",texture:i},{name:"u_bias",texture:s}],this.uniformInts={u_w:t.w,u_h:t.h},this.defaultSetup(),this.dispatch=[n,t.h];}};var xt=`#version 300 es
1894
+ // up_final_skip (C/D alpha head): cat(u[2], d_full[4], rgb[3]) \u2192 conv 9\u21921 \u2192
1895
+ // sigmoid. Weights = 27 vec4 (3 per kpos): [kpos*3+0]=(w0,w1,0,0) u;
1896
+ // [kpos*3+1]=(w2..w5) d_full; [kpos*3+2]=(w6,w7,w8,0) rgb.
1897
+
1898
+ precision highp float;
1899
+ precision highp int;
1900
+
1901
+ uniform sampler2D u_u_gru; // (W, H) \u2014 .xy
1902
+ uniform sampler2D u_d_full; // (W, H) \u2014 full vec4
1903
+ uniform sampler2D u_rgb; // (W, H) \u2014 .xyz
1904
+ uniform sampler2D u_weights; // (27, 1)
1905
+ uniform sampler2D u_bias; // (1, 1) \u2014 .x
1906
+ uniform int u_w;
1907
+ uniform int u_h;
1908
+
1909
+ out vec4 fragColor;
1910
+
1911
+ float wf(int i) { return texelFetch(u_weights, ivec2(i / 4, 0), 0)[i & 3]; }
1912
+
1913
+ void main() {
1914
+ int x = int(gl_FragCoord.x);
1915
+ int y = int(gl_FragCoord.y);
1916
+
1917
+ float acc = texelFetch(u_bias, ivec2(0, 0), 0).x;
1918
+ for (int ky = 0; ky < 3; ky++) {
1919
+ for (int kx = 0; kx < 3; kx++) {
1920
+ int iy = y + ky - 1;
1921
+ int ix = x + kx - 1;
1922
+ if (iy < 0 || ix < 0 || iy >= u_h || ix >= u_w) continue;
1923
+ int kpos = ky * 3 + kx;
1924
+ vec4 u = texelFetch(u_u_gru, ivec2(ix, iy), 0);
1925
+ vec4 d = texelFetch(u_d_full, ivec2(ix, iy), 0);
1926
+ vec4 r = texelFetch(u_rgb, ivec2(ix, iy), 0);
1927
+ int b0 = (kpos * 3 + 0) * 4, b1 = (kpos * 3 + 1) * 4, b2 = (kpos * 3 + 2) * 4;
1928
+ acc += wf(b0 + 0) * u.x + wf(b0 + 1) * u.y;
1929
+ acc += wf(b1 + 0) * d.x + wf(b1 + 1) * d.y + wf(b1 + 2) * d.z + wf(b1 + 3) * d.w;
1930
+ acc += wf(b2 + 0) * r.x + wf(b2 + 1) * r.y + wf(b2 + 2) * r.z;
1931
+ }
1932
+ }
1933
+
1934
+ fragColor = vec4(1.0 / (1.0 + exp(-acc)), 0.0, 0.0, 0.0);
1935
+ }
1936
+ `;var ue=class extends c{inputs;weights;output;dispatch;shader=xt;constructor(e,t,r,o,i){super(e);let s=this.makeTexture(f$1(i.weights),27,1),n=this.makeTexture(g(i.bias),1,1),u=t.w,l=this.makeTexture(null,u,t.h);this.output={h:t.h,w:t.w,c:4,texture:l,texW:u,texH:t.h},this.inputs=[t,r,o],this.weights=[],this.samplers=[{name:"u_u_gru",texture:t.texture},{name:"u_d_full",texture:r.texture},{name:"u_rgb",texture:o.texture},{name:"u_weights",texture:s},{name:"u_bias",texture:n}],this.uniformInts={u_w:t.w,u_h:t.h},this.defaultSetup(),this.dispatch=[u,t.h];}};var gt=`#version 300 es
1937
+ // Composite an RGBA image over a solid background color, gated by a
1938
+ // 1-channel alpha mask. Output: premultiplied RGBA.
1939
+ //
1940
+ // Assumes image and alpha textures are the same h\xD7w and that the canvas
1941
+ // (viewport) matches that resolution \u2014 no resampling here. The upscaler op
1942
+ // (separate) handles aligning alpha to the image resolution upstream.
1943
+
1944
+ precision highp float;
1945
+
1946
+ uniform sampler2D u_image; // image as NHWC vec4 (RGBA in vec4)
1947
+ uniform sampler2D u_alpha; // alpha as NHWC vec4 (value in .r)
1948
+ uniform vec3 u_bgColor; // straight-alpha background color, [0,1]
1949
+
1950
+ out vec4 fragColor;
1951
+
1952
+ void main() {
1953
+ // WebGL gl_FragCoord origin is bottom-left, but tensor textures are
1954
+ // stored top-down (matches NHWC + getImageData). Flip y when sampling so
1955
+ // the displayed image is upright. (WebGPU's @builtin(position) is already
1956
+ // top-down, so its compositor doesn't need this.)
1957
+ int H = textureSize(u_image, 0).y;
1958
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
1959
+ vec3 fg = texelFetch(u_image, px, 0).rgb;
1960
+ float a = texelFetch(u_alpha, px, 0).r;
1961
+
1962
+ // Straight-alpha composite, then premultiply for the canvas's
1963
+ // premultiplied surface format.
1964
+ vec3 rgb = fg * a + u_bgColor * (1.0 - a);
1965
+ fragColor = vec4(rgb, 1.0);
1966
+ }
1967
+ `;var _o=`#version 300 es
1968
+ const vec2 VERTS[6] = vec2[6](
1969
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
1970
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
1971
+ );
1972
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,le=class{constructor(e,t,r,o){this.backend=e;if(t.h!==r.h||t.w!==r.w)throw new Error(`CompositeSolid: image (${t.h}\xD7${t.w}) and alpha (${r.h}\xD7${r.w}) must match. Run the upscaler first.`);let i=t,s=r;this.imageTex=i.texture,this.alphaTex=s.texture,this.bgColor=o;let n=e.gl,u=n.createShader(n.VERTEX_SHADER);n.shaderSource(u,_o),n.compileShader(u);let l=n.createShader(n.FRAGMENT_SHADER);if(n.shaderSource(l,gt),n.compileShader(l),!n.getShaderParameter(l,n.COMPILE_STATUS))throw new Error(`composite_solid GLSL compile error: ${n.getShaderInfoLog(l)}`);if(this.program=n.createProgram(),n.attachShader(this.program,u),n.attachShader(this.program,l),n.linkProgram(this.program),!n.getProgramParameter(this.program,n.LINK_STATUS))throw new Error(`composite_solid GLSL link error: ${n.getProgramInfoLog(this.program)}`)}backend;program;imageTex;alphaTex;bgColor;run(){let e=this.backend.gl;e.useProgram(this.program),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.imageTex),e.uniform1i(e.getUniformLocation(this.program,"u_image"),0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.alphaTex),e.uniform1i(e.getUniformLocation(this.program,"u_alpha"),1),e.uniform3f(e.getUniformLocation(this.program,"u_bgColor"),this.bgColor[0],this.bgColor[1],this.bgColor[2]),this.backend.bindDisplayFramebuffer(),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6);}};var bt=`#version 300 es
1973
+ // Same as composite_solid but bg is a texture (NHWC vec4 tensor) instead of
1974
+ // a uniform color. Caller invariants: image, alpha, bg all share h \xD7 w, and
1975
+ // canvas dimensions match.
1976
+
1977
+ precision highp float;
1978
+ precision highp int;
1979
+
1980
+ uniform sampler2D u_image;
1981
+ uniform sampler2D u_alpha;
1982
+ uniform sampler2D u_bg;
1983
+
1984
+ out vec4 fragColor;
1985
+
1986
+ void main() {
1987
+ // WebGL gl_FragCoord origin is bottom-left; tensor textures are top-down.
1988
+ int H = textureSize(u_image, 0).y;
1989
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
1990
+
1991
+ vec3 fg = texelFetch(u_image, px, 0).rgb;
1992
+ float a = texelFetch(u_alpha, px, 0).r;
1993
+ vec3 bg = texelFetch(u_bg, px, 0).rgb;
1994
+
1995
+ vec3 rgb = fg * a + bg * (1.0 - a);
1996
+ fragColor = vec4(rgb, 1.0);
1997
+ }
1998
+ `;var xo=`#version 300 es
1999
+ const vec2 VERTS[6] = vec2[6](
2000
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2001
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2002
+ );
2003
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,ce=class{constructor(e,t,r,o){this.backend=e;if(t.h!==r.h||t.w!==r.w||t.h!==o.h||t.w!==o.w)throw new Error(`CompositeImage: image (${t.h}\xD7${t.w}), alpha (${r.h}\xD7${r.w}), and bg (${o.h}\xD7${o.w}) must all match. Run upscaler / resizer first.`);this.imageTex=t.texture,this.alphaTex=r.texture,this.bgTex=o.texture;let i=e.gl,s=i.createShader(i.VERTEX_SHADER);i.shaderSource(s,xo),i.compileShader(s);let n=i.createShader(i.FRAGMENT_SHADER);if(i.shaderSource(n,bt),i.compileShader(n),!i.getShaderParameter(n,i.COMPILE_STATUS))throw new Error(`composite_image GLSL compile error: ${i.getShaderInfoLog(n)}`);if(this.program=i.createProgram(),i.attachShader(this.program,s),i.attachShader(this.program,n),i.linkProgram(this.program),!i.getProgramParameter(this.program,i.LINK_STATUS))throw new Error(`composite_image GLSL link error: ${i.getProgramInfoLog(this.program)}`)}backend;program;imageTex;alphaTex;bgTex;run(){let e=this.backend.gl;e.useProgram(this.program),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.imageTex),e.uniform1i(e.getUniformLocation(this.program,"u_image"),0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.alphaTex),e.uniform1i(e.getUniformLocation(this.program,"u_alpha"),1),e.activeTexture(e.TEXTURE2),e.bindTexture(e.TEXTURE_2D,this.bgTex),e.uniform1i(e.getUniformLocation(this.program,"u_bg"),2),this.backend.bindDisplayFramebuffer(),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6);}};var vt=`#version 300 es
2004
+ // Like composite_image but bg is bilinearly sampled \u2014 bg is at a smaller
2005
+ // resolution than (image, alpha) at canvas h \xD7 w. Used by CompositorBlur to
2006
+ // skip the final full-res upsample in the blur pyramid.
2007
+ //
2008
+ // Tensor textures are top-down (origin at top-left of the source image),
2009
+ // while gl_FragCoord origin is bottom-left \u2014 flip y when computing px.
2010
+
2011
+ precision highp float;
2012
+ precision highp int;
2013
+
2014
+ uniform sampler2D u_image;
2015
+ uniform sampler2D u_alpha;
2016
+ uniform sampler2D u_bg;
2017
+
2018
+ uniform int u_out_w;
2019
+ uniform int u_out_h;
2020
+ uniform int u_bg_w;
2021
+ uniform int u_bg_h;
2022
+
2023
+ out vec4 fragColor;
2024
+
2025
+ void main() {
2026
+ int x = int(gl_FragCoord.x);
2027
+ int y = u_out_h - 1 - int(gl_FragCoord.y);
2028
+
2029
+ vec3 fg = texelFetch(u_image, ivec2(x, y), 0).rgb;
2030
+ float a = texelFetch(u_alpha, ivec2(x, y), 0).r;
2031
+
2032
+ // Bilinear sample bg at the corresponding location. align_corners=False.
2033
+ float src_x = (float(x) + 0.5) * (float(u_bg_w) / float(u_out_w)) - 0.5;
2034
+ float src_y = (float(y) + 0.5) * (float(u_bg_h) / float(u_out_h)) - 0.5;
2035
+ int x0 = clamp(int(floor(src_x)), 0, u_bg_w - 1);
2036
+ int x1 = clamp(int(floor(src_x)) + 1, 0, u_bg_w - 1);
2037
+ int y0 = clamp(int(floor(src_y)), 0, u_bg_h - 1);
2038
+ int y1 = clamp(int(floor(src_y)) + 1, 0, u_bg_h - 1);
2039
+ float wx = src_x - floor(src_x);
2040
+ float wy = src_y - floor(src_y);
2041
+
2042
+ vec3 tl = texelFetch(u_bg, ivec2(x0, y0), 0).rgb;
2043
+ vec3 tr = texelFetch(u_bg, ivec2(x1, y0), 0).rgb;
2044
+ vec3 bl = texelFetch(u_bg, ivec2(x0, y1), 0).rgb;
2045
+ vec3 br = texelFetch(u_bg, ivec2(x1, y1), 0).rgb;
2046
+ vec3 bg = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
2047
+ + wy * ((1.0 - wx) * bl + wx * br);
2048
+
2049
+ vec3 rgb = fg * a + bg * (1.0 - a);
2050
+ fragColor = vec4(rgb, 1.0);
2051
+ }
2052
+ `;var bo=`#version 300 es
2053
+ const vec2 VERTS[6] = vec2[6](
2054
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2055
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2056
+ );
2057
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,pe=class{constructor(e,t,r,o){this.backend=e;if(t.h!==r.h||t.w!==r.w)throw new Error(`CompositeImageBilinear: image (${t.h}\xD7${t.w}) and alpha (${r.h}\xD7${r.w}) must match.`);this.imageTex=t.texture,this.alphaTex=r.texture,this.bgTex=o.texture,this.outW=t.w,this.outH=t.h,this.bgW=o.w,this.bgH=o.h;let i=e.gl,s=i.createShader(i.VERTEX_SHADER);i.shaderSource(s,bo),i.compileShader(s);let n=i.createShader(i.FRAGMENT_SHADER);if(i.shaderSource(n,vt),i.compileShader(n),!i.getShaderParameter(n,i.COMPILE_STATUS))throw new Error(`composite_image_bilinear GLSL compile error: ${i.getShaderInfoLog(n)}`);if(this.program=i.createProgram(),i.attachShader(this.program,s),i.attachShader(this.program,n),i.linkProgram(this.program),!i.getProgramParameter(this.program,i.LINK_STATUS))throw new Error(`composite_image_bilinear GLSL link error: ${i.getProgramInfoLog(this.program)}`)}backend;program;imageTex;alphaTex;bgTex;outW;outH;bgW;bgH;run(){let e=this.backend.gl;e.useProgram(this.program),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.imageTex),e.uniform1i(e.getUniformLocation(this.program,"u_image"),0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.alphaTex),e.uniform1i(e.getUniformLocation(this.program,"u_alpha"),1),e.activeTexture(e.TEXTURE2),e.bindTexture(e.TEXTURE_2D,this.bgTex),e.uniform1i(e.getUniformLocation(this.program,"u_bg"),2),e.uniform1i(e.getUniformLocation(this.program,"u_out_w"),this.outW),e.uniform1i(e.getUniformLocation(this.program,"u_out_h"),this.outH),e.uniform1i(e.getUniformLocation(this.program,"u_bg_w"),this.bgW),e.uniform1i(e.getUniformLocation(this.program,"u_bg_h"),this.bgH),this.backend.bindDisplayFramebuffer(),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6);}};var vo=`#version 300 es
2058
+ const vec2 VERTS[6] = vec2[6](
2059
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2060
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2061
+ );
2062
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,he=class{constructor(e,t){this.backend=e;this.imageTex=t.texture;let r=e.gl,o=r.createShader(r.VERTEX_SHADER);r.shaderSource(o,vo),r.compileShader(o);let i=r.createShader(r.FRAGMENT_SHADER);if(r.shaderSource(i,I),r.compileShader(i),!r.getShaderParameter(i,r.COMPILE_STATUS))throw new Error(`composite_passthrough GLSL compile error: ${r.getShaderInfoLog(i)}`);if(this.program=r.createProgram(),r.attachShader(this.program,o),r.attachShader(this.program,i),r.linkProgram(this.program),!r.getProgramParameter(this.program,r.LINK_STATUS))throw new Error(`composite_passthrough GLSL link error: ${r.getProgramInfoLog(this.program)}`)}backend;program;imageTex;run(){let e=this.backend.gl;e.useProgram(this.program),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.imageTex),e.uniform1i(e.getUniformLocation(this.program,"u_image"),0),this.backend.bindDisplayFramebuffer(),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6);}};var yt=`#version 300 es
2063
+ precision highp float;
2064
+
2065
+ // Input op \u2014 bilinear-sample a source texture (RGBA8 unorm uploaded from an
2066
+ // ImageBitmap or VideoFrame) at the target resolution. Output texture format
2067
+ // is RGBA32F or RGBA16F depending on backend.dtype; the framebuffer write
2068
+ // stores fragColor into whatever format is bound.
2069
+
2070
+ uniform sampler2D u_src;
2071
+ uniform int u_out_w;
2072
+ uniform int u_out_h;
2073
+
2074
+ out vec4 fragColor;
2075
+
2076
+ void main() {
2077
+ vec2 uv = (gl_FragCoord.xy + vec2(0.5)) / vec2(float(u_out_w), float(u_out_h));
2078
+ fragColor = texture(u_src, uv);
2079
+ }
2080
+ `;var wo=`#version 300 es
2081
+ const vec2 VERTS[6] = vec2[6](
2082
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2083
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2084
+ );
2085
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }`,me=class{constructor(e,t,r){this.backend=e;let o=e.gl;this.gl=o,this.output=e.tensor(t,r,4),this.srcTex=o.createTexture(),o.bindTexture(o.TEXTURE_2D,this.srcTex),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE);let i=o.createShader(o.VERTEX_SHADER);o.shaderSource(i,wo),o.compileShader(i);let s=o.createShader(o.FRAGMENT_SHADER);if(o.shaderSource(s,yt),o.compileShader(s),!o.getShaderParameter(s,o.COMPILE_STATUS))throw new Error(`Input GLSL compile error: ${o.getShaderInfoLog(s)}`);if(this.program=o.createProgram(),o.attachShader(this.program,i),o.attachShader(this.program,s),o.linkProgram(this.program),!o.getProgramParameter(this.program,o.LINK_STATUS))throw new Error(`Input GLSL link error: ${o.getProgramInfoLog(this.program)}`);this.uSrc=o.getUniformLocation(this.program,"u_src"),this.uOutW=o.getUniformLocation(this.program,"u_out_w"),this.uOutH=o.getUniformLocation(this.program,"u_out_h");}backend;output;gl;program;srcTex;uOutW;uOutH;uSrc;source=null;setSource(e){this.source=e;}run(){if(!this.source)throw new Error("InputWebGL.run() called before setSource()");let e=this.gl;e.bindTexture(e.TEXTURE_2D,this.srcTex),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,this.source),e.useProgram(this.program),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.srcTex),e.uniform1i(this.uSrc,0),e.uniform1i(this.uOutW,this.output.w),e.uniform1i(this.uOutH,this.output.h),e.bindFramebuffer(e.FRAMEBUFFER,this.backend.fbo),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,this.output.texture,0),e.viewport(0,0,this.output.texW,this.output.texH),e.bindVertexArray(null),e.drawArrays(e.TRIANGLES,0,6);}};var We=class a$1{constructor(e,t,r){this.gl=e;this.canvas=t;this.dtype=r;this.fbo=e.createFramebuffer(),this.textureFormat=r==="f16"?{internalFormat:e.RGBA16F,format:e.RGBA,type:e.HALF_FLOAT,bytesPerElement:2}:{internalFormat:e.RGBA32F,format:e.RGBA,type:e.FLOAT,bytesPerElement:4},this.ops={Conv2d:(o,i,s)=>new E(this,o,i,s),ConvTranspose2d:(o,i,s)=>new G(this,o,i,s),DepthwiseConv2d:(o,i,s)=>new F(this,o,i,s),Add:(o,i)=>new A(this,o,i),Sigmoid:o=>new S(this,o),Tanh:o=>new R(this,o),ElementwiseMul:(o,i)=>new D(this,o,i),Warp:(o,i,s)=>new B(this,o,i,s),FaceBoxFromHeatmaps:(o,i)=>new U(this,o,i),FaceBoxesFromHeatmaps:(o,i)=>new P(this,o,i),ReframeState:(o,i,s,n)=>new M(this,o,i,s,n),Reframe:(o,i)=>new O(this,o,i),CropResample:(o,i,s)=>new H(this,o,i,s),FaceTouchupStage:(o,i,s,n,u)=>new X(this,o,i,s,n,u),Stabilize:(o,i,s,n,u)=>new z(this,o,i,s,n,u),BilinearUpsample:(o,i)=>new $(this,o,i),Crop:(o,i)=>new j(this,o,i),BicubicUpsample:(o,i)=>new K(this,o,i),ChannelConcat:(o,i)=>new q(this,o,i),Conv2dAdd:(o,i,s,n)=>new Z(this,o,i,s,n),ProjResidual:(o,i,s,n)=>new ee(this,o,i,s,n),ConcatConv2d:(o,i,s,n)=>new te(this,o,i,s,n),GatesFused:(o,i,s)=>new oe(this,o,i,s),CandUpdateFused:(o,i,s,n,u)=>new re(this,o,i,s,n,u),ConvExpand:(o,i)=>new ie(this,o,i),CatConv6to2:(o,i,s)=>new ne(this,o,i,s),DownAdapter:(o,i,s,n)=>new se(this,o,i,s,n),UpFinal:(o,i,s)=>new ae(this,o,i,s),UpFinalSkip:(o,i,s,n)=>new ue(this,o,i,s,n),UpsampleConcat:(o,i,s)=>new Q(this,o,i,s),UpsampleConv1x1:(o,i,s)=>new J(this,o,i,s),UpsampleSigmoid:(o,i)=>new Y(this,o,i),Input:(o,i)=>new me(this,o,i)},this.presenters={CompositeSolid:(o,i,s)=>new le(this,o,i,s),CompositeImage:(o,i,s)=>new ce(this,o,i,s),CompositeImageBilinear:(o,i,s)=>new pe(this,o,i,s),CompositePassthrough:o=>new he(this,o),LandmarkOverlay:(o,i,s,n)=>new N(this,o,i,s,n),FaceTouchup:(o,i,s,n,u)=>new V(this,o,i,s,n,u)};}gl;canvas;dtype;ops;presenters;fbo;textureFormat;static isAvailable(){let e=null;try{return e=new OffscreenCanvas(1,1).getContext("webgl2"),!(!e||!e.getExtension("EXT_color_buffer_float"))}catch{return false}finally{e?.getExtension("WEBGL_lose_context")?.loseContext();}}static create(e){let t=e.canvas.getContext("webgl2");if(!t)throw new Error("WebGL2 not available");if(!t.getExtension("EXT_color_buffer_float"))throw new Error("EXT_color_buffer_float not available");return new a$1(t,e.canvas,e.dtype??"f32")}attachCanvas(e){throw new Error(`WebGLBackend.attachCanvas('${e}'): WebGL cannot render to a second canvas; use the renderer's snapshot-and-present path for preview output`)}bindDisplayFramebuffer(){this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,null),this.gl.viewport(0,0,this.canvas.width,this.canvas.height);}toTextureView(e){if(e===null)return null;let t=this.dtype==="f16",r=e instanceof Uint16Array;return t===r?e:t?a(e):b(e)}tensor(e,t,r,o){let i=t*(r/4),s=e,n=this.gl,u=n.createTexture(),l=this.textureFormat,p=this.toTextureView(o??null);return n.bindTexture(n.TEXTURE_2D,u),n.texImage2D(n.TEXTURE_2D,0,l.internalFormat,i,s,0,l.format,l.type,p),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.NEAREST),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.NEAREST),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),{h:e,w:t,c:r,texture:u,texW:i,texH:s}}upload(e){return {data:e}}async readback(e){let t=this.gl,r=this.textureFormat;if(t.bindFramebuffer(t.FRAMEBUFFER,this.fbo),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,e.texture,0),this.dtype==="f16"){let i=new Uint16Array(e.texW*e.texH*4);return t.readPixels(0,0,e.texW,e.texH,r.format,t.HALF_FLOAT,i),t.bindFramebuffer(t.FRAMEBUFFER,null),b(i)}let o=new Float32Array(e.texW*e.texH*4);return t.readPixels(0,0,e.texW,e.texH,r.format,t.FLOAT,o),t.bindFramebuffer(t.FRAMEBUFFER,null),o}copyTensor(e,t){if(e.texW!==t.texW||e.texH!==t.texH)throw new Error(`copyTensor: size mismatch (src ${e.texW}\xD7${e.texH} vs dst ${t.texW}\xD7${t.texH})`);let r=this.gl;r.bindFramebuffer(r.FRAMEBUFFER,this.fbo),r.framebufferTexture2D(r.FRAMEBUFFER,r.COLOR_ATTACHMENT0,r.TEXTURE_2D,e.texture,0),r.bindTexture(r.TEXTURE_2D,t.texture),r.copyTexSubImage2D(r.TEXTURE_2D,0,0,0,0,0,e.texW,e.texH),r.bindFramebuffer(r.FRAMEBUFFER,null);}async sync(){let e=this.gl,t=e.fenceSync(e.SYNC_GPU_COMMANDS_COMPLETE,0);if(!t){e.finish();return}for(e.flush();;){let r=e.clientWaitSync(t,0,0);if(r===e.ALREADY_SIGNALED||r===e.CONDITION_SATISFIED){e.deleteSync(t);return}if(r===e.WAIT_FAILED)throw e.deleteSync(t),new Error("WebGLBackend.sync: clientWaitSync returned WAIT_FAILED");await new Promise(o=>setTimeout(o,1));}}destroy(){this.gl.deleteFramebuffer(this.fbo),this.gl.getExtension("WEBGL_lose_context")?.loseContext();}};function wt(a,e,t,r){let o=r.variant,i=o==="E"||o==="D",s=o==="D",n=e.h,u=e.w,l=[],p=null,h=null,m=0,x=0,_;if(i)if(s){if(!t.down2)throw new Error("variant D requires w.down2");m=Math.round(n/1.25),x=Math.round(u/1.25);let g=a.ops.Conv2d(e,t.down1,{outChannels:r.cHigh,kernel:3,stride:1,padding:1,activation:"relu"});l.push(g),h=g.output;let b=a.ops.BilinearUpsample(h,{outH:m,outW:x});l.push(b),p=b.output;let L=a.ops.DownAdapter(p,t.down2,t.adapter,{stride:2});l.push(L),_=L.output;}else {if(!t.down2)throw new Error("variant E requires w.down2");let g=a.ops.Conv2d(e,t.down1,{outChannels:r.cHigh,kernel:3,stride:2,padding:1,activation:"relu"});l.push(g),p=g.output,m=p.h,x=p.w;let b=a.ops.DownAdapter(p,t.down2,t.adapter,{stride:2});l.push(b),_=b.output;}else {let g=o==="A"?2:3,b=a.ops.DownAdapter(e,t.down1,t.adapter,{stride:g});l.push(b),_=b.output;}return {steps:l,adapted:_,d1:p,dFull:h,midH:m,midW:x}}function Tt(a,e,t,r,o,i,s,n,u){let l=u.variant,p=l==="E"||l==="D",h=l==="D",m=[],x=a.ops.ConvExpand(t,n.expandFeat);m.push(x);let _=x.output,g;if(p){if(!n.up1Combine)throw new Error("two-stage variant requires w.up1Combine");let L=a.ops.BilinearUpsample(_,{outH:i,outW:s});m.push(L);let Fe=a.ops.CatConv6to2(L.output,r,n.up1Combine);m.push(Fe);let Ae=a.ops.BilinearUpsample(Fe.output,{outH:e.h,outW:e.w});m.push(Ae),g=Ae.output;}else {let L=a.ops.BilinearUpsample(_,{outH:e.h,outW:e.w});m.push(L),g=L.output;}let b=h?a.ops.UpFinalSkip(g,o,e,n.upCombine):a.ops.UpFinal(g,e,n.upCombine);return m.push(b),{steps:m,alpha:b.output}}var Ce=class{inputs;output;encoderTaps;halfTap;steps;constructor(e,t,r,o,i,s){this.inputs=[t];let n=wt(e,t,o,i),u=new s(e,n.adapted,r),l=e.ops.BilinearUpsample(u.featLowRes,{outH:n.adapted.h,outW:n.adapted.w}),p=Tt(e,t,l.output,n.d1,n.dFull,n.midH,n.midW,o,i);this.steps=[...n.steps,u,l,...p.steps],this.output=p.alpha,this.encoderTaps=u.encoderTaps,this.halfTap=u.halfTap;}run(){for(let e of this.steps)e.run();}};var C=class{inputs;output;dwOp;pwOp;constructor(e,t,r,o){this.inputs=[t],this.dwOp=e.ops.DepthwiseConv2d(t,r.dw,{kernel:o.kernel,stride:o.stride,padding:o.padding,activation:"relu6"}),this.pwOp=e.ops.Conv2d(this.dwOp.output,r.pw,{outChannels:o.outChannels,kernel:1,stride:1,padding:0,activation:"none"}),this.output=this.pwOp.output;}run(){this.dwOp.run(),this.pwOp.run();}};var f=class{inputs;output;expandOp;dwOp;projOp;constructor(e,t,r,o){this.inputs=[t];let i=o.midChannels!==o.inChannels,s=o.stride===1&&o.inChannels===o.outChannels;this.expandOp=i?e.ops.Conv2d(t,r.expand,{outChannels:o.midChannels,kernel:1,stride:1,padding:0,activation:"relu6"}):null;let n=this.expandOp?this.expandOp.output:t;this.dwOp=e.ops.DepthwiseConv2d(n,r.dw,{kernel:o.kernel,stride:o.stride,padding:o.padding,activation:"relu6"}),this.projOp=s?e.ops.ProjResidual(this.dwOp.output,t,r.proj,{outChannels:o.outChannels}):e.ops.Conv2d(this.dwOp.output,r.proj,{outChannels:o.outChannels,kernel:1,stride:1,padding:0,activation:"none"}),this.output=this.projOp.output;}run(){this.expandOp?.run(),this.dwOp.run(),this.projOp.run();}};var y=class{inputs;output;upOp;concatConvOp;conv2Op;constructor(e,t,r,o,i){this.inputs=[t,r],this.upOp=e.ops.BilinearUpsample(t,{outH:r.h,outW:r.w}),this.concatConvOp=e.ops.ConcatConv2d(this.upOp.output,r,o.conv1,{outChannels:i.outChannels}),this.conv2Op=e.ops.Conv2d(this.concatConvOp.output,o.conv2,{outChannels:i.outChannels,kernel:3,stride:1,padding:1,activation:"relu6"}),this.output=this.conv2Op.output;}run(){this.upOp.run(),this.concatConvOp.run(),this.conv2Op.run();}};var de=class{output;featLowRes;encoderTaps;halfTap;stem;s0;s1b0;s1b1;s2b0;s2b1;s3b0;s3b1;s3b2;s4b0;s4b1;s4b2;bottleneck;dec0;dec1;finalUp;outConv;alpha;constructor(e,t,r){this.stem=e.ops.Conv2d(t,r.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,r.encoder.s0,{outChannels:16,kernel:3,stride:1,padding:1}),this.s1b0=new f(e,this.s0.output,r.encoder.s1[0],{inChannels:16,midChannels:96,outChannels:24,kernel:3,stride:2,padding:"same"}),this.s1b1=new f(e,this.s1b0.output,r.encoder.s1[1],{inChannels:24,midChannels:144,outChannels:24,kernel:3,stride:1,padding:1}),this.s2b0=new f(e,this.s1b1.output,r.encoder.s2[0],{inChannels:24,midChannels:144,outChannels:40,kernel:5,stride:2,padding:"same"}),this.s2b1=new f(e,this.s2b0.output,r.encoder.s2[1],{inChannels:40,midChannels:240,outChannels:40,kernel:5,stride:1,padding:2}),this.s3b0=new f(e,this.s2b1.output,r.encoder.s3[0],{inChannels:40,midChannels:240,outChannels:80,kernel:3,stride:2,padding:"same"}),this.s3b1=new f(e,this.s3b0.output,r.encoder.s3[1],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s3b2=new f(e,this.s3b1.output,r.encoder.s3[2],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s4b0=new f(e,this.s3b2.output,r.encoder.s4[0],{inChannels:80,midChannels:480,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b1=new f(e,this.s4b0.output,r.encoder.s4[1],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b2=new f(e,this.s4b1.output,r.encoder.s4[2],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.bottleneck=e.ops.Conv2d(this.s4b2.output,r.bottleneck,{outChannels:128,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,this.s2b1.output,r.decoder.blocks[0],{outChannels:64}),this.dec1=new y(e,this.dec0.output,this.s1b1.output,r.decoder.blocks[1],{outChannels:32}),this.finalUp=e.ops.UpsampleConv1x1(this.dec1.output,r.decoder.finalUpsample,{outH:this.dec1.output.h*2,outW:this.dec1.output.w*2,outChannels:32,activation:"relu6"}),this.featLowRes=this.finalUp.output,this.encoderTaps=[this.s1b1.output,this.s2b1.output,this.s4b2.output],this.halfTap=this.s0.output,this.outConv=e.ops.Conv2d(this.finalUp.output,r.decoder.outputConv,{outChannels:4,kernel:1,stride:1,padding:0,activation:"none"}),this.alpha=e.ops.UpsampleSigmoid(this.outConv.output,{outH:this.outConv.output.h*2,outW:this.outConv.output.w*2}),this.output=this.alpha.output;}run(){this.stem.run(),this.s0.run(),this.s1b0.run(),this.s1b1.run(),this.s2b0.run(),this.s2b1.run(),this.s3b0.run(),this.s3b1.run(),this.s3b2.run(),this.s4b0.run(),this.s4b1.run(),this.s4b2.run(),this.bottleneck.run(),this.dec0.run(),this.dec1.run(),this.finalUp.run(),this.outConv.run(),this.alpha.run();}};var _e=class{output;featLowRes;encoderTaps;stem;s0;s1b0;s1b1;s2b0;s2b1;s3b0;s3b1;s3b2;s4b0;s4b1;s4b2;s5b0;s5b1;s5b2;s5b3;s6b0;bottleneck;dec0;dec1;dec2;finalUp;outConv;alpha;constructor(e,t,r){this.stem=e.ops.Conv2d(t,r.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,r.encoder.s0,{outChannels:16,kernel:3,stride:1,padding:1}),this.s1b0=new f(e,this.s0.output,r.encoder.s1[0],{inChannels:16,midChannels:96,outChannels:24,kernel:3,stride:2,padding:"same"}),this.s1b1=new f(e,this.s1b0.output,r.encoder.s1[1],{inChannels:24,midChannels:144,outChannels:24,kernel:3,stride:1,padding:1}),this.s2b0=new f(e,this.s1b1.output,r.encoder.s2[0],{inChannels:24,midChannels:144,outChannels:40,kernel:5,stride:2,padding:"same"}),this.s2b1=new f(e,this.s2b0.output,r.encoder.s2[1],{inChannels:40,midChannels:240,outChannels:40,kernel:5,stride:1,padding:2}),this.s3b0=new f(e,this.s2b1.output,r.encoder.s3[0],{inChannels:40,midChannels:240,outChannels:80,kernel:3,stride:2,padding:"same"}),this.s3b1=new f(e,this.s3b0.output,r.encoder.s3[1],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s3b2=new f(e,this.s3b1.output,r.encoder.s3[2],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s4b0=new f(e,this.s3b2.output,r.encoder.s4[0],{inChannels:80,midChannels:480,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b1=new f(e,this.s4b0.output,r.encoder.s4[1],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b2=new f(e,this.s4b1.output,r.encoder.s4[2],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s5b0=new f(e,this.s4b2.output,r.encoder.s5[0],{inChannels:112,midChannels:672,outChannels:192,kernel:5,stride:2,padding:"same"}),this.s5b1=new f(e,this.s5b0.output,r.encoder.s5[1],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b2=new f(e,this.s5b1.output,r.encoder.s5[2],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b3=new f(e,this.s5b2.output,r.encoder.s5[3],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s6b0=new f(e,this.s5b3.output,r.encoder.s6[0],{inChannels:192,midChannels:1152,outChannels:320,kernel:3,stride:1,padding:1}),this.bottleneck=e.ops.Conv2d(this.s6b0.output,r.bottleneck,{outChannels:128,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,this.s4b2.output,r.decoder.blocks[0],{outChannels:64}),this.dec1=new y(e,this.dec0.output,this.s2b1.output,r.decoder.blocks[1],{outChannels:32}),this.dec2=new y(e,this.dec1.output,this.s1b1.output,r.decoder.blocks[2],{outChannels:16}),this.finalUp=e.ops.UpsampleConv1x1(this.dec2.output,r.decoder.finalUpsample,{outH:this.dec2.output.h*2,outW:this.dec2.output.w*2,outChannels:16,activation:"relu6"}),this.featLowRes=this.finalUp.output,this.encoderTaps=[this.s1b1.output,this.s2b1.output,this.s4b2.output,this.s6b0.output],this.outConv=e.ops.Conv2d(this.finalUp.output,r.decoder.outputConv,{outChannels:4,kernel:1,stride:1,padding:0,activation:"none"}),this.alpha=e.ops.UpsampleSigmoid(this.outConv.output,{outH:this.outConv.output.h*2,outW:this.outConv.output.w*2}),this.output=this.alpha.output;}run(){this.stem.run(),this.s0.run(),this.s1b0.run(),this.s1b1.run(),this.s2b0.run(),this.s2b1.run(),this.s3b0.run(),this.s3b1.run(),this.s3b2.run(),this.s4b0.run(),this.s4b1.run(),this.s4b2.run(),this.s5b0.run(),this.s5b1.run(),this.s5b2.run(),this.s5b3.run(),this.s6b0.run(),this.bottleneck.run(),this.dec0.run(),this.dec1.run(),this.dec2.run(),this.finalUp.run(),this.outConv.run(),this.alpha.run();}};var Ee=[[{in:24,mid:144,out:32,k:3,s:2},{in:32,mid:192,out:32,k:3,s:1},{in:32,mid:192,out:32,k:3,s:1}],[{in:32,mid:192,out:48,k:5,s:2},{in:48,mid:288,out:48,k:5,s:1},{in:48,mid:288,out:48,k:5,s:1}],[{in:48,mid:288,out:96,k:3,s:2},{in:96,mid:576,out:96,k:3,s:1},{in:96,mid:576,out:96,k:3,s:1},{in:96,mid:576,out:96,k:3,s:1},{in:96,mid:576,out:96,k:3,s:1}],[{in:96,mid:576,out:136,k:5,s:1},{in:136,mid:816,out:136,k:5,s:1},{in:136,mid:816,out:136,k:5,s:1},{in:136,mid:816,out:136,k:5,s:1},{in:136,mid:816,out:136,k:5,s:1}],[{in:136,mid:816,out:232,k:5,s:2},{in:232,mid:1392,out:232,k:5,s:1},{in:232,mid:1392,out:232,k:5,s:1},{in:232,mid:1392,out:232,k:5,s:1},{in:232,mid:1392,out:232,k:5,s:1},{in:232,mid:1392,out:232,k:5,s:1}],[{in:232,mid:1392,out:384,k:3,s:1}]],fe=class{output;featLowRes;encoderTaps;stem;s0;stages;bottleneck;dec0;dec1;dec2;finalUp;outConv;alpha;constructor(e,t,r){this.stem=e.ops.Conv2d(t,r.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,r.encoder.s0,{outChannels:24,kernel:3,stride:1,padding:1});let o=[r.encoder.s1,r.encoder.s2,r.encoder.s3,r.encoder.s4,r.encoder.s5,r.encoder.s6];this.stages=[];let i=this.s0.output;for(let h=0;h<Ee.length;h++){let m=[];for(let x=0;x<Ee[h].length;x++){let _=Ee[h][x],g=new f(e,i,o[h][x],{inChannels:_.in,midChannels:_.mid,outChannels:_.out,kernel:_.k,stride:_.s,padding:_.s===2?"same":(_.k-1)/2});m.push(g),i=g.output;}this.stages.push(m);}let s=h=>this.stages[h][this.stages[h].length-1].output,n=s(0),u=s(1),l=s(3),p=s(5);this.bottleneck=e.ops.Conv2d(p,r.bottleneck,{outChannels:256,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,l,r.decoder.blocks[0],{outChannels:128}),this.dec1=new y(e,this.dec0.output,u,r.decoder.blocks[1],{outChannels:64}),this.dec2=new y(e,this.dec1.output,n,r.decoder.blocks[2],{outChannels:32}),this.finalUp=e.ops.UpsampleConv1x1(this.dec2.output,r.decoder.finalUpsample,{outH:this.dec2.output.h*2,outW:this.dec2.output.w*2,outChannels:32,activation:"relu6"}),this.featLowRes=this.finalUp.output,this.encoderTaps=[n,u,l,p],this.outConv=e.ops.Conv2d(this.finalUp.output,r.decoder.outputConv,{outChannels:4,kernel:1,stride:1,padding:0,activation:"none"}),this.alpha=e.ops.UpsampleSigmoid(this.outConv.output,{outH:this.outConv.output.h*2,outW:this.outConv.output.w*2}),this.output=this.alpha.output;}run(){this.stem.run(),this.s0.run();for(let e of this.stages)for(let t of e)t.run();this.bottleneck.run(),this.dec0.run(),this.dec1.run(),this.dec2.run(),this.finalUp.run(),this.outConv.run(),this.alpha.run();}};var kt=de,Lt=_e,To=fe,ko={xs:{base:kt,wrapper:{variant:"B",cHigh:4,cLow:4,cUp:2},canvasRes:{w:384,h:240},baseRes:{w:128,h:80},flowFuseStem:true},small:{base:kt,wrapper:{variant:"A",cHigh:4,cLow:4,cUp:2},canvasRes:{w:384,h:224},baseRes:{w:192,h:112}},medium:{base:Lt,wrapper:{variant:"A",cHigh:4,cLow:4,cUp:2},canvasRes:{w:512,h:320},baseRes:{w:256,h:160}},large:{base:Lt,wrapper:{variant:"D",cHigh:4,cLow:4,cUp:2},canvasRes:{w:640,h:400},baseRes:{w:256,h:160}},xl:{base:To,wrapper:{variant:"E",cHigh:4,cLow:4,cUp:2},canvasRes:{w:1280,h:768},baseRes:{w:320,h:192}}};var Lo=a=>typeof a=="object"&&a!==null&&typeof a.offset=="number"&&typeof a.length=="number";function Wo(a){if(a.byteLength<4)throw new Error("weights buffer too small");let e=new DataView(a,0,4).getUint32(0,true),t=4+e;if(a.byteLength<t)throw new Error("weights buffer truncated (header)");let r=new Uint8Array(a,4,e),o=JSON.parse(new TextDecoder().decode(r)),i=o.__dtype__??"f32",s=i==="f16"?2:4;if(t%s!==0)throw new Error(`payload not ${s}-byte aligned (header=${e})`);let n=(a.byteLength-t)/s,u=l=>{if(Lo(l)){let{offset:p,length:h}=l;if(p+h>n)throw new Error(`array ref out of range: offset=${p} length=${h}`);let m=t+p*s;return i==="f16"?new Uint16Array(a,m,h):new Float32Array(a,m,h)}if(Array.isArray(l))return l.map(u);if(typeof l=="object"&&l!==null){let p={};for(let h of Object.keys(l))h!=="__dtype__"&&(p[h]=u(l[h]));return p}return l};return u(o)}var xe=class{op;constructor(e,t,r,o){this.op=e.ops.BilinearUpsample(t,{outH:r,outW:o});}get output(){return this.op.output}run(){this.op.run();}};var ge=class{op;constructor(e,t,r,o){this.op=e.ops.BicubicUpsample(t,{outH:r,outW:o});}get output(){return this.op.output}run(){this.op.run();}};var be=class{presenter;constructor(e,t,r,o,i="main"){this.presenter=e.presenters.CompositeSolid(t,r,o,i);}run(){this.presenter.run();}};var ve=class{presenter;constructor(e,t,r,o,i="main"){this.presenter=e.presenters.CompositeImage(t,r,o,i);}run(){this.presenter.run();}};var ye=class{downs=[];ups=[];constructor(e,t,r){let o=Co(r),i=t;for(let s=0;s<o;s++){let n=e.ops.BilinearUpsample(i,{outH:Math.max(1,Math.floor(i.h/2)),outW:Math.max(1,Math.floor(i.w/2))});this.downs.push(n),i=n.output;}for(let s=o-1;s>=1;s--){let n=this.downs[s-1].output,u=e.ops.BilinearUpsample(i,{outH:n.h,outW:n.w});this.ups.push(u),i=u.output;}}get output(){return this.ups.length>0?this.ups[this.ups.length-1].output:this.downs[0].output}run(){for(let e of this.downs)e.run();for(let e of this.ups)e.run();}};function Co(a){let e=Math.max(1,3*a);return Math.max(2,Math.min(6,Math.round(Math.log2(e))))}var Eo=.05,we=class{blur;presenter;constructor(e,t,r,o,i="main"){o<=Eo?(this.blur=null,this.presenter=e.presenters.CompositeImage(t,r,t,i)):(this.blur=new ye(e,t,o),this.presenter=e.presenters.CompositeImageBilinear(t,r,this.blur.output,i));}run(){this.blur?.run(),this.presenter.run();}};var Ge=class{constructor(e){this.backend=e;let t=e.canvas;this.displayInput=e.ops.Input(t.height,t.width),this.image=this.displayInput.output;}backend;displayInput;image;network=null;networkInput=null;upscaler=null;upscalerMode="bilinear";bgConfig={mode:"solid",color:[0,0,0]};effectChain=[];viewRect=null;viewFg=null;viewAlpha=null;displayDirty=false;compositors=new Map;attachNetwork(e,t,r){this.network=e,this.networkInput=t,this.upscalerMode=r.upscaler,this.bgConfig=r.background,this.upscaler=this.makeUpscaler(),this.rebuildView(),this.compositors.clear();}hasNetwork(){return this.network!==null}setSource(e){this.networkInput?.setSource(e),this.displayInput.setSource(e),this.displayDirty=true;}setUpscaler(e){e!==this.upscalerMode&&(this.upscalerMode=e,this.network&&(this.upscaler=this.makeUpscaler(),this.rebuildView(),this.compositors.clear()));}setBackground(e){this.bgConfig=e;}get displayImage(){return this.image}fgImage(){return this.viewFg?this.viewFg.output:this.chainTail()}chainTail(){return this.effectChain.length?this.effectChain[this.effectChain.length-1].output:this.image}alphaImage(){if(!this.upscaler)throw new Error("RenderOp.alphaImage called before attachNetwork");return this.viewAlpha?this.viewAlpha.output:this.upscaler.output}setViewTransform(e){this.viewRect=e,this.rebuildView(),this.compositors.clear();}rebuildView(){this.viewFg=null,this.viewAlpha=null,this.viewRect&&(this.viewFg=this.backend.ops.Reframe(this.chainTail(),this.viewRect),this.upscaler&&(this.viewAlpha=this.backend.ops.Reframe(this.upscaler.output,this.viewRect)));}setEffectChain(e){this.effectChain=e,this.rebuildView(),this.compositors.clear();}runEffects(){for(let e of this.effectChain)e.run();this.viewFg?.run(),this.viewAlpha?.run();}run(){this.runModel(),this.runDisplay();}runDisplay(){this.refreshDisplayInput(),this.runEffects(),this.compositeMain("effect");}runFgPassthrough(){this.refreshDisplayInput(),this.runEffects(),this.compositeMain("fg");}runPassthrough(){this.refreshDisplayInput(),this.compositeMain("raw");}compositeMain(e){let t=e==="effect"?this.bgConfig:e==="fg"?{mode:"fg-passthrough"}:{mode:"passthrough"};this.compositeTo("main",t);}refreshDisplayInput(){this.displayDirty&&(this.displayInput.run(),this.displayDirty=false);}compositeTo(e,t){let r=this.compositors.get(e);if(r&&Go(r.spec,t)){r.handle.run();return}let o=this.buildCompositor(e,t);this.compositors.set(e,{spec:t,handle:o}),o.run();}applyAlpha(e){if(!this.network||!this.upscaler)throw new Error("RenderOp.applyAlpha called before attachNetwork");this.backend.copyTensor(e,this.network.output),this.upscaler.run();}runModel(){if(!this.network||!this.networkInput||!this.upscaler)throw new Error("RenderOp.runModel called before attachNetwork");this.networkInput.run(),this.network.run(),this.upscaler.run();}makeUpscaler(){if(!this.network)throw new Error("makeUpscaler called with no network");let{backend:e,network:t,image:r}=this;return this.upscalerMode==="bicubic"?new ge(e,t.output,r.h,r.w):new xe(e,t.output,r.h,r.w)}buildCompositor(e,t){let{backend:r,image:o}=this;if(t.mode==="passthrough")return r.presenters.CompositePassthrough(o,e);let i=this.fgImage();if(t.mode==="fg-passthrough")return r.presenters.CompositePassthrough(i,e);if(!this.upscaler)throw new Error("RenderOp.compositeTo (effect spec) called before attachNetwork");let s=this.alphaImage();switch(t.mode){case "solid":return new be(r,i,s,t.color,e);case "image":return new ve(r,i,s,t.image,e);case "blur":return new we(r,i,s,t.sigma,e)}}};function Go(a,e){if(a.mode!==e.mode)return false;switch(e.mode){case "passthrough":return true;case "fg-passthrough":return true;case "solid":{let t=a.color;return t[0]===e.color[0]&&t[1]===e.color[1]&&t[2]===e.color[2]}case "blur":return a.sigma===e.sigma;case "image":return a.image===e.image}}export{Ge as RenderOp,ko as TIER_CONFIG,Ce as TierModel,We as WebGLBackend,Wo as loadWeightsFromBinary};