diff options
| author | skal <pascal.massimino@gmail.com> | 2026-03-22 17:47:17 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-03-22 17:47:17 +0100 |
| commit | 9bca6af775b723dd5bb972f6178516708abf58e2 (patch) | |
| tree | c179830be9c238ba20daca5b9ff17c6170d671e7 /cnn_v3/tools | |
| parent | 3573654c3a60b8bd7545f4240273ed767699f1ae (diff) | |
feat(cnn_v3/tools): zoom canvas shows region around clicked texel
Diffstat (limited to 'cnn_v3/tools')
| -rw-r--r-- | cnn_v3/tools/shaders.js | 20 | ||||
| -rw-r--r-- | cnn_v3/tools/tester.js | 30 |
2 files changed, 34 insertions, 16 deletions
diff --git a/cnn_v3/tools/shaders.js b/cnn_v3/tools/shaders.js index a1b2929..f178637 100644 --- a/cnn_v3/tools/shaders.js +++ b/cnn_v3/tools/shaders.js @@ -223,32 +223,40 @@ const DISP_SHADER=` }`; // Viz f32: show one channel of rgba16float layer +// Uniform layout: ch(u32) _p(u32) ox(i32) oy(i32) — 16 bytes +// ox/oy = texel offset (top-left of view); 0,0 for full-texture vignettes. const VIZ_F32=` +struct Vu{ch:u32,_p:u32,ox:i32,oy:i32} @group(0) @binding(0) var t:texture_2d<f32>; -@group(0) @binding(1) var<uniform> ch:u32; +@group(0) @binding(1) var<uniform> u:Vu; @vertex fn vs(@builtin(vertex_index) i:u32)->@builtin(position) vec4f{ var p=array<vec2f,6>(vec2f(-1.,-1.),vec2f(1.,-1.),vec2f(-1.,1.),vec2f(-1.,1.),vec2f(1.,-1.),vec2f(1.,1.)); return vec4f(p[i],0.,1.); } @fragment fn fs(@builtin(position) pos:vec4f)->@location(0) vec4f{ - let v=textureLoad(t,vec2i(pos.xy),0); var a=array<f32,4>(v.x,v.y,v.z,v.w); - let x=clamp(a[min(ch,3u)],0.,1.); return vec4f(x,x,x,1.); + let dim=vec2i(textureDimensions(t)); + let tc=clamp(vec2i(i32(pos.x)+u.ox,i32(pos.y)+u.oy),vec2i(0),dim-vec2i(1)); + let v=textureLoad(t,tc,0); var a=array<f32,4>(v.x,v.y,v.z,v.w); + let x=clamp(a[min(u.ch,3u)],0.,1.); return vec4f(x,x,x,1.); }`; // Viz u32: show one f16 channel of rgba32uint layer (8 channels packed) const VIZ_U32=` +struct Vu{ch:u32,_p:u32,ox:i32,oy:i32} @group(0) @binding(0) var t:texture_2d<u32>; -@group(0) @binding(1) var<uniform> ch:u32; +@group(0) @binding(1) var<uniform> u:Vu; @vertex fn vs(@builtin(vertex_index) i:u32)->@builtin(position) vec4f{ var p=array<vec2f,6>(vec2f(-1.,-1.),vec2f(1.,-1.),vec2f(-1.,1.),vec2f(-1.,1.),vec2f(1.,-1.),vec2f(1.,1.)); return vec4f(p[i],0.,1.); } @fragment fn fs(@builtin(position) pos:vec4f)->@location(0) vec4f{ - let t2=textureLoad(t,vec2i(pos.xy),0); + let dim=vec2i(textureDimensions(t)); + let tc=clamp(vec2i(i32(pos.x)+u.ox,i32(pos.y)+u.oy),vec2i(0),dim-vec2i(1)); + let t2=textureLoad(t,tc,0); let a=unpack2x16float(t2.x);let b=unpack2x16float(t2.y); let c=unpack2x16float(t2.z);let d=unpack2x16float(t2.w); var v=array<f32,8>(a.x,a.y,b.x,b.y,c.x,c.y,d.x,d.y); - let x=clamp(v[min(ch,7u)],0.,1.); return vec4f(x,x,x,1.); + let x=clamp(v[min(u.ch,7u)],0.,1.); return vec4f(x,x,x,1.); }`; // Full G-buffer pack: assembles feat_tex0/feat_tex1 from individual G-buffer images. diff --git a/cnn_v3/tools/tester.js b/cnn_v3/tools/tester.js index dbc7414..0412cae 100644 --- a/cnn_v3/tools/tester.js +++ b/cnn_v3/tools/tester.js @@ -465,7 +465,12 @@ class CNNv3Tester { const cvs=document.createElement('canvas'); const name=chName(c); cvs.title=name; - cvs.onclick=()=>tester.zoomChannel(id,c,name); + cvs.onclick=(e)=>{ + const r=cvs.getBoundingClientRect(); + const tx=Math.round(e.offsetX/r.width*tex.width); + const ty=Math.round(e.offsetY/r.height*tex.height); + tester.zoomChannel(id,c,name,tx,ty); + }; cell.appendChild(lbl); cell.appendChild(cvs); grid.appendChild(cell); } const pl=def.t==='f32'?this.getVizF32():this.getVizU32(); @@ -474,8 +479,8 @@ class CNNv3Tester { cvs.width=tex.width; cvs.height=tex.height; const ctx=cvs.getContext('webgpu'); if(!ctx)continue; try{ctx.configure({device:this.device,format:this.format});}catch(_){continue;} - const chBuf=this.device.createBuffer({size:4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); - this.device.queue.writeBuffer(chBuf,0,new Uint32Array([c])); + const chBuf=this.device.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); + this.device.queue.writeBuffer(chBuf,0,new Int32Array([c,0,0,0])); const bg=this.device.createBindGroup({layout:pl.getBindGroupLayout(0), entries:[{binding:0,resource:tex.createView()},{binding:1,resource:{buffer:chBuf}}]}); const enc=this.device.createCommandEncoder(); @@ -487,13 +492,13 @@ class CNNv3Tester { await this.device.queue.onSubmittedWorkDone(); } - zoomChannel(layerId, ch, label) { + zoomChannel(layerId, ch, label, clickTx=0, clickTy=0) { const def = this.vizDefs?.find(d => d.id === layerId); const tex = this.layerTextures[layerId]; if (!def || !tex || !this.device) return; const wrap = document.getElementById('chzoomWrap'); const lbl = document.getElementById('chzoomLbl'); - this.activeZoom = {layerId, ch, label}; + this.activeZoom = {layerId, ch, label, clickTx, clickTy}; lbl.textContent = label; wrap.style.display = 'flex'; // Wait for layout so clientWidth/clientHeight reflect the flex-distributed size @@ -506,12 +511,17 @@ class CNNv3Tester { const scale = Math.min(1, availW / tex.width, availH / tex.height); dst.width = Math.round(tex.width * scale); dst.height = Math.round(tex.height * scale); - // Re-render via WebGPU (WebGPU canvas pixels are not readable by drawImage) + // Re-render via WebGPU centered on the clicked texel + const ox = clickTx - Math.floor(dst.width / 2); + const oy = clickTy - Math.floor(dst.height / 2); const pl = def.t === 'f32' ? this.getVizF32() : this.getVizU32(); const ctx = dst.getContext('webgpu'); try { ctx.configure({device: this.device, format: this.format}); } catch(_) { return; } - const chBuf = this.device.createBuffer({size:4, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); - this.device.queue.writeBuffer(chBuf, 0, new Uint32Array([ch])); + const uData = new ArrayBuffer(16); + const dv = new DataView(uData); + dv.setUint32(0, ch, true); dv.setInt32(8, ox, true); dv.setInt32(12, oy, true); + const chBuf = this.device.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}); + this.device.queue.writeBuffer(chBuf, 0, uData); const bg = this.device.createBindGroup({layout: pl.getBindGroupLayout(0), entries:[{binding:0, resource:tex.createView()}, {binding:1, resource:{buffer:chBuf}}]}); const enc = this.device.createCommandEncoder(); @@ -525,8 +535,8 @@ class CNNv3Tester { refreshZoom() { if (this.activeZoom) { - const {layerId, ch, label} = this.activeZoom; - this.zoomChannel(layerId, ch, label); + const {layerId, ch, label, clickTx, clickTy} = this.activeZoom; + this.zoomChannel(layerId, ch, label, clickTx, clickTy); } } |
