Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/benchmarks/doctor.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"$schema": "https://react.doctor/schema/config.json",
"noScore": true,
"supplyChain": {
"enabled": false
},
Expand Down
11 changes: 8 additions & 3 deletions apps/benchmarks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
"check:size": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/measure-package-sizes.mts --check",
"check:paragraph-bidi-contract": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/generate-paragraph-bidi-contract.mts --check",
"check:paragraph-cjk-contract": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/generate-paragraph-cjk-contract.mts --check",
"check:react-doctor": "pnpm dlx react-doctor@0.7.2 . --scope full --blocking warning --no-supply-chain --no-color",
"capture:presentation-workloads": "node ./scripts/capture-presentation-workloads.mts",
"check:mtsdf-render-fixture": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/generate-mtsdf-render-fixture.mts --check",
"check:slug-render-fixture": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/generate-slug-render-fixture.mts --check",
"check:japanese-showcase-subset": "node ./scripts/provision-harfbuzz.mts --check && node ./scripts/generate-japanese-showcase-subset.mts --check",
Expand Down Expand Up @@ -57,15 +59,18 @@
"profile:paragraph-font-size:bitmap": "vitexec --gpu --path '/presentation?mode=benchmark&technique=bitmap&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=paragraph-stress&profileControl=font-size' ./vitexec/paragraph-layout-profile.probe.ts",
"profile:paragraph-font-size:bitmap:cpu": "vitexec --gpu --cpu-profile ./.cache/paragraph-font-size-bitmap.cpuprofile --path '/presentation?mode=benchmark&technique=bitmap&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=paragraph-stress&profileControl=font-size' ./vitexec/paragraph-layout-profile.probe.ts",
"profile:inspect:paragraph-font-size:bitmap": "node ./scripts/summarize-cpu-profile.mts ./.cache/paragraph-font-size-bitmap.cpuprofile",
"profile:icon-grid:gc": "vitexec --gpu --cpu-profile ./.cache/icon-grid.cpuprofile --performance-trace ./.cache/icon-grid-performance.json --heap-snapshot ./.cache/icon-grid-heap.txt --path '/presentation?mode=benchmark&technique=bitmap&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=icon-grid' ./vitexec/icon-grid-gc.probe.ts",
"profile:icon-grid:gc": "vitexec --gpu --cpu-profile ./.cache/icon-grid.cpuprofile --performance-trace ./.cache/icon-grid-performance.json --path '/presentation?mode=benchmark&technique=bitmap&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=icon-grid' ./vitexec/icon-grid-gc.probe.ts",
"profile:presentation-sweep": "vitexec --gpu --path '/presentation?mode=benchmark&technique=bitmap&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=benchmark-ipsum' ./vitexec/presentation-framerate-sweep.probe.ts",
"test": "pnpm check:size && pnpm check:autoresearch-baseline && pnpm check:paragraph-bidi-contract && pnpm check:paragraph-cjk-contract && pnpm test:unit && pnpm test:headless && pnpm test:packed",
"test:external-raster-proof": "vitexec --gpu ./vitexec/external-raster-proof.probe.ts",
"test:headless": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/run-headless.mts --suite conformance --dpr 1 --samples 3 --warmup 1 --port 5181",
"test:packed": "pnpm --filter @pmndrs/text build && pnpm --filter @pmndrs/text-font-baker build && node ./scripts/run-packed-consumer.mts",
"test:live": "node ./scripts/run-live-probe.mts",
"test:presentation-demo": "node ./scripts/run-presentation-demo-probe.mts",
"test:presentation-workloads": "node ./scripts/run-presentation-workload-probe.mts",
"test:presentation": "pnpm test:presentation-workloads && pnpm test:presentation-demo",
"test:presentation-demo": "node ./scripts/run-presentation-demo-matrix.mts",
"test:presentation-demo-case": "node ./scripts/run-presentation-demo-probe.mts",
"test:presentation-workload-case": "node ./scripts/run-presentation-workload-probe.mts",
"test:presentation-workloads": "node ./scripts/run-presentation-workload-matrix.mts",
"test:presentation-controls": "vitexec --gpu --path '/presentation?mode=benchmark&technique=mtsdf&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=icon-grid' ./vitexec/presentation-controls.probe.ts",
"test:raster-technique-compare": "pnpm test:raster-technique-compare:webgpu && pnpm test:raster-technique-compare:webgl",
"test:raster-technique-compare:webgl": "vitexec --gpu --path '/?mode=conformance&technique=mtsdf&backend=webgl2&delivery=baked&dpr=1&font=inter&workload=mtsdf-slug-compare' ./vitexec/raster-technique-compare.probe.ts",
Expand Down
12 changes: 12 additions & 0 deletions apps/benchmarks/scripts/capture-presentation-workloads.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
process.env.PRESENTATION_SCREENSHOT_DIR ??= './.cache/presentation-screenshots';

const { runPresentationProbeMatrix } = await import('./support/run-presentation-probe-matrix.mts');

await runPresentationProbeMatrix({
cases: [
{ backend: 'webgpu', technique: 'mtsdf' },
{ backend: 'webgl2', technique: 'mtsdf' },
],
label: 'presentation screenshot',
script: new URL('./run-presentation-workload-probe.mts', import.meta.url),
});
7 changes: 7 additions & 0 deletions apps/benchmarks/scripts/run-presentation-demo-matrix.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { runPresentationProbeMatrix } from './support/run-presentation-probe-matrix.mts';

await runPresentationProbeMatrix({
cases: [{ backend: 'webgpu' }, { backend: 'webgl2' }],
label: 'timed demo',
script: new URL('./run-presentation-demo-probe.mts', import.meta.url),
});
46 changes: 40 additions & 6 deletions apps/benchmarks/scripts/run-presentation-demo-probe.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { launchProjectChromium } from './support/project-chromium.mts';

const root = fileURLToPath(new URL('..', import.meta.url));
process.chdir(root);
const backend = presentationBackend(process.env.PRESENTATION_BACKEND);
const server = await createServer({ root, server: { host: '127.0.0.1', port: 0 } });
await server.listen();
const address = server.httpServer?.address();
Expand Down Expand Up @@ -37,12 +38,12 @@ try {
});
page.on('pageerror', (error) => consoleProblems.push(`pageerror: ${error.message}`));
await page.goto(
`http://127.0.0.1:${String(address.port)}/presentation?mode=benchmark&technique=mtsdf&backend=webgpu&delivery=baked&dpr=2&font=inter&workload=off-axis-3d`,
`http://127.0.0.1:${String(address.port)}/presentation?mode=benchmark&technique=mtsdf&backend=${backend}&delivery=baked&dpr=2&font=inter&workload=off-axis-3d`,
{ waitUntil: 'domcontentloaded' },
);

const initialViewport = page.locator(
'[data-testid="comparison-live-viewport"][data-workload="off-axis-3d"][data-presentation-pending="false"]',
`[data-testid="comparison-live-viewport"][data-workload="off-axis-3d"][data-presentation-pending="false"][data-backend="${backend}"]`,
);
await initialViewport.waitFor();
await page.waitForFunction(() => document.querySelector('canvas[data-configured-renderer-active="true"]') !== null);
Expand Down Expand Up @@ -74,7 +75,7 @@ try {
if (!(await animationSwitch.isChecked()))
throw new Error('Presentation Space shortcut toggled the focused Animate control');

await page.waitForFunction(() => {
await page.waitForFunction((expectedBackend) => {
const viewport = document.querySelector<HTMLElement>(
'[data-testid="comparison-live-viewport"][data-workload="icon-grid"]',
);
Expand All @@ -83,6 +84,7 @@ try {
Math.abs(Number(viewport.getAttribute(attribute)) - value) < 0.000_001;
return (
viewport.dataset.cameraKind === 'orthographic' &&
viewport.dataset.backend === expectedBackend &&
viewport.dataset.canvasGrid === 'true' &&
viewport.dataset.animationEnabled === 'true' &&
close('data-applied-font-size', 64) &&
Expand All @@ -93,7 +95,7 @@ try {
Number(viewport.dataset.drawCount) > 0 &&
Number(viewport.dataset.framesPerSecond) > 0
);
});
}, backend);

const retainedCanvas = await page.evaluate(() => {
const scope = globalThis as typeof globalThis & { presentationDemoCanvas: Element | null };
Expand All @@ -118,8 +120,25 @@ try {
const observedWorkloads = ['off-axis-3d', 'icon-grid'];
const waitForWorkload = async (workload: string): Promise<void> => {
await page.waitForFunction(
(expected) => new URLSearchParams(location.search).get('workload') === expected,
workload,
({ expectedBackend, expectedWorkload }) => {
if (new URLSearchParams(location.search).get('workload') !== expectedWorkload) return false;
const canvas = document.querySelector('canvas[data-configured-renderer-active="true"]');
if (canvas === null || Number(document.documentElement.dataset.activeConfiguredRenderers) !== 1) return false;
if (expectedWorkload === 'advanced-shaping') {
return (
document.querySelector('[data-testid="benchmark-surface"]')?.hasAttribute('data-advanced-case') === true
);
}
const viewport = document.querySelector<HTMLElement>('[data-testid="comparison-live-viewport"]');
return (
viewport?.dataset.workload === expectedWorkload &&
viewport.dataset.presentationPending === 'false' &&
viewport.dataset.backend === expectedBackend &&
Number(viewport.dataset.glyphCount) > 0 &&
Number(viewport.dataset.drawCount) > 0
);
},
{ expectedBackend: backend, expectedWorkload: workload },
);
observedWorkloads.push(workload);
};
Expand Down Expand Up @@ -177,6 +196,12 @@ try {
if (new URLSearchParams(new URL(page.url()).search).get('workload') !== 'off-axis-3d') {
throw new Error('Timed demo did not finish on Off-axis / 3D');
}
const finalBackend = await page
.locator('[data-testid="comparison-live-viewport"][data-workload="off-axis-3d"]')
.getAttribute('data-backend');
if (finalBackend !== backend) {
throw new Error(`Timed demo changed the configured backend from ${backend} to ${finalBackend ?? 'missing'}`);
}

const workloadControl = page.getByLabel('Live workload', { exact: true });
await workloadControl.focus();
Expand All @@ -201,6 +226,7 @@ try {
'presentation-demo-ready',
JSON.stringify({
focusedControlCaptured: true,
backend,
iconGridDefaults: true,
iconGridFps,
observedWorkloads,
Expand All @@ -213,3 +239,11 @@ try {
await browser?.close();
await server.close();
}

function presentationBackend(value: string | undefined): PresentationBackend {
if (value === undefined || value === 'webgpu') return 'webgpu';
if (value === 'webgl2') return value;
throw new RangeError(`PRESENTATION_BACKEND must be webgpu or webgl2; received ${value}`);
}

type PresentationBackend = 'webgpu' | 'webgl2';
10 changes: 10 additions & 0 deletions apps/benchmarks/scripts/run-presentation-workload-matrix.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { runPresentationProbeMatrix } from './support/run-presentation-probe-matrix.mts';

const backends = ['webgpu', 'webgl2'] as const;
const techniques = ['bitmap', 'mtsdf', 'slug'] as const;

await runPresentationProbeMatrix({
cases: backends.flatMap((backend) => techniques.map((technique) => ({ backend, technique }))),
label: 'sequential workloads',
script: new URL('./run-presentation-workload-probe.mts', import.meta.url),
});
Loading