Files
RecipesCam/web/spike/bench.ts
T

100 lines
3.4 KiB
TypeScript

// 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);
});