mirror of
https://github.com/penpot/penpot.git
synced 2026-11-06 01:50:39 +00:00
Share seven deterministic fixtures with the playground pages and measure renderer lifecycle, pan, and zoom through a prepared Playwright runner. Retain raw attempts and failures with offline statistical comparisons. Add focused tests and separate browser fault diagnostics. Full-suite calibration and shadow GPU validation remain tracked follow-up work. AI-assisted-by: GPT-6
78 lines
2.4 KiB
JavaScript
78 lines
2.4 KiB
JavaScript
// Browser-independent protocol; the runtime owns the clock and cancellation.
|
|
export async function drain(
|
|
rt,
|
|
{ flags = 0, origin = rt.now(), immediate = false } = {},
|
|
) {
|
|
const slices = [];
|
|
let viewportReadyMs;
|
|
do {
|
|
rt.check();
|
|
const timestamp = immediate ? rt.now() : await rt.frame();
|
|
immediate = false;
|
|
rt.check();
|
|
const start = rt.now();
|
|
const frameType = rt.module._render(timestamp, flags);
|
|
const end = rt.now();
|
|
slices.push({ timestamp, flags, frameType, durationMs: end - start });
|
|
rt.recordSlice?.(slices.at(-1));
|
|
rt.check();
|
|
if (![1, 2, 3].includes(frameType))
|
|
throw new Error(`Unexpected frame type: ${frameType}`);
|
|
if (frameType === 3 || frameType === 2) viewportReadyMs ??= end - origin;
|
|
if (frameType === 2)
|
|
return { slices, viewportReadyMs, fullMs: end - origin };
|
|
// The production scheduler passes the previous FrameType as flags.
|
|
flags = frameType;
|
|
} while (true);
|
|
}
|
|
|
|
export async function restore(rt, view) {
|
|
rt.check();
|
|
rt.module._set_view_start();
|
|
rt.module._set_view(view.scale, view.x, view.y);
|
|
rt.module._set_view_end();
|
|
return drain(rt, { flags: 4, immediate: true });
|
|
}
|
|
|
|
export async function interact(rt, interaction) {
|
|
rt.check();
|
|
const start = rt.now();
|
|
rt.module._set_view_start();
|
|
const cachedSlices = [];
|
|
let lastInput;
|
|
for (const view of interaction.frames) {
|
|
const timestamp = await rt.frame();
|
|
rt.check();
|
|
lastInput = rt.now();
|
|
rt.module._set_view(view.scale, view.x, view.y);
|
|
const cacheStart = rt.now();
|
|
rt.module._render_from_cache(0);
|
|
cachedSlices.push({ timestamp, durationMs: rt.now() - cacheStart });
|
|
rt.recordCached?.(cachedSlices.at(-1));
|
|
}
|
|
const activeEnd = rt.now();
|
|
await rt.sleep(interaction.settleMs);
|
|
rt.check();
|
|
const finalizationStart = rt.now();
|
|
rt.module._set_view_end();
|
|
const setViewEndMs = rt.now() - finalizationStart;
|
|
const result = await drain(rt, {
|
|
flags: 4,
|
|
origin: finalizationStart,
|
|
immediate: true,
|
|
});
|
|
return {
|
|
metrics: {
|
|
activeInteractionMs: activeEnd - start,
|
|
settlingRequestedMs: interaction.settleMs,
|
|
settlingActualMs: finalizationStart - activeEnd,
|
|
setViewEndMs,
|
|
timeToViewportReadyMs: result.viewportReadyMs,
|
|
timeToFullMs: result.fullMs,
|
|
lastInputToFullMs: rt.now() - lastInput,
|
|
},
|
|
slices: result.slices,
|
|
cachedSlices,
|
|
};
|
|
}
|