P3 native cinema/clarity/denoise/grain (exportEngine port) verified on Xiaomi (gates pass)
This commit is contained in:
+42
-11
@@ -1,5 +1,6 @@
|
||||
import RecipescamExport from '../../modules/recipescam-export';
|
||||
import RecipescamExport, { PhotoAdjust } from '../../modules/recipescam-export';
|
||||
import { applyExposureGain, getSkiaColorMatrix } from '../utils/colorUtils';
|
||||
import { getCinemaUniforms } from '../utils/cinemaShader';
|
||||
import { BaseFilter, ColorAdjustments } from '../types';
|
||||
|
||||
const RUNS = 2;
|
||||
@@ -28,14 +29,20 @@ const NEUTRAL_ADJ: ColorAdjustments = {
|
||||
};
|
||||
|
||||
/**
|
||||
* P0/P1/P2 probe harness (temporary — removed at P7).
|
||||
* Native logs (RecipesCamExport tag) are the only visible channel: release
|
||||
* Hermes strips console.*. Each gate prints its own centerPixel / timing lines.
|
||||
* P0-P3 probe harness (temporary — removed at P7). Reads the native
|
||||
* RecipesCamExport "centerPixel"/"finalPixel" log lines (probe gates compare
|
||||
* those against the node reference sim).
|
||||
*/
|
||||
export async function nativeBenchProbe(): Promise<void> {
|
||||
const m = RecipescamExport as any;
|
||||
try {
|
||||
const pong = await m.ping();
|
||||
const run = async (tag: string, src: string, o: PhotoAdjust & { matrix?: number[] } = {}) => {
|
||||
const { matrix, ...adj } = o;
|
||||
const dst = src.replace(/\.png$/, `-${tag}-${Date.now()}.jpg`);
|
||||
const t = await m.processPhotoAsync(src, dst, matrix ?? IDENTITY, null, adj);
|
||||
console.error(`[BENCH] ${tag} totalMs=${t.totalMs} decodeMs=${t.decodeMs} toneMs=${t.toneMs ?? 0} cinemaMs=${t.cinemaMs ?? 0} colorMs=${t.colorMs} clarityMs=${t.clarityMs ?? 0} grainMs=${t.grainMs ?? 0} encodeWriteMs=${t.encodeWriteMs}`);
|
||||
};
|
||||
const src: string = await m.materializeBenchAsset('wallframe');
|
||||
|
||||
// P1 parity: classic-neg matrix on black -> expect center ~(13,5,0).
|
||||
@@ -57,18 +64,42 @@ export async function nativeBenchProbe(): Promise<void> {
|
||||
];
|
||||
for (const g of toneGates) {
|
||||
const solid = await m.materializeSolidAsset(g.rgb[0], g.rgb[1], g.rgb[2]);
|
||||
const out = solid.replace(/\.png$/, `-tone-${Date.now()}.jpg`);
|
||||
const t = await m.processPhotoAsync(solid, out, IDENTITY, null, g.tone.dr, g.tone.hl, g.tone.sh, 95);
|
||||
console.error('[BENCH] toneGate ' + g.tag + ' totalMs=' + t.totalMs + ' toneMs=' + (t.toneMs ?? 0));
|
||||
await run(`tone-${g.tag}`, solid, { toneDr: g.tone.dr, toneHl: g.tone.hl, toneSh: g.tone.sh });
|
||||
}
|
||||
|
||||
// Full-artwork run: provia + mild tone to sanity-check timing on 12MP.
|
||||
// P3 cinema parity (CINEMA_SKSL): lit+warm+glow path, shade+cool+fog path,
|
||||
// and a saturated pixel to exercise the saturation mix.
|
||||
const summer = getCinemaUniforms('summer')!.flat;
|
||||
const winter = getCinemaUniforms('winter')!.flat;
|
||||
const s192 = await m.materializeSolidAsset(192, 192, 192);
|
||||
await run('cinema-summer-192', s192, { cinema: summer });
|
||||
const s32 = await m.materializeSolidAsset(32, 32, 32);
|
||||
await run('cinema-winter-32', s32, { cinema: winter });
|
||||
const sat = await m.materializeSolidAsset(200, 120, 60);
|
||||
await run('cinema-summer-sat', sat, { cinema: summer });
|
||||
|
||||
// P3 enhance gates on the gray ramp (gradient makes blur/conv non-trivial).
|
||||
const ramp = await m.materializeRampAsset();
|
||||
await run('denoise10-ramp', ramp, { denoise: 10 });
|
||||
await run('clarity10-ramp', ramp, { clarity: 10 });
|
||||
await run('mist-ramp', ramp, { clarity: -10 });
|
||||
// Step gates: rows 0..31=0, 32..63=255. A ramp is invariant under box
|
||||
// blur, so the step makes heavy blur non-trivial (expected center row32:
|
||||
// denoise r=1 -> 170, mist r=4 -> 142 per the padded-boxBlur reference).
|
||||
const step = await m.materializeStepAsset();
|
||||
await run('denoise10-step', step, { denoise: 10 });
|
||||
await run('mist-step', step, { clarity: -10 });
|
||||
// Grain is structurally-gated only (float32 hash noise vs node doubles
|
||||
// differ) — the run must complete and the center must stay plausible.
|
||||
const gray100 = await m.materializeSolidAsset(100, 100, 100);
|
||||
await run('grain10-100', gray100, { grain: 10 });
|
||||
|
||||
// Full-artwork run: provia + tone + cinema + all enhance passes, 12MP.
|
||||
const provia = applyExposureGain(getSkiaColorMatrix('provia' as BaseFilter, NEUTRAL_ADJ), 0);
|
||||
for (let i = 0; i < RUNS; i++) {
|
||||
const dst = src.replace(/\.png$/, `-full-${Date.now()}-${i}.jpg`);
|
||||
const t = await m.processPhotoAsync(src, dst, provia, null, 0.33, 0.3, 0.3, 95);
|
||||
console.error('[BENCH] fullRun=' + i + ' decodeMs=' + t.decodeMs + ' toneMs=' + (t.toneMs ?? 0) + ' colorMs=' + t.colorMs + ' encodeWriteMs=' + t.encodeWriteMs + ' totalMs=' + t.totalMs + ' pong=' + pong);
|
||||
await run(`fullRun-${i}`, src, { matrix: provia, toneDr: 0.33, toneHl: 0.3, toneSh: 0.3, cinema: summer, denoise: 10, clarity: 10, grain: 10 });
|
||||
}
|
||||
console.error('[BENCH] done pong=' + pong);
|
||||
} catch (e) {
|
||||
console.error('[BENCH] failed', e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user