Render continuously and optimize the glTF viewer

Remove vsync and dirty-flag frame gating, optimize PBR sampling and lighting, add a direct-to-canvas loadout, and preserve camera state on viewer resize.

Add regression coverage and update rendering documentation.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0b382-1e79-712b-abd1-d29201c9bb5e
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-09-18 08:58:27 +00:00
co-authored by heaust
parent bc957cf40d
commit 4784ad7e87
16 changed files with 573 additions and 178 deletions
+57 -1
View File
@@ -28,7 +28,9 @@ const root = resolve(directory, "../../../..");
const baselines = resolve(
process.env.YAWN_SCREENSHOT_BASELINES ?? join(directory, "baselines"),
);
const settleMilliseconds = Number(process.env.YAWN_SCREENSHOT_SETTLE_MS ?? 1200);
const settleMilliseconds = Number(
process.env.YAWN_SCREENSHOT_SETTLE_MS ?? 1200,
);
const output = resolve(
process.env.YAWN_SCREENSHOT_OUTPUT ?? join(directory, "output"),
);
@@ -310,6 +312,20 @@ before(
await startServer();
await browser("--webgpu", "--headed", "open", `${url}/view/gltf`);
await browser("set", "viewport", "1000", "720", "1");
await browser(
"eval",
`(async () => {
const { YawnCore } = await import("/pkg/core.js");
const onProfile = YawnCore.prototype.onProfile;
YawnCore.prototype.onProfile = function (listener) {
window.__viewerCore = this;
return onProfile.call(this, profile => {
window.__viewerProfile = profile;
listener(profile);
});
};
})()`,
);
},
{ timeout: 900_000 },
);
@@ -392,3 +408,43 @@ for (const model of models) {
{ timeout: 120_000 },
);
}
test(
"viewer rebuilds at the resized pixel size without losing camera pose",
async () => {
await browser(
"eval",
`(() => {
const canvas = document.querySelector("canvas");
canvas.dispatchEvent(new WheelEvent("wheel", { deltaY: -80, cancelable: true }));
window.__resizeCore = window.__viewerCore;
window.__resizePose = Array.from(__viewerCore.array("cameras").row(0));
})()`,
);
await browser("set", "viewport", "640", "480", "2");
await browser(
"wait",
"--fn",
`window.__viewerCore !== window.__resizeCore &&
window.__viewerProfile?.canvas.width === 1280 &&
window.__viewerProfile?.canvas.height === 960 &&
document.querySelector("#drop-prompt").hidden`,
);
const state = await browser(
"eval",
`(() => {
const row = Array.from(__viewerCore.array("cameras").row(0));
return {
size: [document.querySelector("canvas").width, document.querySelector("canvas").height],
aspect: row[1],
posePreserved: [0, 2, 3, 13, 14, 15, 16, 17, 18].every(i => row[i] === __resizePose[i]),
};
})()`,
);
assert.deepEqual(state.result.size, [1280, 960]);
assert.ok(Math.abs(state.result.aspect - 4 / 3) < 1e-6);
assert.equal(state.result.posePreserved, true);
await diagnostics();
},
{ timeout: 120_000 },
);