// Per-step cost of the web pipeline, measured at three sizes. The 12MP run of // spike/w0.ts was unusably slow; this isolates WHICH pass eats the time. import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { initSkia, Skia } from '../src/engine/skiaShim'; import { CLARITY_SKSL } from '../../src/utils/toneShader'; const here = path.dirname(fileURLToPath(import.meta.url)); const repo = path.resolve(here, '../..'); const SAMPLE = process.env.W0_SAMPLE ?? '/tmp/w0/p2.jpg'; function time(label: string, fn: () => void): number { const t = performance.now(); fn(); const ms = performance.now() - t; console.log(` ${label.padEnd(26)} ${ms.toFixed(0)}ms`); return ms; } async function main() { await initSkia(); const bytes = new Uint8Array(fs.readFileSync(SAMPLE)); const src = Skia.Image.MakeImageFromEncoded(bytes); console.log(`source ${src.width()}x${src.height()}`); const full = { w: src.width(), h: src.height() }; const sizes = [ { name: '1024', w: 1024, h: Math.round((1024 * full.h) / full.w) }, { name: '2048', w: 2048, h: Math.round((2048 * full.h) / full.w) }, { name: 'full', w: full.w, h: full.h }, ]; const sharpen = Skia.RuntimeEffect.Make(CLARITY_SKSL); const tone = Skia.RuntimeEffect.Make( 'uniform shader src; uniform float a; vec4 main(vec2 p){ vec4 c = src.eval(p); return vec4(c.rgb*(1.0+a), c.a);}' ); for (const s of sizes) { console.log(`\n[sample downscaled to ${s.w}x${s.h}]`); const surf = Skia.Surface.Make(s.w, s.h); const canvas = surf.getCanvas(); const paint = Skia.Paint(); time('draw downscaled image', () => { canvas.drawImageRect( src, Skia.XYWHRect(0, 0, full.w, full.h), Skia.XYWHRect(0, 0, s.w, s.h), paint ); }); const shader = src.makeShaderOptions( Skia.TileMode.Clamp, Skia.TileMode.Clamp, Skia.FilterMode.Linear, Skia.MipmapMode.None ); const toneShader = tone.makeShaderWithChildren([0.2], [shader]); time('tone skSL pass', () => { const p = Skia.Paint(); p.setShader(toneShader); canvas.drawRect(Skia.XYWHRect(0, 0, s.w, s.h), p); p.dispose(); }); time('clarity skSL 5-tap', () => { const snap = surf.makeImageSnapshot(); const child = snap.makeShaderOptions( Skia.TileMode.Clamp, Skia.TileMode.Clamp, Skia.FilterMode.Linear, Skia.MipmapMode.None ); const sh = sharpen.makeShaderWithChildren([0.3, 1, 1], [child]); const p = Skia.Paint(); p.setShader(sh); canvas.drawRect(Skia.XYWHRect(0, 0, s.w, s.h), p); p.dispose(); sh.dispose(); child.dispose(); snap.dispose(); }); time('blur sigma=40 (imagefilter)', () => { const snap = surf.makeImageSnapshot(); const p = Skia.Paint(); p.setImageFilter(Skia.ImageFilter.MakeBlur(40, 40, Skia.TileMode.Clamp, null)); canvas.clear(Skia.Color('transparent')); canvas.drawImage(snap, 0, 0, p); p.dispose(); snap.dispose(); }); time('jpeg q95 encode', () => { const snap = surf.makeImageSnapshot(); snap.encodeToBytes(Skia.ImageFormat.JPEG, 95); snap.dispose(); }); surf.dispose(); } // Full-res decode cost (what happens once per loaded photo). time('decode 12MP jpeg', () => { Skia.Image.MakeImageFromEncoded(bytes); }); } main().catch((e) => { console.error(e); process.exit(1); });