diff --git a/tools/trace_replay/render_retrace_summary.mjs b/tools/trace_replay/render_retrace_summary.mjs
index 2d8a989e..2869ee8e 100644
--- a/tools/trace_replay/render_retrace_summary.mjs
+++ b/tools/trace_replay/render_retrace_summary.mjs
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
+import { createWriteStream } from "node:fs";
+import { mkdir, readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
@@ -393,37 +394,16 @@ async function renderHtml(rows, groupLabels, outputDir, title, groupLabel, htmlN
`;
};
- const groupSections = groups.map((group) => {
- const displayGroup = groupLabels.get(group) ?? group;
- const groupCells = CASES.flatMap((caseName) => BACKENDS.map((backend) => cellFor(group, backend, caseName)));
- const passed = groupCells.filter((row) => effectiveStatus(row) === "PASS").length;
- const failed = groupCells.filter((row) => effectiveStatus(row) === "FAIL").length;
- const noResult = groupCells.filter((row) => effectiveStatus(row) === "NO_RESULT").length;
- const passRateDenominator = passed + failed;
- const passRate = passRateDenominator === 0 ? "0.0%" : `${(passed * 100 / passRateDenominator).toFixed(1)}%`;
- return `
-
- ${htmlEscape(title)} - ${htmlEscape(displayGroup)}
- Each backend cell shows actual / golden / diff. Failed cases are red; absent results are orange; incomplete image sets get an orange notch. PASS ${passed}, FAIL ${failed}, NO RESULT ${noResult}, PASS RATE ${passRate}.
-
- ${CASES.map((caseName, index) => `
-
-
- ${htmlEscape(shortCase(caseName))}
- ${caseName.includes("iterationt") && !rowMap.has(`${group}\0DirectGLES\0${caseName}`) && !rowMap.has(`${group}\0DirectVulkan\0${caseName}`)
- ? `LFS fixture may be unavailable ` : ""}
-
- ${backendCell(group, "DirectGLES", caseName)}
- ${backendCell(group, "DirectVulkan", caseName)}
-
`).join("")}
- `;
- }).join("");
+ const output = path.join(outputDir, htmlName);
+ const stream = createWriteStream(output, { encoding: "utf8" });
+ const write = (chunk) => new Promise((resolve, reject) => {
+ stream.write(chunk, (error) => error ? reject(error) : resolve());
+ });
+ const finish = () => new Promise((resolve, reject) => {
+ stream.end((error) => error ? reject(error) : resolve());
+ });
- const html = `
+ await write(`
@@ -452,9 +432,8 @@ async function renderHtml(rows, groupLabels, outputDir, title, groupLabel, htmlN
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
- main { width: 1170px; margin: 0 auto; padding: 14px 10px 24px; }
- .group { break-after: page; margin-bottom: 28px; }
- .group:last-child { break-after: auto; }
+ main { width: 1170px; margin: 0 auto; padding: 14px 10px 96px; }
+ .group { margin-bottom: 28px; }
h1 { margin: 0; font-size: 24px; line-height: 1.2; }
p { margin: 4px 0 12px; color: var(--muted); }
.grid {
@@ -576,17 +555,62 @@ async function renderHtml(rows, groupLabels, outputDir, title, groupLabel, htmlN
object-fit: contain;
box-shadow: 0 18px 70px rgb(0 0 0 / 70%);
}
+ .summary-footer {
+ margin-top: 24px;
+ padding: 18px 0 8px;
+ color: var(--muted);
+ border-top: 1px solid var(--line);
+ }
@page { size: 1170px 2380px; margin: 0; }
@media print {
main { padding: 10px; }
- .group { margin-bottom: 0; }
+ .group { break-after: page; margin-bottom: 0; }
+ .group:last-child { break-after: auto; }
+ .summary-footer { display: none; }
.lightbox { display: none; }
}
-${groupSections}
+`);
+
+ for (const group of groups) {
+ const displayGroup = groupLabels.get(group) ?? group;
+ const groupCells = CASES.flatMap((caseName) => BACKENDS.map((backend) => cellFor(group, backend, caseName)));
+ const passed = groupCells.filter((row) => effectiveStatus(row) === "PASS").length;
+ const failed = groupCells.filter((row) => effectiveStatus(row) === "FAIL").length;
+ const noResult = groupCells.filter((row) => effectiveStatus(row) === "NO_RESULT").length;
+ const passRateDenominator = passed + failed;
+ const passRate = passRateDenominator === 0 ? "0.0%" : `${(passed * 100 / passRateDenominator).toFixed(1)}%`;
+ await write(`
+
+ ${htmlEscape(title)} - ${htmlEscape(displayGroup)}
+ Each backend cell shows actual / golden / diff. Failed cases are red; absent results are orange; incomplete image sets get an orange notch. PASS ${passed}, FAIL ${failed}, NO RESULT ${noResult}, PASS RATE ${passRate}.
+
+`);
+ for (const [index, caseName] of CASES.entries()) {
+ await write(`
+
+
+ ${htmlEscape(shortCase(caseName))}
+ ${caseName.includes("iterationt") && !rowMap.has(`${group}\0DirectGLES\0${caseName}`) && !rowMap.has(`${group}\0DirectVulkan\0${caseName}`)
+ ? `LFS fixture may be unavailable ` : ""}
+
+ ${backendCell(group, "DirectGLES", caseName)}
+ ${backendCell(group, "DirectVulkan", caseName)}
+
`);
+ }
+ await write(`
+ `);
+ }
+
+ await write(`
+
@@ -619,9 +643,8 @@ ${groupSections}
-`;
- const output = path.join(outputDir, htmlName);
- await writeFile(output, html, "utf8");
+`);
+ await finish();
return output;
}