100 lines
3.4 KiB
TypeScript
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);
|
|
});
|