longpipe 0.0.1 → 0.0.3

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/index.js CHANGED
@@ -1,4 +1,2466 @@
1
- var v=[{model:"xxs",dtype:"f16",resolution:{w:128,h:72},skipFrames:3},{model:"xs",dtype:"f16",resolution:{w:192,h:108},skipFrames:1},{model:"small",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"compact",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"medium",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"large",dtype:"f32",resolution:{w:256,h:144},skipFrames:0},{model:"xl",dtype:"f32",resolution:{w:512,h:288},skipFrames:0}];var w=(...i)=>console.log("[longpipe/adaptive]",...i),Qe=2e3,Ze=1e4,et=30,tt=20,rt=5e3,at=.3,it=15e3,C=class{constructor(t){this.opts=t;this.currentPresetIdx=v.findIndex(e=>e.model===t.initialModel),this.lastSwapAt=performance.now();}opts;timer=null;currentPresetIdx;lastSwapAt=0;overshootStart=0;headroomStart=0;start(){w(`started; backend=${this.opts.backendKind} initial=${this.opts.initialModel} idx=${this.currentPresetIdx}`),this.timer=setInterval(()=>{this.tick().catch(t=>w("tick failed:",t));},Qe);}stop(){this.timer&&clearInterval(this.timer),this.timer=null;}async tick(){if(typeof document<"u"&&document.hidden)return;let t=performance.now();if(t-this.lastSwapAt<Ze)return;let e=await this.opts.getStats();if(e.fps<tt?(this.overshootStart===0&&(this.overshootStart=t),t-this.overshootStart>=rt&&this.tryDowngrade(e.fps)):this.overshootStart=0,this.opts.backendKind==="webgpu"&&e.modelMs>0){let r=1e3/et*at;e.modelMs<r?(this.headroomStart===0&&(this.headroomStart=t),t-this.headroomStart>=it&&this.tryUpgrade(e.modelMs,r)):this.headroomStart=0;}}tryDowngrade(t){if(this.currentPresetIdx<=0)return;let e=this.currentPresetIdx-1;w(`downgrade ${v[this.currentPresetIdx].model} \u2192 ${v[e].model} (fps=${t})`),this.swap(e);}tryUpgrade(t,e){if(this.currentPresetIdx>=v.length-1)return;let r=this.currentPresetIdx+1;w(`upgrade ${v[this.currentPresetIdx].model} \u2192 ${v[r].model} (modelMs=${t.toFixed(1)} < ${e.toFixed(1)})`),this.swap(r);}async swap(t){let e=v[t];this.lastSwapAt=performance.now(),this.overshootStart=0,this.headroomStart=0;try{let r=this.opts.buildWeightsUrl(this.opts.weightsBaseUrl,e.model),a=await fetch(r);if(!a.ok)throw new Error(`weights fetch failed: ${a.status} ${r}`);let o=await a.arrayBuffer();await this.opts.swapPreset(e.model,o),this.currentPresetIdx=t,w(`swap to ${e.model} done`);}catch(r){w("swap failed:",r),this.opts.onError?.({message:`adaptive preset swap failed: ${r.message??String(r)}`,source:"adaptive",recoverable:true,cause:r});}}};async function re(i){let t,e=false;if(i instanceof HTMLVideoElement){if(t=i.currentSrc||i.src,!t)throw new Error("background video: HTMLVideoElement has no src")}else if(i instanceof Blob)t=URL.createObjectURL(i),e=true;else if(typeof i=="string")t=i;else throw new Error("background video: unsupported input shape");let r=document.createElement("video");r.crossOrigin="anonymous",r.muted=true,r.loop=true,r.playsInline=true,r.src=t,await new Promise((p,l)=>{r.addEventListener("loadeddata",()=>p(),{once:true}),r.addEventListener("error",()=>l(new Error(`background video: failed to load ${t}`)),{once:true});});try{await r.play();}catch(p){throw new Error(`background video: play() rejected \u2014 ${p.message}`)}let a=new MessageChannel,o=a.port1,s=a.port2,n=false,u=r.requestVideoFrameCallback?.bind(r);if(u){let p=(l,f)=>{if(!n){try{let b=new VideoFrame(r,{timestamp:f.mediaTime*1e6});o.postMessage({frame:b},[b]);}catch{}u(p);}};u(p);}else {let p=()=>{if(!n){try{let l=new VideoFrame(r,{timestamp:performance.now()*1e3});o.postMessage({frame:l},[l]);}catch{}requestAnimationFrame(p);}};requestAnimationFrame(p);}return {port:s,transferList:[s],cleanup:()=>{n=true,o.close(),r.pause(),r.removeAttribute("src"),r.load(),e&&URL.revokeObjectURL(t);}}}var J=4,X=8,ae=16;async function Q(i){if(typeof i=="string")return i==="none"?{background:{kind:"none"}}:i==="blur"?{background:{kind:"blur",sigma:X}}:{background:{kind:"image",bitmap:await oe(i)}};if(i instanceof ImageBitmap)return {background:{kind:"image",bitmap:i}};if(typeof HTMLImageElement<"u"&&i instanceof HTMLImageElement)return {background:{kind:"image",bitmap:await createImageBitmap(i)}};if(typeof HTMLVideoElement<"u"&&i instanceof HTMLVideoElement)return await ie(i);let t=i;if(t.color)return {background:{kind:"color",rgb:t.color}};if(t.blur!==void 0)return {background:{kind:"blur",sigma:ot(t.blur)}};if(t.image!==void 0)return {background:{kind:"image",bitmap:await st(t.image)}};if(t.video!==void 0)return await ie(t.video);throw new Error(`background: unrecognized input shape \u2014 ${P(i)}`)}async function ie(i){let t;if(typeof i=="string")t=i;else if(typeof HTMLVideoElement<"u"&&i instanceof HTMLVideoElement)t=i;else if(i&&typeof i=="object"&&"data"in i&&"type"in i)t=i.data instanceof Blob?i.data:new Blob([i.data],{type:i.type});else throw new Error(`background.video: unrecognized shape \u2014 ${P(i)}`);let e=await re(t);return {background:{kind:"video",port:e.port},transferList:e.transferList,cleanup:e.cleanup}}function ot(i){if(i===true)return X;if("sigma"in i){if(typeof i.sigma!="number"||!Number.isFinite(i.sigma)||i.sigma<0)throw new Error(`background.blur.sigma must be a non-negative number, got ${i.sigma}`);return i.sigma}if("strength"in i){let t=i.strength;if(typeof t=="number"){let e=Math.max(0,Math.min(1,t));return J+(ae-J)*e}if(t==="low")return J;if(t==="medium")return X;if(t==="high")return ae;throw new Error(`background.blur.strength must be 'low' | 'medium' | 'high' | number, got ${P(t)}`)}throw new Error(`background.blur: must be true, { strength }, or { sigma } \u2014 got ${P(i)}`)}async function st(i){if(i instanceof ImageBitmap)return i;if(typeof HTMLImageElement<"u"&&i instanceof HTMLImageElement)return createImageBitmap(i);if(typeof i=="string")return oe(i);if(i&&typeof i=="object"&&"data"in i&&"type"in i){let t=i.data instanceof Blob?i.data:new Blob([i.data],{type:i.type});return createImageBitmap(t)}throw new Error(`background.image: unrecognized shape \u2014 ${P(i)}`)}async function oe(i){let t=await fetch(i);if(!t.ok)throw new Error(`background: failed to load image from ${i} (HTTP ${t.status})`);let e=await t.blob();return createImageBitmap(e)}function P(i){return i===null?"null":typeof i!="object"?typeof i+"("+String(i)+")":`${i.constructor?.name??"Object"} ${JSON.stringify(i).slice(0,80)}`}function se(){return {input:"MediaStreamTrackProcessor"in self?"mstp":"rvfc-postmessage",output:"MediaStreamTrackGenerator"in self?"mstg":nt()?"transfer-capture":"bitmap-shuttle"}}var T=null;function nt(){return T!==null||(T=!/Firefox/.test(navigator.userAgent)),T}function ne(i,t,e){let r=[i];return e==="passthrough"&&r.push(...t.getAudioTracks()),new MediaStream(r)}var W=(...i)=>console.log("[longpipe/worker_controller]",...i),M=class{worker;listeners=new Map;persistentEvents=new Set;constructor(t){this.worker=t,this.worker.addEventListener("message",this.handleMessage.bind(this)),W("constructed; message listener attached");}handleMessage(t){let{request_id:e,res:r}=t.data,a=this.listeners.get(e);W("msg received: request_id=",e,"handler?",!!a,"persistent?",this.persistentEvents.has(e)),a&&(a(r),this.persistentEvents.has(e)||this.listeners.delete(e));}addPersistentListener(t,e){this.persistentEvents.add(t),this.listeners.set(t,e),W("addPersistentListener:",t);}removePersistentListener(t){this.persistentEvents.delete(t),this.listeners.delete(t);}sendMessage(t,e,r=[]){let a=crypto.randomUUID();return W("sendMessage:",t,"request_id=",a,"transferables=",r.length),new Promise(o=>{this.listeners.set(a,n=>o(n));let s={cmd:t,data:e,request_id:a};this.worker.postMessage(s,r);})}terminate(){this.worker.terminate(),this.listeners.clear(),this.persistentEvents.clear();}};function ue(i){let t=document.createElement("video");t.srcObject=i,t.muted=true,t.autoplay=true,t.playsInline=true,t.play().catch(c=>{console.warn("[input-postmessage/main] video.play() rejected:",c);});let e=t.requestVideoFrameCallback?.bind(t);if(!e)throw new Error("setupPostMessageInput: requestVideoFrameCallback not supported on this browser");let r=new MessageChannel,a=r.port1,o=r.port2,s=false,n=(c,p)=>{if(!s){try{let l=new VideoFrame(t,{timestamp:p.mediaTime*1e6});a.postMessage({frame:l},[l]);}catch(l){console.warn("[input-postmessage/main] VideoFrame() failed; skipping frame:",l);}e(n);}};return e(n),{port:o,transferList:[o],cleanup:()=>{s=true,a.close(),t.srcObject=null,t.pause();}}}function pe(i){let t=i.getVideoTracks();if(t.length===0)throw new Error("setupMstpInput: input MediaStream has no video tracks");let e=t[0],r=self.MediaStreamTrackProcessor;if(!r)throw new Error("setupMstpInput: MediaStreamTrackProcessor not supported on this browser");let o=new r({track:e}).readable;return {readable:o,transferList:[o],cleanup:()=>{o.cancel().catch(()=>{});}}}function le(i,t){switch(i){case "mstp":{let e=pe(t);return {initFields:{inputReadable:e.readable},transferList:e.transferList,cleanup:e.cleanup}}case "rvfc-postmessage":{let e=ue(t);return {initFields:{inputPort:e.port},transferList:e.transferList,cleanup:e.cleanup}}}}function ce(i,t=30){let e=document.createElement("canvas");e.width=i.w,e.height=i.h;let r=e.getContext("bitmaprenderer");if(!r)throw new Error("setupBitmapShuttleOutput: bitmaprenderer context not available");let o=e.captureStream(t).getVideoTracks();if(o.length===0)throw new Error("setupBitmapShuttleOutput: captureStream produced no video tracks");let s=o[0],n=new MessageChannel,u=n.port1,c=n.port2,p=false,l=null,f=()=>{},b=3,U=0;return u.onmessage=I=>{if(p){if(U++,U<b){I.data.bmp.close();return}f();}try{r.transferFromImageBitmap(I.data.bmp);}catch(g){console.warn("[bitmap-shuttle/main] transferFromImageBitmap failed:",g);}},u.start(),{videoTrack:s,port:c,transferList:[c],startPassthrough:I=>{if(p)return;p=true;let g=document.createElement("video");g.srcObject=I,g.muted=true,g.playsInline=true,g.play().catch(()=>{}),l=g;let G="requestVideoFrameCallback"in g?y=>{g.requestVideoFrameCallback(y);}:y=>{requestAnimationFrame(y);},B=()=>{if(p){if(g.videoWidth===0){G(B);return}createImageBitmap(g,{resizeWidth:e.width,resizeHeight:e.height,resizeQuality:"medium"}).then(y=>{if(!p){y.close();return}try{r.transferFromImageBitmap(y);}catch{}G(B);}).catch(()=>{G(B);});}};G(B),f=()=>{p=false,l&&(l.srcObject=null,l.pause(),l=null);};},cleanup:()=>{f(),u.onmessage=null,u.close(),s.stop();}}}function de(){let i=self.MediaStreamTrackGenerator;if(!i)throw new Error("setupMstgOutput: MediaStreamTrackGenerator not supported on this browser");let t=new i({kind:"video"}),e=t.writable;return {videoTrack:t,writable:e,transferList:[e],cleanup:()=>{e.abort().catch(()=>{}),t.stop();}}}function fe(i,t=30){let e=document.createElement("canvas");e.width=i.w,e.height=i.h;let a=e.captureStream(t).getVideoTracks();if(a.length===0)throw new Error("setupTransferCaptureOutput: captureStream produced no video tracks");let o=a[0],s=e.transferControlToOffscreen();return {videoTrack:o,canvas:s,transferList:[s],cleanup:()=>{o.stop();}}}function me(i,t){switch(i){case "mstg":{let e=de();return {videoTrack:e.videoTrack,initFields:{outputWritable:e.writable},transferList:e.transferList,cleanup:e.cleanup}}case "transfer-capture":{let e=fe(t);return {videoTrack:e.videoTrack,initFields:{outputCanvas:e.canvas},transferList:e.transferList,cleanup:e.cleanup}}case "bitmap-shuttle":{let e=ce(t);return {videoTrack:e.videoTrack,initFields:{outputPort:e.port},transferList:e.transferList,startPassthrough:e.startPassthrough,cleanup:e.cleanup}}}}var ut="https://cdn.longpipe.dev/models/v/0.0.1/",pt={background:"blur",preset:"auto",weightsBaseUrl:ut,audio:"passthrough",enabled:true,adaptive:true},ge={w:1280,h:720};function lt(i,t){if(t)return t;let r=i.getVideoTracks()[0]?.getSettings()??{};return {w:r.width||ge.w,h:r.height||ge.h}}function be(i,t){return `${i.replace(/\/$/,"")}/model_${t}.bin`}var Z=class{stream;ready;controller;worker;inputCleanup;outputCleanup;adaptive=null;bgCleanup=null;constructor(t,e){let r={...pt,...e},a=se(),o=le(a.input,t),s=lt(t,r.outputResolution),n=me(a.output,s);this.inputCleanup=o.cleanup,this.outputCleanup=n.cleanup,this.stream=ne(n.videoTrack,t,r.audio),n.startPassthrough?.(t),this.worker=new Worker(new URL("./worker/index.ts",import.meta.url),{type:"module"}),this.controller=new M(this.worker),this.ready=new Promise((p,l)=>{this.controller.addPersistentListener("ready",()=>{console.log("[longpipe/pipeline] ready handler invoked; resolving .ready"),r.onReady?.(),p();}),this.controller.addPersistentListener("error",f=>{console.error(`[longpipe/pipeline] error (${f.source}):`,f.message),f.recoverable||l(new Error(f.message)),r.onError?.(f);});});let u={topology:a,preset:r.preset,enabled:r.enabled,backend:"auto",dtype:"f16",canvasSize:s,...o.initFields,...n.initFields},c=[...o.transferList,...n.transferList];this.bootstrap(u,r.background,c,r.weightsBaseUrl,r.adaptive,r.onError);}async bootstrap(t,e,r,a,o,s){try{console.log("[longpipe/pipeline] normalizing background\u2026");let n=await Q(e);this.bgCleanup=n.cleanup??null;let u={...t,background:n.background},c=[...r,...n.transferList??[]];console.log("[longpipe/pipeline] sending init\u2026");let p=await this.controller.sendMessage("init",u,c);console.log("[longpipe/pipeline] init resolved:",p);let l=be(a,p.resolvedPreset.model);console.log("[longpipe/pipeline] fetching weights:",l);let f=await fetch(l);if(!f.ok)throw new Error(`weights fetch failed: ${f.status} ${l}`);let b=await f.arrayBuffer();console.log("[longpipe/pipeline] weights bytes:",b.byteLength),console.log("[longpipe/pipeline] sending startRender\u2026"),await this.controller.sendMessage("startRender",{weights:b},[b]),console.log("[longpipe/pipeline] startRender resolved; awaiting first frame"),u.preset==="auto"&&o&&(this.adaptive=new C({backendKind:p.resolvedBackend,initialModel:p.resolvedPreset.model,weightsBaseUrl:a,buildWeightsUrl:be,getStats:()=>this.getStats(),swapPreset:async(U,K)=>{await this.controller.sendMessage("setPreset",{preset:U,weights:K},[K]);},onError:s}),this.adaptive.start());}catch(n){console.error("[longpipe/pipeline] bootstrap failed:",n),this.controller.handleMessage.call(this.controller,{data:{request_id:"error",res:{message:n.message??String(n),source:"worker",recoverable:false,cause:n}}});}}then(t,e){return this.ready.then(()=>t?t(this):this,e)}async setBackground(t){let e=await Q(t),r=this.bgCleanup;this.bgCleanup=e.cleanup??null,await this.controller.sendMessage("setBackground",e.background,e.transferList),r?.();}setPreset(t,e){this.controller.sendMessage("setPreset",{preset:t,weights:e});}setEnabled(t){this.controller.sendMessage("setEnabled",{enabled:t});}async getStats(){return this.controller.sendMessage("getStats",{})}destroy(){this.adaptive?.stop(),this.adaptive=null,this.bgCleanup?.(),this.bgCleanup=null,this.controller.sendMessage("destroy",{}),this.controller.terminate(),this.inputCleanup(),this.outputCleanup();}};var x=new Float32Array(1),k=new Uint32Array(x.buffer);function ct(i){x[0]=i;let t=k[0],e=t>>>16&32768,r=t&2147483647,a=(r>>>23)-127+15;if((t&2139095040)===2139095040)return (t&8388607)!==0?e|32256:e|31744;if(a>=31)return e|31744;if(a<=0){if(a<-10)return e;let c=r&8388607|8388608,p=14-a,l=c>>>p,f=c>>>p-1&1,b=(c&(1<<p-1)-1)!==0?1:0;return f&&(b||l&1)&&(l+=1),e|l}let o=r>>>13&1023,s=r>>>12&1,n=(r&4095)!==0?1:0,u=a<<10|o;return s&&(n||o&1)&&(u+=1,(u>>>10&31)===31)?e|31744:e|u}function dt(i){let t=(i&32768)<<16,e=(i&31744)>>>10,r=i&1023;if(e===0){if(r===0)return k[0]=t,x[0];let a=r,o=1;for(;(a&1024)===0;)a<<=1,o-=1;return a&=1023,k[0]=t|o+127-15<<23|a<<13,x[0]}return e===31?(k[0]=t|2139095040|r<<13,x[0]):(k[0]=t|e+127-15<<23|r<<13,x[0])}function he(i){let t=new Uint16Array(i.length);for(let e=0;e<i.length;e++)t[e]=ct(i[e]);return t}function ee(i){let t=new Float32Array(i.length);for(let e=0;e<i.length;e++)t[e]=dt(i[e]);return t}var d=class{constructor(t){this.backend=t;}backend;shader="";pipeline;bindGroup;uniformDefs=[];uniformBuffers={};createUniform(t,e){this.uniformDefs.push({name:t,type:e});}setUniform(t,e){let r=this.backend.device.createBuffer({size:e.byteLength,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST,mappedAtCreation:true});e instanceof Uint32Array?new Uint32Array(r.getMappedRange()).set(e):new Float32Array(r.getMappedRange()).set(e),r.unmap(),this.uniformBuffers[t]=r;}defaultBindGroup(){let t=[],e=0;for(let r of this.inputs)t.push({binding:e++,resource:{buffer:r.buffer}});for(let r of this.weights)t.push({binding:e++,resource:{buffer:r.buffer}});for(let r of this.uniformDefs)t.push({binding:e++,resource:{buffer:this.uniformBuffers[r.name]}});return t.push({binding:e,resource:{buffer:this.output.buffer}}),this.backend.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:t})}defaultSetup(){let t=this.backend.device.createShaderModule({code:this.shader});this.pipeline=this.backend.device.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"main"}}),this.bindGroup=this.defaultBindGroup();}run(){let t=this.backend.device.createCommandEncoder(),e=t.beginComputePass();e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.dispatchWorkgroups(...this.dispatch),e.end(),this.backend.device.queue.submit([t.finish()]);}};var _e=`// Conv2d compute shader \u2014 handles all variants:
1
+ var w=[{model:"xxs",dtype:"f16",resolution:{w:128,h:72},skipFrames:3},{model:"xs",dtype:"f16",resolution:{w:192,h:108},skipFrames:1},{model:"small",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"compact",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"medium",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"large",dtype:"f32",resolution:{w:256,h:144},skipFrames:0},{model:"xl",dtype:"f32",resolution:{w:512,h:288},skipFrames:0}];var Q=false;function se(i){Q=i;}function _(i){let t=`[longpipe/${i}]`,e=((...r)=>{Q&&console.log(t,...r);});return e.warn=(...r)=>{Q&&console.warn(t,...r);},e}var k=_("adaptive"),it=2e3,at=1e4,st=30,ot=20,ut=5e3,pt=.3,lt=15e3,W=class{constructor(t){this.opts=t;this.currentPresetIdx=w.findIndex(e=>e.model===t.initialModel),this.lastSwapAt=performance.now();}opts;timer=null;currentPresetIdx;lastSwapAt=0;overshootStart=0;headroomStart=0;start(){k(`started; backend=${this.opts.backendKind} initial=${this.opts.initialModel} idx=${this.currentPresetIdx}`),this.timer=setInterval(()=>{this.tick().catch(t=>k("tick failed:",t));},it);}stop(){this.timer&&clearInterval(this.timer),this.timer=null;}async tick(){if(typeof document<"u"&&document.hidden)return;let t=performance.now();if(t-this.lastSwapAt<at)return;let e=await this.opts.getStats();if(e.fps<ot?(this.overshootStart===0&&(this.overshootStart=t),t-this.overshootStart>=ut&&this.tryDowngrade(e.fps)):this.overshootStart=0,this.opts.backendKind==="webgpu"&&e.modelMs>0){let r=1e3/st*pt;e.modelMs<r?(this.headroomStart===0&&(this.headroomStart=t),t-this.headroomStart>=lt&&this.tryUpgrade(e.modelMs,r)):this.headroomStart=0;}}tryDowngrade(t){if(this.currentPresetIdx<=0)return;let e=this.currentPresetIdx-1;k(`downgrade ${w[this.currentPresetIdx].model} \u2192 ${w[e].model} (fps=${t})`),this.swap(e);}tryUpgrade(t,e){if(this.currentPresetIdx>=w.length-1)return;let r=this.currentPresetIdx+1;k(`upgrade ${w[this.currentPresetIdx].model} \u2192 ${w[r].model} (modelMs=${t.toFixed(1)} < ${e.toFixed(1)})`),this.swap(r);}async swap(t){let e=w[t];this.lastSwapAt=performance.now(),this.overshootStart=0,this.headroomStart=0;try{let r=this.opts.buildWeightsUrl(this.opts.weightsBaseUrl,e.model),n=await fetch(r);if(!n.ok)throw new Error(`weights fetch failed: ${n.status} ${r}`);let a=await n.arrayBuffer();await this.opts.swapPreset(e.model,a),this.currentPresetIdx=t,k(`swap to ${e.model} done`);}catch(r){k("swap failed:",r),this.opts.onError?.({message:`adaptive preset swap failed: ${r.message??String(r)}`,source:"adaptive",recoverable:true,cause:r});}}};async function oe(i){let t,e=false;if(i instanceof HTMLVideoElement){if(t=i.currentSrc||i.src,!t)throw new Error("background video: HTMLVideoElement has no src")}else if(i instanceof Blob)t=URL.createObjectURL(i),e=true;else if(typeof i=="string")t=i;else throw new Error("background video: unsupported input shape");let r=document.createElement("video");r.crossOrigin="anonymous",r.muted=true,r.loop=true,r.playsInline=true,r.src=t,await new Promise((p,l)=>{r.addEventListener("loadeddata",()=>p(),{once:true}),r.addEventListener("error",()=>l(new Error(`background video: failed to load ${t}`)),{once:true});});try{await r.play();}catch(p){throw new Error(`background video: play() rejected \u2014 ${p.message}`)}let n=new MessageChannel,a=n.port1,s=n.port2,o=false,u=r.requestVideoFrameCallback?.bind(r);if(u){let p=(l,h)=>{if(!o){try{let g=new VideoFrame(r,{timestamp:h.mediaTime*1e6});a.postMessage({frame:g},[g]);}catch{}u(p);}};u(p);}else {let p=()=>{if(!o){try{let l=new VideoFrame(r,{timestamp:performance.now()*1e3});a.postMessage({frame:l},[l]);}catch{}requestAnimationFrame(p);}};requestAnimationFrame(p);}return {port:s,transferList:[s],cleanup:()=>{o=true,a.close(),r.pause(),r.removeAttribute("src"),r.load(),e&&URL.revokeObjectURL(t);}}}var Z=4,ee=8,ue=16;async function te(i){if(typeof i=="string")return i==="none"?{background:{kind:"none"}}:i==="blur"?{background:{kind:"blur",sigma:ee}}:{background:{kind:"image",bitmap:await le(i)}};if(i instanceof ImageBitmap)return {background:{kind:"image",bitmap:i}};if(typeof HTMLImageElement<"u"&&i instanceof HTMLImageElement)return {background:{kind:"image",bitmap:await createImageBitmap(i)}};if(typeof HTMLVideoElement<"u"&&i instanceof HTMLVideoElement)return await pe(i);let t=i;if(t.color)return {background:{kind:"color",rgb:t.color}};if(t.blur!==void 0)return {background:{kind:"blur",sigma:ct(t.blur)}};if(t.image!==void 0)return {background:{kind:"image",bitmap:await dt(t.image)}};if(t.video!==void 0)return await pe(t.video);throw new Error(`background: unrecognized input shape \u2014 ${P(i)}`)}async function pe(i){let t;if(typeof i=="string")t=i;else if(typeof HTMLVideoElement<"u"&&i instanceof HTMLVideoElement)t=i;else if(i&&typeof i=="object"&&"data"in i&&"type"in i)t=i.data instanceof Blob?i.data:new Blob([i.data],{type:i.type});else throw new Error(`background.video: unrecognized shape \u2014 ${P(i)}`);let e=await oe(t);return {background:{kind:"video",port:e.port},transferList:e.transferList,cleanup:e.cleanup}}function ct(i){if(i===true)return ee;if("sigma"in i){if(typeof i.sigma!="number"||!Number.isFinite(i.sigma)||i.sigma<0)throw new Error(`background.blur.sigma must be a non-negative number, got ${i.sigma}`);return i.sigma}if("strength"in i){let t=i.strength;if(typeof t=="number"){let e=Math.max(0,Math.min(1,t));return Z+(ue-Z)*e}if(t==="low")return Z;if(t==="medium")return ee;if(t==="high")return ue;throw new Error(`background.blur.strength must be 'low' | 'medium' | 'high' | number, got ${P(t)}`)}throw new Error(`background.blur: must be true, { strength }, or { sigma } \u2014 got ${P(i)}`)}async function dt(i){if(i instanceof ImageBitmap)return i;if(typeof HTMLImageElement<"u"&&i instanceof HTMLImageElement)return createImageBitmap(i);if(typeof i=="string")return le(i);if(i&&typeof i=="object"&&"data"in i&&"type"in i){let t=i.data instanceof Blob?i.data:new Blob([i.data],{type:i.type});return createImageBitmap(t)}throw new Error(`background.image: unrecognized shape \u2014 ${P(i)}`)}async function le(i){let t=await fetch(i);if(!t.ok)throw new Error(`background: failed to load image from ${i} (HTTP ${t.status})`);let e=await t.blob();return createImageBitmap(e)}function P(i){return i===null?"null":typeof i!="object"?typeof i+"("+String(i)+")":`${i.constructor?.name??"Object"} ${JSON.stringify(i).slice(0,80)}`}function ce(){return {input:"MediaStreamTrackProcessor"in self?"mstp":"rvfc-postmessage",output:"MediaStreamTrackGenerator"in self?"mstg":ht()?"transfer-capture":"bitmap-shuttle"}}var B=null;function ht(){return B!==null||(B=!/Firefox/.test(navigator.userAgent)),B}function de(i,t,e){let r=[i];return e==="passthrough"&&r.push(...t.getAudioTracks()),new MediaStream(r)}var G=_("worker_controller"),A=class{worker;listeners=new Map;persistentEvents=new Set;constructor(t){this.worker=t,this.worker.addEventListener("message",this.handleMessage.bind(this)),G("constructed; message listener attached");}handleMessage(t){let{request_id:e,res:r}=t.data,n=this.listeners.get(e);G("msg received: request_id=",e,"handler?",!!n,"persistent?",this.persistentEvents.has(e)),n&&(n(r),this.persistentEvents.has(e)||this.listeners.delete(e));}addPersistentListener(t,e){this.persistentEvents.add(t),this.listeners.set(t,e),G("addPersistentListener:",t);}removePersistentListener(t){this.persistentEvents.delete(t),this.listeners.delete(t);}sendMessage(t,e,r=[]){let n=crypto.randomUUID();return G("sendMessage:",t,"request_id=",n,"transferables=",r.length),new Promise(a=>{this.listeners.set(n,o=>a(o));let s={cmd:t,data:e,request_id:n};this.worker.postMessage(s,r);})}terminate(){this.worker.terminate(),this.listeners.clear(),this.persistentEvents.clear();}};var he=_("input-postmessage/main");function fe(i){let t=document.createElement("video");t.srcObject=i,t.muted=true,t.autoplay=true,t.playsInline=true,t.play().catch(c=>{he.warn("video.play() rejected:",c);});let e=t.requestVideoFrameCallback?.bind(t);if(!e)throw new Error("setupPostMessageInput: requestVideoFrameCallback not supported on this browser");let r=new MessageChannel,n=r.port1,a=r.port2,s=false,o=(c,p)=>{if(!s){try{let l=new VideoFrame(t,{timestamp:p.mediaTime*1e6});n.postMessage({frame:l},[l]);}catch(l){he.warn("VideoFrame() failed; skipping frame:",l);}e(o);}};return e(o),{port:a,transferList:[a],cleanup:()=>{s=true,n.close(),t.srcObject=null,t.pause();}}}function me(i){let t=i.getVideoTracks();if(t.length===0)throw new Error("setupMstpInput: input MediaStream has no video tracks");let e=t[0],r=self.MediaStreamTrackProcessor;if(!r)throw new Error("setupMstpInput: MediaStreamTrackProcessor not supported on this browser");let a=new r({track:e}).readable;return {readable:a,transferList:[a],cleanup:()=>{a.cancel().catch(()=>{});}}}function ge(i,t){switch(i){case "mstp":{let e=me(t);return {initFields:{inputReadable:e.readable},transferList:e.transferList,cleanup:e.cleanup}}case "rvfc-postmessage":{let e=fe(t);return {initFields:{inputPort:e.port},transferList:e.transferList,cleanup:e.cleanup}}}}var ft=_("bitmap-shuttle/main"),mt=30;function _e(i,t=mt){let e=document.createElement("canvas");e.width=i.w,e.height=i.h;let r=e.getContext("bitmaprenderer");if(!r)throw new Error("setupBitmapShuttleOutput: bitmaprenderer context not available");let a=e.captureStream(t).getVideoTracks();if(a.length===0)throw new Error("setupBitmapShuttleOutput: captureStream produced no video tracks");let s=a[0],o=new MessageChannel,u=o.port1,c=o.port2,p=false,l=null,h=()=>{},g=3,T=0;return u.onmessage=U=>{if(p){if(T++,T<g){U.data.bmp.close();return}h();}try{r.transferFromImageBitmap(U.data.bmp);}catch(m){ft.warn("transferFromImageBitmap failed:",m);}},u.start(),{videoTrack:s,port:c,transferList:[c],startPassthrough:U=>{if(p)return;p=true;let m=document.createElement("video");m.srcObject=U,m.muted=true,m.playsInline=true,m.play().catch(()=>{}),l=m;let S="requestVideoFrameCallback"in m?y=>{m.requestVideoFrameCallback(y);}:y=>{requestAnimationFrame(y);},E=()=>{if(p){if(m.videoWidth===0){S(E);return}createImageBitmap(m,{resizeWidth:e.width,resizeHeight:e.height,resizeQuality:"medium"}).then(y=>{if(!p){y.close();return}try{r.transferFromImageBitmap(y);}catch{}S(E);}).catch(()=>{S(E);});}};S(E),h=()=>{p=false,l&&(l.srcObject=null,l.pause(),l=null);};},cleanup:()=>{h(),u.onmessage=null,u.close(),s.stop();}}}function be(){let i=self.MediaStreamTrackGenerator;if(!i)throw new Error("setupMstgOutput: MediaStreamTrackGenerator not supported on this browser");let t=new i({kind:"video"}),e=t.writable;return {videoTrack:t,writable:e,transferList:[e],cleanup:()=>{e.abort().catch(()=>{}),t.stop();}}}function ve(i,t=30){let e=document.createElement("canvas");e.width=i.w,e.height=i.h;let n=e.captureStream(t).getVideoTracks();if(n.length===0)throw new Error("setupTransferCaptureOutput: captureStream produced no video tracks");let a=n[0],s=e.transferControlToOffscreen();return {videoTrack:a,canvas:s,transferList:[s],cleanup:()=>{a.stop();}}}function we(i,t){switch(i){case "mstg":{let e=be();return {videoTrack:e.videoTrack,initFields:{outputWritable:e.writable},transferList:e.transferList,cleanup:e.cleanup}}case "transfer-capture":{let e=ve(t);return {videoTrack:e.videoTrack,initFields:{outputCanvas:e.canvas},transferList:e.transferList,cleanup:e.cleanup}}case "bitmap-shuttle":{let e=_e(t);return {videoTrack:e.videoTrack,initFields:{outputPort:e.port},transferList:e.transferList,startPassthrough:e.startPassthrough,cleanup:e.cleanup}}}}var re=`var jt=Object.defineProperty;var Kt=(s,e,r)=>e in s?jt(s,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):s[e]=r;var n=(s,e,r)=>Kt(s,typeof e!="symbol"?e+"":e,r);var Ee=!1;function Ae(s){Ee=s}function P(s){let e=\`[longpipe/\${s}]\`,r=((...t)=>{Ee&&console.log(e,...t)});return r.warn=(...t)=>{Ee&&console.warn(e,...t)},r}var Ie=[{model:"xxs",dtype:"f16",resolution:{w:128,h:72},skipFrames:3},{model:"xs",dtype:"f16",resolution:{w:192,h:108},skipFrames:1},{model:"small",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"compact",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"medium",dtype:"f16",resolution:{w:256,h:144},skipFrames:1},{model:"large",dtype:"f32",resolution:{w:256,h:144},skipFrames:0},{model:"xl",dtype:"f32",resolution:{w:512,h:288},skipFrames:0}],Yt={fast:1,balanced:4,quality:6};function V(s){return s==="auto"?null:Ie[Yt[s]]}var N=class{constructor(e,r,t,o){n(this,"op");this.op=e.ops.BilinearUpsample(r,{outH:t,outW:o})}get output(){return this.op.output}run(){this.op.run()}};var z=class{constructor(e,r,t,o){n(this,"op");this.op=e.ops.BicubicUpsample(r,{outH:t,outW:o})}get output(){return this.op.output}run(){this.op.run()}};var X=class{constructor(e,r,t,o){n(this,"presenter");this.presenter=e.presenters.CompositeSolid(r,t,o)}run(){this.presenter.run()}};var $=class{constructor(e,r,t,o){n(this,"presenter");this.presenter=e.presenters.CompositeImage(r,t,o)}run(){this.presenter.run()}};var q=class{constructor(e,r,t){n(this,"downs",[]);n(this,"ups",[]);let o=Qt(t),i=r;for(let a=0;a<o;a++){let u=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(u),i=u.output}for(let a=o-1;a>=1;a--){let u=this.downs[a-1].output,p=e.ops.BilinearUpsample(i,{outH:u.h,outW:u.w});this.ups.push(p),i=p.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 Qt(s){let e=Math.max(1,3*s);return Math.max(2,Math.min(6,Math.round(Math.log2(e))))}var j=class{constructor(e,r,t,o){n(this,"blur");n(this,"presenter");this.blur=new q(e,r,o),this.presenter=e.presenters.CompositeImageBilinear(r,t,this.blur.output)}run(){this.blur.run(),this.presenter.run()}};var K=class{constructor(e){n(this,"backend",e);n(this,"displayInput");n(this,"image");n(this,"passthroughCompositor");n(this,"network",null);n(this,"networkInput",null);n(this,"upscaler",null);n(this,"compositor",null);n(this,"upscalerMode","bilinear");n(this,"bgConfig",{mode:"solid",color:[0,0,0]});let r=e.canvas;this.displayInput=e.ops.Input(r.height,r.width),this.image=this.displayInput.output,this.passthroughCompositor=e.presenters.CompositePassthrough(this.image)}attachNetwork(e,r,t){this.network=e,this.networkInput=r,this.upscalerMode=t.upscaler,this.bgConfig=t.background,this.upscaler=this.makeUpscaler(),this.compositor=this.makeCompositor()}hasNetwork(){return this.network!==null}setSource(e){this.networkInput?.setSource(e),this.displayInput.setSource(e)}setUpscaler(e){e!==this.upscalerMode&&(this.upscalerMode=e,this.network&&(this.upscaler=this.makeUpscaler(),this.compositor=this.makeCompositor()))}setBackground(e){this.bgConfig=e,this.network&&(this.compositor=this.makeCompositor())}run(){this.runModel(),this.runDisplay()}runDisplay(){if(!this.compositor)throw new Error("RenderOp.runDisplay called before attachNetwork");this.displayInput.run(),this.compositor.run()}runPassthrough(){this.displayInput.run(),this.passthroughCompositor.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:r,image:t}=this;return this.upscalerMode==="bicubic"?new z(e,r.output,t.h,t.w):new N(e,r.output,t.h,t.w)}makeCompositor(){if(!this.upscaler)throw new Error("makeCompositor called before upscaler exists");let{backend:e,image:r,upscaler:t,bgConfig:o}=this;switch(o.mode){case"solid":return new X(e,r,t.output,o.color);case"image":return new $(e,r,t.output,o.image);case"blur":return new j(e,r,t.output,o.sigma)}}};var Jt=s=>typeof s=="object"&&s!==null&&typeof s.offset=="number"&&typeof s.length=="number";function Fe(s){if(s.byteLength<4)throw new Error("weights buffer too small");let e=new DataView(s,0,4).getUint32(0,!0),r=4+e;if(s.byteLength<r)throw new Error("weights buffer truncated (header)");let t=new Uint8Array(s,4,e),o=JSON.parse(new TextDecoder().decode(t)),i=o.__dtype__??"f32",a=i==="f16"?2:4;if(r%a!==0)throw new Error(\`payload not \${a}-byte aligned (header=\${e})\`);let u=(s.byteLength-r)/a,p=l=>{if(Jt(l)){let{offset:d,length:m}=l;if(d+m>u)throw new Error(\`array ref out of range: offset=\${d} length=\${m}\`);let x=r+d*a;return i==="f16"?new Uint16Array(s,x,m):new Float32Array(s,x,m)}if(Array.isArray(l))return l.map(p);if(typeof l=="object"&&l!==null){let d={};for(let m of Object.keys(l))m!=="__dtype__"&&(d[m]=p(l[m]));return d}return l};return p(o)}var C=class{constructor(e,r,t,o){n(this,"inputs");n(this,"output");n(this,"dwOp");n(this,"pwOp");this.inputs=[r],this.dwOp=e.ops.DepthwiseConv2d(r,t.dw,{kernel:o.kernel,stride:o.stride,padding:o.padding,activation:"relu6"}),this.pwOp=e.ops.Conv2d(this.dwOp.output,t.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 c=class{constructor(e,r,t,o){n(this,"inputs");n(this,"output");n(this,"expandOp");n(this,"dwOp");n(this,"projOp");n(this,"addOp");this.inputs=[r];let i=o.midChannels!==o.inChannels,a=o.stride===1&&o.inChannels===o.outChannels;this.expandOp=i?e.ops.Conv2d(r,t.expand,{outChannels:o.midChannels,kernel:1,stride:1,padding:0,activation:"relu6"}):null;let u=this.expandOp?this.expandOp.output:r;this.dwOp=e.ops.DepthwiseConv2d(u,t.dw,{kernel:o.kernel,stride:o.stride,padding:o.padding,activation:"relu6"}),this.projOp=e.ops.Conv2d(this.dwOp.output,t.proj,{outChannels:o.outChannels,kernel:1,stride:1,padding:0,activation:"none"}),this.addOp=a?e.ops.Add(r,this.projOp.output):null,this.output=this.addOp?this.addOp.output:this.projOp.output}run(){this.expandOp?.run(),this.dwOp.run(),this.projOp.run(),this.addOp?.run()}};var y=class{constructor(e,r,t,o,i){n(this,"inputs");n(this,"output");n(this,"upConcatOp");n(this,"conv1Op");n(this,"conv2Op");this.inputs=[r,t],this.upConcatOp=e.ops.UpsampleConcat(r,t,{outH:t.h,outW:t.w}),this.conv1Op=e.ops.Conv2d(this.upConcatOp.output,o.conv1,{outChannels:i.outChannels,kernel:3,stride:1,padding:1,activation:"relu6"}),this.conv2Op=e.ops.Conv2d(this.conv1Op.output,o.conv2,{outChannels:i.outChannels,kernel:3,stride:1,padding:1,activation:"relu6"}),this.output=this.conv2Op.output}run(){this.upConcatOp.run(),this.conv1Op.run(),this.conv2Op.run()}};var W=class{constructor(e,r,t){n(this,"output");n(this,"stem");n(this,"s0");n(this,"s1b0");n(this,"s1b1");n(this,"s2b0");n(this,"s2b1");n(this,"s3b0");n(this,"s3b1");n(this,"s3b2");n(this,"s4b0");n(this,"s4b1");n(this,"s4b2");n(this,"s5b0");n(this,"s5b1");n(this,"s5b2");n(this,"s5b3");n(this,"s6b0");n(this,"bottleneck");n(this,"dec0");n(this,"dec1");n(this,"dec2");n(this,"finalUp");n(this,"outConv");n(this,"alpha");this.stem=e.ops.Conv2d(r,t.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,t.encoder.s0,{outChannels:16,kernel:3,stride:1,padding:1}),this.s1b0=new c(e,this.s0.output,t.encoder.s1[0],{inChannels:16,midChannels:96,outChannels:24,kernel:3,stride:2,padding:"same"}),this.s1b1=new c(e,this.s1b0.output,t.encoder.s1[1],{inChannels:24,midChannels:144,outChannels:24,kernel:3,stride:1,padding:1}),this.s2b0=new c(e,this.s1b1.output,t.encoder.s2[0],{inChannels:24,midChannels:144,outChannels:40,kernel:5,stride:2,padding:"same"}),this.s2b1=new c(e,this.s2b0.output,t.encoder.s2[1],{inChannels:40,midChannels:240,outChannels:40,kernel:5,stride:1,padding:2}),this.s3b0=new c(e,this.s2b1.output,t.encoder.s3[0],{inChannels:40,midChannels:240,outChannels:80,kernel:3,stride:2,padding:"same"}),this.s3b1=new c(e,this.s3b0.output,t.encoder.s3[1],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s3b2=new c(e,this.s3b1.output,t.encoder.s3[2],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s4b0=new c(e,this.s3b2.output,t.encoder.s4[0],{inChannels:80,midChannels:480,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b1=new c(e,this.s4b0.output,t.encoder.s4[1],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b2=new c(e,this.s4b1.output,t.encoder.s4[2],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s5b0=new c(e,this.s4b2.output,t.encoder.s5[0],{inChannels:112,midChannels:672,outChannels:192,kernel:5,stride:2,padding:"same"}),this.s5b1=new c(e,this.s5b0.output,t.encoder.s5[1],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b2=new c(e,this.s5b1.output,t.encoder.s5[2],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b3=new c(e,this.s5b2.output,t.encoder.s5[3],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s6b0=new c(e,this.s5b3.output,t.encoder.s6[0],{inChannels:192,midChannels:1152,outChannels:320,kernel:3,stride:1,padding:1}),this.bottleneck=e.ops.Conv2d(this.s6b0.output,t.bottleneck,{outChannels:128,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,this.s4b2.output,t.decoder.blocks[0],{outChannels:64}),this.dec1=new y(e,this.dec0.output,this.s2b1.output,t.decoder.blocks[1],{outChannels:32}),this.dec2=new y(e,this.dec1.output,this.s1b1.output,t.decoder.blocks[2],{outChannels:16}),this.finalUp=e.ops.UpsampleConv1x1(this.dec2.output,t.decoder.finalUpsample,{outH:this.dec2.output.h*2,outW:this.dec2.output.w*2,outChannels:16,activation:"relu6"}),this.outConv=e.ops.Conv2d(this.finalUp.output,t.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 E=class{constructor(e,r,t){n(this,"output");n(this,"stem");n(this,"s0");n(this,"s1b0");n(this,"s1b1");n(this,"s2b0");n(this,"s2b1");n(this,"s3b0");n(this,"s3b1");n(this,"s3b2");n(this,"s4b0");n(this,"s4b1");n(this,"s4b2");n(this,"s5b0");n(this,"s5b1");n(this,"s5b2");n(this,"s5b3");n(this,"s6b0");n(this,"bottleneck");n(this,"dec0");n(this,"dec1");n(this,"dec2");n(this,"finalUp");n(this,"outConv");n(this,"alpha");this.stem=e.ops.Conv2d(r,t.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,t.encoder.s0,{outChannels:16,kernel:3,stride:1,padding:1}),this.s1b0=new c(e,this.s0.output,t.encoder.s1[0],{inChannels:16,midChannels:96,outChannels:24,kernel:3,stride:2,padding:"same"}),this.s1b1=new c(e,this.s1b0.output,t.encoder.s1[1],{inChannels:24,midChannels:144,outChannels:24,kernel:3,stride:1,padding:1}),this.s2b0=new c(e,this.s1b1.output,t.encoder.s2[0],{inChannels:24,midChannels:144,outChannels:40,kernel:5,stride:2,padding:"same"}),this.s2b1=new c(e,this.s2b0.output,t.encoder.s2[1],{inChannels:40,midChannels:240,outChannels:40,kernel:5,stride:1,padding:2}),this.s3b0=new c(e,this.s2b1.output,t.encoder.s3[0],{inChannels:40,midChannels:240,outChannels:80,kernel:3,stride:2,padding:"same"}),this.s3b1=new c(e,this.s3b0.output,t.encoder.s3[1],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s3b2=new c(e,this.s3b1.output,t.encoder.s3[2],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s4b0=new c(e,this.s3b2.output,t.encoder.s4[0],{inChannels:80,midChannels:480,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b1=new c(e,this.s4b0.output,t.encoder.s4[1],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b2=new c(e,this.s4b1.output,t.encoder.s4[2],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s5b0=new c(e,this.s4b2.output,t.encoder.s5[0],{inChannels:112,midChannels:672,outChannels:192,kernel:5,stride:2,padding:"same"}),this.s5b1=new c(e,this.s5b0.output,t.encoder.s5[1],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b2=new c(e,this.s5b1.output,t.encoder.s5[2],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b3=new c(e,this.s5b2.output,t.encoder.s5[3],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s6b0=new c(e,this.s5b3.output,t.encoder.s6[0],{inChannels:192,midChannels:1152,outChannels:320,kernel:3,stride:1,padding:1}),this.bottleneck=e.ops.Conv2d(this.s6b0.output,t.bottleneck,{outChannels:64,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,this.s4b2.output,t.decoder.blocks[0],{outChannels:32}),this.dec1=new y(e,this.dec0.output,this.s2b1.output,t.decoder.blocks[1],{outChannels:16}),this.dec2=new y(e,this.dec1.output,this.s1b1.output,t.decoder.blocks[2],{outChannels:8}),this.finalUp=e.ops.UpsampleConv1x1(this.dec2.output,t.decoder.finalUpsample,{outH:this.dec2.output.h*2,outW:this.dec2.output.w*2,outChannels:8,activation:"relu6"}),this.outConv=e.ops.Conv2d(this.finalUp.output,t.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 k=class{constructor(e,r,t){n(this,"output");n(this,"stem");n(this,"s0");n(this,"s1b0");n(this,"s1b1");n(this,"s2b0");n(this,"s2b1");n(this,"s3b0");n(this,"s3b1");n(this,"s3b2");n(this,"s4b0");n(this,"s4b1");n(this,"s4b2");n(this,"bottleneck");n(this,"dec0");n(this,"dec1");n(this,"finalUp");n(this,"outConv");n(this,"alpha");this.stem=e.ops.Conv2d(r,t.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,t.encoder.s0,{outChannels:16,kernel:3,stride:1,padding:1}),this.s1b0=new c(e,this.s0.output,t.encoder.s1[0],{inChannels:16,midChannels:96,outChannels:24,kernel:3,stride:2,padding:"same"}),this.s1b1=new c(e,this.s1b0.output,t.encoder.s1[1],{inChannels:24,midChannels:144,outChannels:24,kernel:3,stride:1,padding:1}),this.s2b0=new c(e,this.s1b1.output,t.encoder.s2[0],{inChannels:24,midChannels:144,outChannels:40,kernel:5,stride:2,padding:"same"}),this.s2b1=new c(e,this.s2b0.output,t.encoder.s2[1],{inChannels:40,midChannels:240,outChannels:40,kernel:5,stride:1,padding:2}),this.s3b0=new c(e,this.s2b1.output,t.encoder.s3[0],{inChannels:40,midChannels:240,outChannels:80,kernel:3,stride:2,padding:"same"}),this.s3b1=new c(e,this.s3b0.output,t.encoder.s3[1],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s3b2=new c(e,this.s3b1.output,t.encoder.s3[2],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s4b0=new c(e,this.s3b2.output,t.encoder.s4[0],{inChannels:80,midChannels:480,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b1=new c(e,this.s4b0.output,t.encoder.s4[1],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b2=new c(e,this.s4b1.output,t.encoder.s4[2],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.bottleneck=e.ops.Conv2d(this.s4b2.output,t.bottleneck,{outChannels:128,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,this.s2b1.output,t.decoder.blocks[0],{outChannels:64}),this.dec1=new y(e,this.dec0.output,this.s1b1.output,t.decoder.blocks[1],{outChannels:32}),this.finalUp=e.ops.UpsampleConv1x1(this.dec1.output,t.decoder.finalUpsample,{outH:this.dec1.output.h*2,outW:this.dec1.output.w*2,outChannels:32,activation:"relu6"}),this.outConv=e.ops.Conv2d(this.finalUp.output,t.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 I=class{constructor(e,r,t){n(this,"output");n(this,"stem");n(this,"s0");n(this,"s1b0");n(this,"s1b1");n(this,"s2b0");n(this,"s2b1");n(this,"s3b0");n(this,"s3b1");n(this,"s3b2");n(this,"s4b0");n(this,"s4b1");n(this,"s4b2");n(this,"s5b0");n(this,"s5b1");n(this,"s5b2");n(this,"s5b3");n(this,"s6b0");n(this,"bottleneck");n(this,"dec0");n(this,"dec1");n(this,"dec2");n(this,"finalUp");n(this,"outConv");n(this,"alpha");this.stem=e.ops.Conv2d(r,t.encoder.stem,{outChannels:32,kernel:3,stride:2,padding:"same",activation:"relu6"}),this.s0=new C(e,this.stem.output,t.encoder.s0,{outChannels:16,kernel:3,stride:1,padding:1}),this.s1b0=new c(e,this.s0.output,t.encoder.s1[0],{inChannels:16,midChannels:96,outChannels:24,kernel:3,stride:2,padding:"same"}),this.s1b1=new c(e,this.s1b0.output,t.encoder.s1[1],{inChannels:24,midChannels:144,outChannels:24,kernel:3,stride:1,padding:1}),this.s2b0=new c(e,this.s1b1.output,t.encoder.s2[0],{inChannels:24,midChannels:144,outChannels:40,kernel:5,stride:2,padding:"same"}),this.s2b1=new c(e,this.s2b0.output,t.encoder.s2[1],{inChannels:40,midChannels:240,outChannels:40,kernel:5,stride:1,padding:2}),this.s3b0=new c(e,this.s2b1.output,t.encoder.s3[0],{inChannels:40,midChannels:240,outChannels:80,kernel:3,stride:2,padding:"same"}),this.s3b1=new c(e,this.s3b0.output,t.encoder.s3[1],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s3b2=new c(e,this.s3b1.output,t.encoder.s3[2],{inChannels:80,midChannels:480,outChannels:80,kernel:3,stride:1,padding:1}),this.s4b0=new c(e,this.s3b2.output,t.encoder.s4[0],{inChannels:80,midChannels:480,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b1=new c(e,this.s4b0.output,t.encoder.s4[1],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s4b2=new c(e,this.s4b1.output,t.encoder.s4[2],{inChannels:112,midChannels:672,outChannels:112,kernel:5,stride:1,padding:2}),this.s5b0=new c(e,this.s4b2.output,t.encoder.s5[0],{inChannels:112,midChannels:672,outChannels:192,kernel:5,stride:2,padding:"same"}),this.s5b1=new c(e,this.s5b0.output,t.encoder.s5[1],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b2=new c(e,this.s5b1.output,t.encoder.s5[2],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s5b3=new c(e,this.s5b2.output,t.encoder.s5[3],{inChannels:192,midChannels:1152,outChannels:192,kernel:5,stride:1,padding:2}),this.s6b0=new c(e,this.s5b3.output,t.encoder.s6[0],{inChannels:192,midChannels:1152,outChannels:320,kernel:3,stride:1,padding:1}),this.bottleneck=e.ops.Conv2d(this.s6b0.output,t.bottleneck,{outChannels:256,kernel:1,stride:1,padding:0,activation:"relu6"}),this.dec0=new y(e,this.bottleneck.output,this.s4b2.output,t.decoder.blocks[0],{outChannels:128}),this.dec1=new y(e,this.dec0.output,this.s2b1.output,t.decoder.blocks[1],{outChannels:64}),this.dec2=new y(e,this.dec1.output,this.s1b1.output,t.decoder.blocks[2],{outChannels:32}),this.finalUp=e.ops.UpsampleConv1x1(this.dec2.output,t.decoder.finalUpsample,{outH:this.dec2.output.h*2,outW:this.dec2.output.w*2,outChannels:32,activation:"relu6"}),this.outConv=e.ops.Conv2d(this.finalUp.output,t.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 Zt=5,er={xxs:k,xs:k,small:k,compact:E,medium:W,large:W,xl:I},Oe=1e3,Y=class{constructor(e){n(this,"canvas");n(this,"backend");n(this,"backendKind");n(this,"topology");n(this,"preset",null);n(this,"enabled");n(this,"currentBackground");n(this,"modelTimingCounter",0);n(this,"renderOp");n(this,"bgImageInput",null);n(this,"bgVideoInput",null);n(this,"bgVideoPort",null);n(this,"skipCounter",0);n(this,"framesRenderedAt",[]);n(this,"modelRunSamples",[]);n(this,"displayRunSamples",[]);n(this,"skippedCount",0);this.backend=e.backend,this.backendKind=e.backendKind,this.canvas=e.canvas,this.enabled=e.enabled,this.currentBackground=e.background,this.topology=e.topology,this.renderOp=new K(this.backend)}process(e){if(this.renderOp.setSource(e),!this.enabled||this.currentBackground.kind==="none"||!this.renderOp.hasNetwork()){this.renderOp.runPassthrough(),this.framesRenderedAt.push(performance.now()),this.trim(this.framesRenderedAt);return}if(this.shouldRunModel()){let t=performance.now();this.renderOp.runModel(),this.backendKind==="webgpu"&&this.modelTimingCounter%Zt===0&&this.backend.sync().then(()=>{this.modelRunSamples.push({ts:performance.now(),ms:performance.now()-t}),this.trimSamples(this.modelRunSamples)}).catch(()=>{}),this.modelTimingCounter++}else this.skippedCount++;let r=performance.now();this.renderOp.runDisplay(),this.displayRunSamples.push({ts:performance.now(),ms:performance.now()-r}),this.trimSamples(this.displayRunSamples),this.framesRenderedAt.push(performance.now()),this.trim(this.framesRenderedAt)}shouldRunModel(){return this.skipCounter===0?(this.skipCounter=this.preset?.skipFrames??0,!0):(this.skipCounter--,!1)}setBackground(e){this.currentBackground=e,this.renderOp.setBackground(this.translateBackground(e))}setEnabled(e){this.enabled=e}setPreset(e,r){let t=er[e.model];if(!t)throw new Error(\`renderer: model '\${e.model}' not implemented in src/model/networks/\`);let o=Fe(r),i=this.backend.ops.Input(e.resolution.h,e.resolution.w),a=new t(this.backend,i.output,o);this.renderOp.attachNetwork(a,i,{upscaler:"bilinear",background:this.translateBackground(this.currentBackground)}),this.preset=e,this.skipCounter=0}destroy(){this.bgVideoPort&&(this.bgVideoPort.close(),this.bgVideoPort=null),this.backend.destroy()}getStats(){let e=performance.now();return this.trimRecent(this.framesRenderedAt,e),this.trimSamplesAt(this.modelRunSamples,e),this.trimSamplesAt(this.displayRunSamples,e),{fps:this.framesRenderedAt.length,modelFps:this.modelRunSamples.length,modelMs:Me(this.modelRunSamples.map(r=>r.ms)),displayMs:Me(this.displayRunSamples.map(r=>r.ms)),skipped:this.skippedCount,preset:this.preset?.model??"boot",skipFrames:this.preset?.skipFrames??0,enabled:this.enabled,inputPath:this.topology.input,outputPath:this.topology.output}}translateBackground(e){switch(e.kind!=="video"&&this.bgVideoPort&&(this.bgVideoPort.close(),this.bgVideoPort=null),e.kind){case"none":return{mode:"solid",color:[0,0,0]};case"color":return{mode:"solid",color:e.rgb};case"blur":return{mode:"blur",sigma:e.sigma};case"image":return this.bgImageInput||(this.bgImageInput=this.backend.ops.Input(this.canvas.height,this.canvas.width)),this.bgImageInput.setSource(e.bitmap),this.bgImageInput.run(),{mode:"image",image:this.bgImageInput.output};case"video":{this.bgVideoInput||(this.bgVideoInput=this.backend.ops.Input(this.canvas.height,this.canvas.width)),this.bgVideoPort&&this.bgVideoPort.close(),this.bgVideoPort=e.port;let r=this.bgVideoInput;return e.port.onmessage=t=>{let o=t.data.frame;try{r.setSource(o),r.run()}finally{o.close()}},e.port.start?.(),{mode:"image",image:this.bgVideoInput.output}}}}trim(e){e.length>240&&e.splice(0,e.length-240)}trimSamples(e){e.length>240&&e.splice(0,e.length-240)}trimRecent(e,r){let t=r-Oe,o=0;for(;o<e.length&&e[o]<t;)o++;o>0&&e.splice(0,o)}trimSamplesAt(e,r){let t=r-Oe,o=0;for(;o<e.length&&e[o].ts<t;)o++;o>0&&e.splice(0,o)}};function Me(s){if(s.length===0)return 0;let e=s.slice().sort((t,o)=>t-o),r=e.length>>1;return e.length%2?e[r]:(e[r-1]+e[r])/2}var L=new Float32Array(1),D=new Uint32Array(L.buffer);function tr(s){L[0]=s;let e=D[0],r=e>>>16&32768,t=e&2147483647,o=(t>>>23)-127+15;if((e&2139095040)===2139095040)return(e&8388607)!==0?r|32256:r|31744;if(o>=31)return r|31744;if(o<=0){if(o<-10)return r;let l=t&8388607|8388608,d=14-o,m=l>>>d,x=l>>>d-1&1,T=(l&(1<<d-1)-1)!==0?1:0;return x&&(T||m&1)&&(m+=1),r|m}let i=t>>>13&1023,a=t>>>12&1,u=(t&4095)!==0?1:0,p=o<<10|i;return a&&(u||i&1)&&(p+=1,(p>>>10&31)===31)?r|31744:r|p}function rr(s){let e=(s&32768)<<16,r=(s&31744)>>>10,t=s&1023;if(r===0){if(t===0)return D[0]=e,L[0];let o=t,i=1;for(;(o&1024)===0;)o<<=1,i-=1;return o&=1023,D[0]=e|i+127-15<<23|o<<13,L[0]}return r===31?(D[0]=e|2139095040|t<<13,L[0]):(D[0]=e|r+127-15<<23|t<<13,L[0])}function Q(s){let e=new Uint16Array(s.length);for(let r=0;r<s.length;r++)e[r]=tr(s[r]);return e}function A(s){let e=new Float32Array(s.length);for(let r=0;r<s.length;r++)e[r]=rr(s[r]);return e}var h=class{constructor(e){n(this,"backend",e);n(this,"shader","");n(this,"pipeline");n(this,"bindGroup");n(this,"uniformDefs",[]);n(this,"uniformBuffers",{})}createUniform(e,r){this.uniformDefs.push({name:e,type:r})}setUniform(e,r){let t=this.backend.device.createBuffer({size:r.byteLength,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});r instanceof Uint32Array?new Uint32Array(t.getMappedRange()).set(r):new Float32Array(t.getMappedRange()).set(r),t.unmap(),this.uniformBuffers[e]=t}defaultBindGroup(){let e=[],r=0;for(let t of this.inputs)e.push({binding:r++,resource:{buffer:t.buffer}});for(let t of this.weights)e.push({binding:r++,resource:{buffer:t.buffer}});for(let t of this.uniformDefs)e.push({binding:r++,resource:{buffer:this.uniformBuffers[t.name]}});return e.push({binding:r,resource:{buffer:this.output.buffer}}),this.backend.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:e})}defaultSetup(){let e=this.backend.device.createShaderModule({code:this.shader});this.pipeline=this.backend.device.createComputePipeline({layout:"auto",compute:{module:e,entryPoint:"main"}}),this.bindGroup=this.defaultBindGroup()}run(){let e=this.backend.device.createCommandEncoder(),r=e.beginComputePass();r.setPipeline(this.pipeline),r.setBindGroup(0,this.bindGroup),r.dispatchWorkgroups(...this.dispatch),r.end(),this.backend.device.queue.submit([e.finish()])}};var Re=\`// Conv2d compute shader \\u2014 handles all variants:
2
+ // 1\\xD71 (pointwise), 3\\xD73, 5\\xD75, strided, BN-fused
3
+ //
4
+ // Tensor layout: NHWC with channels in vec4 groups
5
+ // buffer index = y * out_w * out_groups + x * out_groups + o
6
+ //
7
+ // Weight layout: [K*K][out_groups][in_groups] array of mat4x4
8
+ // weight index = (ky*kernel_w + kx) * out_groups * in_groups + o * in_groups + i
9
+ // mat4x4[col][row] = weight(in_channel=col, out_channel=row)
10
+ // so (mat4x4 * vec4)[r] = sum_c(w(in=c, out=r) * input[c]) \\u2014 correct matmul direction
11
+ //
12
+ // Padding model: only \\\`pad_top\\\` and \\\`pad_left\\\` are applied to the input offset.
13
+ // Asymmetric SAME padding (top<bottom or left<right) is handled implicitly:
14
+ // out-of-bounds reads on the right/bottom side are skipped via the bounds
15
+ // check, so the runner only needs to specify the top/left pad.
16
+
17
+ struct Params {
18
+ in_h : u32,
19
+ in_w : u32,
20
+ out_h : u32,
21
+ out_w : u32,
22
+ in_groups : u32, // in_channels / 4
23
+ out_groups : u32, // out_channels / 4
24
+ kernel_h : u32,
25
+ kernel_w : u32,
26
+ stride : u32,
27
+ pad_top : u32,
28
+ pad_left : u32,
29
+ activation : u32, // 0 = none, 1 = relu6, 2 = relu
30
+ }
31
+
32
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
33
+ @group(0) @binding(1) var<storage, read> weight_buf : array<mat4x4<f32>>;
34
+ @group(0) @binding(2) var<storage, read> bias_buf : array<vec4<f32>>;
35
+ @group(0) @binding(3) var<uniform> params : Params;
36
+ @group(0) @binding(4) var<storage, read_write> output_buf : array<vec4<f32>>;
37
+
38
+ @compute @workgroup_size(8, 8, 1)
39
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
40
+ let x = gid.x; // output column
41
+ let y = gid.y; // output row
42
+ let o = gid.z; // output channel group
43
+
44
+ if (x >= params.out_w || y >= params.out_h || o >= params.out_groups) {
45
+ return;
46
+ }
47
+
48
+ let I = params.in_groups;
49
+ let O = params.out_groups;
50
+
51
+ var result = bias_buf[o];
52
+
53
+ for (var ky = 0u; ky < params.kernel_h; ky++) {
54
+ for (var kx = 0u; kx < params.kernel_w; kx++) {
55
+ let in_y_s = i32(y * params.stride + ky) - i32(params.pad_top);
56
+ let in_x_s = i32(x * params.stride + kx) - i32(params.pad_left);
57
+
58
+ if (in_y_s < 0 || in_x_s < 0 ||
59
+ u32(in_y_s) >= params.in_h || u32(in_x_s) >= params.in_w) {
60
+ continue;
61
+ }
62
+
63
+ let in_y = u32(in_y_s);
64
+ let in_x = u32(in_x_s);
65
+ let z = ky * params.kernel_w + kx;
66
+
67
+ for (var i = 0u; i < I; i++) {
68
+ let in_idx = in_y * params.in_w * I + in_x * I + i;
69
+ let w_idx = z * O * I + o * I + i;
70
+ result += weight_buf[w_idx] * input_buf[in_idx];
71
+ }
72
+ }
73
+ }
74
+
75
+ if (params.activation == 1u) {
76
+ result = clamp(result, vec4<f32>(0.0), vec4<f32>(6.0));
77
+ } else if (params.activation == 2u) {
78
+ result = max(result, vec4<f32>(0.0));
79
+ }
80
+
81
+ let out_idx = y * params.out_w * O + x * O + o;
82
+ output_buf[out_idx] = result;
83
+ }
84
+ \`;var De=\`enable f16;
85
+
86
+ // Conv2d \\u2014 full f16 variant.
87
+ // All buffers (input, output, weights, bias) are f16. Weights are converted
88
+ // from f32\\u2192f16 in JavaScript before upload. mat4x4<f16> * vec4<f16> runs
89
+ // entirely in f16 on hardware that has native f16 ALUs.
90
+
91
+ struct Params {
92
+ in_h : u32,
93
+ in_w : u32,
94
+ out_h : u32,
95
+ out_w : u32,
96
+ in_groups : u32,
97
+ out_groups : u32,
98
+ kernel_h : u32,
99
+ kernel_w : u32,
100
+ stride : u32,
101
+ pad_top : u32,
102
+ pad_left : u32,
103
+ activation : u32,
104
+ }
105
+
106
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
107
+ @group(0) @binding(1) var<storage, read> weight_buf : array<mat4x4<f16>>;
108
+ @group(0) @binding(2) var<storage, read> bias_buf : array<vec4<f16>>;
109
+ @group(0) @binding(3) var<uniform> params : Params;
110
+ @group(0) @binding(4) var<storage, read_write> output_buf : array<vec4<f16>>;
111
+
112
+ @compute @workgroup_size(8, 8, 1)
113
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
114
+ let x = gid.x;
115
+ let y = gid.y;
116
+ let o = gid.z;
117
+
118
+ if (x >= params.out_w || y >= params.out_h || o >= params.out_groups) {
119
+ return;
120
+ }
121
+
122
+ let I = params.in_groups;
123
+ let O = params.out_groups;
124
+
125
+ var result = bias_buf[o];
126
+
127
+ for (var ky = 0u; ky < params.kernel_h; ky++) {
128
+ for (var kx = 0u; kx < params.kernel_w; kx++) {
129
+ let in_y_s = i32(y * params.stride + ky) - i32(params.pad_top);
130
+ let in_x_s = i32(x * params.stride + kx) - i32(params.pad_left);
131
+
132
+ if (in_y_s < 0 || in_x_s < 0 ||
133
+ u32(in_y_s) >= params.in_h || u32(in_x_s) >= params.in_w) {
134
+ continue;
135
+ }
136
+
137
+ let in_y = u32(in_y_s);
138
+ let in_x = u32(in_x_s);
139
+ let z = ky * params.kernel_w + kx;
140
+
141
+ for (var i = 0u; i < I; i++) {
142
+ let in_idx = in_y * params.in_w * I + in_x * I + i;
143
+ let w_idx = z * O * I + o * I + i;
144
+ result += weight_buf[w_idx] * input_buf[in_idx];
145
+ }
146
+ }
147
+ }
148
+
149
+ if (params.activation == 1u) {
150
+ result = clamp(result, vec4<f16>(0.0h), vec4<f16>(6.0h));
151
+ } else if (params.activation == 2u) {
152
+ result = max(result, vec4<f16>(0.0h));
153
+ }
154
+
155
+ output_buf[y * params.out_w * O + x * O + o] = result;
156
+ }
157
+ \`;function b(s,e,r,t){return typeof t=="number"?Math.floor((s+2*t-e)/r)+1:t==="same"?Math.ceil(s/r):Math.floor((s-e)/r)+1}function ir(s,e,r,t){return Math.floor(Math.max((e-1)*t+r-s,0)/2)}function v(s,e,r,t,o){return typeof s=="number"?s:s==="same"?ir(e,r,t,o):0}function f(s){return s instanceof Float32Array||s instanceof Uint16Array?s:new Float32Array(s)}var J=class extends h{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?De:Re;let a=b(t.h,i.kernel,i.stride,i.padding),u=b(t.w,i.kernel,i.stride,i.padding),p=t.c/4,l=i.outChannels/4,d=v(i.padding,t.h,a,i.kernel,i.stride),m=v(i.padding,t.w,u,i.kernel,i.stride);this.output=r.tensor(a,u,i.outChannels),this.inputs=[t],this.weights=[r.upload(f(o.weights)),r.upload(f(o.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,a,u,p,l,i.kernel,i.kernel,i.stride,d,m,i.activation==="relu6"?1:0])),this.defaultSetup(),this.dispatch=[Math.ceil(u/8),Math.ceil(a/8),l]}};var He=\`// Depthwise Conv2d \\u2014 groups = in_channels (each channel convolved independently).
158
+ //
159
+ // Weight layout: [K*K][channel_groups] array of vec4
160
+ // weight index = (ky*kernel_w + kx) * channel_groups + c
161
+ // Each vec4 holds the kernel weight for 4 consecutive channels at one spatial position.
162
+ // Operation: element-wise multiply (each input channel multiplied by its own weight).
163
+ //
164
+ // Contrast with conv2d.wgsl which uses mat4x4 (dense cross-channel mixing).
165
+ // 4\\xD7 smaller weight buffer than a diagonal mat4x4 representation (4 floats vs 16 per group).
166
+ //
167
+ // Padding model: only \\\`pad_top\\\` and \\\`pad_left\\\` are applied to the input offset.
168
+ // Asymmetric SAME padding is handled implicitly via the in_h/in_w bounds check.
169
+
170
+ struct Params {
171
+ in_h : u32,
172
+ in_w : u32,
173
+ out_h : u32,
174
+ out_w : u32,
175
+ channel_groups : u32, // channels / 4
176
+ kernel_h : u32,
177
+ kernel_w : u32,
178
+ stride : u32,
179
+ pad_top : u32,
180
+ pad_left : u32,
181
+ apply_relu6 : u32,
182
+ _pad0 : u32,
183
+ }
184
+
185
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
186
+ @group(0) @binding(1) var<storage, read> weight_buf : array<vec4<f32>>;
187
+ @group(0) @binding(2) var<storage, read> bias_buf : array<vec4<f32>>;
188
+ @group(0) @binding(3) var<uniform> params : Params;
189
+ @group(0) @binding(4) var<storage, read_write> output_buf : array<vec4<f32>>;
190
+
191
+ @compute @workgroup_size(8, 8, 1)
192
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
193
+ let x = gid.x; // output column
194
+ let y = gid.y; // output row
195
+ let c = gid.z; // channel group
196
+
197
+ if (x >= params.out_w || y >= params.out_h || c >= params.channel_groups) {
198
+ return;
199
+ }
200
+
201
+ let C = params.channel_groups;
202
+
203
+ var result = bias_buf[c];
204
+
205
+ for (var ky = 0u; ky < params.kernel_h; ky++) {
206
+ for (var kx = 0u; kx < params.kernel_w; kx++) {
207
+ let in_y_s = i32(y * params.stride + ky) - i32(params.pad_top);
208
+ let in_x_s = i32(x * params.stride + kx) - i32(params.pad_left);
209
+
210
+ if (in_y_s < 0 || in_x_s < 0 ||
211
+ u32(in_y_s) >= params.in_h || u32(in_x_s) >= params.in_w) {
212
+ continue;
213
+ }
214
+
215
+ let in_y = u32(in_y_s);
216
+ let in_x = u32(in_x_s);
217
+ let z = ky * params.kernel_w + kx;
218
+
219
+ let in_idx = in_y * params.in_w * C + in_x * C + c;
220
+ let w_idx = z * C + c;
221
+ result += weight_buf[w_idx] * input_buf[in_idx];
222
+ }
223
+ }
224
+
225
+ if (params.apply_relu6 == 1u) {
226
+ result = clamp(result, vec4<f32>(0.0), vec4<f32>(6.0));
227
+ }
228
+
229
+ let out_idx = y * params.out_w * C + x * C + c;
230
+ output_buf[out_idx] = result;
231
+ }
232
+ \`;var Ve=\`enable f16;
233
+
234
+ // Depthwise conv2d \\u2014 full f16 variant. All buffers f16.
235
+
236
+ struct Params {
237
+ in_h : u32,
238
+ in_w : u32,
239
+ out_h : u32,
240
+ out_w : u32,
241
+ channel_groups : u32,
242
+ kernel_h : u32,
243
+ kernel_w : u32,
244
+ stride : u32,
245
+ pad_top : u32,
246
+ pad_left : u32,
247
+ apply_relu6 : u32,
248
+ _pad0 : u32,
249
+ }
250
+
251
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
252
+ @group(0) @binding(1) var<storage, read> weight_buf : array<vec4<f16>>;
253
+ @group(0) @binding(2) var<storage, read> bias_buf : array<vec4<f16>>;
254
+ @group(0) @binding(3) var<uniform> params : Params;
255
+ @group(0) @binding(4) var<storage, read_write> output_buf : array<vec4<f16>>;
256
+
257
+ @compute @workgroup_size(8, 8, 1)
258
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
259
+ let x = gid.x;
260
+ let y = gid.y;
261
+ let c = gid.z;
262
+
263
+ if (x >= params.out_w || y >= params.out_h || c >= params.channel_groups) {
264
+ return;
265
+ }
266
+
267
+ let C = params.channel_groups;
268
+
269
+ var result = bias_buf[c];
270
+
271
+ for (var ky = 0u; ky < params.kernel_h; ky++) {
272
+ for (var kx = 0u; kx < params.kernel_w; kx++) {
273
+ let in_y_s = i32(y * params.stride + ky) - i32(params.pad_top);
274
+ let in_x_s = i32(x * params.stride + kx) - i32(params.pad_left);
275
+
276
+ if (in_y_s < 0 || in_x_s < 0 ||
277
+ u32(in_y_s) >= params.in_h || u32(in_x_s) >= params.in_w) {
278
+ continue;
279
+ }
280
+
281
+ let in_y = u32(in_y_s);
282
+ let in_x = u32(in_x_s);
283
+ let z = ky * params.kernel_w + kx;
284
+
285
+ let in_idx = in_y * params.in_w * C + in_x * C + c;
286
+ let w_idx = z * C + c;
287
+ result += weight_buf[w_idx] * input_buf[in_idx];
288
+ }
289
+ }
290
+
291
+ if (params.apply_relu6 == 1u) {
292
+ result = clamp(result, vec4<f16>(0.0h), vec4<f16>(6.0h));
293
+ }
294
+
295
+ output_buf[y * params.out_w * C + x * C + c] = result;
296
+ }
297
+ \`;var Z=class extends h{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?Ve:He;let a=b(t.h,i.kernel,i.stride,i.padding),u=b(t.w,i.kernel,i.stride,i.padding),p=t.c/4,l=v(i.padding,t.h,a,i.kernel,i.stride),d=v(i.padding,t.w,u,i.kernel,i.stride);this.output=r.tensor(a,u,t.c),this.inputs=[t],this.weights=[r.upload(f(o.weights)),r.upload(f(o.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,a,u,p,i.kernel,i.kernel,i.stride,l,d,i.activation==="relu6"?1:0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(u/8),Math.ceil(a/8),p]}};var Ne=\`// Element-wise add of two tensors \\u2014 used for residual connections in MBConv blocks.
298
+ // Operates on the flat float buffer directly; layout is irrelevant for a pure element-wise op.
299
+
300
+ struct Params {
301
+ size : u32, // total number of f32 elements
302
+ _pad0 : u32,
303
+ _pad1 : u32,
304
+ _pad2 : u32,
305
+ }
306
+
307
+ @group(0) @binding(0) var<storage, read> input_a : array<f32>;
308
+ @group(0) @binding(1) var<storage, read> input_b : array<f32>;
309
+ @group(0) @binding(2) var<uniform> params : Params;
310
+ @group(0) @binding(3) var<storage, read_write> output : array<f32>;
311
+
312
+ @compute @workgroup_size(256)
313
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
314
+ let idx = gid.x;
315
+ if (idx >= params.size) { return; }
316
+ output[idx] = input_a[idx] + input_b[idx];
317
+ }
318
+ \`;var ze=\`enable f16;
319
+
320
+ // Element-wise add \\u2014 full f16 variant.
321
+ // array<f16> is binary-compatible with array<vec4<f16>> written by the f16 conv shaders.
322
+
323
+ struct Params {
324
+ size : u32,
325
+ _pad0 : u32,
326
+ _pad1 : u32,
327
+ _pad2 : u32,
328
+ }
329
+
330
+ @group(0) @binding(0) var<storage, read> input_a : array<f16>;
331
+ @group(0) @binding(1) var<storage, read> input_b : array<f16>;
332
+ @group(0) @binding(2) var<uniform> params : Params;
333
+ @group(0) @binding(3) var<storage, read_write> output : array<f16>;
334
+
335
+ @compute @workgroup_size(256)
336
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
337
+ let idx = gid.x;
338
+ if (idx >= params.size) { return; }
339
+ output[idx] = input_a[idx] + input_b[idx];
340
+ }
341
+ \`;var ee=class extends h{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?ze:Ne;let i=t.h*t.w*t.c;this.output=r.tensor(t.h,t.w,t.c),this.inputs=[t,o],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([i,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(i/256),1,1]}};var Xe=\`// Element-wise sigmoid: output = 1 / (1 + exp(-x)).
342
+ // Operates on packed vec4 buffers (NHWC layout). exp() and arithmetic are element-wise on vec4.
343
+
344
+ struct Params {
345
+ n_groups : u32, // total vec4 elements (H * W * channel_groups)
346
+ _pad0 : u32,
347
+ _pad1 : u32,
348
+ _pad2 : u32,
349
+ }
350
+
351
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
352
+ @group(0) @binding(1) var<uniform> params : Params;
353
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f32>>;
354
+
355
+ @compute @workgroup_size(256)
356
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
357
+ let idx = gid.x;
358
+ if (idx >= params.n_groups) { return; }
359
+ output_buf[idx] = 1.0 / (1.0 + exp(-input_buf[idx]));
360
+ }
361
+ \`;var $e=\`enable f16;
362
+
363
+ // Sigmoid \\u2014 full f16 variant.
364
+
365
+ struct Params {
366
+ n_groups : u32,
367
+ _pad0 : u32,
368
+ _pad1 : u32,
369
+ _pad2 : u32,
370
+ }
371
+
372
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
373
+ @group(0) @binding(1) var<uniform> params : Params;
374
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f16>>;
375
+
376
+ @compute @workgroup_size(256)
377
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
378
+ let idx = gid.x;
379
+ if (idx >= params.n_groups) { return; }
380
+ let x = vec4<f32>(input_buf[idx]);
381
+ output_buf[idx] = vec4<f16>(1.0 / (1.0 + exp(-x)));
382
+ }
383
+ \`;var te=class extends h{constructor(r,t){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?$e:Xe;let o=t.h*t.w*(t.c/4);this.output=r.tensor(t.h,t.w,t.c),this.inputs=[t],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([o,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(o/256),1,1]}};var qe=\`// Bilinear upsample (arbitrary ratio), align_corners=False (matches PyTorch default).
384
+ // Input/output in NHWC vec4 format: index = y*W*(C/4) + x*(C/4) + c_group.
385
+ // Each thread computes one output pixel for one channel group (vec4 = 4 channels).
386
+
387
+ struct Params {
388
+ in_h : u32,
389
+ in_w : u32,
390
+ out_h : u32,
391
+ out_w : u32,
392
+ channel_groups : u32,
393
+ _pad0 : u32,
394
+ _pad1 : u32,
395
+ _pad2 : u32,
396
+ }
397
+
398
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
399
+ @group(0) @binding(1) var<uniform> params : Params;
400
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f32>>;
401
+
402
+ @compute @workgroup_size(8, 8, 1)
403
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
404
+ let ox = gid.x;
405
+ let oy = gid.y;
406
+ let c = gid.z;
407
+
408
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.channel_groups) {
409
+ return;
410
+ }
411
+
412
+ let IH = params.in_h;
413
+ let IW = params.in_w;
414
+ let C = params.channel_groups;
415
+
416
+ // align_corners=False: src = (out + 0.5) * (in / out) - 0.5
417
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
418
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
419
+
420
+ // Clamp to [0, in-1] for border replication. Use i32 intermediates so that
421
+ // floor() returning -1.0 doesn't produce an invalid u32 conversion.
422
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
423
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
424
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
425
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
426
+
427
+ let wx = src_x - floor(src_x);
428
+ let wy = src_y - floor(src_y);
429
+
430
+ let tl = input_buf[y0 * IW * C + x0 * C + c];
431
+ let tr = input_buf[y0 * IW * C + x1 * C + c];
432
+ let bl = input_buf[y1 * IW * C + x0 * C + c];
433
+ let br = input_buf[y1 * IW * C + x1 * C + c];
434
+
435
+ // Bilinear blend \\u2014 vec4 ops are element-wise, so all 4 channels blend identically.
436
+ let result = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
437
+ + wy * ((1.0 - wx) * bl + wx * br);
438
+
439
+ output_buf[oy * params.out_w * C + ox * C + c] = result;
440
+ }
441
+ \`;var je=\`enable f16;
442
+
443
+ // Bilinear upsample 2\\xD7 \\u2014 full f16 variant.
444
+ // Interpolation weights (wx, wy) and intermediate blends computed in f32 for
445
+ // accuracy; result cast to f16 on write.
446
+
447
+ struct Params {
448
+ in_h : u32,
449
+ in_w : u32,
450
+ out_h : u32,
451
+ out_w : u32,
452
+ channel_groups : u32,
453
+ _pad0 : u32,
454
+ _pad1 : u32,
455
+ _pad2 : u32,
456
+ }
457
+
458
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
459
+ @group(0) @binding(1) var<uniform> params : Params;
460
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f16>>;
461
+
462
+ @compute @workgroup_size(8, 8, 1)
463
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
464
+ let ox = gid.x;
465
+ let oy = gid.y;
466
+ let c = gid.z;
467
+
468
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.channel_groups) {
469
+ return;
470
+ }
471
+
472
+ let IH = params.in_h;
473
+ let IW = params.in_w;
474
+ let C = params.channel_groups;
475
+
476
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
477
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
478
+
479
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
480
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
481
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
482
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
483
+
484
+ let wx = src_x - floor(src_x);
485
+ let wy = src_y - floor(src_y);
486
+
487
+ let tl = vec4<f32>(input_buf[y0 * IW * C + x0 * C + c]);
488
+ let tr = vec4<f32>(input_buf[y0 * IW * C + x1 * C + c]);
489
+ let bl = vec4<f32>(input_buf[y1 * IW * C + x0 * C + c]);
490
+ let br = vec4<f32>(input_buf[y1 * IW * C + x1 * C + c]);
491
+
492
+ let result = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
493
+ + wy * ((1.0 - wx) * bl + wx * br);
494
+
495
+ output_buf[oy * params.out_w * C + ox * C + c] = vec4<f16>(result);
496
+ }
497
+ \`;var re=class extends h{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?je:qe;let i=t.c/4;this.output=r.tensor(o.outH,o.outW,t.c),this.inputs=[t],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,o.outH,o.outW,i,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(o.outW/8),Math.ceil(o.outH/8),i]}};var Ke=\`// Bicubic upsample (arbitrary scale) \\u2014 Keys cubic, a=-0.75 (PyTorch default
498
+ // for mode='bicubic', align_corners=False). Direct 2D, 4\\xD74 = 16 taps per
499
+ // output pixel. NHWC vec4 layout: index = y*W*(C/4) + x*(C/4) + c_group.
500
+
501
+ struct Params {
502
+ in_h : u32,
503
+ in_w : u32,
504
+ out_h : u32,
505
+ out_w : u32,
506
+ channel_groups : u32,
507
+ _pad0 : u32,
508
+ _pad1 : u32,
509
+ _pad2 : u32,
510
+ }
511
+
512
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
513
+ @group(0) @binding(1) var<uniform> params : Params;
514
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f32>>;
515
+
516
+ const A: f32 = -0.75;
517
+
518
+ fn wcubic(d: f32) -> f32 {
519
+ let ad = abs(d);
520
+ if (ad <= 1.0) { return ((A + 2.0) * ad - (A + 3.0)) * ad * ad + 1.0; }
521
+ if (ad < 2.0) { return ((A * ad - 5.0 * A) * ad + 8.0 * A) * ad - 4.0 * A; }
522
+ return 0.0;
523
+ }
524
+
525
+ @compute @workgroup_size(8, 8, 1)
526
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
527
+ let ox = gid.x;
528
+ let oy = gid.y;
529
+ let c = gid.z;
530
+
531
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.channel_groups) {
532
+ return;
533
+ }
534
+
535
+ let IH = params.in_h;
536
+ let IW = params.in_w;
537
+ let C = params.channel_groups;
538
+
539
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
540
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
541
+
542
+ let x0 = i32(floor(src_x));
543
+ let y0 = i32(floor(src_y));
544
+ let fx = src_x - f32(x0);
545
+ let fy = src_y - f32(y0);
546
+
547
+ var wx: array<f32, 4>;
548
+ var wy: array<f32, 4>;
549
+ wx[0] = wcubic(1.0 + fx); wx[1] = wcubic(fx); wx[2] = wcubic(1.0 - fx); wx[3] = wcubic(2.0 - fx);
550
+ wy[0] = wcubic(1.0 + fy); wy[1] = wcubic(fy); wy[2] = wcubic(1.0 - fy); wy[3] = wcubic(2.0 - fy);
551
+
552
+ var acc: vec4<f32> = vec4<f32>(0.0);
553
+ for (var j: i32 = 0; j < 4; j = j + 1) {
554
+ let sy = u32(clamp(y0 + j - 1, 0, i32(IH) - 1));
555
+ for (var i: i32 = 0; i < 4; i = i + 1) {
556
+ let sx = u32(clamp(x0 + i - 1, 0, i32(IW) - 1));
557
+ let v = input_buf[sy * IW * C + sx * C + c];
558
+ acc = acc + (wx[i] * wy[j]) * v;
559
+ }
560
+ }
561
+
562
+ output_buf[oy * params.out_w * C + ox * C + c] = acc;
563
+ }
564
+ \`;var Ye=\`enable f16;
565
+
566
+ // Bicubic upsample \\u2014 full f16 storage variant. Bicubic weights and the per-
567
+ // pixel accumulator are computed in f32 (cheap to keep precision around the
568
+ // kernel arithmetic), then demoted to f16 when written to the output buffer.
569
+
570
+ struct Params {
571
+ in_h : u32,
572
+ in_w : u32,
573
+ out_h : u32,
574
+ out_w : u32,
575
+ channel_groups : u32,
576
+ _pad0 : u32,
577
+ _pad1 : u32,
578
+ _pad2 : u32,
579
+ }
580
+
581
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
582
+ @group(0) @binding(1) var<uniform> params : Params;
583
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f16>>;
584
+
585
+ const A: f32 = -0.75;
586
+
587
+ fn wcubic(d: f32) -> f32 {
588
+ let ad = abs(d);
589
+ if (ad <= 1.0) { return ((A + 2.0) * ad - (A + 3.0)) * ad * ad + 1.0; }
590
+ if (ad < 2.0) { return ((A * ad - 5.0 * A) * ad + 8.0 * A) * ad - 4.0 * A; }
591
+ return 0.0;
592
+ }
593
+
594
+ @compute @workgroup_size(8, 8, 1)
595
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
596
+ let ox = gid.x;
597
+ let oy = gid.y;
598
+ let c = gid.z;
599
+
600
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.channel_groups) {
601
+ return;
602
+ }
603
+
604
+ let IH = params.in_h;
605
+ let IW = params.in_w;
606
+ let C = params.channel_groups;
607
+
608
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
609
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
610
+
611
+ let x0 = i32(floor(src_x));
612
+ let y0 = i32(floor(src_y));
613
+ let fx = src_x - f32(x0);
614
+ let fy = src_y - f32(y0);
615
+
616
+ var wx: array<f32, 4>;
617
+ var wy: array<f32, 4>;
618
+ wx[0] = wcubic(1.0 + fx); wx[1] = wcubic(fx); wx[2] = wcubic(1.0 - fx); wx[3] = wcubic(2.0 - fx);
619
+ wy[0] = wcubic(1.0 + fy); wy[1] = wcubic(fy); wy[2] = wcubic(1.0 - fy); wy[3] = wcubic(2.0 - fy);
620
+
621
+ var acc: vec4<f32> = vec4<f32>(0.0);
622
+ for (var j: i32 = 0; j < 4; j = j + 1) {
623
+ let sy = u32(clamp(y0 + j - 1, 0, i32(IH) - 1));
624
+ for (var i: i32 = 0; i < 4; i = i + 1) {
625
+ let sx = u32(clamp(x0 + i - 1, 0, i32(IW) - 1));
626
+ let v = vec4<f32>(input_buf[sy * IW * C + sx * C + c]);
627
+ acc = acc + (wx[i] * wy[j]) * v;
628
+ }
629
+ }
630
+
631
+ output_buf[oy * params.out_w * C + ox * C + c] = vec4<f16>(acc);
632
+ }
633
+ \`;var oe=class extends h{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?Ye:Ke;let i=t.c/4;this.output=r.tensor(o.outH,o.outW,t.c),this.inputs=[t],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,o.outH,o.outW,i,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(o.outW/8),Math.ceil(o.outH/8),i]}};var Qe=\`// Channel concatenation: output = cat(A, B, dim=channel).
634
+ // Both A and B must share the same H\\xD7W and be in NHWC vec4 format.
635
+ // Output layout: for each (y, x), the first a_groups vec4s come from A,
636
+ // followed by b_groups vec4s from B.
637
+
638
+ struct Params {
639
+ height : u32,
640
+ width : u32,
641
+ a_groups : u32, // Ca / 4
642
+ b_groups : u32, // Cb / 4
643
+ out_groups: u32, // a_groups + b_groups
644
+ _pad0 : u32,
645
+ _pad1 : u32,
646
+ _pad2 : u32,
647
+ }
648
+
649
+ @group(0) @binding(0) var<storage, read> input_a : array<vec4<f32>>;
650
+ @group(0) @binding(1) var<storage, read> input_b : array<vec4<f32>>;
651
+ @group(0) @binding(2) var<uniform> params : Params;
652
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f32>>;
653
+
654
+ @compute @workgroup_size(8, 8, 1)
655
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
656
+ let x = gid.x;
657
+ let y = gid.y;
658
+ let c = gid.z; // output channel group
659
+
660
+ let W = params.width;
661
+ let Ag = params.a_groups;
662
+ let Bg = params.b_groups;
663
+ let Cg = params.out_groups;
664
+
665
+ if (x >= W || y >= params.height || c >= Cg) {
666
+ return;
667
+ }
668
+
669
+ let out_idx = y * W * Cg + x * Cg + c;
670
+
671
+ if (c < Ag) {
672
+ output_buf[out_idx] = input_a[y * W * Ag + x * Ag + c];
673
+ } else {
674
+ let c_b = c - Ag;
675
+ output_buf[out_idx] = input_b[y * W * Bg + x * Bg + c_b];
676
+ }
677
+ }
678
+ \`;var Je=\`enable f16;
679
+
680
+ // Channel concatenation \\u2014 full f16 variant.
681
+
682
+ struct Params {
683
+ height : u32,
684
+ width : u32,
685
+ a_groups : u32,
686
+ b_groups : u32,
687
+ out_groups: u32,
688
+ _pad0 : u32,
689
+ _pad1 : u32,
690
+ _pad2 : u32,
691
+ }
692
+
693
+ @group(0) @binding(0) var<storage, read> input_a : array<vec4<f16>>;
694
+ @group(0) @binding(1) var<storage, read> input_b : array<vec4<f16>>;
695
+ @group(0) @binding(2) var<uniform> params : Params;
696
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f16>>;
697
+
698
+ @compute @workgroup_size(8, 8, 1)
699
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
700
+ let x = gid.x;
701
+ let y = gid.y;
702
+ let c = gid.z;
703
+
704
+ let W = params.width;
705
+ let Ag = params.a_groups;
706
+ let Bg = params.b_groups;
707
+ let Cg = params.out_groups;
708
+
709
+ if (x >= W || y >= params.height || c >= Cg) { return; }
710
+
711
+ let out_idx = y * W * Cg + x * Cg + c;
712
+ if (c < Ag) {
713
+ output_buf[out_idx] = input_a[y * W * Ag + x * Ag + c];
714
+ } else {
715
+ let c_b = c - Ag;
716
+ output_buf[out_idx] = input_b[y * W * Bg + x * Bg + c_b];
717
+ }
718
+ }
719
+ \`;var ne=class extends h{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?Je:Qe;let i=t.c/4,a=o.c/4,u=i+a;this.output=r.tensor(t.h,t.w,t.c+o.c),this.inputs=[t,o],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,i,a,u,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(t.w/8),Math.ceil(t.h/8),u]}};var Ze=\`// Conv2d + skip add fused.
720
+ // Identical to conv2d.wgsl except skip is an activation input (binding 1),
721
+ // added element-wise to the conv result at write time.
722
+ // Eliminates the separate add dispatch and its intermediate buffer round-trip.
723
+ // Binding order: input(0), skip(1), weights(2), bias(3), params(4), output(5)
724
+
725
+ struct Params {
726
+ in_h : u32,
727
+ in_w : u32,
728
+ out_h : u32,
729
+ out_w : u32,
730
+ in_groups : u32,
731
+ out_groups : u32,
732
+ kernel_h : u32,
733
+ kernel_w : u32,
734
+ stride : u32,
735
+ pad_top : u32,
736
+ pad_left : u32,
737
+ activation : u32,
738
+ }
739
+
740
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
741
+ @group(0) @binding(1) var<storage, read> skip_buf : array<vec4<f32>>;
742
+ @group(0) @binding(2) var<storage, read> weight_buf : array<mat4x4<f32>>;
743
+ @group(0) @binding(3) var<storage, read> bias_buf : array<vec4<f32>>;
744
+ @group(0) @binding(4) var<uniform> params : Params;
745
+ @group(0) @binding(5) var<storage, read_write> output_buf : array<vec4<f32>>;
746
+
747
+ @compute @workgroup_size(8, 8, 1)
748
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
749
+ let x = gid.x;
750
+ let y = gid.y;
751
+ let o = gid.z;
752
+
753
+ if (x >= params.out_w || y >= params.out_h || o >= params.out_groups) {
754
+ return;
755
+ }
756
+
757
+ let I = params.in_groups;
758
+ let O = params.out_groups;
759
+
760
+ var result = bias_buf[o];
761
+
762
+ for (var ky = 0u; ky < params.kernel_h; ky++) {
763
+ for (var kx = 0u; kx < params.kernel_w; kx++) {
764
+ let in_y_s = i32(y * params.stride + ky) - i32(params.pad_top);
765
+ let in_x_s = i32(x * params.stride + kx) - i32(params.pad_left);
766
+
767
+ if (in_y_s < 0 || in_x_s < 0 ||
768
+ u32(in_y_s) >= params.in_h || u32(in_x_s) >= params.in_w) {
769
+ continue;
770
+ }
771
+
772
+ let in_y = u32(in_y_s);
773
+ let in_x = u32(in_x_s);
774
+ let z = ky * params.kernel_w + kx;
775
+
776
+ for (var i = 0u; i < I; i++) {
777
+ let in_idx = in_y * params.in_w * I + in_x * I + i;
778
+ let w_idx = z * O * I + o * I + i;
779
+ result += weight_buf[w_idx] * input_buf[in_idx];
780
+ }
781
+ }
782
+ }
783
+
784
+ if (params.activation == 1u) {
785
+ result = clamp(result, vec4<f32>(0.0), vec4<f32>(6.0));
786
+ } else if (params.activation == 2u) {
787
+ result = max(result, vec4<f32>(0.0));
788
+ }
789
+
790
+ let out_idx = y * params.out_w * O + x * O + o;
791
+ output_buf[out_idx] = result + skip_buf[out_idx];
792
+ }
793
+ \`;var et=\`enable f16;
794
+
795
+ // Conv2d + skip add fused \\u2014 full f16 variant.
796
+
797
+ struct Params {
798
+ in_h : u32,
799
+ in_w : u32,
800
+ out_h : u32,
801
+ out_w : u32,
802
+ in_groups : u32,
803
+ out_groups : u32,
804
+ kernel_h : u32,
805
+ kernel_w : u32,
806
+ stride : u32,
807
+ pad_top : u32,
808
+ pad_left : u32,
809
+ activation : u32,
810
+ }
811
+
812
+ // Binding order matches conv2d_add.wgsl: input(0), skip(1), weight(2), bias(3),
813
+ // params(4), output(5).
814
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
815
+ @group(0) @binding(1) var<storage, read> skip_buf : array<vec4<f16>>;
816
+ @group(0) @binding(2) var<storage, read> weight_buf : array<mat4x4<f16>>;
817
+ @group(0) @binding(3) var<storage, read> bias_buf : array<vec4<f16>>;
818
+ @group(0) @binding(4) var<uniform> params : Params;
819
+ @group(0) @binding(5) var<storage, read_write> output_buf : array<vec4<f16>>;
820
+
821
+ @compute @workgroup_size(8, 8, 1)
822
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
823
+ let x = gid.x;
824
+ let y = gid.y;
825
+ let o = gid.z;
826
+
827
+ if (x >= params.out_w || y >= params.out_h || o >= params.out_groups) {
828
+ return;
829
+ }
830
+
831
+ let I = params.in_groups;
832
+ let O = params.out_groups;
833
+
834
+ var result = bias_buf[o];
835
+
836
+ for (var ky = 0u; ky < params.kernel_h; ky++) {
837
+ for (var kx = 0u; kx < params.kernel_w; kx++) {
838
+ let in_y_s = i32(y * params.stride + ky) - i32(params.pad_top);
839
+ let in_x_s = i32(x * params.stride + kx) - i32(params.pad_left);
840
+
841
+ if (in_y_s < 0 || in_x_s < 0 ||
842
+ u32(in_y_s) >= params.in_h || u32(in_x_s) >= params.in_w) {
843
+ continue;
844
+ }
845
+
846
+ let in_y = u32(in_y_s);
847
+ let in_x = u32(in_x_s);
848
+ let z = ky * params.kernel_w + kx;
849
+
850
+ for (var i = 0u; i < I; i++) {
851
+ let in_idx = in_y * params.in_w * I + in_x * I + i;
852
+ let w_idx = z * O * I + o * I + i;
853
+ result += weight_buf[w_idx] * input_buf[in_idx];
854
+ }
855
+ }
856
+ }
857
+
858
+ if (params.activation == 1u) {
859
+ result = clamp(result, vec4<f16>(0.0h), vec4<f16>(6.0h));
860
+ } else if (params.activation == 2u) {
861
+ result = max(result, vec4<f16>(0.0h));
862
+ }
863
+
864
+ let out_idx = y * params.out_w * O + x * O + o;
865
+ output_buf[out_idx] = result + skip_buf[out_idx];
866
+ }
867
+ \`;var ie=class extends h{constructor(r,t,o,i,a){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?et:Ze;let u=b(t.h,a.kernel,a.stride,a.padding),p=b(t.w,a.kernel,a.stride,a.padding),l=t.c/4,d=a.outChannels/4,m=v(a.padding,t.h,u,a.kernel,a.stride),x=v(a.padding,t.w,p,a.kernel,a.stride);this.output=r.tensor(u,p,a.outChannels),this.inputs=[t,o],this.weights=[r.upload(f(i.weights)),r.upload(f(i.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,u,p,l,d,a.kernel,a.kernel,a.stride,m,x,a.activation==="relu6"?1:0])),this.defaultSetup(),this.dispatch=[Math.ceil(p/8),Math.ceil(u/8),d]}};var tt=\`// Bilinear upsample + channel concat fused.
868
+ // input_a is the decoder tensor at small spatial resolution (in_h \\xD7 in_w).
869
+ // input_b is the encoder skip feature already at output resolution (out_h \\xD7 out_w).
870
+ // For output channels 0..a_groups-1: bilinearly interpolate from input_a.
871
+ // For output channels a_groups..out_groups-1: copy directly from input_b.
872
+ // Eliminates the intermediate upsample buffer and the separate concat dispatch.
873
+
874
+ struct Params {
875
+ in_h : u32, // input_a spatial height
876
+ in_w : u32, // input_a spatial width
877
+ out_h : u32,
878
+ out_w : u32,
879
+ a_groups : u32, // input_a channel groups (upsampled)
880
+ b_groups : u32, // input_b channel groups (encoder feature)
881
+ out_groups : u32, // a_groups + b_groups
882
+ _pad0 : u32,
883
+ }
884
+
885
+ @group(0) @binding(0) var<storage, read> input_a : array<vec4<f32>>;
886
+ @group(0) @binding(1) var<storage, read> input_b : array<vec4<f32>>;
887
+ @group(0) @binding(2) var<uniform> params : Params;
888
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f32>>;
889
+
890
+ @compute @workgroup_size(8, 8, 1)
891
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
892
+ let ox = gid.x;
893
+ let oy = gid.y;
894
+ let c = gid.z;
895
+
896
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.out_groups) { return; }
897
+
898
+ let out_idx = oy * params.out_w * params.out_groups + ox * params.out_groups + c;
899
+
900
+ if (c < params.a_groups) {
901
+ let IH = params.in_h;
902
+ let IW = params.in_w;
903
+ let AG = params.a_groups;
904
+
905
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
906
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
907
+
908
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
909
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
910
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
911
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
912
+
913
+ let wx = src_x - floor(src_x);
914
+ let wy = src_y - floor(src_y);
915
+
916
+ let tl = input_a[y0 * IW * AG + x0 * AG + c];
917
+ let tr = input_a[y0 * IW * AG + x1 * AG + c];
918
+ let bl = input_a[y1 * IW * AG + x0 * AG + c];
919
+ let br = input_a[y1 * IW * AG + x1 * AG + c];
920
+
921
+ output_buf[out_idx] = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
922
+ + wy * ((1.0 - wx) * bl + wx * br);
923
+ } else {
924
+ let c_b = c - params.a_groups;
925
+ output_buf[out_idx] = input_b[oy * params.out_w * params.b_groups + ox * params.b_groups + c_b];
926
+ }
927
+ }
928
+ \`;var rt=\`enable f16;
929
+
930
+ // Bilinear upsample + channel concat fused \\u2014 full f16 variant.
931
+ // Bilinear weights (wx, wy) and intermediate blends computed in f32; result cast to f16.
932
+
933
+ struct Params {
934
+ in_h : u32,
935
+ in_w : u32,
936
+ out_h : u32,
937
+ out_w : u32,
938
+ a_groups : u32,
939
+ b_groups : u32,
940
+ out_groups : u32,
941
+ _pad0 : u32,
942
+ }
943
+
944
+ @group(0) @binding(0) var<storage, read> input_a : array<vec4<f16>>;
945
+ @group(0) @binding(1) var<storage, read> input_b : array<vec4<f16>>;
946
+ @group(0) @binding(2) var<uniform> params : Params;
947
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f16>>;
948
+
949
+ @compute @workgroup_size(8, 8, 1)
950
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
951
+ let ox = gid.x;
952
+ let oy = gid.y;
953
+ let c = gid.z;
954
+
955
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.out_groups) { return; }
956
+
957
+ let out_idx = oy * params.out_w * params.out_groups + ox * params.out_groups + c;
958
+
959
+ if (c < params.a_groups) {
960
+ let IH = params.in_h;
961
+ let IW = params.in_w;
962
+ let AG = params.a_groups;
963
+
964
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
965
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
966
+
967
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
968
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
969
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
970
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
971
+
972
+ let wx = src_x - floor(src_x);
973
+ let wy = src_y - floor(src_y);
974
+
975
+ let tl = vec4<f32>(input_a[y0 * IW * AG + x0 * AG + c]);
976
+ let tr = vec4<f32>(input_a[y0 * IW * AG + x1 * AG + c]);
977
+ let bl = vec4<f32>(input_a[y1 * IW * AG + x0 * AG + c]);
978
+ let br = vec4<f32>(input_a[y1 * IW * AG + x1 * AG + c]);
979
+
980
+ let result = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
981
+ + wy * ((1.0 - wx) * bl + wx * br);
982
+ output_buf[out_idx] = vec4<f16>(result);
983
+ } else {
984
+ let c_b = c - params.a_groups;
985
+ output_buf[out_idx] = input_b[oy * params.out_w * params.b_groups + ox * params.b_groups + c_b];
986
+ }
987
+ }
988
+ \`;var se=class extends h{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?rt:tt;let a=t.c/4,u=o.c/4,p=a+u;this.output=r.tensor(i.outH,i.outW,t.c+o.c),this.inputs=[t,o],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,i.outH,i.outW,a,u,p,0])),this.defaultSetup(),this.dispatch=[Math.ceil(i.outW/8),Math.ceil(i.outH/8),p]}};var ot=\`// Bilinear upsample + 1\\xD71 pointwise conv fused.
989
+ // For each output pixel, bilinearly samples the small input for each in_group,
990
+ // immediately applies the 1\\xD71 conv weights, and writes the activated result.
991
+ // Eliminates the intermediate full-resolution upsample buffer.
992
+
993
+ struct Params {
994
+ in_h : u32,
995
+ in_w : u32,
996
+ out_h : u32,
997
+ out_w : u32,
998
+ in_groups : u32,
999
+ out_groups : u32,
1000
+ activation : u32,
1001
+ _pad0 : u32,
1002
+ }
1003
+
1004
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
1005
+ @group(0) @binding(1) var<storage, read> weight_buf : array<mat4x4<f32>>;
1006
+ @group(0) @binding(2) var<storage, read> bias_buf : array<vec4<f32>>;
1007
+ @group(0) @binding(3) var<uniform> params : Params;
1008
+ @group(0) @binding(4) var<storage, read_write> output_buf : array<vec4<f32>>;
1009
+
1010
+ @compute @workgroup_size(8, 8, 1)
1011
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1012
+ let ox = gid.x;
1013
+ let oy = gid.y;
1014
+ let og = gid.z;
1015
+
1016
+ if (ox >= params.out_w || oy >= params.out_h || og >= params.out_groups) { return; }
1017
+
1018
+ let IH = params.in_h;
1019
+ let IW = params.in_w;
1020
+ let IG = params.in_groups;
1021
+
1022
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
1023
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
1024
+
1025
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
1026
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
1027
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
1028
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
1029
+
1030
+ let wx = src_x - floor(src_x);
1031
+ let wy = src_y - floor(src_y);
1032
+
1033
+ var result = bias_buf[og];
1034
+
1035
+ for (var ig = 0u; ig < IG; ig++) {
1036
+ let tl = input_buf[y0 * IW * IG + x0 * IG + ig];
1037
+ let tr = input_buf[y0 * IW * IG + x1 * IG + ig];
1038
+ let bl = input_buf[y1 * IW * IG + x0 * IG + ig];
1039
+ let br = input_buf[y1 * IW * IG + x1 * IG + ig];
1040
+
1041
+ let sampled = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
1042
+ + wy * ((1.0 - wx) * bl + wx * br);
1043
+
1044
+ result += weight_buf[og * IG + ig] * sampled;
1045
+ }
1046
+
1047
+ if (params.activation == 1u) {
1048
+ result = clamp(result, vec4<f32>(0.0), vec4<f32>(6.0));
1049
+ } else if (params.activation == 2u) {
1050
+ result = result * clamp(result + 3.0, vec4<f32>(0.0), vec4<f32>(6.0)) / 6.0;
1051
+ }
1052
+
1053
+ output_buf[oy * params.out_w * params.out_groups + ox * params.out_groups + og] = result;
1054
+ }
1055
+ \`;var nt=\`enable f16;
1056
+
1057
+ // Bilinear upsample + 1\\xD71 pointwise conv fused \\u2014 full f16 variant.
1058
+ // Bilinear weights and intermediate blends computed in f32; conv accumulation in f16.
1059
+
1060
+ struct Params {
1061
+ in_h : u32,
1062
+ in_w : u32,
1063
+ out_h : u32,
1064
+ out_w : u32,
1065
+ in_groups : u32,
1066
+ out_groups : u32,
1067
+ activation : u32,
1068
+ _pad0 : u32,
1069
+ }
1070
+
1071
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
1072
+ @group(0) @binding(1) var<storage, read> weight_buf : array<mat4x4<f16>>;
1073
+ @group(0) @binding(2) var<storage, read> bias_buf : array<vec4<f16>>;
1074
+ @group(0) @binding(3) var<uniform> params : Params;
1075
+ @group(0) @binding(4) var<storage, read_write> output_buf : array<vec4<f16>>;
1076
+
1077
+ @compute @workgroup_size(8, 8, 1)
1078
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1079
+ let ox = gid.x;
1080
+ let oy = gid.y;
1081
+ let og = gid.z;
1082
+
1083
+ if (ox >= params.out_w || oy >= params.out_h || og >= params.out_groups) { return; }
1084
+
1085
+ let IH = params.in_h;
1086
+ let IW = params.in_w;
1087
+ let IG = params.in_groups;
1088
+
1089
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
1090
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
1091
+
1092
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
1093
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
1094
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
1095
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
1096
+
1097
+ let wx = src_x - floor(src_x);
1098
+ let wy = src_y - floor(src_y);
1099
+
1100
+ var result = bias_buf[og];
1101
+
1102
+ for (var ig = 0u; ig < IG; ig++) {
1103
+ let tl = vec4<f32>(input_buf[y0 * IW * IG + x0 * IG + ig]);
1104
+ let tr = vec4<f32>(input_buf[y0 * IW * IG + x1 * IG + ig]);
1105
+ let bl = vec4<f32>(input_buf[y1 * IW * IG + x0 * IG + ig]);
1106
+ let br = vec4<f32>(input_buf[y1 * IW * IG + x1 * IG + ig]);
1107
+
1108
+ let sampled = vec4<f16>((1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
1109
+ + wy * ((1.0 - wx) * bl + wx * br));
1110
+
1111
+ result += weight_buf[og * IG + ig] * sampled;
1112
+ }
1113
+
1114
+ if (params.activation == 1u) {
1115
+ result = clamp(result, vec4<f16>(0.0h), vec4<f16>(6.0h));
1116
+ } else if (params.activation == 2u) {
1117
+ result = result * clamp(result + 3.0h, vec4<f16>(0.0h), vec4<f16>(6.0h)) / 6.0h;
1118
+ }
1119
+
1120
+ output_buf[oy * params.out_w * params.out_groups + ox * params.out_groups + og] = result;
1121
+ }
1122
+ \`;var ae=class extends h{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?nt:ot;let a=t.c/4,u=i.outChannels/4;this.output=r.tensor(i.outH,i.outW,i.outChannels),this.inputs=[t],this.weights=[r.upload(f(o.weights)),r.upload(f(o.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,i.outH,i.outW,a,u,i.activation==="relu6"?1:0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(i.outW/8),Math.ceil(i.outH/8),u]}};var it=\`// Bilinear upsample + sigmoid fused.
1123
+ // Identical to bilinear_upsample.wgsl except sigmoid is applied at write time.
1124
+ // Eliminates the intermediate full-resolution buffer and the separate sigmoid dispatch.
1125
+
1126
+ struct Params {
1127
+ in_h : u32,
1128
+ in_w : u32,
1129
+ out_h : u32,
1130
+ out_w : u32,
1131
+ channel_groups : u32,
1132
+ _pad0 : u32,
1133
+ _pad1 : u32,
1134
+ _pad2 : u32,
1135
+ }
1136
+
1137
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f32>>;
1138
+ @group(0) @binding(1) var<uniform> params : Params;
1139
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f32>>;
1140
+
1141
+ @compute @workgroup_size(8, 8, 1)
1142
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1143
+ let ox = gid.x;
1144
+ let oy = gid.y;
1145
+ let c = gid.z;
1146
+
1147
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.channel_groups) {
1148
+ return;
1149
+ }
1150
+
1151
+ let IH = params.in_h;
1152
+ let IW = params.in_w;
1153
+ let C = params.channel_groups;
1154
+
1155
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
1156
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
1157
+
1158
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
1159
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
1160
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
1161
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
1162
+
1163
+ let wx = src_x - floor(src_x);
1164
+ let wy = src_y - floor(src_y);
1165
+
1166
+ let tl = input_buf[y0 * IW * C + x0 * C + c];
1167
+ let tr = input_buf[y0 * IW * C + x1 * C + c];
1168
+ let bl = input_buf[y1 * IW * C + x0 * C + c];
1169
+ let br = input_buf[y1 * IW * C + x1 * C + c];
1170
+
1171
+ let result = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
1172
+ + wy * ((1.0 - wx) * bl + wx * br);
1173
+
1174
+ output_buf[oy * params.out_w * C + ox * C + c] = 1.0 / (1.0 + exp(-result));
1175
+ }
1176
+ \`;var st=\`enable f16;
1177
+
1178
+ // Bilinear upsample + sigmoid fused \\u2014 full f16 variant.
1179
+ // Interpolation computed in f32 for accuracy; sigmoid and result cast to f16.
1180
+
1181
+ struct Params {
1182
+ in_h : u32,
1183
+ in_w : u32,
1184
+ out_h : u32,
1185
+ out_w : u32,
1186
+ channel_groups : u32,
1187
+ _pad0 : u32,
1188
+ _pad1 : u32,
1189
+ _pad2 : u32,
1190
+ }
1191
+
1192
+ @group(0) @binding(0) var<storage, read> input_buf : array<vec4<f16>>;
1193
+ @group(0) @binding(1) var<uniform> params : Params;
1194
+ @group(0) @binding(2) var<storage, read_write> output_buf : array<vec4<f16>>;
1195
+
1196
+ @compute @workgroup_size(8, 8, 1)
1197
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1198
+ let ox = gid.x;
1199
+ let oy = gid.y;
1200
+ let c = gid.z;
1201
+
1202
+ if (ox >= params.out_w || oy >= params.out_h || c >= params.channel_groups) {
1203
+ return;
1204
+ }
1205
+
1206
+ let IH = params.in_h;
1207
+ let IW = params.in_w;
1208
+ let C = params.channel_groups;
1209
+
1210
+ let src_x = (f32(ox) + 0.5) * (f32(IW) / f32(params.out_w)) - 0.5;
1211
+ let src_y = (f32(oy) + 0.5) * (f32(IH) / f32(params.out_h)) - 0.5;
1212
+
1213
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(IW) - 1));
1214
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(IW) - 1));
1215
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(IH) - 1));
1216
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(IH) - 1));
1217
+
1218
+ let wx = src_x - floor(src_x);
1219
+ let wy = src_y - floor(src_y);
1220
+
1221
+ let tl = vec4<f32>(input_buf[y0 * IW * C + x0 * C + c]);
1222
+ let tr = vec4<f32>(input_buf[y0 * IW * C + x1 * C + c]);
1223
+ let bl = vec4<f32>(input_buf[y1 * IW * C + x0 * C + c]);
1224
+ let br = vec4<f32>(input_buf[y1 * IW * C + x1 * C + c]);
1225
+
1226
+ let result = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
1227
+ + wy * ((1.0 - wx) * bl + wx * br);
1228
+
1229
+ output_buf[oy * params.out_w * C + ox * C + c] = vec4<f16>(1.0 / (1.0 + exp(-result)));
1230
+ }
1231
+ \`;var ue=class extends h{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader");this.shader=r.dtype==="f16"?st:it;let i=t.c/4;this.output=r.tensor(o.outH,o.outW,t.c),this.inputs=[t],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([t.h,t.w,o.outH,o.outW,i,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(o.outW/8),Math.ceil(o.outH/8),i]}};var at=\`// Composite an RGBA image over a solid background, gated by a 1-ch alpha.
1232
+ // Fragment writes the canvas's swapchain texture (premultiplied output).
1233
+ //
1234
+ // Caller invariants (matched in CompositeSolidWebGPU):
1235
+ // - image and alpha are NHWC vec4 storage buffers, same h \\xD7 w
1236
+ // - canvas.width === image.w, canvas.height === image.h (no resampling)
1237
+
1238
+ struct VertexOut {
1239
+ @builtin(position) pos: vec4<f32>,
1240
+ };
1241
+
1242
+ @vertex
1243
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1244
+ let verts = array<vec2<f32>, 6>(
1245
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1246
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1247
+ );
1248
+ var out: VertexOut;
1249
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1250
+ return out;
1251
+ }
1252
+
1253
+ struct Params {
1254
+ width: u32, // image width in pixels (= canvas width)
1255
+ _pad0: u32,
1256
+ _pad1: u32,
1257
+ _pad2: u32,
1258
+ bgColor: vec4<f32>, // .rgb used; .a ignored
1259
+ };
1260
+
1261
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f32>>;
1262
+ @group(0) @binding(1) var<storage, read> alpha: array<vec4<f32>>;
1263
+ @group(0) @binding(2) var<uniform> params: Params;
1264
+
1265
+ @fragment
1266
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1267
+ let x = u32(in.pos.x);
1268
+ let y = u32(in.pos.y);
1269
+ let i = y * params.width + x;
1270
+
1271
+ let fg = image[i].rgb;
1272
+ let a = alpha[i].r;
1273
+ let rgb = fg * a + params.bgColor.rgb * (1.0 - a);
1274
+ return vec4<f32>(rgb, 1.0);
1275
+ }
1276
+ \`;var ut=\`enable f16;
1277
+
1278
+ // Composite an RGBA image over a solid background, gated by a 1-ch alpha.
1279
+ // f16 variant: image and alpha are stored as f16; values promote to f32 on
1280
+ // read and the fragment writes f32 to the canvas swapchain (color attachment
1281
+ // format is the swapchain's preferred format, always f32-equivalent).
1282
+ //
1283
+ // Caller invariants (matched in CompositeSolidWebGPU):
1284
+ // - image and alpha are NHWC vec4 storage buffers, same h \\xD7 w
1285
+ // - canvas.width === image.w, canvas.height === image.h (no resampling)
1286
+
1287
+ struct VertexOut {
1288
+ @builtin(position) pos: vec4<f32>,
1289
+ };
1290
+
1291
+ @vertex
1292
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1293
+ let verts = array<vec2<f32>, 6>(
1294
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1295
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1296
+ );
1297
+ var out: VertexOut;
1298
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1299
+ return out;
1300
+ }
1301
+
1302
+ struct Params {
1303
+ width: u32,
1304
+ _pad0: u32,
1305
+ _pad1: u32,
1306
+ _pad2: u32,
1307
+ bgColor: vec4<f32>,
1308
+ };
1309
+
1310
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f16>>;
1311
+ @group(0) @binding(1) var<storage, read> alpha: array<vec4<f16>>;
1312
+ @group(0) @binding(2) var<uniform> params: Params;
1313
+
1314
+ @fragment
1315
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1316
+ let x = u32(in.pos.x);
1317
+ let y = u32(in.pos.y);
1318
+ let i = y * params.width + x;
1319
+
1320
+ let fg = vec3<f32>(image[i].rgb);
1321
+ let a = f32(alpha[i].r);
1322
+ let rgb = fg * a + params.bgColor.rgb * (1.0 - a);
1323
+ return vec4<f32>(rgb, 1.0);
1324
+ }
1325
+ \`;var pe=class{constructor(e,r,t,o){n(this,"backend",e);n(this,"pipeline");n(this,"bindGroup");n(this,"uniformBuffer");n(this,"outputView",null);if(r.h!==t.h||r.w!==t.w)throw new Error(\`CompositeSolid: image (\${r.h}\\xD7\${r.w}) and alpha (\${t.h}\\xD7\${t.w}) must match. Run the upscaler first.\`);let i=e.device;this.uniformBuffer=i.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let a=new ArrayBuffer(32);new Uint32Array(a,0,1)[0]=r.w,new Float32Array(a,16,4).set([o[0],o[1],o[2],0]),i.queue.writeBuffer(this.uniformBuffer,0,a);let u=e.dtype==="f16"?ut:at,p=i.createShaderModule({code:u});this.pipeline=i.createRenderPipeline({layout:"auto",vertex:{module:p,entryPoint:"vs"},fragment:{module:p,entryPoint:"fs",targets:[{format:e.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=i.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.buffer}},{binding:1,resource:{buffer:t.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}}]})}setOutput(e){this.outputView=e.createView()}run(){if(!this.outputView)throw new Error("CompositeSolidWebGPU.run() called before setOutput()");let e=this.backend.device.createCommandEncoder(),r=e.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});r.setPipeline(this.pipeline),r.setBindGroup(0,this.bindGroup),r.draw(6),r.end(),this.backend.device.queue.submit([e.finish()]),this.outputView=null}};var pt=\`// Like composite_solid but bg is an NHWC vec4 storage buffer (e.g. virtual
1326
+ // background image, or a blurred copy of the input).
1327
+
1328
+ struct VertexOut {
1329
+ @builtin(position) pos: vec4<f32>,
1330
+ };
1331
+
1332
+ @vertex
1333
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1334
+ let verts = array<vec2<f32>, 6>(
1335
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1336
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1337
+ );
1338
+ var out: VertexOut;
1339
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1340
+ return out;
1341
+ }
1342
+
1343
+ struct Params {
1344
+ width: u32,
1345
+ _pad0: u32,
1346
+ _pad1: u32,
1347
+ _pad2: u32,
1348
+ };
1349
+
1350
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f32>>;
1351
+ @group(0) @binding(1) var<storage, read> alpha: array<vec4<f32>>;
1352
+ @group(0) @binding(2) var<uniform> params: Params;
1353
+ @group(0) @binding(3) var<storage, read> bg: array<vec4<f32>>;
1354
+
1355
+ @fragment
1356
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1357
+ let x = u32(in.pos.x);
1358
+ let y = u32(in.pos.y);
1359
+ let i = y * params.width + x;
1360
+
1361
+ let fg = image[i].rgb;
1362
+ let a = alpha[i].r;
1363
+ let bgc = bg[i].rgb;
1364
+ let rgb = fg * a + bgc * (1.0 - a);
1365
+ return vec4<f32>(rgb, 1.0);
1366
+ }
1367
+ \`;var lt=\`enable f16;
1368
+
1369
+ // Like composite_solid_f16 but bg is an NHWC vec4<f16> storage buffer (e.g. a
1370
+ // virtual background image, or a blurred copy of the input).
1371
+
1372
+ struct VertexOut {
1373
+ @builtin(position) pos: vec4<f32>,
1374
+ };
1375
+
1376
+ @vertex
1377
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1378
+ let verts = array<vec2<f32>, 6>(
1379
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1380
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1381
+ );
1382
+ var out: VertexOut;
1383
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1384
+ return out;
1385
+ }
1386
+
1387
+ struct Params {
1388
+ width: u32,
1389
+ _pad0: u32,
1390
+ _pad1: u32,
1391
+ _pad2: u32,
1392
+ };
1393
+
1394
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f16>>;
1395
+ @group(0) @binding(1) var<storage, read> alpha: array<vec4<f16>>;
1396
+ @group(0) @binding(2) var<uniform> params: Params;
1397
+ @group(0) @binding(3) var<storage, read> bg: array<vec4<f16>>;
1398
+
1399
+ @fragment
1400
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1401
+ let x = u32(in.pos.x);
1402
+ let y = u32(in.pos.y);
1403
+ let i = y * params.width + x;
1404
+
1405
+ let fg = vec3<f32>(image[i].rgb);
1406
+ let a = f32(alpha[i].r);
1407
+ let bgc = vec3<f32>(bg[i].rgb);
1408
+ let rgb = fg * a + bgc * (1.0 - a);
1409
+ return vec4<f32>(rgb, 1.0);
1410
+ }
1411
+ \`;var le=class{constructor(e,r,t,o){n(this,"backend",e);n(this,"pipeline");n(this,"bindGroup");n(this,"uniformBuffer");n(this,"outputView",null);if(r.h!==t.h||r.w!==t.w||r.h!==o.h||r.w!==o.w)throw new Error(\`CompositeImage: image (\${r.h}\\xD7\${r.w}), alpha (\${t.h}\\xD7\${t.w}), and bg (\${o.h}\\xD7\${o.w}) must all match. Run upscaler / resizer first.\`);let i=e.device;this.uniformBuffer=i.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let a=new ArrayBuffer(16);new Uint32Array(a,0,1)[0]=r.w,i.queue.writeBuffer(this.uniformBuffer,0,a);let u=e.dtype==="f16"?lt:pt,p=i.createShaderModule({code:u});this.pipeline=i.createRenderPipeline({layout:"auto",vertex:{module:p,entryPoint:"vs"},fragment:{module:p,entryPoint:"fs",targets:[{format:e.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=i.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.buffer}},{binding:1,resource:{buffer:t.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:o.buffer}}]})}setOutput(e){this.outputView=e.createView()}run(){if(!this.outputView)throw new Error("CompositeImageWebGPU.run() called before setOutput()");let e=this.backend.device.createCommandEncoder(),r=e.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});r.setPipeline(this.pipeline),r.setBindGroup(0,this.bindGroup),r.draw(6),r.end(),this.backend.device.queue.submit([e.finish()]),this.outputView=null}};var ct=\`// Like composite_image but bg is sampled bilinearly \\u2014 bg may be smaller than
1412
+ // (image, alpha). Used by CompositorBlur to skip the final full-res upsample
1413
+ // in the blur pyramid and let this shader's own per-pixel scan do the
1414
+ // expansion (the work is already happening here for the composite anyway).
1415
+ //
1416
+ // Layout invariant: image, alpha share output dims (canvas h \\xD7 w); bg is
1417
+ // at a smaller resolution (bg_h \\xD7 bg_w). All NHWC vec4 storage buffers,
1418
+ // channelGroups = 1 (RGB padded to vec4).
1419
+
1420
+ struct VertexOut {
1421
+ @builtin(position) pos: vec4<f32>,
1422
+ };
1423
+
1424
+ @vertex
1425
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1426
+ let verts = array<vec2<f32>, 6>(
1427
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1428
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1429
+ );
1430
+ var out: VertexOut;
1431
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1432
+ return out;
1433
+ }
1434
+
1435
+ struct Params {
1436
+ out_w: u32,
1437
+ out_h: u32,
1438
+ bg_w: u32,
1439
+ bg_h: u32,
1440
+ };
1441
+
1442
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f32>>;
1443
+ @group(0) @binding(1) var<storage, read> alpha: array<vec4<f32>>;
1444
+ @group(0) @binding(2) var<uniform> params: Params;
1445
+ @group(0) @binding(3) var<storage, read> bg: array<vec4<f32>>;
1446
+
1447
+ @fragment
1448
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1449
+ let x = u32(in.pos.x);
1450
+ let y = u32(in.pos.y);
1451
+ let i = y * params.out_w + x;
1452
+
1453
+ let fg = image[i].rgb;
1454
+ let a = alpha[i].r;
1455
+
1456
+ // Bilinear sample bg at the corresponding location. align_corners=False.
1457
+ let src_x = (f32(x) + 0.5) * (f32(params.bg_w) / f32(params.out_w)) - 0.5;
1458
+ let src_y = (f32(y) + 0.5) * (f32(params.bg_h) / f32(params.out_h)) - 0.5;
1459
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(params.bg_w) - 1));
1460
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(params.bg_w) - 1));
1461
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(params.bg_h) - 1));
1462
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(params.bg_h) - 1));
1463
+ let wx = src_x - floor(src_x);
1464
+ let wy = src_y - floor(src_y);
1465
+
1466
+ let tl = bg[y0 * params.bg_w + x0].rgb;
1467
+ let tr = bg[y0 * params.bg_w + x1].rgb;
1468
+ let bl = bg[y1 * params.bg_w + x0].rgb;
1469
+ let br = bg[y1 * params.bg_w + x1].rgb;
1470
+ let bgc = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
1471
+ + wy * ((1.0 - wx) * bl + wx * br);
1472
+
1473
+ let rgb = fg * a + bgc * (1.0 - a);
1474
+ return vec4<f32>(rgb, 1.0);
1475
+ }
1476
+ \`;var dt=\`enable f16;
1477
+
1478
+ // f16 storage variant of composite_image_bilinear. bg is at a smaller
1479
+ // resolution and is bilinearly sampled to match (image, alpha) at full res.
1480
+ // Computation in f32; storage in f16.
1481
+
1482
+ struct VertexOut {
1483
+ @builtin(position) pos: vec4<f32>,
1484
+ };
1485
+
1486
+ @vertex
1487
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1488
+ let verts = array<vec2<f32>, 6>(
1489
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1490
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1491
+ );
1492
+ var out: VertexOut;
1493
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1494
+ return out;
1495
+ }
1496
+
1497
+ struct Params {
1498
+ out_w: u32,
1499
+ out_h: u32,
1500
+ bg_w: u32,
1501
+ bg_h: u32,
1502
+ };
1503
+
1504
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f16>>;
1505
+ @group(0) @binding(1) var<storage, read> alpha: array<vec4<f16>>;
1506
+ @group(0) @binding(2) var<uniform> params: Params;
1507
+ @group(0) @binding(3) var<storage, read> bg: array<vec4<f16>>;
1508
+
1509
+ @fragment
1510
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1511
+ let x = u32(in.pos.x);
1512
+ let y = u32(in.pos.y);
1513
+ let i = y * params.out_w + x;
1514
+
1515
+ let fg = vec3<f32>(image[i].rgb);
1516
+ let a = f32(alpha[i].r);
1517
+
1518
+ let src_x = (f32(x) + 0.5) * (f32(params.bg_w) / f32(params.out_w)) - 0.5;
1519
+ let src_y = (f32(y) + 0.5) * (f32(params.bg_h) / f32(params.out_h)) - 0.5;
1520
+ let x0 = u32(clamp(i32(floor(src_x)), 0, i32(params.bg_w) - 1));
1521
+ let x1 = u32(clamp(i32(floor(src_x)) + 1, 0, i32(params.bg_w) - 1));
1522
+ let y0 = u32(clamp(i32(floor(src_y)), 0, i32(params.bg_h) - 1));
1523
+ let y1 = u32(clamp(i32(floor(src_y)) + 1, 0, i32(params.bg_h) - 1));
1524
+ let wx = src_x - floor(src_x);
1525
+ let wy = src_y - floor(src_y);
1526
+
1527
+ let tl = vec3<f32>(bg[y0 * params.bg_w + x0].rgb);
1528
+ let tr = vec3<f32>(bg[y0 * params.bg_w + x1].rgb);
1529
+ let bl = vec3<f32>(bg[y1 * params.bg_w + x0].rgb);
1530
+ let br = vec3<f32>(bg[y1 * params.bg_w + x1].rgb);
1531
+ let bgc = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
1532
+ + wy * ((1.0 - wx) * bl + wx * br);
1533
+
1534
+ let rgb = fg * a + bgc * (1.0 - a);
1535
+ return vec4<f32>(rgb, 1.0);
1536
+ }
1537
+ \`;var ce=class{constructor(e,r,t,o){n(this,"backend",e);n(this,"pipeline");n(this,"bindGroup");n(this,"uniformBuffer");n(this,"outputView",null);if(r.h!==t.h||r.w!==t.w)throw new Error(\`CompositeImageBilinear: image (\${r.h}\\xD7\${r.w}) and alpha (\${t.h}\\xD7\${t.w}) must match.\`);let i=e.device;this.uniformBuffer=i.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let a=new ArrayBuffer(16),u=new Uint32Array(a);u[0]=r.w,u[1]=r.h,u[2]=o.w,u[3]=o.h,i.queue.writeBuffer(this.uniformBuffer,0,a);let p=e.dtype==="f16"?dt:ct,l=i.createShaderModule({code:p});this.pipeline=i.createRenderPipeline({layout:"auto",vertex:{module:l,entryPoint:"vs"},fragment:{module:l,entryPoint:"fs",targets:[{format:e.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=i.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.buffer}},{binding:1,resource:{buffer:t.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:o.buffer}}]})}setOutput(e){this.outputView=e.createView()}run(){if(!this.outputView)throw new Error("CompositeImageBilinearWebGPU.run() called before setOutput()");let e=this.backend.device.createCommandEncoder(),r=e.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});r.setPipeline(this.pipeline),r.setBindGroup(0,this.bindGroup),r.draw(6),r.end(),this.backend.device.queue.submit([e.finish()]),this.outputView=null}};var mt=\`// Passthrough "compositor" \\u2014 writes the image directly to the canvas
1538
+ // swapchain texture with no alpha math and no background. Used by RenderOp
1539
+ // when the renderer is in disabled state, so the output canvas reflects
1540
+ // the unmodified input frame instead of a stale matted result.
1541
+ //
1542
+ // Caller invariants (matched in CompositePassthroughWebGPU):
1543
+ // - image is an NHWC vec4 storage buffer
1544
+ // - canvas.width === image.w, canvas.height === image.h (no resampling)
1545
+
1546
+ struct VertexOut {
1547
+ @builtin(position) pos: vec4<f32>,
1548
+ };
1549
+
1550
+ @vertex
1551
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1552
+ let verts = array<vec2<f32>, 6>(
1553
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1554
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1555
+ );
1556
+ var out: VertexOut;
1557
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1558
+ return out;
1559
+ }
1560
+
1561
+ struct Params {
1562
+ width: u32,
1563
+ };
1564
+
1565
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f32>>;
1566
+ @group(0) @binding(1) var<uniform> params: Params;
1567
+
1568
+ @fragment
1569
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1570
+ let x = u32(in.pos.x);
1571
+ let y = u32(in.pos.y);
1572
+ let i = y * params.width + x;
1573
+ return vec4<f32>(image[i].rgb, 1.0);
1574
+ }
1575
+ \`;var ht=\`enable f16;
1576
+
1577
+ // Passthrough "compositor" \\u2014 f16 variant. Image is stored as f16; values
1578
+ // promote to f32 on read and the fragment writes f32 to the canvas
1579
+ // swapchain (color attachment is the swapchain's preferred f32-equivalent
1580
+ // format). See composite_passthrough.wgsl for the f32 version.
1581
+
1582
+ struct VertexOut {
1583
+ @builtin(position) pos: vec4<f32>,
1584
+ };
1585
+
1586
+ @vertex
1587
+ fn vs(@builtin(vertex_index) vi: u32) -> VertexOut {
1588
+ let verts = array<vec2<f32>, 6>(
1589
+ vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2(-1.0, 1.0),
1590
+ vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0),
1591
+ );
1592
+ var out: VertexOut;
1593
+ out.pos = vec4<f32>(verts[vi], 0.0, 1.0);
1594
+ return out;
1595
+ }
1596
+
1597
+ struct Params {
1598
+ width: u32,
1599
+ };
1600
+
1601
+ @group(0) @binding(0) var<storage, read> image: array<vec4<f16>>;
1602
+ @group(0) @binding(1) var<uniform> params: Params;
1603
+
1604
+ @fragment
1605
+ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1606
+ let x = u32(in.pos.x);
1607
+ let y = u32(in.pos.y);
1608
+ let i = y * params.width + x;
1609
+ return vec4<f32>(vec3<f32>(image[i].rgb), 1.0);
1610
+ }
1611
+ \`;var de=class{constructor(e,r){n(this,"backend",e);n(this,"pipeline");n(this,"bindGroup");n(this,"uniformBuffer");n(this,"outputView",null);let t=e.device;this.uniformBuffer=t.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let o=new ArrayBuffer(16);new Uint32Array(o,0,1)[0]=r.w,t.queue.writeBuffer(this.uniformBuffer,0,o);let i=e.dtype==="f16"?ht:mt,a=t.createShaderModule({code:i});this.pipeline=t.createRenderPipeline({layout:"auto",vertex:{module:a,entryPoint:"vs"},fragment:{module:a,entryPoint:"fs",targets:[{format:e.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.buffer}},{binding:1,resource:{buffer:this.uniformBuffer}}]})}setOutput(e){this.outputView=e.createView()}run(){if(!this.outputView)throw new Error("CompositePassthroughWebGPU.run() called before setOutput()");let e=this.backend.device.createCommandEncoder(),r=e.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});r.setPipeline(this.pipeline),r.setBindGroup(0,this.bindGroup),r.draw(6),r.end(),this.backend.device.queue.submit([e.finish()]),this.outputView=null}};var ft=\`// Input op \\u2014 sample a regular 2D source texture (RGBA8 unorm) into the NHWC
1612
+ // vec4<f32> output buffer at the target resolution. Used for ImageBitmap
1613
+ // sources, which are uploaded into a persistent staging texture via
1614
+ // copyExternalImageToTexture before each dispatch.
1615
+
1616
+ struct Params {
1617
+ out_w : u32,
1618
+ out_h : u32,
1619
+ _pad0 : u32,
1620
+ _pad1 : u32,
1621
+ }
1622
+
1623
+ @group(0) @binding(0) var src_tex : texture_2d<f32>;
1624
+ @group(0) @binding(1) var src_sampler : sampler;
1625
+ @group(0) @binding(2) var<uniform> params : Params;
1626
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f32>>;
1627
+
1628
+ @compute @workgroup_size(8, 8, 1)
1629
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1630
+ let x = gid.x;
1631
+ let y = gid.y;
1632
+ if (x >= params.out_w || y >= params.out_h) { return; }
1633
+
1634
+ let uv = vec2<f32>(
1635
+ (f32(x) + 0.5) / f32(params.out_w),
1636
+ (f32(y) + 0.5) / f32(params.out_h),
1637
+ );
1638
+ let rgba = textureSampleLevel(src_tex, src_sampler, uv, 0.0);
1639
+ output_buf[y * params.out_w + x] = rgba;
1640
+ }
1641
+ \`;var gt=\`enable f16;
1642
+
1643
+ // Input op \\u2014 f16 storage variant. Source is sampled as vec4<f32>; only the
1644
+ // store is demoted to vec4<f16>.
1645
+
1646
+ struct Params {
1647
+ out_w : u32,
1648
+ out_h : u32,
1649
+ _pad0 : u32,
1650
+ _pad1 : u32,
1651
+ }
1652
+
1653
+ @group(0) @binding(0) var src_tex : texture_2d<f32>;
1654
+ @group(0) @binding(1) var src_sampler : sampler;
1655
+ @group(0) @binding(2) var<uniform> params : Params;
1656
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f16>>;
1657
+
1658
+ @compute @workgroup_size(8, 8, 1)
1659
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1660
+ let x = gid.x;
1661
+ let y = gid.y;
1662
+ if (x >= params.out_w || y >= params.out_h) { return; }
1663
+
1664
+ let uv = vec2<f32>(
1665
+ (f32(x) + 0.5) / f32(params.out_w),
1666
+ (f32(y) + 0.5) / f32(params.out_h),
1667
+ );
1668
+ let rgba = textureSampleLevel(src_tex, src_sampler, uv, 0.0);
1669
+ output_buf[y * params.out_w + x] = vec4<f16>(rgba);
1670
+ }
1671
+ \`;var _t=\`// Input op \\u2014 sample a GPUExternalTexture (zero-copy VideoFrame import) into
1672
+ // the NHWC vec4<f32> output buffer at the target resolution. Used when the
1673
+ // caller passes a VideoFrame to setSource(); requires importExternalTexture
1674
+ // to be called in the same task as the dispatch (the texture is invalidated
1675
+ // after the current task completes).
1676
+
1677
+ struct Params {
1678
+ out_w : u32,
1679
+ out_h : u32,
1680
+ _pad0 : u32,
1681
+ _pad1 : u32,
1682
+ }
1683
+
1684
+ @group(0) @binding(0) var src_tex : texture_external;
1685
+ @group(0) @binding(1) var src_sampler : sampler;
1686
+ @group(0) @binding(2) var<uniform> params : Params;
1687
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f32>>;
1688
+
1689
+ @compute @workgroup_size(8, 8, 1)
1690
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1691
+ let x = gid.x;
1692
+ let y = gid.y;
1693
+ if (x >= params.out_w || y >= params.out_h) { return; }
1694
+
1695
+ let uv = vec2<f32>(
1696
+ (f32(x) + 0.5) / f32(params.out_w),
1697
+ (f32(y) + 0.5) / f32(params.out_h),
1698
+ );
1699
+ let rgba = textureSampleBaseClampToEdge(src_tex, src_sampler, uv);
1700
+ output_buf[y * params.out_w + x] = rgba;
1701
+ }
1702
+ \`;var bt=\`enable f16;
1703
+
1704
+ // Input op \\u2014 texture_external + f16 storage. Sample in f32, store as f16.
1705
+
1706
+ struct Params {
1707
+ out_w : u32,
1708
+ out_h : u32,
1709
+ _pad0 : u32,
1710
+ _pad1 : u32,
1711
+ }
1712
+
1713
+ @group(0) @binding(0) var src_tex : texture_external;
1714
+ @group(0) @binding(1) var src_sampler : sampler;
1715
+ @group(0) @binding(2) var<uniform> params : Params;
1716
+ @group(0) @binding(3) var<storage, read_write> output_buf : array<vec4<f16>>;
1717
+
1718
+ @compute @workgroup_size(8, 8, 1)
1719
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1720
+ let x = gid.x;
1721
+ let y = gid.y;
1722
+ if (x >= params.out_w || y >= params.out_h) { return; }
1723
+
1724
+ let uv = vec2<f32>(
1725
+ (f32(x) + 0.5) / f32(params.out_w),
1726
+ (f32(y) + 0.5) / f32(params.out_h),
1727
+ );
1728
+ let rgba = textureSampleBaseClampToEdge(src_tex, src_sampler, uv);
1729
+ output_buf[y * params.out_w + x] = vec4<f16>(rgba);
1730
+ }
1731
+ \`;var Mr=s=>typeof VideoFrame<"u"&&s instanceof VideoFrame,me=class{constructor(e,r,t){n(this,"output");n(this,"device");n(this,"dtype");n(this,"sampler");n(this,"uniformBuffer");n(this,"dispatch");n(this,"pipeline2d",null);n(this,"pipelineExternal",null);n(this,"stagingTex",null);n(this,"stagingW",0);n(this,"stagingH",0);n(this,"source",null);this.device=e.device,this.dtype=e.dtype,this.output=e.tensor(r,t,4),this.sampler=this.device.createSampler({magFilter:"linear",minFilter:"linear",addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"}),this.uniformBuffer=this.device.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let o=new ArrayBuffer(16);new Uint32Array(o,0,2).set([t,r]),this.device.queue.writeBuffer(this.uniformBuffer,0,o),this.dispatch=[Math.ceil(t/8),Math.ceil(r/8),1],this.pipeline2d=this.buildPipeline(this.dtype==="f16"?gt:ft)}setSource(e){this.source=e}run(){if(!this.source)throw new Error("InputWebGPU.run() called before setSource()");Mr(this.source)?this.runExternal(this.source):this.run2d(this.source)}run2d(e){this.ensureStagingTexture(e.width,e.height),this.device.queue.copyExternalImageToTexture({source:e,flipY:!1},{texture:this.stagingTex},[e.width,e.height]);let r=this.device.createBindGroup({layout:this.pipeline2d.getBindGroupLayout(0),entries:[{binding:0,resource:this.stagingTex.createView()},{binding:1,resource:this.sampler},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:this.output.buffer}}]});this.dispatchOnce(this.pipeline2d,r)}runExternal(e){this.pipelineExternal||(this.pipelineExternal=this.buildPipeline(this.dtype==="f16"?bt:_t));let r=this.device.importExternalTexture({source:e}),t=this.device.createBindGroup({layout:this.pipelineExternal.getBindGroupLayout(0),entries:[{binding:0,resource:r},{binding:1,resource:this.sampler},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:this.output.buffer}}]});this.dispatchOnce(this.pipelineExternal,t)}ensureStagingTexture(e,r){this.stagingTex&&this.stagingW===e&&this.stagingH===r||(this.stagingTex?.destroy(),this.stagingTex=this.device.createTexture({size:[e,r,1],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),this.stagingW=e,this.stagingH=r)}buildPipeline(e){let r=this.device.createShaderModule({code:e});return this.device.createComputePipeline({layout:"auto",compute:{module:r,entryPoint:"main"}})}dispatchOnce(e,r){let t=this.device.createCommandEncoder(),o=t.beginComputePass();o.setPipeline(e),o.setBindGroup(0,r),o.dispatchWorkgroups(...this.dispatch),o.end(),this.device.queue.submit([t.finish()])}};var Rr=navigator.gpu?GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST:0,F=class s{constructor(e,r,t){n(this,"device",e);n(this,"canvas",r);n(this,"dtype",t);n(this,"ops");n(this,"presenters");n(this,"canvasContext");n(this,"canvasFormat");n(this,"bytesPerElement");this.bytesPerElement=t==="f16"?2:4;let o=r.getContext("webgpu");if(!o)throw new Error("Failed to get WebGPU context from canvas");this.canvasContext=o,this.canvasFormat=navigator.gpu.getPreferredCanvasFormat(),this.canvasContext.configure({device:e,format:this.canvasFormat,alphaMode:"premultiplied"}),this.ops={Conv2d:(i,a,u)=>new J(this,i,a,u),DepthwiseConv2d:(i,a,u)=>new Z(this,i,a,u),Add:(i,a)=>new ee(this,i,a),Sigmoid:i=>new te(this,i),BilinearUpsample:(i,a)=>new re(this,i,a),BicubicUpsample:(i,a)=>new oe(this,i,a),ChannelConcat:(i,a)=>new ne(this,i,a),Conv2dAdd:(i,a,u,p)=>new ie(this,i,a,u,p),UpsampleConcat:(i,a,u)=>new se(this,i,a,u),UpsampleConv1x1:(i,a,u)=>new ae(this,i,a,u),UpsampleSigmoid:(i,a)=>new ue(this,i,a),Input:(i,a)=>new me(this,i,a)},this.presenters={CompositeSolid:(i,a,u)=>{let p=new pe(this,i,a,u);return{run:()=>{p.setOutput(this.getCurrentDisplayTexture()),p.run()}}},CompositeImage:(i,a,u)=>{let p=new le(this,i,a,u);return{run:()=>{p.setOutput(this.getCurrentDisplayTexture()),p.run()}}},CompositeImageBilinear:(i,a,u)=>{let p=new ce(this,i,a,u);return{run:()=>{p.setOutput(this.getCurrentDisplayTexture()),p.run()}}},CompositePassthrough:i=>{let a=new de(this,i);return{run:()=>{a.setOutput(this.getCurrentDisplayTexture()),a.run()}}}}}static async isAvailable(){return navigator.gpu?await navigator.gpu.requestAdapter()!==null:!1}static async hasF16Support(){if(!navigator.gpu)return!1;let e=await navigator.gpu.requestAdapter();return e?e.features.has("shader-f16"):!1}static async create(e){let r=e.dtype??"f32",t=e.device;if(t){if(r==="f16"&&!t.features.has("shader-f16"))throw new Error("WebGPU dtype='f16' requested but supplied device lacks \`shader-f16\`")}else{let o=await navigator.gpu.requestAdapter();if(!o)throw new Error("WebGPU adapter not available");if(r==="f16"&&!o.features.has("shader-f16"))throw new Error("WebGPU dtype='f16' requested but adapter lacks \`shader-f16\` feature");t=await o.requestDevice({requiredFeatures:r==="f16"?["shader-f16"]:[]})}return new s(t,e.canvas,r)}getCurrentDisplayTexture(){return this.canvasContext.getCurrentTexture()}tensor(e,r,t,o){let a=e*r*t*this.bytesPerElement,u=this.device.createBuffer({size:a,usage:Rr,mappedAtCreation:o!==void 0});if(o!==void 0){let p=u.getMappedRange();this.writeView(p,o),u.unmap()}return{h:e,w:r,c:t,buffer:u}}upload(e){let t=e.length*this.bytesPerElement,o=this.device.createBuffer({size:t,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});return this.writeView(o.getMappedRange(),e),o.unmap(),{buffer:o}}writeView(e,r){let t=this.dtype==="f16",o=r instanceof Uint16Array;if(t===o){t?new Uint16Array(e).set(r):new Float32Array(e).set(r);return}t?new Uint16Array(e).set(Q(r)):new Float32Array(e).set(A(r))}async readback(e){let r=this.device.createBuffer({size:e.buffer.size,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),t=this.device.createCommandEncoder();t.copyBufferToBuffer(e.buffer,0,r,0,e.buffer.size),this.device.queue.submit([t.finish()]),await r.mapAsync(GPUMapMode.READ);let o=r.getMappedRange(),i=this.dtype==="f16"?A(new Uint16Array(o.slice(0))):new Float32Array(o.slice(0));return r.unmap(),r.destroy(),i}async sync(){await this.device.queue.onSubmittedWorkDone()}destroy(){this.device.destroy()}};var Dr=\`#version 300 es
1732
+ const vec2 VERTS[6] = vec2[6](
1733
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
1734
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
1735
+ );
1736
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }\`,g=class{constructor(e){n(this,"backend",e);n(this,"shader","");n(this,"program");n(this,"samplers",[]);n(this,"uniformInts",{})}makeTexture(e,r,t){let o=this.backend.gl,i=this.backend.textureFormat,a=this.backend.toTextureView(e),u=o.createTexture();return o.bindTexture(o.TEXTURE_2D,u),o.texImage2D(o.TEXTURE_2D,0,i.internalFormat,r,t,0,i.format,i.type,a),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),u}defaultSetup(){let e=this.backend.gl,r=e.createShader(e.VERTEX_SHADER);e.shaderSource(r,Dr),e.compileShader(r);let t=e.createShader(e.FRAGMENT_SHADER);if(e.shaderSource(t,this.shader),e.compileShader(t),!e.getShaderParameter(t,e.COMPILE_STATUS))throw new Error(\`GLSL compile error: \${e.getShaderInfoLog(t)}\`);if(this.program=e.createProgram(),e.attachShader(this.program,r),e.attachShader(this.program,t),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:r,texture:t},o)=>{e.activeTexture(e.TEXTURE0+o),e.bindTexture(e.TEXTURE_2D,t),e.uniform1i(e.getUniformLocation(this.program,r),o)});for(let[r,t]of Object.entries(this.uniformInts))e.uniform1i(e.getUniformLocation(this.program,r),t);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 vt=\`#version 300 es
1737
+ // Conv2d \\u2014 handles all variants: 1\\xD71 (pointwise), 3\\xD73, 5\\xD75, strided, BN-fused.
1738
+ //
1739
+ // Tensor layout: NHWC vec4 \\u2014 texture dimensions (W * C/4, H).
1740
+ // texel (x * C_groups + c_group, y) = channels [c_group*4 .. c_group*4+3] at pixel (x,y).
1741
+ //
1742
+ // Weight layout: (I_groups * 4, K\\xB2 * O_groups) RGBA32F texture.
1743
+ // Row = z * u_out_c_groups + o_group (kernel_pos outer, out_group inner)
1744
+ // Col = i_group * 4 + mat_col (4 texels per mat4)
1745
+ // mat4[col][row] = weight(in_channel=col, out_channel=row)
1746
+ // Operation: (mat4 * in_val)[r] = \\u03A3_c weight(in=c, out=r) * in_val[c] \\u2014 same as WGSL.
1747
+ //
1748
+ // Bias layout: (O_groups, 1).
1749
+ //
1750
+ // Padding model: pad_top / pad_left applied to input offset; right/bottom asymmetry
1751
+ // is handled implicitly by the bounds check, same as the WGSL implementation.
1752
+
1753
+ precision highp float;
1754
+ precision highp int;
1755
+
1756
+ uniform sampler2D u_input; // (in_W * in_C_groups, in_H)
1757
+ uniform sampler2D u_weights; // (in_C_groups * 4, K\\xB2 * out_C_groups)
1758
+ uniform sampler2D u_bias; // (out_C_groups, 1)
1759
+
1760
+ uniform int u_in_w;
1761
+ uniform int u_in_h;
1762
+ uniform int u_in_c_groups;
1763
+ uniform int u_out_c_groups;
1764
+ uniform int u_kernel_h;
1765
+ uniform int u_kernel_w;
1766
+ uniform int u_stride;
1767
+ uniform int u_pad_top;
1768
+ uniform int u_pad_left;
1769
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu
1770
+
1771
+ out vec4 fragColor;
1772
+
1773
+ void main() {
1774
+ int fx = int(gl_FragCoord.x);
1775
+ int fy = int(gl_FragCoord.y);
1776
+ int x_out = fx / u_out_c_groups;
1777
+ int y_out = fy;
1778
+ int o_group = fx - x_out * u_out_c_groups;
1779
+
1780
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
1781
+
1782
+ for (int ky = 0; ky < u_kernel_h; ky++) {
1783
+ for (int kx = 0; kx < u_kernel_w; kx++) {
1784
+ int in_y = y_out * u_stride + ky - u_pad_top;
1785
+ int in_x = x_out * u_stride + kx - u_pad_left;
1786
+
1787
+ if (in_y < 0 || in_y >= u_in_h || in_x < 0 || in_x >= u_in_w) continue;
1788
+
1789
+ int z = ky * u_kernel_w + kx;
1790
+ int w_row = z * u_out_c_groups + o_group;
1791
+
1792
+ for (int i = 0; i < u_in_c_groups; i++) {
1793
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_in_c_groups + i, in_y), 0);
1794
+
1795
+ int base = i * 4;
1796
+ mat4 w = mat4(
1797
+ texelFetch(u_weights, ivec2(base, w_row), 0),
1798
+ texelFetch(u_weights, ivec2(base + 1, w_row), 0),
1799
+ texelFetch(u_weights, ivec2(base + 2, w_row), 0),
1800
+ texelFetch(u_weights, ivec2(base + 3, w_row), 0)
1801
+ );
1802
+ result += w * in_val;
1803
+ }
1804
+ }
1805
+ }
1806
+
1807
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
1808
+ else if (u_activation == 2) result = max(result, vec4(0.0));
1809
+
1810
+ fragColor = result;
1811
+ }
1812
+ \`;var he=class extends g{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader",vt);let a=b(t.h,i.kernel,i.stride,i.padding),u=b(t.w,i.kernel,i.stride,i.padding),p=t.c/4,l=i.outChannels/4,d=v(i.padding,t.h,a,i.kernel,i.stride),m=v(i.padding,t.w,u,i.kernel,i.stride),x=f(o.weights),T=f(o.bias),M=this.makeTexture(x,p*4,i.kernel*i.kernel*l),G=this.makeTexture(T,l,1),B=u*l,R=this.makeTexture(null,B,a);this.output={h:a,w:u,c:i.outChannels,texture:R,texW:B,texH:a},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:M},{name:"u_bias",texture:G}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_in_c_groups:p,u_out_c_groups:l,u_kernel_h:i.kernel,u_kernel_w:i.kernel,u_stride:i.stride,u_pad_top:d,u_pad_left:m,u_activation:i.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[B,a]}};var yt=\`#version 300 es
1813
+ // Depthwise Conv2d \\u2014 groups = in_channels (each channel convolved independently).
1814
+ //
1815
+ // Weight layout: (C_groups, K\\xB2) RGBA32F texture \\u2014 contrast with conv2d which uses mat4.
1816
+ // texel (c_group, z) = vec4 kernel weights for 4 channels at kernel position z.
1817
+ // Operation: element-wise multiply (not matmul). 4\\xD7 smaller than a mat4 approach.
1818
+ //
1819
+ // Padding model: same asymmetric SAME-pad handling as conv2d.glsl.
1820
+
1821
+ precision highp float;
1822
+ precision highp int;
1823
+
1824
+ uniform sampler2D u_input; // (in_W * C_groups, in_H)
1825
+ uniform sampler2D u_weights; // (C_groups, K\\xB2)
1826
+ uniform sampler2D u_bias; // (C_groups, 1)
1827
+
1828
+ uniform int u_in_w;
1829
+ uniform int u_in_h;
1830
+ uniform int u_c_groups;
1831
+ uniform int u_kernel_h;
1832
+ uniform int u_kernel_w;
1833
+ uniform int u_stride;
1834
+ uniform int u_pad_top;
1835
+ uniform int u_pad_left;
1836
+ uniform int u_apply_relu6; // 0 = none, 1 = relu6
1837
+
1838
+ out vec4 fragColor;
1839
+
1840
+ void main() {
1841
+ int fx = int(gl_FragCoord.x);
1842
+ int fy = int(gl_FragCoord.y);
1843
+ int x_out = fx / u_c_groups;
1844
+ int y_out = fy;
1845
+ int c_group = fx - x_out * u_c_groups;
1846
+
1847
+ vec4 result = texelFetch(u_bias, ivec2(c_group, 0), 0);
1848
+
1849
+ for (int ky = 0; ky < u_kernel_h; ky++) {
1850
+ for (int kx = 0; kx < u_kernel_w; kx++) {
1851
+ int in_y = y_out * u_stride + ky - u_pad_top;
1852
+ int in_x = x_out * u_stride + kx - u_pad_left;
1853
+
1854
+ if (in_y < 0 || in_y >= u_in_h || in_x < 0 || in_x >= u_in_w) continue;
1855
+
1856
+ int z = ky * u_kernel_w + kx;
1857
+
1858
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_c_groups + c_group, in_y), 0);
1859
+ vec4 w = texelFetch(u_weights, ivec2(c_group, z), 0);
1860
+
1861
+ result += w * in_val; // element-wise multiply, not matmul
1862
+ }
1863
+ }
1864
+
1865
+ if (u_apply_relu6 == 1) result = clamp(result, 0.0, 6.0);
1866
+
1867
+ fragColor = result;
1868
+ }
1869
+ \`;var fe=class extends g{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader",yt);let a=b(t.h,i.kernel,i.stride,i.padding),u=b(t.w,i.kernel,i.stride,i.padding),p=t.c/4,l=v(i.padding,t.h,a,i.kernel,i.stride),d=v(i.padding,t.w,u,i.kernel,i.stride),m=f(o.weights),x=f(o.bias),T=this.makeTexture(m,p,i.kernel*i.kernel),M=this.makeTexture(x,p,1),G=u*p,B=this.makeTexture(null,G,a);this.output={h:a,w:u,c:t.c,texture:B,texW:G,texH:a},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:T},{name:"u_bias",texture:M}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_c_groups:p,u_kernel_h:i.kernel,u_kernel_w:i.kernel,u_stride:i.stride,u_pad_top:l,u_pad_left:d,u_apply_relu6:i.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[G,a]}};var xt=\`#version 300 es
1870
+ // Element-wise add \\u2014 both inputs and output share the same texture dimensions.
1871
+ // Works for any 2D layout; used here with a flat (nVec4, 1) texture.
1872
+
1873
+ precision highp float;
1874
+
1875
+ uniform sampler2D u_input_a;
1876
+ uniform sampler2D u_input_b;
1877
+
1878
+ out vec4 fragColor;
1879
+
1880
+ void main() {
1881
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1882
+ fragColor = texelFetch(u_input_a, fc, 0) + texelFetch(u_input_b, fc, 0);
1883
+ }
1884
+ \`;var ge=class extends g{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",xt);let i=t,a=this.makeTexture(null,i.texW,i.texH);this.output={h:t.h,w:t.w,c:t.c,texture:a,texW:i.texW,texH:i.texH},this.inputs=[t,o],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:o.texture}],this.defaultSetup(),this.dispatch=[i.texW,i.texH]}};var wt=\`#version 300 es
1885
+ // Element-wise sigmoid \\u2014 works on any texture layout (flat or spatial).
1886
+
1887
+ precision highp float;
1888
+
1889
+ uniform sampler2D u_input;
1890
+
1891
+ out vec4 fragColor;
1892
+
1893
+ void main() {
1894
+ vec4 x = texelFetch(u_input, ivec2(gl_FragCoord.xy), 0);
1895
+ fragColor = 1.0 / (1.0 + exp(-x));
1896
+ }
1897
+ \`;var _e=class extends g{constructor(r,t){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",wt);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],this.samplers=[{name:"u_input",texture:o.texture}],this.defaultSetup(),this.dispatch=[o.texW,o.texH]}};var Ct=\`#version 300 es
1898
+ // Bilinear upsample (arbitrary scale) \\u2014 align_corners=False.
1899
+ //
1900
+ // Hardware texture filtering can't be used: adjacent texels in x belong to
1901
+ // different channel groups, not spatial neighbours. Manual 4-tap blend.
1902
+ //
1903
+ // Input: (in_W * c_groups, in_H)
1904
+ // Output: (out_W * c_groups, out_H)
1905
+
1906
+ precision highp float;
1907
+ precision highp int;
1908
+
1909
+ uniform sampler2D u_input;
1910
+ uniform int u_in_w;
1911
+ uniform int u_in_h;
1912
+ uniform int u_out_w;
1913
+ uniform int u_out_h;
1914
+ uniform int u_c_groups;
1915
+
1916
+ out vec4 fragColor;
1917
+
1918
+ void main() {
1919
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1920
+ int x_out = fc.x / u_c_groups;
1921
+ int y_out = fc.y;
1922
+ int c_group = fc.x - x_out * u_c_groups;
1923
+
1924
+ float scale_x = float(u_in_w) / float(u_out_w);
1925
+ float scale_y = float(u_in_h) / float(u_out_h);
1926
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
1927
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
1928
+
1929
+ int x0 = int(floor(src_x));
1930
+ int y0 = int(floor(src_y));
1931
+ int x1 = x0 + 1;
1932
+ int y1 = y0 + 1;
1933
+ float fx = src_x - float(x0);
1934
+ float fy = src_y - float(y0);
1935
+
1936
+ x0 = clamp(x0, 0, u_in_w - 1);
1937
+ y0 = clamp(y0, 0, u_in_h - 1);
1938
+ x1 = clamp(x1, 0, u_in_w - 1);
1939
+ y1 = clamp(y1, 0, u_in_h - 1);
1940
+
1941
+ vec4 v00 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y0), 0);
1942
+ vec4 v10 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y0), 0);
1943
+ vec4 v01 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y1), 0);
1944
+ vec4 v11 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y1), 0);
1945
+
1946
+ fragColor = mix(mix(v00, v10, fx), mix(v01, v11, fx), fy);
1947
+ }
1948
+ \`;var be=class extends g{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",Ct);let i=t.c/4,a=o.outW*i,u=this.makeTexture(null,a,o.outH);this.output={h:o.outH,w:o.outW,c:t.c,texture:u,texW:a,texH:o.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:o.outW,u_out_h:o.outH,u_c_groups:i},this.defaultSetup(),this.dispatch=[a,o.outH]}};var kt=\`#version 300 es
1949
+ // Bicubic upsample (arbitrary scale) \\u2014 Keys cubic, a=-0.75 (PyTorch default
1950
+ // for mode='bicubic', align_corners=False).
1951
+ //
1952
+ // Direct 2D, 4\\xD74 = 16 taps per output pixel. Hardware texture filtering
1953
+ // can't be used (NHWC vec4 layout means adjacent texels in x belong to
1954
+ // different channel groups, not spatial neighbours).
1955
+
1956
+ precision highp float;
1957
+ precision highp int;
1958
+
1959
+ uniform sampler2D u_input;
1960
+ uniform int u_in_w;
1961
+ uniform int u_in_h;
1962
+ uniform int u_out_w;
1963
+ uniform int u_out_h;
1964
+ uniform int u_c_groups;
1965
+
1966
+ const float A = -0.75;
1967
+
1968
+ float wcubic(float d) {
1969
+ float ad = abs(d);
1970
+ if (ad <= 1.0) return ((A + 2.0) * ad - (A + 3.0)) * ad * ad + 1.0;
1971
+ if (ad < 2.0) return ((A * ad - 5.0 * A) * ad + 8.0 * A) * ad - 4.0 * A;
1972
+ return 0.0;
1973
+ }
1974
+
1975
+ out vec4 fragColor;
1976
+
1977
+ void main() {
1978
+ ivec2 fc = ivec2(gl_FragCoord.xy);
1979
+ int x_out = fc.x / u_c_groups;
1980
+ int y_out = fc.y;
1981
+ int c_group = fc.x - x_out * u_c_groups;
1982
+
1983
+ float src_x = (float(x_out) + 0.5) * float(u_in_w) / float(u_out_w) - 0.5;
1984
+ float src_y = (float(y_out) + 0.5) * float(u_in_h) / float(u_out_h) - 0.5;
1985
+
1986
+ int x0 = int(floor(src_x));
1987
+ int y0 = int(floor(src_y));
1988
+ float fx = src_x - float(x0);
1989
+ float fy = src_y - float(y0);
1990
+
1991
+ // Weights for offsets {-1, 0, 1, 2} from x0/y0.
1992
+ float wx[4];
1993
+ float wy[4];
1994
+ wx[0] = wcubic(1.0 + fx); wx[1] = wcubic(fx); wx[2] = wcubic(1.0 - fx); wx[3] = wcubic(2.0 - fx);
1995
+ wy[0] = wcubic(1.0 + fy); wy[1] = wcubic(fy); wy[2] = wcubic(1.0 - fy); wy[3] = wcubic(2.0 - fy);
1996
+
1997
+ vec4 acc = vec4(0.0);
1998
+ for (int j = 0; j < 4; j++) {
1999
+ int sy = clamp(y0 + j - 1, 0, u_in_h - 1);
2000
+ for (int i = 0; i < 4; i++) {
2001
+ int sx = clamp(x0 + i - 1, 0, u_in_w - 1);
2002
+ vec4 v = texelFetch(u_input, ivec2(sx * u_c_groups + c_group, sy), 0);
2003
+ acc += (wx[i] * wy[j]) * v;
2004
+ }
2005
+ }
2006
+ fragColor = acc;
2007
+ }
2008
+ \`;var ve=class extends g{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",kt);let i=t.c/4,a=o.outW*i,u=this.makeTexture(null,a,o.outH);this.output={h:o.outH,w:o.outW,c:t.c,texture:u,texW:a,texH:o.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:o.outW,u_out_h:o.outH,u_c_groups:i},this.defaultSetup(),this.dispatch=[a,o.outH]}};var Tt=\`#version 300 es
2009
+ // Channel concat \\u2014 concatenates two NHWC textures along the channel dimension.
2010
+ // Both inputs must share the same spatial dimensions (W, H). Channel counts are
2011
+ // multiples of 4 so each "group" is one vec4 texel.
2012
+ //
2013
+ // Input A: (W * a_c_groups, H)
2014
+ // Input B: (W * b_c_groups, H)
2015
+ // Output: (W * (a_c_groups + b_c_groups), H)
2016
+
2017
+ precision highp float;
2018
+ precision highp int;
2019
+
2020
+ uniform sampler2D u_input_a;
2021
+ uniform sampler2D u_input_b;
2022
+ uniform int u_a_c_groups;
2023
+ uniform int u_b_c_groups;
2024
+
2025
+ out vec4 fragColor;
2026
+
2027
+ void main() {
2028
+ ivec2 fc = ivec2(gl_FragCoord.xy);
2029
+ int out_groups = u_a_c_groups + u_b_c_groups;
2030
+ int x_spatial = fc.x / out_groups;
2031
+ int c_out = fc.x - x_spatial * out_groups;
2032
+ int y = fc.y;
2033
+
2034
+ if (c_out < u_a_c_groups) {
2035
+ fragColor = texelFetch(u_input_a, ivec2(x_spatial * u_a_c_groups + c_out, y), 0);
2036
+ } else {
2037
+ int c_b = c_out - u_a_c_groups;
2038
+ fragColor = texelFetch(u_input_b, ivec2(x_spatial * u_b_c_groups + c_b, y), 0);
2039
+ }
2040
+ }
2041
+ \`;var ye=class extends g{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",Tt);let i=t.c/4,a=o.c/4,u=i+a,p=t.c+o.c,l=t.w*u,d=this.makeTexture(null,l,t.h);this.output={h:t.h,w:t.w,c:p,texture:d,texW:l,texH:t.h},this.inputs=[t,o],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:o.texture}],this.uniformInts={u_a_c_groups:i,u_b_c_groups:a},this.defaultSetup(),this.dispatch=[l,t.h]}};var Pt=\`#version 300 es
2042
+ // Bilinear upsample + sigmoid fused \\u2014 sigmoid applied at write time, no intermediate buffer.
2043
+ // See bilinear_upsample.glsl for the manual 4-tap blend rationale.
2044
+ //
2045
+ // Input: (in_W * c_groups, in_H)
2046
+ // Output: (out_W * c_groups, out_H)
2047
+
2048
+ precision highp float;
2049
+ precision highp int;
2050
+
2051
+ uniform sampler2D u_input;
2052
+ uniform int u_in_w;
2053
+ uniform int u_in_h;
2054
+ uniform int u_out_w;
2055
+ uniform int u_out_h;
2056
+ uniform int u_c_groups;
2057
+
2058
+ out vec4 fragColor;
2059
+
2060
+ void main() {
2061
+ ivec2 fc = ivec2(gl_FragCoord.xy);
2062
+ int x_out = fc.x / u_c_groups;
2063
+ int y_out = fc.y;
2064
+ int c_group = fc.x - x_out * u_c_groups;
2065
+
2066
+ float scale_x = float(u_in_w) / float(u_out_w);
2067
+ float scale_y = float(u_in_h) / float(u_out_h);
2068
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
2069
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
2070
+
2071
+ int x0 = int(floor(src_x));
2072
+ int y0 = int(floor(src_y));
2073
+ int x1 = x0 + 1;
2074
+ int y1 = y0 + 1;
2075
+ float fx = src_x - float(x0);
2076
+ float fy = src_y - float(y0);
2077
+
2078
+ x0 = clamp(x0, 0, u_in_w - 1);
2079
+ y0 = clamp(y0, 0, u_in_h - 1);
2080
+ x1 = clamp(x1, 0, u_in_w - 1);
2081
+ y1 = clamp(y1, 0, u_in_h - 1);
2082
+
2083
+ vec4 v00 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y0), 0);
2084
+ vec4 v10 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y0), 0);
2085
+ vec4 v01 = texelFetch(u_input, ivec2(x0 * u_c_groups + c_group, y1), 0);
2086
+ vec4 v11 = texelFetch(u_input, ivec2(x1 * u_c_groups + c_group, y1), 0);
2087
+
2088
+ vec4 result = mix(mix(v00, v10, fx), mix(v01, v11, fx), fy);
2089
+ fragColor = 1.0 / (1.0 + exp(-result));
2090
+ }
2091
+ \`;var xe=class extends g{constructor(r,t,o){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",Pt);let i=t.c/4,a=o.outW*i,u=this.makeTexture(null,a,o.outH);this.output={h:o.outH,w:o.outW,c:t.c,texture:u,texW:a,texH:o.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:o.outW,u_out_h:o.outH,u_c_groups:i},this.defaultSetup(),this.dispatch=[a,o.outH]}};var Wt=\`#version 300 es
2092
+ // Bilinear upsample (input_a) + channel concat (with input_b) fused.
2093
+ // input_a is the decoder tensor at small spatial res (in_h \\xD7 in_w), bilinearly resized to (out_h \\xD7 out_w).
2094
+ // input_b is the encoder skip feature already at output resolution.
2095
+ // Output channels [0..a_groups-1] come from upsampled input_a.
2096
+ // Output channels [a_groups..out_groups-1] come from input_b (passthrough).
2097
+
2098
+ precision highp float;
2099
+ precision highp int;
2100
+
2101
+ uniform sampler2D u_input_a;
2102
+ uniform sampler2D u_input_b;
2103
+ uniform int u_in_w;
2104
+ uniform int u_in_h;
2105
+ uniform int u_out_w;
2106
+ uniform int u_out_h;
2107
+ uniform int u_a_c_groups;
2108
+ uniform int u_b_c_groups;
2109
+
2110
+ out vec4 fragColor;
2111
+
2112
+ void main() {
2113
+ ivec2 fc = ivec2(gl_FragCoord.xy);
2114
+ int out_groups = u_a_c_groups + u_b_c_groups;
2115
+ int x_out = fc.x / out_groups;
2116
+ int c_out = fc.x - x_out * out_groups;
2117
+ int y_out = fc.y;
2118
+
2119
+ if (c_out < u_a_c_groups) {
2120
+ // Bilinear sample from input_a at (x_out, y_out) for channel group c_out.
2121
+ float scale_x = float(u_in_w) / float(u_out_w);
2122
+ float scale_y = float(u_in_h) / float(u_out_h);
2123
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
2124
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
2125
+
2126
+ int x0 = int(floor(src_x));
2127
+ int y0 = int(floor(src_y));
2128
+ int x1 = x0 + 1;
2129
+ int y1 = y0 + 1;
2130
+ float fx = src_x - float(x0);
2131
+ float fy = src_y - float(y0);
2132
+
2133
+ x0 = clamp(x0, 0, u_in_w - 1);
2134
+ y0 = clamp(y0, 0, u_in_h - 1);
2135
+ x1 = clamp(x1, 0, u_in_w - 1);
2136
+ y1 = clamp(y1, 0, u_in_h - 1);
2137
+
2138
+ vec4 v00 = texelFetch(u_input_a, ivec2(x0 * u_a_c_groups + c_out, y0), 0);
2139
+ vec4 v10 = texelFetch(u_input_a, ivec2(x1 * u_a_c_groups + c_out, y0), 0);
2140
+ vec4 v01 = texelFetch(u_input_a, ivec2(x0 * u_a_c_groups + c_out, y1), 0);
2141
+ vec4 v11 = texelFetch(u_input_a, ivec2(x1 * u_a_c_groups + c_out, y1), 0);
2142
+
2143
+ fragColor = mix(mix(v00, v10, fx), mix(v01, v11, fx), fy);
2144
+ } else {
2145
+ int c_b = c_out - u_a_c_groups;
2146
+ fragColor = texelFetch(u_input_b, ivec2(x_out * u_b_c_groups + c_b, y_out), 0);
2147
+ }
2148
+ }
2149
+ \`;var we=class extends g{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights",[]);n(this,"output");n(this,"dispatch");n(this,"shader",Wt);let a=t.c/4,u=o.c/4,p=a+u,l=t.c+o.c,d=i.outW*p,m=this.makeTexture(null,d,i.outH);this.output={h:i.outH,w:i.outW,c:l,texture:m,texW:d,texH:i.outH},this.inputs=[t,o],this.samplers=[{name:"u_input_a",texture:t.texture},{name:"u_input_b",texture:o.texture}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:i.outW,u_out_h:i.outH,u_a_c_groups:a,u_b_c_groups:u},this.defaultSetup(),this.dispatch=[d,i.outH]}};var Gt=\`#version 300 es
2150
+ // Bilinear upsample + 1\\xD71 pointwise conv fused.
2151
+ // For each output pixel + out group, bilinearly samples each in_group from input,
2152
+ // applies the 1\\xD71 conv weight, and writes the activated result.
2153
+ //
2154
+ // Input: (in_W * in_groups, in_H)
2155
+ // Output: (out_W * out_groups, out_H)
2156
+ // Weights: (in_groups * 4, out_groups) (K=1, so kernel-row dimension collapses)
2157
+ // mat4[col][row] = weight(in_channel=col, out_channel=row)
2158
+ // Bias: (out_groups, 1)
2159
+
2160
+ precision highp float;
2161
+ precision highp int;
2162
+
2163
+ uniform sampler2D u_input;
2164
+ uniform sampler2D u_weights;
2165
+ uniform sampler2D u_bias;
2166
+
2167
+ uniform int u_in_w;
2168
+ uniform int u_in_h;
2169
+ uniform int u_out_w;
2170
+ uniform int u_out_h;
2171
+ uniform int u_in_c_groups;
2172
+ uniform int u_out_c_groups;
2173
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu
2174
+
2175
+ out vec4 fragColor;
2176
+
2177
+ void main() {
2178
+ int fx = int(gl_FragCoord.x);
2179
+ int fy = int(gl_FragCoord.y);
2180
+ int x_out = fx / u_out_c_groups;
2181
+ int y_out = fy;
2182
+ int o_group = fx - x_out * u_out_c_groups;
2183
+
2184
+ float scale_x = float(u_in_w) / float(u_out_w);
2185
+ float scale_y = float(u_in_h) / float(u_out_h);
2186
+ float src_x = (float(x_out) + 0.5) * scale_x - 0.5;
2187
+ float src_y = (float(y_out) + 0.5) * scale_y - 0.5;
2188
+
2189
+ int x0 = int(floor(src_x));
2190
+ int y0 = int(floor(src_y));
2191
+ int x1 = x0 + 1;
2192
+ int y1 = y0 + 1;
2193
+ float fx_w = src_x - float(x0);
2194
+ float fy_w = src_y - float(y0);
2195
+
2196
+ x0 = clamp(x0, 0, u_in_w - 1);
2197
+ y0 = clamp(y0, 0, u_in_h - 1);
2198
+ x1 = clamp(x1, 0, u_in_w - 1);
2199
+ y1 = clamp(y1, 0, u_in_h - 1);
2200
+
2201
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
2202
+
2203
+ for (int i = 0; i < u_in_c_groups; i++) {
2204
+ vec4 v00 = texelFetch(u_input, ivec2(x0 * u_in_c_groups + i, y0), 0);
2205
+ vec4 v10 = texelFetch(u_input, ivec2(x1 * u_in_c_groups + i, y0), 0);
2206
+ vec4 v01 = texelFetch(u_input, ivec2(x0 * u_in_c_groups + i, y1), 0);
2207
+ vec4 v11 = texelFetch(u_input, ivec2(x1 * u_in_c_groups + i, y1), 0);
2208
+
2209
+ vec4 sampled = mix(mix(v00, v10, fx_w), mix(v01, v11, fx_w), fy_w);
2210
+
2211
+ int base = i * 4;
2212
+ mat4 w = mat4(
2213
+ texelFetch(u_weights, ivec2(base, o_group), 0),
2214
+ texelFetch(u_weights, ivec2(base + 1, o_group), 0),
2215
+ texelFetch(u_weights, ivec2(base + 2, o_group), 0),
2216
+ texelFetch(u_weights, ivec2(base + 3, o_group), 0)
2217
+ );
2218
+ result += w * sampled;
2219
+ }
2220
+
2221
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
2222
+ else if (u_activation == 2) result = max(result, vec4(0.0));
2223
+
2224
+ fragColor = result;
2225
+ }
2226
+ \`;var Ce=class extends g{constructor(r,t,o,i){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader",Gt);let a=t.c/4,u=i.outChannels/4,p=f(o.weights),l=f(o.bias),d=this.makeTexture(p,a*4,u),m=this.makeTexture(l,u,1),x=i.outW*u,T=this.makeTexture(null,x,i.outH);this.output={h:i.outH,w:i.outW,c:i.outChannels,texture:T,texW:x,texH:i.outH},this.inputs=[t],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_weights",texture:d},{name:"u_bias",texture:m}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_out_w:i.outW,u_out_h:i.outH,u_in_c_groups:a,u_out_c_groups:u,u_activation:i.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[x,i.outH]}};var Bt=\`#version 300 es
2227
+ // Conv2d + skip add fused \\u2014 identical to conv2d.glsl with a skip texture added at write time.
2228
+ // See conv2d.glsl for tensor/weight layout details.
2229
+
2230
+ precision highp float;
2231
+ precision highp int;
2232
+
2233
+ uniform sampler2D u_input;
2234
+ uniform sampler2D u_skip;
2235
+ uniform sampler2D u_weights;
2236
+ uniform sampler2D u_bias;
2237
+
2238
+ uniform int u_in_w;
2239
+ uniform int u_in_h;
2240
+ uniform int u_in_c_groups;
2241
+ uniform int u_out_c_groups;
2242
+ uniform int u_kernel_h;
2243
+ uniform int u_kernel_w;
2244
+ uniform int u_stride;
2245
+ uniform int u_pad_top;
2246
+ uniform int u_pad_left;
2247
+ uniform int u_activation; // 0 = none, 1 = relu6, 2 = relu
2248
+
2249
+ out vec4 fragColor;
2250
+
2251
+ void main() {
2252
+ int fx = int(gl_FragCoord.x);
2253
+ int fy = int(gl_FragCoord.y);
2254
+ int x_out = fx / u_out_c_groups;
2255
+ int y_out = fy;
2256
+ int o_group = fx - x_out * u_out_c_groups;
2257
+
2258
+ vec4 result = texelFetch(u_bias, ivec2(o_group, 0), 0);
2259
+
2260
+ for (int ky = 0; ky < u_kernel_h; ky++) {
2261
+ for (int kx = 0; kx < u_kernel_w; kx++) {
2262
+ int in_y = y_out * u_stride + ky - u_pad_top;
2263
+ int in_x = x_out * u_stride + kx - u_pad_left;
2264
+
2265
+ if (in_y < 0 || in_y >= u_in_h || in_x < 0 || in_x >= u_in_w) continue;
2266
+
2267
+ int z = ky * u_kernel_w + kx;
2268
+ int w_row = z * u_out_c_groups + o_group;
2269
+
2270
+ for (int i = 0; i < u_in_c_groups; i++) {
2271
+ vec4 in_val = texelFetch(u_input, ivec2(in_x * u_in_c_groups + i, in_y), 0);
2272
+
2273
+ int base = i * 4;
2274
+ mat4 w = mat4(
2275
+ texelFetch(u_weights, ivec2(base, w_row), 0),
2276
+ texelFetch(u_weights, ivec2(base + 1, w_row), 0),
2277
+ texelFetch(u_weights, ivec2(base + 2, w_row), 0),
2278
+ texelFetch(u_weights, ivec2(base + 3, w_row), 0)
2279
+ );
2280
+ result += w * in_val;
2281
+ }
2282
+ }
2283
+ }
2284
+
2285
+ if (u_activation == 1) result = clamp(result, 0.0, 6.0);
2286
+ else if (u_activation == 2) result = max(result, vec4(0.0));
2287
+
2288
+ fragColor = result + texelFetch(u_skip, ivec2(fx, fy), 0);
2289
+ }
2290
+ \`;var ke=class extends g{constructor(r,t,o,i,a){super(r);n(this,"inputs");n(this,"weights");n(this,"output");n(this,"dispatch");n(this,"shader",Bt);let u=b(t.h,a.kernel,a.stride,a.padding),p=b(t.w,a.kernel,a.stride,a.padding),l=t.c/4,d=a.outChannels/4,m=v(a.padding,t.h,u,a.kernel,a.stride),x=v(a.padding,t.w,p,a.kernel,a.stride),T=f(i.weights),M=f(i.bias),G=this.makeTexture(T,l*4,a.kernel*a.kernel*d),B=this.makeTexture(M,d,1),R=p*d,qt=this.makeTexture(null,R,u);this.output={h:u,w:p,c:a.outChannels,texture:qt,texW:R,texH:u},this.inputs=[t,o],this.weights=[],this.samplers=[{name:"u_input",texture:t.texture},{name:"u_skip",texture:o.texture},{name:"u_weights",texture:G},{name:"u_bias",texture:B}],this.uniformInts={u_in_w:t.w,u_in_h:t.h,u_in_c_groups:l,u_out_c_groups:d,u_kernel_h:a.kernel,u_kernel_w:a.kernel,u_stride:a.stride,u_pad_top:m,u_pad_left:x,u_activation:a.activation==="relu6"?1:0},this.defaultSetup(),this.dispatch=[R,u]}};var Ut=\`#version 300 es
2291
+ // Composite an RGBA image over a solid background color, gated by a
2292
+ // 1-channel alpha mask. Output: premultiplied RGBA.
2293
+ //
2294
+ // Assumes image and alpha textures are the same h\\xD7w and that the canvas
2295
+ // (viewport) matches that resolution \\u2014 no resampling here. The upscaler op
2296
+ // (separate) handles aligning alpha to the image resolution upstream.
2297
+
2298
+ precision highp float;
2299
+
2300
+ uniform sampler2D u_image; // image as NHWC vec4 (RGBA in vec4)
2301
+ uniform sampler2D u_alpha; // alpha as NHWC vec4 (value in .r)
2302
+ uniform vec3 u_bgColor; // straight-alpha background color, [0,1]
2303
+
2304
+ out vec4 fragColor;
2305
+
2306
+ void main() {
2307
+ // WebGL gl_FragCoord origin is bottom-left, but tensor textures are
2308
+ // stored top-down (matches NHWC + getImageData). Flip y when sampling so
2309
+ // the displayed image is upright. (WebGPU's @builtin(position) is already
2310
+ // top-down, so its compositor doesn't need this.)
2311
+ int H = textureSize(u_image, 0).y;
2312
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
2313
+ vec3 fg = texelFetch(u_image, px, 0).rgb;
2314
+ float a = texelFetch(u_alpha, px, 0).r;
2315
+
2316
+ // Straight-alpha composite, then premultiply for the canvas's
2317
+ // premultiplied surface format.
2318
+ vec3 rgb = fg * a + u_bgColor * (1.0 - a);
2319
+ fragColor = vec4(rgb, 1.0);
2320
+ }
2321
+ \`;var Zr=\`#version 300 es
2322
+ const vec2 VERTS[6] = vec2[6](
2323
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2324
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2325
+ );
2326
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }\`,Te=class{constructor(e,r,t,o){n(this,"backend",e);n(this,"program");n(this,"imageTex");n(this,"alphaTex");n(this,"bgColor");if(r.h!==t.h||r.w!==t.w)throw new Error(\`CompositeSolid: image (\${r.h}\\xD7\${r.w}) and alpha (\${t.h}\\xD7\${t.w}) must match. Run the upscaler first.\`);let i=r,a=t;this.imageTex=i.texture,this.alphaTex=a.texture,this.bgColor=o;let u=e.gl,p=u.createShader(u.VERTEX_SHADER);u.shaderSource(p,Zr),u.compileShader(p);let l=u.createShader(u.FRAGMENT_SHADER);if(u.shaderSource(l,Ut),u.compileShader(l),!u.getShaderParameter(l,u.COMPILE_STATUS))throw new Error(\`composite_solid GLSL compile error: \${u.getShaderInfoLog(l)}\`);if(this.program=u.createProgram(),u.attachShader(this.program,p),u.attachShader(this.program,l),u.linkProgram(this.program),!u.getProgramParameter(this.program,u.LINK_STATUS))throw new Error(\`composite_solid GLSL link error: \${u.getProgramInfoLog(this.program)}\`)}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 St=\`#version 300 es
2327
+ // Same as composite_solid but bg is a texture (NHWC vec4 tensor) instead of
2328
+ // a uniform color. Caller invariants: image, alpha, bg all share h \\xD7 w, and
2329
+ // canvas dimensions match.
2330
+
2331
+ precision highp float;
2332
+ precision highp int;
2333
+
2334
+ uniform sampler2D u_image;
2335
+ uniform sampler2D u_alpha;
2336
+ uniform sampler2D u_bg;
2337
+
2338
+ out vec4 fragColor;
2339
+
2340
+ void main() {
2341
+ // WebGL gl_FragCoord origin is bottom-left; tensor textures are top-down.
2342
+ int H = textureSize(u_image, 0).y;
2343
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
2344
+
2345
+ vec3 fg = texelFetch(u_image, px, 0).rgb;
2346
+ float a = texelFetch(u_alpha, px, 0).r;
2347
+ vec3 bg = texelFetch(u_bg, px, 0).rgb;
2348
+
2349
+ vec3 rgb = fg * a + bg * (1.0 - a);
2350
+ fragColor = vec4(rgb, 1.0);
2351
+ }
2352
+ \`;var to=\`#version 300 es
2353
+ const vec2 VERTS[6] = vec2[6](
2354
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2355
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2356
+ );
2357
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }\`,Pe=class{constructor(e,r,t,o){n(this,"backend",e);n(this,"program");n(this,"imageTex");n(this,"alphaTex");n(this,"bgTex");if(r.h!==t.h||r.w!==t.w||r.h!==o.h||r.w!==o.w)throw new Error(\`CompositeImage: image (\${r.h}\\xD7\${r.w}), alpha (\${t.h}\\xD7\${t.w}), and bg (\${o.h}\\xD7\${o.w}) must all match. Run upscaler / resizer first.\`);this.imageTex=r.texture,this.alphaTex=t.texture,this.bgTex=o.texture;let i=e.gl,a=i.createShader(i.VERTEX_SHADER);i.shaderSource(a,to),i.compileShader(a);let u=i.createShader(i.FRAGMENT_SHADER);if(i.shaderSource(u,St),i.compileShader(u),!i.getShaderParameter(u,i.COMPILE_STATUS))throw new Error(\`composite_image GLSL compile error: \${i.getShaderInfoLog(u)}\`);if(this.program=i.createProgram(),i.attachShader(this.program,a),i.attachShader(this.program,u),i.linkProgram(this.program),!i.getProgramParameter(this.program,i.LINK_STATUS))throw new Error(\`composite_image GLSL link error: \${i.getProgramInfoLog(this.program)}\`)}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 Et=\`#version 300 es
2358
+ // Like composite_image but bg is bilinearly sampled \\u2014 bg is at a smaller
2359
+ // resolution than (image, alpha) at canvas h \\xD7 w. Used by CompositorBlur to
2360
+ // skip the final full-res upsample in the blur pyramid.
2361
+ //
2362
+ // Tensor textures are top-down (origin at top-left of the source image),
2363
+ // while gl_FragCoord origin is bottom-left \\u2014 flip y when computing px.
2364
+
2365
+ precision highp float;
2366
+ precision highp int;
2367
+
2368
+ uniform sampler2D u_image;
2369
+ uniform sampler2D u_alpha;
2370
+ uniform sampler2D u_bg;
2371
+
2372
+ uniform int u_out_w;
2373
+ uniform int u_out_h;
2374
+ uniform int u_bg_w;
2375
+ uniform int u_bg_h;
2376
+
2377
+ out vec4 fragColor;
2378
+
2379
+ void main() {
2380
+ int x = int(gl_FragCoord.x);
2381
+ int y = u_out_h - 1 - int(gl_FragCoord.y);
2382
+
2383
+ vec3 fg = texelFetch(u_image, ivec2(x, y), 0).rgb;
2384
+ float a = texelFetch(u_alpha, ivec2(x, y), 0).r;
2385
+
2386
+ // Bilinear sample bg at the corresponding location. align_corners=False.
2387
+ float src_x = (float(x) + 0.5) * (float(u_bg_w) / float(u_out_w)) - 0.5;
2388
+ float src_y = (float(y) + 0.5) * (float(u_bg_h) / float(u_out_h)) - 0.5;
2389
+ int x0 = clamp(int(floor(src_x)), 0, u_bg_w - 1);
2390
+ int x1 = clamp(int(floor(src_x)) + 1, 0, u_bg_w - 1);
2391
+ int y0 = clamp(int(floor(src_y)), 0, u_bg_h - 1);
2392
+ int y1 = clamp(int(floor(src_y)) + 1, 0, u_bg_h - 1);
2393
+ float wx = src_x - floor(src_x);
2394
+ float wy = src_y - floor(src_y);
2395
+
2396
+ vec3 tl = texelFetch(u_bg, ivec2(x0, y0), 0).rgb;
2397
+ vec3 tr = texelFetch(u_bg, ivec2(x1, y0), 0).rgb;
2398
+ vec3 bl = texelFetch(u_bg, ivec2(x0, y1), 0).rgb;
2399
+ vec3 br = texelFetch(u_bg, ivec2(x1, y1), 0).rgb;
2400
+ vec3 bg = (1.0 - wy) * ((1.0 - wx) * tl + wx * tr)
2401
+ + wy * ((1.0 - wx) * bl + wx * br);
2402
+
2403
+ vec3 rgb = fg * a + bg * (1.0 - a);
2404
+ fragColor = vec4(rgb, 1.0);
2405
+ }
2406
+ \`;var oo=\`#version 300 es
2407
+ const vec2 VERTS[6] = vec2[6](
2408
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2409
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2410
+ );
2411
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }\`,We=class{constructor(e,r,t,o){n(this,"backend",e);n(this,"program");n(this,"imageTex");n(this,"alphaTex");n(this,"bgTex");n(this,"outW");n(this,"outH");n(this,"bgW");n(this,"bgH");if(r.h!==t.h||r.w!==t.w)throw new Error(\`CompositeImageBilinear: image (\${r.h}\\xD7\${r.w}) and alpha (\${t.h}\\xD7\${t.w}) must match.\`);this.imageTex=r.texture,this.alphaTex=t.texture,this.bgTex=o.texture,this.outW=r.w,this.outH=r.h,this.bgW=o.w,this.bgH=o.h;let i=e.gl,a=i.createShader(i.VERTEX_SHADER);i.shaderSource(a,oo),i.compileShader(a);let u=i.createShader(i.FRAGMENT_SHADER);if(i.shaderSource(u,Et),i.compileShader(u),!i.getShaderParameter(u,i.COMPILE_STATUS))throw new Error(\`composite_image_bilinear GLSL compile error: \${i.getShaderInfoLog(u)}\`);if(this.program=i.createProgram(),i.attachShader(this.program,a),i.attachShader(this.program,u),i.linkProgram(this.program),!i.getProgramParameter(this.program,i.LINK_STATUS))throw new Error(\`composite_image_bilinear GLSL link error: \${i.getProgramInfoLog(this.program)}\`)}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 It=\`#version 300 es
2412
+ // Passthrough "compositor": writes the image directly to the canvas
2413
+ // (default framebuffer). No alpha math, no background. Used by RenderOp
2414
+ // when the renderer is in disabled state.
2415
+ //
2416
+ // Caller invariants:
2417
+ // - canvas (viewport) === image h \\xD7 w (no resampling here)
2418
+
2419
+ precision highp float;
2420
+
2421
+ uniform sampler2D u_image; // image as NHWC vec4 (RGBA)
2422
+
2423
+ out vec4 fragColor;
2424
+
2425
+ void main() {
2426
+ // WebGL gl_FragCoord origin is bottom-left; tensor textures are stored
2427
+ // top-down. Flip y so the displayed image is upright. (Matches what the
2428
+ // existing composite_solid.glsl does.)
2429
+ int H = textureSize(u_image, 0).y;
2430
+ ivec2 px = ivec2(int(gl_FragCoord.x), H - 1 - int(gl_FragCoord.y));
2431
+ vec3 rgb = texelFetch(u_image, px, 0).rgb;
2432
+ fragColor = vec4(rgb, 1.0);
2433
+ }
2434
+ \`;var io=\`#version 300 es
2435
+ const vec2 VERTS[6] = vec2[6](
2436
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2437
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2438
+ );
2439
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }\`,Ge=class{constructor(e,r){n(this,"backend",e);n(this,"program");n(this,"imageTex");this.imageTex=r.texture;let t=e.gl,o=t.createShader(t.VERTEX_SHADER);t.shaderSource(o,io),t.compileShader(o);let i=t.createShader(t.FRAGMENT_SHADER);if(t.shaderSource(i,It),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS))throw new Error(\`composite_passthrough GLSL compile error: \${t.getShaderInfoLog(i)}\`);if(this.program=t.createProgram(),t.attachShader(this.program,o),t.attachShader(this.program,i),t.linkProgram(this.program),!t.getProgramParameter(this.program,t.LINK_STATUS))throw new Error(\`composite_passthrough GLSL link error: \${t.getProgramInfoLog(this.program)}\`)}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 Lt=\`#version 300 es
2440
+ precision highp float;
2441
+
2442
+ // Input op \\u2014 bilinear-sample a source texture (RGBA8 unorm uploaded from an
2443
+ // ImageBitmap or VideoFrame) at the target resolution. Output texture format
2444
+ // is RGBA32F or RGBA16F depending on backend.dtype; the framebuffer write
2445
+ // stores fragColor into whatever format is bound.
2446
+
2447
+ uniform sampler2D u_src;
2448
+ uniform int u_out_w;
2449
+ uniform int u_out_h;
2450
+
2451
+ out vec4 fragColor;
2452
+
2453
+ void main() {
2454
+ vec2 uv = (gl_FragCoord.xy + vec2(0.5)) / vec2(float(u_out_w), float(u_out_h));
2455
+ fragColor = texture(u_src, uv);
2456
+ }
2457
+ \`;var ao=\`#version 300 es
2458
+ const vec2 VERTS[6] = vec2[6](
2459
+ vec2(-1.0,-1.0), vec2(1.0,-1.0), vec2(-1.0,1.0),
2460
+ vec2(-1.0,1.0), vec2(1.0,-1.0), vec2(1.0,1.0)
2461
+ );
2462
+ void main() { gl_Position = vec4(VERTS[gl_VertexID], 0.0, 1.0); }\`,Be=class{constructor(e,r,t){n(this,"backend",e);n(this,"output");n(this,"gl");n(this,"program");n(this,"srcTex");n(this,"uOutW");n(this,"uOutH");n(this,"uSrc");n(this,"source",null);let o=e.gl;this.gl=o,this.output=e.tensor(r,t,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,ao),o.compileShader(i);let a=o.createShader(o.FRAGMENT_SHADER);if(o.shaderSource(a,Lt),o.compileShader(a),!o.getShaderParameter(a,o.COMPILE_STATUS))throw new Error(\`Input GLSL compile error: \${o.getShaderInfoLog(a)}\`);if(this.program=o.createProgram(),o.attachShader(this.program,i),o.attachShader(this.program,a),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")}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 U=class s{constructor(e,r,t){n(this,"gl",e);n(this,"canvas",r);n(this,"dtype",t);n(this,"ops");n(this,"presenters");n(this,"fbo");n(this,"textureFormat");this.fbo=e.createFramebuffer(),this.textureFormat=t==="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,a)=>new he(this,o,i,a),DepthwiseConv2d:(o,i,a)=>new fe(this,o,i,a),Add:(o,i)=>new ge(this,o,i),Sigmoid:o=>new _e(this,o),BilinearUpsample:(o,i)=>new be(this,o,i),BicubicUpsample:(o,i)=>new ve(this,o,i),ChannelConcat:(o,i)=>new ye(this,o,i),Conv2dAdd:(o,i,a,u)=>new ke(this,o,i,a,u),UpsampleConcat:(o,i,a)=>new we(this,o,i,a),UpsampleConv1x1:(o,i,a)=>new Ce(this,o,i,a),UpsampleSigmoid:(o,i)=>new xe(this,o,i),Input:(o,i)=>new Be(this,o,i)},this.presenters={CompositeSolid:(o,i,a)=>new Te(this,o,i,a),CompositeImage:(o,i,a)=>new Pe(this,o,i,a),CompositeImageBilinear:(o,i,a)=>new We(this,o,i,a),CompositePassthrough:o=>new Ge(this,o)}}static isAvailable(){let e=null;try{return e=new OffscreenCanvas(1,1).getContext("webgl2"),!(!e||!e.getExtension("EXT_color_buffer_float"))}catch{return!1}finally{e?.getExtension("WEBGL_lose_context")?.loseContext()}}static create(e){let r=e.canvas.getContext("webgl2");if(!r)throw new Error("WebGL2 not available");if(!r.getExtension("EXT_color_buffer_float"))throw new Error("EXT_color_buffer_float not available");return new s(r,e.canvas,e.dtype??"f32")}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 r=this.dtype==="f16",t=e instanceof Uint16Array;return r===t?e:r?Q(e):A(e)}tensor(e,r,t,o){let i=r*(t/4),a=e,u=this.gl,p=u.createTexture(),l=this.textureFormat,d=this.toTextureView(o??null);return u.bindTexture(u.TEXTURE_2D,p),u.texImage2D(u.TEXTURE_2D,0,l.internalFormat,i,a,0,l.format,l.type,d),u.texParameteri(u.TEXTURE_2D,u.TEXTURE_MIN_FILTER,u.NEAREST),u.texParameteri(u.TEXTURE_2D,u.TEXTURE_MAG_FILTER,u.NEAREST),u.texParameteri(u.TEXTURE_2D,u.TEXTURE_WRAP_S,u.CLAMP_TO_EDGE),u.texParameteri(u.TEXTURE_2D,u.TEXTURE_WRAP_T,u.CLAMP_TO_EDGE),{h:e,w:r,c:t,texture:p,texW:i,texH:a}}upload(e){return{data:e}}async readback(e){let r=this.gl,t=this.textureFormat;if(r.bindFramebuffer(r.FRAMEBUFFER,this.fbo),r.framebufferTexture2D(r.FRAMEBUFFER,r.COLOR_ATTACHMENT0,r.TEXTURE_2D,e.texture,0),this.dtype==="f16"){let i=new Uint16Array(e.texW*e.texH*4);return r.readPixels(0,0,e.texW,e.texH,t.format,r.HALF_FLOAT,i),r.bindFramebuffer(r.FRAMEBUFFER,null),A(i)}let o=new Float32Array(e.texW*e.texH*4);return r.readPixels(0,0,e.texW,e.texH,t.format,r.FLOAT,o),r.bindFramebuffer(r.FRAMEBUFFER,null),o}async sync(){let e=this.gl,r=e.fenceSync(e.SYNC_GPU_COMMANDS_COMPLETE,0);if(!r){e.finish();return}for(e.flush();;){let t=e.clientWaitSync(r,0,0);if(t===e.ALREADY_SIGNALED||t===e.CONDITION_SATISFIED){e.deleteSync(r);return}if(t===e.WAIT_FAILED)throw e.deleteSync(r),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()}};var Le=P("setup_backend");async function At(s,e,r={}){let t=s.backend==="webgpu"||s.backend==="auto",o=!!s.outputCanvas,i=()=>s.outputCanvas??new OffscreenCanvas(e.w,e.h),a=null;if(t&&await F.isAvailable()){let p=s.dtype==="f16"&&!await F.hasF16Support()?"f32":s.dtype,l=i();try{let d=await F.create({canvas:l,dtype:p});return d.device.lost.then(m=>{m.reason!=="destroyed"&&r.onContextLost?.({message:\`WebGPU device lost: \${m.reason}\${m.message?" \\u2014 "+m.message:""}\`,source:"backend-lost",recoverable:!1,cause:m})}).catch(()=>{}),{backend:d,resolvedBackend:"webgpu",resolvedDtype:p,canvas:l}}catch(d){Le.warn("WebGPU isAvailable but create() threw; trying WebGL fallback:",d),a=d,o&&Le.warn("transfer-capture topology: WebGL fallback may also fail on the same canvas")}}if(U.isAvailable()){let p=i();try{let l=U.create({canvas:p,dtype:s.dtype});return p.addEventListener("webglcontextlost",d=>{r.onContextLost?.({message:"WebGL context lost",source:"backend-lost",recoverable:!1,cause:d})}),{backend:l,resolvedBackend:"webgl",resolvedDtype:s.dtype,canvas:p}}catch(l){Le.warn("WebGL create() threw:",l),a=l}}let u=a?\` (last error: \${a.message??a})\`:"";throw new Error(\`setup_backend: no usable GPU backend\${u}\`)}var Ue=.5,uo=3,Ft=10,po=30,S=P("autotune"),Ot={xxs:k,xs:k,small:k,compact:E,medium:W,large:W,xl:I};async function Mt(s,e=po){s instanceof U&&(Ue=Ue*.5);let r=1e3/e*Ue;S(\`start; budget per source frame: \${r.toFixed(1)}ms (source \${e}fps \\xD7 \${Ue} safety)\`),S("backend dtype:",s.dtype);let t=null;for(let o of Ie){if(!Ot[o.model]){S("skip",o.model,"(network class not implemented in TS yet)");continue}try{S(\`bench \${o.model} @ \${o.resolution.w}\\xD7\${o.resolution.h} skipFrames=\${o.skipFrames} \\u2026\`);let i=await mo(s,o),a=i<=r;if(S(\` \${o.model}: \${i.toFixed(1)}ms / source frame \${a?"\\u2713 within budget":"\\u2717 over budget"}\`),(a||t===null)&&(t=o),!a)break}catch(i){S(" bench failed for",o.model,":",i);break}}if(!t)throw new Error("autotune: no implementable preset available");return S("picked",t.model),t}function lo(s){let e=Object.create(s);return e.ops={...s.ops,Conv2d:(r,t,o)=>{let i={weights:new Float32Array(o.kernel*o.kernel*r.c*o.outChannels),bias:new Float32Array(o.outChannels)};return s.ops.Conv2d(r,i,o)},DepthwiseConv2d:(r,t,o)=>{let i={weights:new Float32Array(o.kernel*o.kernel*r.c),bias:new Float32Array(r.c)};return s.ops.DepthwiseConv2d(r,i,o)},Conv2dAdd:(r,t,o,i)=>{let a={weights:new Float32Array(i.kernel*i.kernel*r.c*i.outChannels),bias:new Float32Array(i.outChannels)};return s.ops.Conv2dAdd(r,t,a,i)},UpsampleConv1x1:(r,t,o)=>{let i={weights:new Float32Array(r.c*o.outChannels),bias:new Float32Array(o.outChannels)};return s.ops.UpsampleConv1x1(r,i,o)}},e}function co(){let s=new Proxy({},{get:()=>s});return s}async function mo(s,e){let r=Ot[e.model];if(!r)throw new Error(\`microbench: no network class for '\${e.model}'\`);let t=lo(s),o=t.tensor(e.resolution.h,e.resolution.w,4),i=new r(t,o,co());for(let l=0;l<uo;l++)i.run(),await s.sync();let a=e.skipFrames+1,u=performance.now();for(let l=0;l<Ft;l++)l%a===0&&i.run();return await s.sync(),(performance.now()-u)/Ft}async function Rt(s,e,r){let t;if(s==="auto")t=await Mt(r);else if(typeof s=="string"){let o=V(s);if(!o)throw new Error(\`resolve_preset: unknown preset name '\${s}'\`);t=o}else t=s;return t.dtype!==e?{...t,dtype:e}:t}function Dt(s){let e=null,r=null;return s.onmessage=t=>{let{frame:o}=t.data;if(r){let i=r;r=null,i(o)}else e&&e.close(),e=o},s.start?.(),new ReadableStream({pull(t){if(e){t.enqueue(e),e=null;return}return new Promise(o=>{r=i=>{t.enqueue(i),o()}})},cancel(){e&&(e.close(),e=null),s.close()}},{highWaterMark:0})}function Ht(s){switch(s.topology.input){case"mstp":if(!s.inputReadable)throw new Error("create_input: topology.input='mstp' but InitData.inputReadable missing");return s.inputReadable;case"rvfc-postmessage":if(!s.inputPort)throw new Error("create_input: topology.input='rvfc-postmessage' but InitData.inputPort missing");return Dt(s.inputPort)}}function Vt(s){return new TransformStream({transform(e,r){s.process(e);let t=new VideoFrame(s.canvas,{timestamp:e.timestamp});e.close(),r.enqueue(t)}})}function Nt(s){return new WritableStream({write(e){s.process(e),e.close()}})}function zt(s,e){return new WritableStream({write(r){s.process(r),r.close();let t=s.canvas.transferToImageBitmap();e.postMessage({bmp:t},[t])},close(){e.close()}})}var ho=P("create_output");function Xt(s,e,r){switch(s.topology.output){case"mstg":{if(!s.outputWritable)throw new Error("create_output: topology.output='mstg' but InitData.outputWritable missing");let t=Vt(e);return t.readable.pipeTo(s.outputWritable,{signal:r}).catch(o=>{r.aborted||ho.warn("mstg downstream pipe failed:",o)}),t.writable}case"transfer-capture":return Nt(e);case"bitmap-shuttle":{if(!s.outputPort)throw new Error("create_output: topology.output='bitmap-shuttle' but InitData.outputPort missing");return zt(e,s.outputPort)}}}function $t(s){if(!s.onFirstFrame)return s.input.pipeTo(s.output,{signal:s.signal});let e=!1,r=s.onFirstFrame,t=new TransformStream({transform(o,i){i.enqueue(o),e||(e=!0,queueMicrotask(r))}});return s.input.pipeThrough(t,{signal:s.signal}).pipeTo(s.output,{signal:s.signal})}var _=P("worker");_("script loaded");var w=null,O=null,Se=null;self.onmessage=async function(s){let{cmd:e,data:r,request_id:t}=s.data;_("cmd received:",e,"request_id:",t);try{let o=await fo(e,r);_("cmd ok:",e),Co(t,o)}catch(o){_("cmd FAILED:",e,o),H("error",{message:\`\${e} failed: \${o.message??String(o)}\`,source:"worker",recoverable:!1,cause:o})}};async function fo(s,e){switch(s){case"init":return go(e);case"startRender":return _o(e.weights);case"setBackground":return bo(e);case"setEnabled":return vo(e.enabled);case"setPreset":return yo(e);case"getStats":return w?.getStats()??null;case"destroy":return xo();default:throw new Error(\`unknown cmd: \${s}\`)}}async function go(s){if(Ae(!!s.debug),_("handleInit: start; topology=",s.topology,"preset=",s.preset,"backend=",s.backend,"dtype=",s.dtype),w)throw new Error("handleInit: already initialized");_("handleInit: setupBackend\\u2026");let e=await At(s,s.canvasSize,{onContextLost:i=>{_("GPU context lost:",i.message),H("error",i)}});_("handleInit: backend ready:",e.resolvedBackend,e.resolvedDtype,"canvas:",e.canvas.width,"x",e.canvas.height),_("handleInit: constructing Renderer (boot mode)\\u2026"),w=new Y({backend:e.backend,backendKind:e.resolvedBackend,canvas:e.canvas,background:s.background,enabled:s.enabled,topology:s.topology}),O=new AbortController,_("handleInit: createInputStream\\u2026");let r=Ht(s);_("handleInit: createOutputSink\\u2026");let t=Xt(s,w,O.signal);_("handleInit: starting pipe (passthrough)\\u2026"),$t({input:r,output:t,signal:O.signal}).then(()=>{_("pipe completed (input ended)")}).catch(i=>{if(O?.signal.aborted){_("pipe aborted (expected on destroy)");return}_("pipe FAILED:",i),H("error",{message:\`pipe failed: \${i.message}\`,source:"worker",recoverable:!1,cause:i})}),_("handleInit: resolvePreset\\u2026");let o=await Rt(s.preset,e.resolvedDtype,e.backend);return _("handleInit: preset resolved:",o),Se=o,_("handleInit: done; returning InitResponse, awaiting startRender"),{resolvedPreset:o,resolvedBackend:e.resolvedBackend,resolvedDtype:e.resolvedDtype}}async function _o(s){if(_("handleStartRender: start; weights bytes:",s.byteLength),!w||!Se)throw new Error("handleStartRender: handleInit not completed");_("handleStartRender: attaching network via setPreset\\u2026"),w.setPreset(Se,s),_("handleStartRender: ready (effect mode active)"),H("ready",void 0)}async function bo(s){w?.setBackground(s)}async function vo(s){w?.setEnabled(s)}async function yo(s){if(!w)throw new Error("handleSetPreset: not initialized");if(!s.weights)throw new Error("handleSetPreset: weights required for runtime preset swap");if(s.preset==="auto")throw new Error("handleSetPreset: 'auto' not supported on runtime swap (use at init for autotune)");let e=typeof s.preset=="string"?V(s.preset)??wo(s.preset):s.preset;return w.setPreset(e,s.weights),{resolvedPreset:e}}async function xo(){O?.abort(),O=null,w?.destroy(),w=null,Se=null}function wo(s){throw new Error(\`handleSetPreset: unknown preset '\${s}'\`)}function Co(s,e){let r={request_id:s,res:e};self.postMessage(r)}function H(s,e){let r={request_id:s,res:e};self.postMessage(r)}function al(s){H("stats",s)}export{al as emitStats};
2463
+ `;var x=_("pipeline");function gt(){{let i=new Blob([re],{type:"application/javascript"}),t=URL.createObjectURL(i),e=new Worker(t,{type:"module"});return URL.revokeObjectURL(t),e}}var _t="https://cdn.longpipe.dev/models/v/0.0.1/",bt={background:"blur",preset:"auto",weightsBaseUrl:_t,audio:"passthrough",enabled:true,adaptive:true,debug:false},xe={w:1280,h:720};function vt(i,t){if(t)return t;let r=i.getVideoTracks()[0]?.getSettings()??{};return {w:r.width||xe.w,h:r.height||xe.h}}function ye(i,t){return `${i.replace(/\/$/,"")}/model_${t}.bin`}var ne=class{stream;ready;controller;worker;inputCleanup;outputCleanup;adaptive=null;bgCleanup=null;constructor(t,e){let r={...bt,...e};se(r.debug);let n=ce(),a=ge(n.input,t),s=vt(t,r.outputResolution),o=we(n.output,s);this.inputCleanup=a.cleanup,this.outputCleanup=o.cleanup,this.stream=de(o.videoTrack,t,r.audio),o.startPassthrough?.(t),this.worker=gt(),this.controller=new A(this.worker),this.ready=new Promise((p,l)=>{this.controller.addPersistentListener("ready",()=>{x("ready handler invoked; resolving .ready"),r.onReady?.(),p();}),this.controller.addPersistentListener("error",h=>{console.error(`[longpipe/pipeline] error (${h.source}):`,h.message),h.recoverable||l(new Error(h.message)),r.onError?.(h);});});let u={topology:n,preset:r.preset,enabled:r.enabled,backend:"auto",dtype:"f16",canvasSize:s,debug:r.debug,...a.initFields,...o.initFields},c=[...a.transferList,...o.transferList];this.bootstrap(u,r.background,c,r.weightsBaseUrl,r.adaptive,r.onError);}async bootstrap(t,e,r,n,a,s){try{x("normalizing background\u2026");let o=await te(e);this.bgCleanup=o.cleanup??null;let u={...t,background:o.background},c=[...r,...o.transferList??[]];x("sending init\u2026");let p=await this.controller.sendMessage("init",u,c);x("init resolved:",p);let l=ye(n,p.resolvedPreset.model);x("fetching weights:",l);let h=await fetch(l);if(!h.ok)throw new Error(`weights fetch failed: ${h.status} ${l}`);let g=await h.arrayBuffer();x("weights bytes:",g.byteLength),x("sending startRender\u2026"),await this.controller.sendMessage("startRender",{weights:g},[g]),x("startRender resolved; awaiting first frame"),u.preset==="auto"&&a&&(this.adaptive=new W({backendKind:p.resolvedBackend,initialModel:p.resolvedPreset.model,weightsBaseUrl:n,buildWeightsUrl:ye,getStats:()=>this.getStats(),swapPreset:async(T,J)=>{await this.controller.sendMessage("setPreset",{preset:T,weights:J},[J]);},onError:s}),this.adaptive.start());}catch(o){console.error("[longpipe/pipeline] bootstrap failed:",o),this.controller.handleMessage.call(this.controller,{data:{request_id:"error",res:{message:o.message??String(o),source:"worker",recoverable:false,cause:o}}});}}then(t,e){return this.ready.then(()=>t?t(this):this,e)}async setBackground(t){let e=await te(t),r=this.bgCleanup;this.bgCleanup=e.cleanup??null,await this.controller.sendMessage("setBackground",e.background,e.transferList),r?.();}setPreset(t,e){this.controller.sendMessage("setPreset",{preset:t,weights:e});}setEnabled(t){this.controller.sendMessage("setEnabled",{enabled:t});}async getStats(){return this.controller.sendMessage("getStats",{})}destroy(){this.adaptive?.stop(),this.adaptive=null,this.bgCleanup?.(),this.bgCleanup=null,this.controller.sendMessage("destroy",{}),this.controller.terminate(),this.inputCleanup(),this.outputCleanup();}};var C=new Float32Array(1),I=new Uint32Array(C.buffer);function wt(i){C[0]=i;let t=I[0],e=t>>>16&32768,r=t&2147483647,n=(r>>>23)-127+15;if((t&2139095040)===2139095040)return (t&8388607)!==0?e|32256:e|31744;if(n>=31)return e|31744;if(n<=0){if(n<-10)return e;let c=r&8388607|8388608,p=14-n,l=c>>>p,h=c>>>p-1&1,g=(c&(1<<p-1)-1)!==0?1:0;return h&&(g||l&1)&&(l+=1),e|l}let a=r>>>13&1023,s=r>>>12&1,o=(r&4095)!==0?1:0,u=n<<10|a;return s&&(o||a&1)&&(u+=1,(u>>>10&31)===31)?e|31744:e|u}function xt(i){let t=(i&32768)<<16,e=(i&31744)>>>10,r=i&1023;if(e===0){if(r===0)return I[0]=t,C[0];let n=r,a=1;for(;(n&1024)===0;)n<<=1,a-=1;return n&=1023,I[0]=t|a+127-15<<23|n<<13,C[0]}return e===31?(I[0]=t|2139095040|r<<13,C[0]):(I[0]=t|e+127-15<<23|r<<13,C[0])}function ke(i){let t=new Uint16Array(i.length);for(let e=0;e<i.length;e++)t[e]=wt(i[e]);return t}function ie(i){let t=new Float32Array(i.length);for(let e=0;e<i.length;e++)t[e]=xt(i[e]);return t}var d=class{constructor(t){this.backend=t;}backend;shader="";pipeline;bindGroup;uniformDefs=[];uniformBuffers={};createUniform(t,e){this.uniformDefs.push({name:t,type:e});}setUniform(t,e){let r=this.backend.device.createBuffer({size:e.byteLength,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST,mappedAtCreation:true});e instanceof Uint32Array?new Uint32Array(r.getMappedRange()).set(e):new Float32Array(r.getMappedRange()).set(e),r.unmap(),this.uniformBuffers[t]=r;}defaultBindGroup(){let t=[],e=0;for(let r of this.inputs)t.push({binding:e++,resource:{buffer:r.buffer}});for(let r of this.weights)t.push({binding:e++,resource:{buffer:r.buffer}});for(let r of this.uniformDefs)t.push({binding:e++,resource:{buffer:this.uniformBuffers[r.name]}});return t.push({binding:e,resource:{buffer:this.output.buffer}}),this.backend.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:t})}defaultSetup(){let t=this.backend.device.createShaderModule({code:this.shader});this.pipeline=this.backend.device.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"main"}}),this.bindGroup=this.defaultBindGroup();}run(){let t=this.backend.device.createCommandEncoder(),e=t.beginComputePass();e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.dispatchWorkgroups(...this.dispatch),e.end(),this.backend.device.queue.submit([t.finish()]);}};var Ce=`// Conv2d compute shader \u2014 handles all variants:
2
2464
  // 1\xD71 (pointwise), 3\xD73, 5\xD75, strided, BN-fused
3
2465
  //
4
2466
  // Tensor layout: NHWC with channels in vec4 groups
@@ -81,7 +2543,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
81
2543
  let out_idx = y * params.out_w * O + x * O + o;
82
2544
  output_buf[out_idx] = result;
83
2545
  }
84
- `;var ve=`enable f16;
2546
+ `;var Pe=`enable f16;
85
2547
 
86
2548
  // Conv2d \u2014 full f16 variant.
87
2549
  // All buffers (input, output, weights, bias) are f16. Weights are converted
@@ -154,7 +2616,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
154
2616
 
155
2617
  output_buf[y * params.out_w * O + x * O + o] = result;
156
2618
  }
157
- `;function h(i,t,e,r){return typeof r=="number"?Math.floor((i+2*r-t)/e)+1:r==="same"?Math.ceil(i/e):Math.floor((i-t)/e)+1}function gt(i,t,e,r){return Math.floor(Math.max((t-1)*r+e-i,0)/2)}function _(i,t,e,r,a){return typeof i=="number"?i:i==="same"?gt(t,e,r,a):0}function m(i){return i instanceof Float32Array||i instanceof Uint16Array?i:new Float32Array(i)}var S=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,a){super(t),this.shader=t.dtype==="f16"?ve:_e;let o=h(e.h,a.kernel,a.stride,a.padding),s=h(e.w,a.kernel,a.stride,a.padding),n=e.c/4,u=a.outChannels/4,c=_(a.padding,e.h,o,a.kernel,a.stride),p=_(a.padding,e.w,s,a.kernel,a.stride);this.output=t.tensor(o,s,a.outChannels),this.inputs=[e],this.weights=[t.upload(m(r.weights)),t.upload(m(r.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,o,s,n,u,a.kernel,a.kernel,a.stride,c,p,a.activation==="relu6"?1:0])),this.defaultSetup(),this.dispatch=[Math.ceil(s/8),Math.ceil(o/8),u];}};var ye=`// Depthwise Conv2d \u2014 groups = in_channels (each channel convolved independently).
2619
+ `;function b(i,t,e,r){return typeof r=="number"?Math.floor((i+2*r-t)/e)+1:r==="same"?Math.ceil(i/e):Math.floor((i-t)/e)+1}function Ct(i,t,e,r){return Math.floor(Math.max((t-1)*r+e-i,0)/2)}function v(i,t,e,r,n){return typeof i=="number"?i:i==="same"?Ct(t,e,r,n):0}function f(i){return i instanceof Float32Array||i instanceof Uint16Array?i:new Float32Array(i)}var F=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,n){super(t),this.shader=t.dtype==="f16"?Pe:Ce;let a=b(e.h,n.kernel,n.stride,n.padding),s=b(e.w,n.kernel,n.stride,n.padding),o=e.c/4,u=n.outChannels/4,c=v(n.padding,e.h,a,n.kernel,n.stride),p=v(n.padding,e.w,s,n.kernel,n.stride);this.output=t.tensor(a,s,n.outChannels),this.inputs=[e],this.weights=[t.upload(f(r.weights)),t.upload(f(r.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,a,s,o,u,n.kernel,n.kernel,n.stride,c,p,n.activation==="relu6"?1:0])),this.defaultSetup(),this.dispatch=[Math.ceil(s/8),Math.ceil(a/8),u];}};var Ie=`// Depthwise Conv2d \u2014 groups = in_channels (each channel convolved independently).
158
2620
  //
159
2621
  // Weight layout: [K*K][channel_groups] array of vec4
160
2622
  // weight index = (ky*kernel_w + kx) * channel_groups + c
@@ -229,7 +2691,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
229
2691
  let out_idx = y * params.out_w * C + x * C + c;
230
2692
  output_buf[out_idx] = result;
231
2693
  }
232
- `;var we=`enable f16;
2694
+ `;var Te=`enable f16;
233
2695
 
234
2696
  // Depthwise conv2d \u2014 full f16 variant. All buffers f16.
235
2697
 
@@ -294,7 +2756,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
294
2756
 
295
2757
  output_buf[y * params.out_w * C + x * C + c] = result;
296
2758
  }
297
- `;var A=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,a){super(t),this.shader=t.dtype==="f16"?we:ye;let o=h(e.h,a.kernel,a.stride,a.padding),s=h(e.w,a.kernel,a.stride,a.padding),n=e.c/4,u=_(a.padding,e.h,o,a.kernel,a.stride),c=_(a.padding,e.w,s,a.kernel,a.stride);this.output=t.tensor(o,s,e.c),this.inputs=[e],this.weights=[t.upload(m(r.weights)),t.upload(m(r.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,o,s,n,a.kernel,a.kernel,a.stride,u,c,a.activation==="relu6"?1:0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(s/8),Math.ceil(o/8),n];}};var xe=`// Element-wise add of two tensors \u2014 used for residual connections in MBConv blocks.
2759
+ `;var O=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,n){super(t),this.shader=t.dtype==="f16"?Te:Ie;let a=b(e.h,n.kernel,n.stride,n.padding),s=b(e.w,n.kernel,n.stride,n.padding),o=e.c/4,u=v(n.padding,e.h,a,n.kernel,n.stride),c=v(n.padding,e.w,s,n.kernel,n.stride);this.output=t.tensor(a,s,e.c),this.inputs=[e],this.weights=[t.upload(f(r.weights)),t.upload(f(r.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,a,s,o,n.kernel,n.kernel,n.stride,u,c,n.activation==="relu6"?1:0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(s/8),Math.ceil(a/8),o];}};var Ue=`// Element-wise add of two tensors \u2014 used for residual connections in MBConv blocks.
298
2760
  // Operates on the flat float buffer directly; layout is irrelevant for a pure element-wise op.
299
2761
 
300
2762
  struct Params {
@@ -315,7 +2777,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
315
2777
  if (idx >= params.size) { return; }
316
2778
  output[idx] = input_a[idx] + input_b[idx];
317
2779
  }
318
- `;var Pe=`enable f16;
2780
+ `;var Se=`enable f16;
319
2781
 
320
2782
  // Element-wise add \u2014 full f16 variant.
321
2783
  // array<f16> is binary-compatible with array<vec4<f16>> written by the f16 conv shaders.
@@ -338,7 +2800,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
338
2800
  if (idx >= params.size) { return; }
339
2801
  output[idx] = input_a[idx] + input_b[idx];
340
2802
  }
341
- `;var O=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Pe:xe;let a=e.h*e.w*e.c;this.output=t.tensor(e.h,e.w,e.c),this.inputs=[e,r],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([a,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(a/256),1,1];}};var ke=`// Element-wise sigmoid: output = 1 / (1 + exp(-x)).
2803
+ `;var M=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Se:Ue;let n=e.h*e.w*e.c;this.output=t.tensor(e.h,e.w,e.c),this.inputs=[e,r],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([n,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(n/256),1,1];}};var Ee=`// Element-wise sigmoid: output = 1 / (1 + exp(-x)).
342
2804
  // Operates on packed vec4 buffers (NHWC layout). exp() and arithmetic are element-wise on vec4.
343
2805
 
344
2806
  struct Params {
@@ -358,7 +2820,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
358
2820
  if (idx >= params.n_groups) { return; }
359
2821
  output_buf[idx] = 1.0 / (1.0 + exp(-input_buf[idx]));
360
2822
  }
361
- `;var Ue=`enable f16;
2823
+ `;var We=`enable f16;
362
2824
 
363
2825
  // Sigmoid \u2014 full f16 variant.
364
2826
 
@@ -380,7 +2842,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
380
2842
  let x = vec4<f32>(input_buf[idx]);
381
2843
  output_buf[idx] = vec4<f16>(1.0 / (1.0 + exp(-x)));
382
2844
  }
383
- `;var E=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e){super(t),this.shader=t.dtype==="f16"?Ue:ke;let r=e.h*e.w*(e.c/4);this.output=t.tensor(e.h,e.w,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([r,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r/256),1,1];}};var Ie=`// Bilinear upsample (arbitrary ratio), align_corners=False (matches PyTorch default).
2845
+ `;var R=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e){super(t),this.shader=t.dtype==="f16"?We:Ee;let r=e.h*e.w*(e.c/4);this.output=t.tensor(e.h,e.w,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([r,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r/256),1,1];}};var Be=`// Bilinear upsample (arbitrary ratio), align_corners=False (matches PyTorch default).
384
2846
  // Input/output in NHWC vec4 format: index = y*W*(C/4) + x*(C/4) + c_group.
385
2847
  // Each thread computes one output pixel for one channel group (vec4 = 4 channels).
386
2848
 
@@ -494,7 +2956,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
494
2956
 
495
2957
  output_buf[oy * params.out_w * C + ox * C + c] = vec4<f16>(result);
496
2958
  }
497
- `;var F=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Ge:Ie;let a=e.c/4;this.output=t.tensor(r.outH,r.outW,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,r.outH,r.outW,a,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r.outW/8),Math.ceil(r.outH/8),a];}};var Be=`// Bicubic upsample (arbitrary scale) \u2014 Keys cubic, a=-0.75 (PyTorch default
2959
+ `;var D=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Ge:Be;let n=e.c/4;this.output=t.tensor(r.outH,r.outW,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,r.outH,r.outW,n,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r.outW/8),Math.ceil(r.outH/8),n];}};var Ae=`// Bicubic upsample (arbitrary scale) \u2014 Keys cubic, a=-0.75 (PyTorch default
498
2960
  // for mode='bicubic', align_corners=False). Direct 2D, 4\xD74 = 16 taps per
499
2961
  // output pixel. NHWC vec4 layout: index = y*W*(C/4) + x*(C/4) + c_group.
500
2962
 
@@ -561,7 +3023,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
561
3023
 
562
3024
  output_buf[oy * params.out_w * C + ox * C + c] = acc;
563
3025
  }
564
- `;var Ce=`enable f16;
3026
+ `;var Fe=`enable f16;
565
3027
 
566
3028
  // Bicubic upsample \u2014 full f16 storage variant. Bicubic weights and the per-
567
3029
  // pixel accumulator are computed in f32 (cheap to keep precision around the
@@ -630,7 +3092,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
630
3092
 
631
3093
  output_buf[oy * params.out_w * C + ox * C + c] = vec4<f16>(acc);
632
3094
  }
633
- `;var V=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Ce:Be;let a=e.c/4;this.output=t.tensor(r.outH,r.outW,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,r.outH,r.outW,a,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r.outW/8),Math.ceil(r.outH/8),a];}};var Te=`// Channel concatenation: output = cat(A, B, dim=channel).
3095
+ `;var H=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Fe:Ae;let n=e.c/4;this.output=t.tensor(r.outH,r.outW,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,r.outH,r.outW,n,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r.outW/8),Math.ceil(r.outH/8),n];}};var Oe=`// Channel concatenation: output = cat(A, B, dim=channel).
634
3096
  // Both A and B must share the same H\xD7W and be in NHWC vec4 format.
635
3097
  // Output layout: for each (y, x), the first a_groups vec4s come from A,
636
3098
  // followed by b_groups vec4s from B.
@@ -675,7 +3137,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
675
3137
  output_buf[out_idx] = input_b[y * W * Bg + x * Bg + c_b];
676
3138
  }
677
3139
  }
678
- `;var We=`enable f16;
3140
+ `;var Me=`enable f16;
679
3141
 
680
3142
  // Channel concatenation \u2014 full f16 variant.
681
3143
 
@@ -716,7 +3178,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
716
3178
  output_buf[out_idx] = input_b[y * W * Bg + x * Bg + c_b];
717
3179
  }
718
3180
  }
719
- `;var L=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?We:Te;let a=e.c/4,o=r.c/4,s=a+o;this.output=t.tensor(e.h,e.w,e.c+r.c),this.inputs=[e,r],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,a,o,s,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(e.w/8),Math.ceil(e.h/8),s];}};var Me=`// Conv2d + skip add fused.
3181
+ `;var L=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Me:Oe;let n=e.c/4,a=r.c/4,s=n+a;this.output=t.tensor(e.h,e.w,e.c+r.c),this.inputs=[e,r],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,n,a,s,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(e.w/8),Math.ceil(e.h/8),s];}};var Re=`// Conv2d + skip add fused.
720
3182
  // Identical to conv2d.wgsl except skip is an activation input (binding 1),
721
3183
  // added element-wise to the conv result at write time.
722
3184
  // Eliminates the separate add dispatch and its intermediate buffer round-trip.
@@ -790,7 +3252,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
790
3252
  let out_idx = y * params.out_w * O + x * O + o;
791
3253
  output_buf[out_idx] = result + skip_buf[out_idx];
792
3254
  }
793
- `;var Se=`enable f16;
3255
+ `;var De=`enable f16;
794
3256
 
795
3257
  // Conv2d + skip add fused \u2014 full f16 variant.
796
3258
 
@@ -864,7 +3326,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
864
3326
  let out_idx = y * params.out_w * O + x * O + o;
865
3327
  output_buf[out_idx] = result + skip_buf[out_idx];
866
3328
  }
867
- `;var H=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,a,o){super(t),this.shader=t.dtype==="f16"?Se:Me;let s=h(e.h,o.kernel,o.stride,o.padding),n=h(e.w,o.kernel,o.stride,o.padding),u=e.c/4,c=o.outChannels/4,p=_(o.padding,e.h,s,o.kernel,o.stride),l=_(o.padding,e.w,n,o.kernel,o.stride);this.output=t.tensor(s,n,o.outChannels),this.inputs=[e,r],this.weights=[t.upload(m(a.weights)),t.upload(m(a.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,s,n,u,c,o.kernel,o.kernel,o.stride,p,l,o.activation==="relu6"?1:0])),this.defaultSetup(),this.dispatch=[Math.ceil(n/8),Math.ceil(s/8),c];}};var Ae=`// Bilinear upsample + channel concat fused.
3329
+ `;var V=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,n,a){super(t),this.shader=t.dtype==="f16"?De:Re;let s=b(e.h,a.kernel,a.stride,a.padding),o=b(e.w,a.kernel,a.stride,a.padding),u=e.c/4,c=a.outChannels/4,p=v(a.padding,e.h,s,a.kernel,a.stride),l=v(a.padding,e.w,o,a.kernel,a.stride);this.output=t.tensor(s,o,a.outChannels),this.inputs=[e,r],this.weights=[t.upload(f(n.weights)),t.upload(f(n.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,s,o,u,c,a.kernel,a.kernel,a.stride,p,l,a.activation==="relu6"?1:0])),this.defaultSetup(),this.dispatch=[Math.ceil(o/8),Math.ceil(s/8),c];}};var He=`// Bilinear upsample + channel concat fused.
868
3330
  // input_a is the decoder tensor at small spatial resolution (in_h \xD7 in_w).
869
3331
  // input_b is the encoder skip feature already at output resolution (out_h \xD7 out_w).
870
3332
  // For output channels 0..a_groups-1: bilinearly interpolate from input_a.
@@ -925,7 +3387,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
925
3387
  output_buf[out_idx] = input_b[oy * params.out_w * params.b_groups + ox * params.b_groups + c_b];
926
3388
  }
927
3389
  }
928
- `;var Oe=`enable f16;
3390
+ `;var Le=`enable f16;
929
3391
 
930
3392
  // Bilinear upsample + channel concat fused \u2014 full f16 variant.
931
3393
  // Bilinear weights (wx, wy) and intermediate blends computed in f32; result cast to f16.
@@ -985,7 +3447,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
985
3447
  output_buf[out_idx] = input_b[oy * params.out_w * params.b_groups + ox * params.b_groups + c_b];
986
3448
  }
987
3449
  }
988
- `;var R=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r,a){super(t),this.shader=t.dtype==="f16"?Oe:Ae;let o=e.c/4,s=r.c/4,n=o+s;this.output=t.tensor(a.outH,a.outW,e.c+r.c),this.inputs=[e,r],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,a.outH,a.outW,o,s,n,0])),this.defaultSetup(),this.dispatch=[Math.ceil(a.outW/8),Math.ceil(a.outH/8),n];}};var Ee=`// Bilinear upsample + 1\xD71 pointwise conv fused.
3450
+ `;var z=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r,n){super(t),this.shader=t.dtype==="f16"?Le:He;let a=e.c/4,s=r.c/4,o=a+s;this.output=t.tensor(n.outH,n.outW,e.c+r.c),this.inputs=[e,r],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,n.outH,n.outW,a,s,o,0])),this.defaultSetup(),this.dispatch=[Math.ceil(n.outW/8),Math.ceil(n.outH/8),o];}};var Ve=`// Bilinear upsample + 1\xD71 pointwise conv fused.
989
3451
  // For each output pixel, bilinearly samples the small input for each in_group,
990
3452
  // immediately applies the 1\xD71 conv weights, and writes the activated result.
991
3453
  // Eliminates the intermediate full-resolution upsample buffer.
@@ -1052,7 +3514,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1052
3514
 
1053
3515
  output_buf[oy * params.out_w * params.out_groups + ox * params.out_groups + og] = result;
1054
3516
  }
1055
- `;var Fe=`enable f16;
3517
+ `;var ze=`enable f16;
1056
3518
 
1057
3519
  // Bilinear upsample + 1\xD71 pointwise conv fused \u2014 full f16 variant.
1058
3520
  // Bilinear weights and intermediate blends computed in f32; conv accumulation in f16.
@@ -1119,7 +3581,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1119
3581
 
1120
3582
  output_buf[oy * params.out_w * params.out_groups + ox * params.out_groups + og] = result;
1121
3583
  }
1122
- `;var z=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,a){super(t),this.shader=t.dtype==="f16"?Fe:Ee;let o=e.c/4,s=a.outChannels/4;this.output=t.tensor(a.outH,a.outW,a.outChannels),this.inputs=[e],this.weights=[t.upload(m(r.weights)),t.upload(m(r.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,a.outH,a.outW,o,s,a.activation==="relu6"?1:0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(a.outW/8),Math.ceil(a.outH/8),s];}};var Ve=`// Bilinear upsample + sigmoid fused.
3584
+ `;var N=class extends d{inputs;weights;output;dispatch;shader;constructor(t,e,r,n){super(t),this.shader=t.dtype==="f16"?ze:Ve;let a=e.c/4,s=n.outChannels/4;this.output=t.tensor(n.outH,n.outW,n.outChannels),this.inputs=[e],this.weights=[t.upload(f(r.weights)),t.upload(f(r.bias))],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,n.outH,n.outW,a,s,n.activation==="relu6"?1:0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(n.outW/8),Math.ceil(n.outH/8),s];}};var Ne=`// Bilinear upsample + sigmoid fused.
1123
3585
  // Identical to bilinear_upsample.wgsl except sigmoid is applied at write time.
1124
3586
  // Eliminates the intermediate full-resolution buffer and the separate sigmoid dispatch.
1125
3587
 
@@ -1173,7 +3635,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1173
3635
 
1174
3636
  output_buf[oy * params.out_w * C + ox * C + c] = 1.0 / (1.0 + exp(-result));
1175
3637
  }
1176
- `;var Le=`enable f16;
3638
+ `;var $e=`enable f16;
1177
3639
 
1178
3640
  // Bilinear upsample + sigmoid fused \u2014 full f16 variant.
1179
3641
  // Interpolation computed in f32 for accuracy; sigmoid and result cast to f16.
@@ -1228,7 +3690,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1228
3690
 
1229
3691
  output_buf[oy * params.out_w * C + ox * C + c] = vec4<f16>(1.0 / (1.0 + exp(-result)));
1230
3692
  }
1231
- `;var D=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?Le:Ve;let a=e.c/4;this.output=t.tensor(r.outH,r.outW,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,r.outH,r.outW,a,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r.outW/8),Math.ceil(r.outH/8),a];}};var He=`// Composite an RGBA image over a solid background, gated by a 1-ch alpha.
3693
+ `;var $=class extends d{inputs;weights=[];output;dispatch;shader;constructor(t,e,r){super(t),this.shader=t.dtype==="f16"?$e:Ne;let n=e.c/4;this.output=t.tensor(r.outH,r.outW,e.c),this.inputs=[e],this.createUniform("params","Params"),this.setUniform("params",new Uint32Array([e.h,e.w,r.outH,r.outW,n,0,0,0])),this.defaultSetup(),this.dispatch=[Math.ceil(r.outW/8),Math.ceil(r.outH/8),n];}};var qe=`// Composite an RGBA image over a solid background, gated by a 1-ch alpha.
1232
3694
  // Fragment writes the canvas's swapchain texture (premultiplied output).
1233
3695
  //
1234
3696
  // Caller invariants (matched in CompositeSolidWebGPU):
@@ -1273,7 +3735,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1273
3735
  let rgb = fg * a + params.bgColor.rgb * (1.0 - a);
1274
3736
  return vec4<f32>(rgb, 1.0);
1275
3737
  }
1276
- `;var Re=`enable f16;
3738
+ `;var Xe=`enable f16;
1277
3739
 
1278
3740
  // Composite an RGBA image over a solid background, gated by a 1-ch alpha.
1279
3741
  // f16 variant: image and alpha are stored as f16; values promote to f32 on
@@ -1322,7 +3784,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1322
3784
  let rgb = fg * a + params.bgColor.rgb * (1.0 - a);
1323
3785
  return vec4<f32>(rgb, 1.0);
1324
3786
  }
1325
- `;var N=class{constructor(t,e,r,a){this.backend=t;if(e.h!==r.h||e.w!==r.w)throw new Error(`CompositeSolid: image (${e.h}\xD7${e.w}) and alpha (${r.h}\xD7${r.w}) must match. Run the upscaler first.`);let o=t.device;this.uniformBuffer=o.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=new ArrayBuffer(32);new Uint32Array(s,0,1)[0]=e.w,new Float32Array(s,16,4).set([a[0],a[1],a[2],0]),o.queue.writeBuffer(this.uniformBuffer,0,s);let n=t.dtype==="f16"?Re:He,u=o.createShaderModule({code:n});this.pipeline=o.createRenderPipeline({layout:"auto",vertex:{module:u,entryPoint:"vs"},fragment:{module:u,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=o.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:r.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositeSolidWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var ze=`// Like composite_solid but bg is an NHWC vec4 storage buffer (e.g. virtual
3787
+ `;var q=class{constructor(t,e,r,n){this.backend=t;if(e.h!==r.h||e.w!==r.w)throw new Error(`CompositeSolid: image (${e.h}\xD7${e.w}) and alpha (${r.h}\xD7${r.w}) must match. Run the upscaler first.`);let a=t.device;this.uniformBuffer=a.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=new ArrayBuffer(32);new Uint32Array(s,0,1)[0]=e.w,new Float32Array(s,16,4).set([n[0],n[1],n[2],0]),a.queue.writeBuffer(this.uniformBuffer,0,s);let o=t.dtype==="f16"?Xe:qe,u=a.createShaderModule({code:o});this.pipeline=a.createRenderPipeline({layout:"auto",vertex:{module:u,entryPoint:"vs"},fragment:{module:u,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=a.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:r.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositeSolidWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var je=`// Like composite_solid but bg is an NHWC vec4 storage buffer (e.g. virtual
1326
3788
  // background image, or a blurred copy of the input).
1327
3789
 
1328
3790
  struct VertexOut {
@@ -1364,7 +3826,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1364
3826
  let rgb = fg * a + bgc * (1.0 - a);
1365
3827
  return vec4<f32>(rgb, 1.0);
1366
3828
  }
1367
- `;var De=`enable f16;
3829
+ `;var Ke=`enable f16;
1368
3830
 
1369
3831
  // Like composite_solid_f16 but bg is an NHWC vec4<f16> storage buffer (e.g. a
1370
3832
  // virtual background image, or a blurred copy of the input).
@@ -1408,7 +3870,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1408
3870
  let rgb = fg * a + bgc * (1.0 - a);
1409
3871
  return vec4<f32>(rgb, 1.0);
1410
3872
  }
1411
- `;var $=class{constructor(t,e,r,a){this.backend=t;if(e.h!==r.h||e.w!==r.w||e.h!==a.h||e.w!==a.w)throw new Error(`CompositeImage: image (${e.h}\xD7${e.w}), alpha (${r.h}\xD7${r.w}), and bg (${a.h}\xD7${a.w}) must all match. Run upscaler / resizer first.`);let o=t.device;this.uniformBuffer=o.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=new ArrayBuffer(16);new Uint32Array(s,0,1)[0]=e.w,o.queue.writeBuffer(this.uniformBuffer,0,s);let n=t.dtype==="f16"?De:ze,u=o.createShaderModule({code:n});this.pipeline=o.createRenderPipeline({layout:"auto",vertex:{module:u,entryPoint:"vs"},fragment:{module:u,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=o.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:r.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:a.buffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositeImageWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var Ne=`// Like composite_image but bg is sampled bilinearly \u2014 bg may be smaller than
3873
+ `;var X=class{constructor(t,e,r,n){this.backend=t;if(e.h!==r.h||e.w!==r.w||e.h!==n.h||e.w!==n.w)throw new Error(`CompositeImage: image (${e.h}\xD7${e.w}), alpha (${r.h}\xD7${r.w}), and bg (${n.h}\xD7${n.w}) must all match. Run upscaler / resizer first.`);let a=t.device;this.uniformBuffer=a.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=new ArrayBuffer(16);new Uint32Array(s,0,1)[0]=e.w,a.queue.writeBuffer(this.uniformBuffer,0,s);let o=t.dtype==="f16"?Ke:je,u=a.createShaderModule({code:o});this.pipeline=a.createRenderPipeline({layout:"auto",vertex:{module:u,entryPoint:"vs"},fragment:{module:u,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=a.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:r.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:n.buffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositeImageWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var Ye=`// Like composite_image but bg is sampled bilinearly \u2014 bg may be smaller than
1412
3874
  // (image, alpha). Used by CompositorBlur to skip the final full-res upsample
1413
3875
  // in the blur pyramid and let this shader's own per-pixel scan do the
1414
3876
  // expansion (the work is already happening here for the composite anyway).
@@ -1473,7 +3935,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1473
3935
  let rgb = fg * a + bgc * (1.0 - a);
1474
3936
  return vec4<f32>(rgb, 1.0);
1475
3937
  }
1476
- `;var $e=`enable f16;
3938
+ `;var Je=`enable f16;
1477
3939
 
1478
3940
  // f16 storage variant of composite_image_bilinear. bg is at a smaller
1479
3941
  // resolution and is bilinearly sampled to match (image, alpha) at full res.
@@ -1534,7 +3996,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1534
3996
  let rgb = fg * a + bgc * (1.0 - a);
1535
3997
  return vec4<f32>(rgb, 1.0);
1536
3998
  }
1537
- `;var q=class{constructor(t,e,r,a){this.backend=t;if(e.h!==r.h||e.w!==r.w)throw new Error(`CompositeImageBilinear: image (${e.h}\xD7${e.w}) and alpha (${r.h}\xD7${r.w}) must match.`);let o=t.device;this.uniformBuffer=o.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=new ArrayBuffer(16),n=new Uint32Array(s);n[0]=e.w,n[1]=e.h,n[2]=a.w,n[3]=a.h,o.queue.writeBuffer(this.uniformBuffer,0,s);let u=t.dtype==="f16"?$e:Ne,c=o.createShaderModule({code:u});this.pipeline=o.createRenderPipeline({layout:"auto",vertex:{module:c,entryPoint:"vs"},fragment:{module:c,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=o.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:r.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:a.buffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositeImageBilinearWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var qe=`// Passthrough "compositor" \u2014 writes the image directly to the canvas
3999
+ `;var j=class{constructor(t,e,r,n){this.backend=t;if(e.h!==r.h||e.w!==r.w)throw new Error(`CompositeImageBilinear: image (${e.h}\xD7${e.w}) and alpha (${r.h}\xD7${r.w}) must match.`);let a=t.device;this.uniformBuffer=a.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=new ArrayBuffer(16),o=new Uint32Array(s);o[0]=e.w,o[1]=e.h,o[2]=n.w,o[3]=n.h,a.queue.writeBuffer(this.uniformBuffer,0,s);let u=t.dtype==="f16"?Je:Ye,c=a.createShaderModule({code:u});this.pipeline=a.createRenderPipeline({layout:"auto",vertex:{module:c,entryPoint:"vs"},fragment:{module:c,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=a.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:r.buffer}},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:n.buffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositeImageBilinearWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var Qe=`// Passthrough "compositor" \u2014 writes the image directly to the canvas
1538
4000
  // swapchain texture with no alpha math and no background. Used by RenderOp
1539
4001
  // when the renderer is in disabled state, so the output canvas reflects
1540
4002
  // the unmodified input frame instead of a stale matted result.
@@ -1572,7 +4034,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1572
4034
  let i = y * params.width + x;
1573
4035
  return vec4<f32>(image[i].rgb, 1.0);
1574
4036
  }
1575
- `;var je=`enable f16;
4037
+ `;var Ze=`enable f16;
1576
4038
 
1577
4039
  // Passthrough "compositor" \u2014 f16 variant. Image is stored as f16; values
1578
4040
  // promote to f32 on read and the fragment writes f32 to the canvas
@@ -1608,7 +4070,7 @@ fn fs(in: VertexOut) -> @location(0) vec4<f32> {
1608
4070
  let i = y * params.width + x;
1609
4071
  return vec4<f32>(vec3<f32>(image[i].rgb), 1.0);
1610
4072
  }
1611
- `;var j=class{constructor(t,e){this.backend=t;let r=t.device;this.uniformBuffer=r.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let a=new ArrayBuffer(16);new Uint32Array(a,0,1)[0]=e.w,r.queue.writeBuffer(this.uniformBuffer,0,a);let o=t.dtype==="f16"?je:qe,s=r.createShaderModule({code:o});this.pipeline=r.createRenderPipeline({layout:"auto",vertex:{module:s,entryPoint:"vs"},fragment:{module:s,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:this.uniformBuffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositePassthroughWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var Ye=`// Input op \u2014 sample a regular 2D source texture (RGBA8 unorm) into the NHWC
4073
+ `;var K=class{constructor(t,e){this.backend=t;let r=t.device;this.uniformBuffer=r.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let n=new ArrayBuffer(16);new Uint32Array(n,0,1)[0]=e.w,r.queue.writeBuffer(this.uniformBuffer,0,n);let a=t.dtype==="f16"?Ze:Qe,s=r.createShaderModule({code:a});this.pipeline=r.createRenderPipeline({layout:"auto",vertex:{module:s,entryPoint:"vs"},fragment:{module:s,entryPoint:"fs",targets:[{format:t.canvasFormat}]},primitive:{topology:"triangle-list"}}),this.bindGroup=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e.buffer}},{binding:1,resource:{buffer:this.uniformBuffer}}]});}backend;pipeline;bindGroup;uniformBuffer;outputView=null;setOutput(t){this.outputView=t.createView();}run(){if(!this.outputView)throw new Error("CompositePassthroughWebGPU.run() called before setOutput()");let t=this.backend.device.createCommandEncoder(),e=t.beginRenderPass({colorAttachments:[{view:this.outputView,clearValue:[0,0,0,1],loadOp:"clear",storeOp:"store"}]});e.setPipeline(this.pipeline),e.setBindGroup(0,this.bindGroup),e.draw(6),e.end(),this.backend.device.queue.submit([t.finish()]),this.outputView=null;}};var et=`// Input op \u2014 sample a regular 2D source texture (RGBA8 unorm) into the NHWC
1612
4074
  // vec4<f32> output buffer at the target resolution. Used for ImageBitmap
1613
4075
  // sources, which are uploaded into a persistent staging texture via
1614
4076
  // copyExternalImageToTexture before each dispatch.
@@ -1638,7 +4100,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1638
4100
  let rgba = textureSampleLevel(src_tex, src_sampler, uv, 0.0);
1639
4101
  output_buf[y * params.out_w + x] = rgba;
1640
4102
  }
1641
- `;var Ke=`enable f16;
4103
+ `;var tt=`enable f16;
1642
4104
 
1643
4105
  // Input op \u2014 f16 storage variant. Source is sampled as vec4<f32>; only the
1644
4106
  // store is demoted to vec4<f16>.
@@ -1668,7 +4130,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1668
4130
  let rgba = textureSampleLevel(src_tex, src_sampler, uv, 0.0);
1669
4131
  output_buf[y * params.out_w + x] = vec4<f16>(rgba);
1670
4132
  }
1671
- `;var Je=`// Input op \u2014 sample a GPUExternalTexture (zero-copy VideoFrame import) into
4133
+ `;var rt=`// Input op \u2014 sample a GPUExternalTexture (zero-copy VideoFrame import) into
1672
4134
  // the NHWC vec4<f32> output buffer at the target resolution. Used when the
1673
4135
  // caller passes a VideoFrame to setSource(); requires importExternalTexture
1674
4136
  // to be called in the same task as the dispatch (the texture is invalidated
@@ -1699,7 +4161,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1699
4161
  let rgba = textureSampleBaseClampToEdge(src_tex, src_sampler, uv);
1700
4162
  output_buf[y * params.out_w + x] = rgba;
1701
4163
  }
1702
- `;var Xe=`enable f16;
4164
+ `;var nt=`enable f16;
1703
4165
 
1704
4166
  // Input op \u2014 texture_external + f16 storage. Sample in f32, store as f16.
1705
4167
 
@@ -1728,4 +4190,4 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
1728
4190
  let rgba = textureSampleBaseClampToEdge(src_tex, src_sampler, uv);
1729
4191
  output_buf[y * params.out_w + x] = vec4<f16>(rgba);
1730
4192
  }
1731
- `;var Yt=i=>typeof VideoFrame<"u"&&i instanceof VideoFrame,Y=class{output;device;dtype;sampler;uniformBuffer;dispatch;pipeline2d=null;pipelineExternal=null;stagingTex=null;stagingW=0;stagingH=0;source=null;constructor(t,e,r){this.device=t.device,this.dtype=t.dtype,this.output=t.tensor(e,r,4),this.sampler=this.device.createSampler({magFilter:"linear",minFilter:"linear",addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"}),this.uniformBuffer=this.device.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let a=new ArrayBuffer(16);new Uint32Array(a,0,2).set([r,e]),this.device.queue.writeBuffer(this.uniformBuffer,0,a),this.dispatch=[Math.ceil(r/8),Math.ceil(e/8),1],this.pipeline2d=this.buildPipeline(this.dtype==="f16"?Ke:Ye);}setSource(t){this.source=t;}run(){if(!this.source)throw new Error("InputWebGPU.run() called before setSource()");Yt(this.source)?this.runExternal(this.source):this.run2d(this.source);}run2d(t){this.ensureStagingTexture(t.width,t.height),this.device.queue.copyExternalImageToTexture({source:t,flipY:false},{texture:this.stagingTex},[t.width,t.height]);let e=this.device.createBindGroup({layout:this.pipeline2d.getBindGroupLayout(0),entries:[{binding:0,resource:this.stagingTex.createView()},{binding:1,resource:this.sampler},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:this.output.buffer}}]});this.dispatchOnce(this.pipeline2d,e);}runExternal(t){this.pipelineExternal||(this.pipelineExternal=this.buildPipeline(this.dtype==="f16"?Xe:Je));let e=this.device.importExternalTexture({source:t}),r=this.device.createBindGroup({layout:this.pipelineExternal.getBindGroupLayout(0),entries:[{binding:0,resource:e},{binding:1,resource:this.sampler},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:this.output.buffer}}]});this.dispatchOnce(this.pipelineExternal,r);}ensureStagingTexture(t,e){this.stagingTex&&this.stagingW===t&&this.stagingH===e||(this.stagingTex?.destroy(),this.stagingTex=this.device.createTexture({size:[t,e,1],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),this.stagingW=t,this.stagingH=e);}buildPipeline(t){let e=this.device.createShaderModule({code:t});return this.device.createComputePipeline({layout:"auto",compute:{module:e,entryPoint:"main"}})}dispatchOnce(t,e){let r=this.device.createCommandEncoder(),a=r.beginComputePass();a.setPipeline(t),a.setBindGroup(0,e),a.dispatchWorkgroups(...this.dispatch),a.end(),this.device.queue.submit([r.finish()]);}};var Kt=navigator.gpu?GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST:0,te=class i{constructor(t,e,r){this.device=t;this.canvas=e;this.dtype=r;this.bytesPerElement=r==="f16"?2:4;let a=e.getContext("webgpu");if(!a)throw new Error("Failed to get WebGPU context from canvas");this.canvasContext=a,this.canvasFormat=navigator.gpu.getPreferredCanvasFormat(),this.canvasContext.configure({device:t,format:this.canvasFormat,alphaMode:"premultiplied"}),this.ops={Conv2d:(o,s,n)=>new S(this,o,s,n),DepthwiseConv2d:(o,s,n)=>new A(this,o,s,n),Add:(o,s)=>new O(this,o,s),Sigmoid:o=>new E(this,o),BilinearUpsample:(o,s)=>new F(this,o,s),BicubicUpsample:(o,s)=>new V(this,o,s),ChannelConcat:(o,s)=>new L(this,o,s),Conv2dAdd:(o,s,n,u)=>new H(this,o,s,n,u),UpsampleConcat:(o,s,n)=>new R(this,o,s,n),UpsampleConv1x1:(o,s,n)=>new z(this,o,s,n),UpsampleSigmoid:(o,s)=>new D(this,o,s),Input:(o,s)=>new Y(this,o,s)},this.presenters={CompositeSolid:(o,s,n)=>{let u=new N(this,o,s,n);return {run:()=>{u.setOutput(this.getCurrentDisplayTexture()),u.run();}}},CompositeImage:(o,s,n)=>{let u=new $(this,o,s,n);return {run:()=>{u.setOutput(this.getCurrentDisplayTexture()),u.run();}}},CompositeImageBilinear:(o,s,n)=>{let u=new q(this,o,s,n);return {run:()=>{u.setOutput(this.getCurrentDisplayTexture()),u.run();}}},CompositePassthrough:o=>{let s=new j(this,o);return {run:()=>{s.setOutput(this.getCurrentDisplayTexture()),s.run();}}}};}device;canvas;dtype;ops;presenters;canvasContext;canvasFormat;bytesPerElement;static async isAvailable(){return navigator.gpu?await navigator.gpu.requestAdapter()!==null:false}static async hasF16Support(){if(!navigator.gpu)return false;let t=await navigator.gpu.requestAdapter();return t?t.features.has("shader-f16"):false}static async create(t){let e=t.dtype??"f32",r=t.device;if(r){if(e==="f16"&&!r.features.has("shader-f16"))throw new Error("WebGPU dtype='f16' requested but supplied device lacks `shader-f16`")}else {let a=await navigator.gpu.requestAdapter();if(!a)throw new Error("WebGPU adapter not available");if(e==="f16"&&!a.features.has("shader-f16"))throw new Error("WebGPU dtype='f16' requested but adapter lacks `shader-f16` feature");r=await a.requestDevice({requiredFeatures:e==="f16"?["shader-f16"]:[]});}return new i(r,t.canvas,e)}getCurrentDisplayTexture(){return this.canvasContext.getCurrentTexture()}tensor(t,e,r,a){let s=t*e*r*this.bytesPerElement,n=this.device.createBuffer({size:s,usage:Kt,mappedAtCreation:a!==void 0});if(a!==void 0){let u=n.getMappedRange();this.writeView(u,a),n.unmap();}return {h:t,w:e,c:r,buffer:n}}upload(t){let r=t.length*this.bytesPerElement,a=this.device.createBuffer({size:r,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});return this.writeView(a.getMappedRange(),t),a.unmap(),{buffer:a}}writeView(t,e){let r=this.dtype==="f16",a=e instanceof Uint16Array;if(r===a){r?new Uint16Array(t).set(e):new Float32Array(t).set(e);return}r?new Uint16Array(t).set(he(e)):new Float32Array(t).set(ee(e));}async readback(t){let e=this.device.createBuffer({size:t.buffer.size,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(t.buffer,0,e,0,t.buffer.size),this.device.queue.submit([r.finish()]),await e.mapAsync(GPUMapMode.READ);let a=e.getMappedRange(),o=this.dtype==="f16"?ee(new Uint16Array(a.slice(0))):new Float32Array(a.slice(0));return e.unmap(),e.destroy(),o}async sync(){await this.device.queue.onSubmittedWorkDone();}destroy(){this.device.destroy();}};export{Z as EffectsPipeline,te as WebGPUBackend};
4193
+ `;var nr=i=>typeof VideoFrame<"u"&&i instanceof VideoFrame,Y=class{output;device;dtype;sampler;uniformBuffer;dispatch;pipeline2d=null;pipelineExternal=null;stagingTex=null;stagingW=0;stagingH=0;source=null;constructor(t,e,r){this.device=t.device,this.dtype=t.dtype,this.output=t.tensor(e,r,4),this.sampler=this.device.createSampler({magFilter:"linear",minFilter:"linear",addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"}),this.uniformBuffer=this.device.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let n=new ArrayBuffer(16);new Uint32Array(n,0,2).set([r,e]),this.device.queue.writeBuffer(this.uniformBuffer,0,n),this.dispatch=[Math.ceil(r/8),Math.ceil(e/8),1],this.pipeline2d=this.buildPipeline(this.dtype==="f16"?tt:et);}setSource(t){this.source=t;}run(){if(!this.source)throw new Error("InputWebGPU.run() called before setSource()");nr(this.source)?this.runExternal(this.source):this.run2d(this.source);}run2d(t){this.ensureStagingTexture(t.width,t.height),this.device.queue.copyExternalImageToTexture({source:t,flipY:false},{texture:this.stagingTex},[t.width,t.height]);let e=this.device.createBindGroup({layout:this.pipeline2d.getBindGroupLayout(0),entries:[{binding:0,resource:this.stagingTex.createView()},{binding:1,resource:this.sampler},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:this.output.buffer}}]});this.dispatchOnce(this.pipeline2d,e);}runExternal(t){this.pipelineExternal||(this.pipelineExternal=this.buildPipeline(this.dtype==="f16"?nt:rt));let e=this.device.importExternalTexture({source:t}),r=this.device.createBindGroup({layout:this.pipelineExternal.getBindGroupLayout(0),entries:[{binding:0,resource:e},{binding:1,resource:this.sampler},{binding:2,resource:{buffer:this.uniformBuffer}},{binding:3,resource:{buffer:this.output.buffer}}]});this.dispatchOnce(this.pipelineExternal,r);}ensureStagingTexture(t,e){this.stagingTex&&this.stagingW===t&&this.stagingH===e||(this.stagingTex?.destroy(),this.stagingTex=this.device.createTexture({size:[t,e,1],format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),this.stagingW=t,this.stagingH=e);}buildPipeline(t){let e=this.device.createShaderModule({code:t});return this.device.createComputePipeline({layout:"auto",compute:{module:e,entryPoint:"main"}})}dispatchOnce(t,e){let r=this.device.createCommandEncoder(),n=r.beginComputePass();n.setPipeline(t),n.setBindGroup(0,e),n.dispatchWorkgroups(...this.dispatch),n.end(),this.device.queue.submit([r.finish()]);}};var ir=navigator.gpu?GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST:0,ae=class i{constructor(t,e,r){this.device=t;this.canvas=e;this.dtype=r;this.bytesPerElement=r==="f16"?2:4;let n=e.getContext("webgpu");if(!n)throw new Error("Failed to get WebGPU context from canvas");this.canvasContext=n,this.canvasFormat=navigator.gpu.getPreferredCanvasFormat(),this.canvasContext.configure({device:t,format:this.canvasFormat,alphaMode:"premultiplied"}),this.ops={Conv2d:(a,s,o)=>new F(this,a,s,o),DepthwiseConv2d:(a,s,o)=>new O(this,a,s,o),Add:(a,s)=>new M(this,a,s),Sigmoid:a=>new R(this,a),BilinearUpsample:(a,s)=>new D(this,a,s),BicubicUpsample:(a,s)=>new H(this,a,s),ChannelConcat:(a,s)=>new L(this,a,s),Conv2dAdd:(a,s,o,u)=>new V(this,a,s,o,u),UpsampleConcat:(a,s,o)=>new z(this,a,s,o),UpsampleConv1x1:(a,s,o)=>new N(this,a,s,o),UpsampleSigmoid:(a,s)=>new $(this,a,s),Input:(a,s)=>new Y(this,a,s)},this.presenters={CompositeSolid:(a,s,o)=>{let u=new q(this,a,s,o);return {run:()=>{u.setOutput(this.getCurrentDisplayTexture()),u.run();}}},CompositeImage:(a,s,o)=>{let u=new X(this,a,s,o);return {run:()=>{u.setOutput(this.getCurrentDisplayTexture()),u.run();}}},CompositeImageBilinear:(a,s,o)=>{let u=new j(this,a,s,o);return {run:()=>{u.setOutput(this.getCurrentDisplayTexture()),u.run();}}},CompositePassthrough:a=>{let s=new K(this,a);return {run:()=>{s.setOutput(this.getCurrentDisplayTexture()),s.run();}}}};}device;canvas;dtype;ops;presenters;canvasContext;canvasFormat;bytesPerElement;static async isAvailable(){return navigator.gpu?await navigator.gpu.requestAdapter()!==null:false}static async hasF16Support(){if(!navigator.gpu)return false;let t=await navigator.gpu.requestAdapter();return t?t.features.has("shader-f16"):false}static async create(t){let e=t.dtype??"f32",r=t.device;if(r){if(e==="f16"&&!r.features.has("shader-f16"))throw new Error("WebGPU dtype='f16' requested but supplied device lacks `shader-f16`")}else {let n=await navigator.gpu.requestAdapter();if(!n)throw new Error("WebGPU adapter not available");if(e==="f16"&&!n.features.has("shader-f16"))throw new Error("WebGPU dtype='f16' requested but adapter lacks `shader-f16` feature");r=await n.requestDevice({requiredFeatures:e==="f16"?["shader-f16"]:[]});}return new i(r,t.canvas,e)}getCurrentDisplayTexture(){return this.canvasContext.getCurrentTexture()}tensor(t,e,r,n){let s=t*e*r*this.bytesPerElement,o=this.device.createBuffer({size:s,usage:ir,mappedAtCreation:n!==void 0});if(n!==void 0){let u=o.getMappedRange();this.writeView(u,n),o.unmap();}return {h:t,w:e,c:r,buffer:o}}upload(t){let r=t.length*this.bytesPerElement,n=this.device.createBuffer({size:r,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});return this.writeView(n.getMappedRange(),t),n.unmap(),{buffer:n}}writeView(t,e){let r=this.dtype==="f16",n=e instanceof Uint16Array;if(r===n){r?new Uint16Array(t).set(e):new Float32Array(t).set(e);return}r?new Uint16Array(t).set(ke(e)):new Float32Array(t).set(ie(e));}async readback(t){let e=this.device.createBuffer({size:t.buffer.size,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(t.buffer,0,e,0,t.buffer.size),this.device.queue.submit([r.finish()]),await e.mapAsync(GPUMapMode.READ);let n=e.getMappedRange(),a=this.dtype==="f16"?ie(new Uint16Array(n.slice(0))):new Float32Array(n.slice(0));return e.unmap(),e.destroy(),a}async sync(){await this.device.queue.onSubmittedWorkDone();}destroy(){this.device.destroy();}};export{ne as EffectsPipeline,ae as WebGPUBackend};