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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promi
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';

import { ADVANCED_SHAPING_CASES } from '../src/benchmark/advanced-shaping.ts';
import { ADVANCED_SHAPING_CASES } from '../src/workloads/advanced-shaping.ts';

const harfBuzzVersion = '13.0.0';
const sourceDirectory = resolve('fixtures/fonts/noto-sans-cjk-2.004');
Expand Down
4 changes: 2 additions & 2 deletions apps/benchmarks/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
type RefObject,
} from 'react';

import { BENCHMARK_IPSUM_INTER_GLYPH_COUNT } from './benchmark/benchmark-ipsum';
import { BENCHMARK_IPSUM_INTER_GLYPH_COUNT } from './workloads/benchmark-ipsum';
import {
advanceAdvancedShapingByTime,
advancedShapingCase,
Expand All @@ -23,7 +23,7 @@ import {
updateAdvancedShaping,
type AdvancedShapingCommand,
type AdvancedShapingFrame,
} from './benchmark/advanced-shaping';
} from './workloads/advanced-shaping';
import type { BenchmarkSummary, RunnerEvent } from './benchmark/contracts';
import { environmentResource } from './benchmark/environment';
import { runRegisteredBenchmark } from './benchmark/execution';
Expand Down
4 changes: 2 additions & 2 deletions apps/benchmarks/src/benchmark/font-fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AdvancedShapingFontFixture } from './advanced-shaping';
import { BENCHMARK_IPSUM_CONFORMANCE_TEXT, BENCHMARK_IPSUM_TEXT } from './benchmark-ipsum';
import type { AdvancedShapingFontFixture } from '../workloads/advanced-shaping';
import { BENCHMARK_IPSUM_CONFORMANCE_TEXT, BENCHMARK_IPSUM_TEXT } from '../workloads/benchmark-ipsum';

export type BenchmarkFontFixture =
| AdvancedShapingFontFixture
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { ADVANCED_SHAPING_PRESENTATION_CYCLE_DURATION_MS } from './advanced-shaping';
import { ADVANCED_SHAPING_PRESENTATION_CYCLE_DURATION_MS } from '../workloads/advanced-shaping';
import {
adjacentPresentationWorkload,
PRESENTATION_SCENES,
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/src/benchmark/presentation-sequence.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ADVANCED_SHAPING_PRESENTATION_CYCLE_DURATION_MS } from './advanced-shaping';
import { ADVANCED_SHAPING_PRESENTATION_CYCLE_DURATION_MS } from '../workloads/advanced-shaping';

export const TEXT_LADDER_PRESENTATION_DURATION_MS = 7_200;
// Three complete 3.5-second base cycles at the default 1.125× animation rate, rounded up to the next millisecond.
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/src/benchmark/scenarios.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { BenchmarkScenario } from './contracts';
import { ADVANCED_SHAPING_CASES } from './advanced-shaping';
import { ADVANCED_SHAPING_CASES } from '../workloads/advanced-shaping';
import paragraphBidiContract from '../../fixtures/contracts/paragraph-bidi-layout-v0.json';
import cjkContract from '../../fixtures/contracts/paragraph-cjk-layout-v0.json';
import cjkManifest from '../../fixtures/fonts/noto-sans-cjk-2.004/manifest.json';
Expand Down
5 changes: 5 additions & 0 deletions apps/benchmarks/src/benchmark/target-boundary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,16 @@ describe('benchmark target boundaries', () => {
it('resolves targets through the lazy group registry without importing the monolithic implementation facade', async () => {
const registry = await readFile(new URL('./targets/registry.ts', import.meta.url), 'utf8');
const execution = await readFile(new URL('./execution.ts', import.meta.url), 'utf8');
const conformance = await readFile(new URL('./targets/conformance/index.ts', import.meta.url), 'utf8');

expect(registry).toContain("import('./product')");
expect(registry).toContain("import('./measurement/font-baker')");
expect(registry).toContain("import('./conformance')");
expect(execution).toContain('await loadRegisteredTarget(request.targetId)');
expect(conformance).toContain("import('./advanced-shaping')");
expect(conformance).not.toContain('renderer/advanced-shaping-conformance');
const { createAdvancedShapingConformanceTarget } = await import('./targets/conformance/advanced-shaping');
expect(createAdvancedShapingConformanceTarget().id).toBe('advanced-shaping-conformance');
expect(await loadRegisteredTarget('missing')).toBeUndefined();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { FontRegistry, Text, type RegisteredFont } from '@pmndrs/text';
import { bitmap } from '@pmndrs/text/raster/bitmap';
import * as THREE from 'three/webgpu';

import amiriBitmapFontUrl from '../../fixtures/rendering/amiri-bitmap-16.font.glb?url';
import notoCjkShowcaseBitmapFontUrl from '../../fixtures/rendering/noto-sans-cjk-showcase-bitmap-16.font.glb?url';
import interBitmapFontUrl from '../../fixtures/rendering/inter-bitmap-16.font.glb?url';
import devanagariBitmapFontUrl from '../../fixtures/rendering/noto-sans-devanagari-bitmap-16.font.glb?url';
import amiriBitmapFontUrl from '../../../../fixtures/rendering/amiri-bitmap-16.font.glb?url';
import notoCjkShowcaseBitmapFontUrl from '../../../../fixtures/rendering/noto-sans-cjk-showcase-bitmap-16.font.glb?url';
import interBitmapFontUrl from '../../../../fixtures/rendering/inter-bitmap-16.font.glb?url';
import devanagariBitmapFontUrl from '../../../../fixtures/rendering/noto-sans-devanagari-bitmap-16.font.glb?url';
import {
ADVANCED_SHAPING_CASES,
advancedShapingFrames,
type AdvancedShapingFontFixture,
} from '../benchmark/advanced-shaping';
import type { BenchmarkTarget } from '../benchmark/contracts';
import { hashParagraphLayout, paragraphLayoutBytes } from '../benchmark/paragraph-layout-digest';
} from '../../../workloads/advanced-shaping';
import type { BenchmarkTarget } from '../../contracts';
import { hashParagraphLayout, paragraphLayoutBytes } from '../../paragraph-layout-digest';

const VIEWPORT_WIDTH = 800;
const FONT_SIZE = 16;
Expand Down
3 changes: 1 addition & 2 deletions apps/benchmarks/src/benchmark/targets/conformance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ const advancedShapingTarget = () =>
capabilities: rasterCapabilities,
status: () => 'ready',
},
async () =>
(await import('../../../renderer/advanced-shaping-conformance')).createAdvancedShapingConformanceTarget(),
async () => (await import('./advanced-shaping')).createAdvancedShapingConformanceTarget(),
);

function sourceOutlineFidelityTarget(technique: Technique, backend: Backend): BenchmarkTarget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
type AdvancedShapingCommand,
type AdvancedShapingFrame,
type AdvancedShapingState,
} from '../benchmark/advanced-shaping';
} from '../workloads/advanced-shaping';
/*
* The dock intentionally consumes the same typed control state as the main panel.
* shadcn/Base UI owns interaction semantics; this component only maps state to a compact presentation.
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/src/components/render-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type AdvancedShapingCommand,
type AdvancedShapingFrame,
type AdvancedShapingState,
} from '../benchmark/advanced-shaping';
} from '../workloads/advanced-shaping';
import {
BENCHMARK_FONT_LABELS,
ICON_GRID_FONT_FIXTURE,
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/src/components/workload-rail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState, type Dispatch, type SetStateAction } from 'react';
import type { AdvancedShapingFrame } from '../benchmark/advanced-shaping';
import type { AdvancedShapingFrame } from '../workloads/advanced-shaping';
import {
ADVANCED_FONT_FIXTURES,
BENCHMARK_FONT_LABELS,
Expand Down
95 changes: 40 additions & 55 deletions apps/benchmarks/src/renderer/comparison-workload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
comparisonWorkloadRequiresIconWindowSuspension as registryRequiresIconWindowSuspension,
comparisonWorkloadUpdateKind as registryUpdateKind,
} from '../workloads/registry';
import { DYNAMIC_LAYOUT_TEXT, animateDynamicLayoutEntries, dynamicLayoutWidths } from '../workloads/dynamic-layout';
import { DYNAMIC_LAYOUT_TEXT, dynamicLayoutWidths } from '../workloads/dynamic-layout';
import {
ICON_GRID_ITEMS,
ICON_GRID_LABEL_SIZE,
Expand All @@ -33,14 +33,14 @@ import {
type IconGridLayout,
type IconGridVirtualWindow,
} from '../workloads/icon-grid';
import { animateOffAxis3dEntries } from '../workloads/off-axis-3d';
import { animatePaintEffectsEntries, applyPaintEffectsRetainedConfiguration } from '../workloads/paint-effects';
import { animateParagraphStressScene } from '../workloads/paragraph-stress';
import { animateTextLadderScene } from '../workloads/text-ladder';
import type { MutableTextLadderScenePosition } from '../workloads/text-ladder';
import { ZOOM_TEXT_BASE_CSS_PX, animateZoomTextEntries } from '../workloads/zoom-text';
import { ZOOM_TEXT_BASE_CSS_PX } from '../workloads/zoom-text';
import type { ZoomTextAnimationState } from '../workloads/zoom-text';
import type { ComparisonWorkloadConfiguration, ComparisonWorkloadId } from '../workloads/contracts';
import type {
ComparisonWorkloadAnimationScratch,
ComparisonWorkloadConfiguration,
ComparisonWorkloadId,
} from '../workloads/contracts';
import { committedTextLayout, type ComparisonWorkloadEntry } from '../workloads/factory-contracts';
import { loadBitmapFont, registeredBitmapAtlas, type BitmapTextLiveStats } from './bitmap-text';
import { createCanvasSurface } from './canvas-surface';
Expand Down Expand Up @@ -401,6 +401,11 @@ async function createComparisonWorkloadRuntime(
const zoomAnimationState: ZoomTextAnimationState = { phraseIndex: 0, phraseRevision: 0, progress: 0 };
const textLadderPositionScratch: MutableTextLadderScenePosition = { x: 0, y: 0 };
const dynamicWidthsScratch = new Float64Array(DYNAMIC_LAYOUT_TEXT.length);
const workloadAnimationScratch: ComparisonWorkloadAnimationScratch = {
dynamicWidths: dynamicWidthsScratch,
textLadderPosition: textLadderPositionScratch,
zoomText: zoomAnimationState,
};
const iconAutoPanState: IconGridAutoPanState = { directionX: 1, directionY: 1, scrollX: 0, scrollY: 0 };
const iconFrameDeltaState: IconGridFrameDeltaState = { smoothedElapsedMs: undefined };
let iconAutoPanTimestamp: number | undefined;
Expand Down Expand Up @@ -1064,37 +1069,34 @@ async function createComparisonWorkloadRuntime(
if (
renderScene &&
!fontFixtureSwitching &&
configuration.workload === 'text-ladder' &&
configuration.animationEnabled
(configuration.workload === 'text-ladder' || configuration.workload === 'paragraph-stress')
) {
animateTextLadderScene(
scene,
animateEntries(
entries,
configuration,
Math.max(0, timestamp - animationEpoch),
width,
height,
textLadderPositionScratch,
scene,
workloadAnimationScratch,
onError,
recordReflow,
);
}
if (
renderScene &&
!fontFixtureSwitching &&
configuration.workload === 'paragraph-stress' &&
configuration.animationEnabled
) {
animateParagraphStressScene(scene, entries, configuration, Math.max(0, timestamp - animationEpoch), height);
}
if (renderScene && !fontFixtureCommitting) {
if (!fontFixtureSwitching) {
if (
!fontFixtureSwitching &&
configuration.workload !== 'text-ladder' &&
configuration.workload !== 'paragraph-stress'
) {
animateEntries(
entries,
configuration,
Math.max(0, timestamp - animationEpoch),
zoomAnimationState,
dynamicWidthsScratch,
width,
height,
scene,
workloadAnimationScratch,
onError,
recordReflow,
);
Expand Down Expand Up @@ -1422,50 +1424,33 @@ function layoutEntries(
function animateEntries(
entries: readonly WorkloadEntry[],
configuration: ComparisonWorkloadConfiguration,
timestamp: number,
zoomAnimationState: ZoomTextAnimationState,
dynamicWidthsScratch: Float64Array,
elapsedMs: number,
width: number,
height: number,
scene: THREE.Scene,
scratch: ComparisonWorkloadAnimationScratch,
onError: (error: unknown) => void,
onReflow: (duration: number) => void,
): void {
if (configuration.workload === 'zoom-text') {
animateZoomTextEntries(entries, configuration, timestamp, zoomAnimationState);
return;
}
if (configuration.workload === 'paint-effects') {
animatePaintEffectsEntries(entries, configuration, timestamp);
return;
}
if (configuration.workload === 'dynamic-layout') {
animateDynamicLayoutEntries(
entries,
configuration,
timestamp,
width,
height,
dynamicWidthsScratch,
onError,
onReflow,
);
return;
}
if (configuration.workload === 'off-axis-3d') {
animateOffAxis3dEntries(entries, configuration, timestamp);
}
comparisonWorkloadDefinition(configuration.workload).animate(
entries,
configuration,
elapsedMs,
width,
height,
scene,
scratch,
onError,
onReflow,
);
}

function applyRetainedConfiguration(
entries: readonly WorkloadEntry[],
technique: RasterTechnique,
configuration: ComparisonWorkloadConfiguration,
): void {
for (const entry of entries) {
if (entry.bounds !== undefined) entry.bounds.visible = configuration.showLayoutBounds;
}
if (configuration.workload !== 'paint-effects') return;
applyPaintEffectsRetainedConfiguration(entries, technique, configuration);
comparisonWorkloadDefinition(configuration.workload).applyRetainedConfiguration(entries, configuration, technique);
}

export function comparisonWorkloadUpdateKind(
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/src/renderer/mtsdf-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import showcaseManifest from '../../fixtures/rendering/showcase-mtsdf-fixtures-v
import { conformanceText, type BenchmarkFontFixture, type SelectableFontFixture } from '../benchmark/font-fixtures';
import type { BenchmarkTarget, TargetRunOutput } from '../benchmark/contracts';
import type { FontDelivery } from '../benchmark/url-state';
import { BENCHMARK_IPSUM_CONFORMANCE_TEXT } from '../benchmark/benchmark-ipsum';
import { BENCHMARK_IPSUM_CONFORMANCE_TEXT } from '../workloads/benchmark-ipsum';
import { createCanvasSurface, type CanvasSurface } from './canvas-surface';
import { finiteCanvasDelta } from './canvas-view';
import type { LiveFrameHistoryCursor } from './live-frame-telemetry';
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/src/renderer/slug-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import devanagariCompressedFontUrl from '../../fixtures/rendering/noto-sans-deva
import notoCjkShowcaseCompressedFontUrl from '../../fixtures/rendering/noto-sans-cjk-showcase-slug.font.glb.gz?url';
import sourceSerifCompressedFontUrl from '../../fixtures/rendering/source-serif-4-slug.font.glb.gz?url';
import showcaseManifest from '../../fixtures/rendering/showcase-slug-fixtures-v0.json';
import { BENCHMARK_IPSUM_CONFORMANCE_TEXT } from '../benchmark/benchmark-ipsum';
import { BENCHMARK_IPSUM_CONFORMANCE_TEXT } from '../workloads/benchmark-ipsum';
import type { BenchmarkTarget, TargetRunOutput } from '../benchmark/contracts';
import { rasterConformanceSpecimen, type BenchmarkFontFixture } from '../benchmark/font-fixtures';
import type { FontDelivery } from '../benchmark/url-state';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import type { BenchmarkMeasurement } from './contracts';
import { scenarioById } from './scenarios';
import type { BenchmarkMeasurement } from '../benchmark/contracts';
import { scenarioById } from '../benchmark/scenarios';

import {
ADVANCED_SHAPING_CASES,
Expand All @@ -14,7 +14,7 @@ import {
updateAdvancedShaping,
} from './advanced-shaping';

describe('advanced-shaping timeline', () => {
describe('Advanced Shaping workload timeline', () => {
it('starts the live showcase on CJK without publishing an empty playing frame', () => {
expect(initialAdvancedShapingState()).toMatchObject({
playing: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** Authored Advanced Shaping workload state and timeline. */
import { graphemeSegments } from 'unicode-segmenter/grapheme';

export type AdvancedShapingCaseId =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
BENCHMARK_IPSUM_TEXT,
} from './benchmark-ipsum';

describe('benchmark ipsum', () => {
describe('Benchmark Ipsum workload', () => {
it('keeps five independently diagnosable conformance lanes', () => {
expect(BENCHMARK_IPSUM_CONFORMANCE_LINES).toHaveLength(5);
expect(BENCHMARK_IPSUM_CONFORMANCE_TEXT).toBe(BENCHMARK_IPSUM_CONFORMANCE_LINES.join('\n'));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Stable product-demo corpus for native-strike rendering checks.
* Stable Benchmark Ipsum workload corpus for native-strike rendering checks.
*
* Keep each concern on its own line so a visual failure can be localized without
* changing the corpus. Missing fixture coverage is reported rather than hidden by
Expand Down
24 changes: 24 additions & 0 deletions apps/benchmarks/src/workloads/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AnyRasterInput, RegisteredFont } from '@pmndrs/text';
import type * as THREE from 'three/webgpu';

import type { RasterConformanceSpecimen, BenchmarkFontFixture } from '../benchmark/font-fixtures';
import type { RasterTechnique } from '../benchmark/url-state';
Expand Down Expand Up @@ -56,6 +57,13 @@ export interface ComparisonWorkloadCreateContext extends ComparisonWorkloadLayou
readonly textLadderSpecimen?: RasterConformanceSpecimen;
}

/** Reused host-owned scratch storage exposed to workload frame hooks without per-frame allocation. */
export interface ComparisonWorkloadAnimationScratch {
readonly dynamicWidths: Float64Array;
readonly textLadderPosition: { x: number; y: number };
readonly zoomText: { phraseIndex: number; phraseRevision: number; progress: number };
}

/**
* App-private workload policy and scene hooks. The retained host remains responsible for renderer,
* scene activation, cancellation, telemetry, and transactional Text publication.
Expand All @@ -67,6 +75,22 @@ export interface ComparisonWorkloadDefinition {
readonly suspendsIconWindow: boolean;
create(context: ComparisonWorkloadCreateContext): readonly ComparisonWorkloadEntry[];
layout(entries: readonly ComparisonWorkloadEntry[], context: ComparisonWorkloadLayoutContext): void;
animate(
entries: readonly ComparisonWorkloadEntry[],
configuration: ComparisonWorkloadConfiguration,
elapsedMs: number,
viewportWidth: number,
viewportHeight: number,
scene: THREE.Scene,
scratch: ComparisonWorkloadAnimationScratch,
onError: (error: unknown) => void,
onReflow: (duration: number) => void,
): void;
applyRetainedConfiguration(
entries: readonly ComparisonWorkloadEntry[],
configuration: ComparisonWorkloadConfiguration,
technique: RasterTechnique,
): void;
updateKind(
previous: ComparisonWorkloadConfiguration,
next: ComparisonWorkloadConfiguration,
Expand Down
Loading
Loading