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
36 changes: 6 additions & 30 deletions apps/benchmarks/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,8 @@ function importComparisonWorkload() {
return import('./renderer/comparison-workload');
}

function loadBitmapTextRenderer() {
return import('./renderer/bitmap-text');
}

function loadMtsdfTextRenderer() {
return import('./renderer/mtsdf-text');
}

function loadSlugTextRenderer() {
return import('./renderer/slug-text');
function loadBenchmarkFontAssets() {
return import('./workloads/font-assets');
}

function preloadComparisonWorkload(): ReturnType<typeof importComparisonWorkload> {
Expand Down Expand Up @@ -152,16 +144,8 @@ async function preloadPresentationAssets(
): Promise<void> {
if (delivery !== 'baked') return;
const fixtures = Array.from(new Set<BenchmarkFontFixture>([selectedFont, ...PRESENTATION_FONT_FIXTURES]));
if (technique === 'bitmap') {
const { preloadBitmapFontAssets } = await loadBitmapTextRenderer();
await preloadBitmapFontAssets(fixtures, signal);
} else if (technique === 'mtsdf') {
const { preloadMtsdfFontAssets } = await loadMtsdfTextRenderer();
await preloadMtsdfFontAssets(fixtures, signal);
} else {
const { preloadSlugFontAssets } = await loadSlugTextRenderer();
await preloadSlugFontAssets(fixtures, signal);
}
const { preloadBenchmarkFontAssets } = await loadBenchmarkFontAssets();
await preloadBenchmarkFontAssets({ technique, fixtures, signal, bitmapDensity: 'live' });
}

function liveSceneAssetResource(
Expand All @@ -180,16 +164,8 @@ function liveSceneAssetResource(
const resource = (async () => {
if (comparison) await preloadComparisonWorkload();
if (delivery !== 'baked') return;
if (technique === 'bitmap') {
const { preloadBitmapFontAssets } = await loadBitmapTextRenderer();
await preloadBitmapFontAssets(fixtures);
} else if (technique === 'mtsdf') {
const { preloadMtsdfFontAssets } = await loadMtsdfTextRenderer();
await preloadMtsdfFontAssets(fixtures);
} else {
const { preloadSlugFontAssets } = await loadSlugTextRenderer();
await preloadSlugFontAssets(fixtures);
}
const { preloadBenchmarkFontAssets } = await loadBenchmarkFontAssets();
await preloadBenchmarkFontAssets({ technique, fixtures, bitmapDensity: 'live' });
})();
liveSceneAssetResources.set(key, resource);
void resource.catch(() => liveSceneAssetResources.delete(key));
Expand Down
112 changes: 14 additions & 98 deletions apps/benchmarks/src/renderer/bitmap-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,6 @@ import {
} from '@pmndrs/text/raster/bitmap';
import * as THREE from 'three/webgpu';

import amiriBitmapFontUrl from '../../fixtures/rendering/amiri-bitmap-16.font.glb?url';
import amiriBitmapDensityFontUrl from '../../fixtures/rendering/amiri-bitmap-16-32.font.glb?url';
import dotGothicBitmapFontUrl from '../../fixtures/rendering/dot-gothic-16-bitmap-16.font.glb?url';
import dotGothicBitmapDensityFontUrl from '../../fixtures/rendering/dot-gothic-16-bitmap-16-32.font.glb?url';
import dancingScriptBitmapFontUrl from '../../fixtures/rendering/dancing-script-bitmap-16.font.glb?url';
import dancingScriptBitmapDensityFontUrl from '../../fixtures/rendering/dancing-script-bitmap-16-32.font.glb?url';
import fontAwesomeBitmapFontUrl from '../../fixtures/rendering/font-awesome-free-6.7.2-bitmap-16.font.glb?url';
import fontAwesomeBitmapDensityFontUrl from '../../fixtures/rendering/font-awesome-free-6.7.2-bitmap-16-32.font.glb?url';
import bitmapFontUrl from '../../fixtures/rendering/inter-bitmap-16.font.glb?url';
import bitmapDensityFontUrl from '../../fixtures/rendering/inter-bitmap-16-32.font.glb?url';
import devanagariBitmapFontUrl from '../../fixtures/rendering/noto-sans-devanagari-bitmap-16.font.glb?url';
import devanagariBitmapDensityFontUrl from '../../fixtures/rendering/noto-sans-devanagari-bitmap-16-32.font.glb?url';
import notoCjkShowcaseBitmapFontUrl from '../../fixtures/rendering/noto-sans-cjk-showcase-bitmap-16.font.glb?url';
import notoCjkShowcaseBitmapDensityFontUrl from '../../fixtures/rendering/noto-sans-cjk-showcase-bitmap-16-32.font.glb?url';
import sourceSerifBitmapFontUrl from '../../fixtures/rendering/source-serif-4-bitmap-16.font.glb?url';
import sourceSerifBitmapDensityFontUrl from '../../fixtures/rendering/source-serif-4-bitmap-16-32.font.glb?url';
import { conformanceText, type BenchmarkFontFixture, type SelectableFontFixture } from '../benchmark/font-fixtures';
import type { FontDelivery } from '../benchmark/url-state';
import type { BenchmarkTarget, TargetRunOutput } from '../benchmark/contracts';
Expand Down Expand Up @@ -71,12 +55,8 @@ import {
} from './persistent-render-host';
import { createPersistentSceneActivation } from './persistent-scene-activation';
import { withRendererStateRestored } from './renderer-state-transaction';
import {
createFontDeliveryMetrics,
loadRuntimeFont,
measuredBitmapRaster,
type FontDeliveryMetrics,
} from './font-delivery';
import { loadBitmapFontAsset } from '../workloads/font-assets/bitmap';
import type { BitmapFixtureDensity } from '../workloads/font-assets';

const WIDTH = 384;
const HEIGHT = 128;
Expand All @@ -86,38 +66,8 @@ const BITMAP_FONT_SIZE = 16;
const CONFORMANCE_BITMAP_STRIKES = [16] as const;
const LIVE_BITMAP_STRIKES = [16, 32] as const;
const bitmapRequest = bitmap({ strikes: CONFORMANCE_BITMAP_STRIKES });
const liveBitmapRequest = bitmap({ strikes: LIVE_BITMAP_STRIKES });
export type BitmapFixtureDensity = 'conformance' | 'live';
const bitmapFontUrls: Readonly<Record<BenchmarkFontFixture, string>> = {
inter: bitmapFontUrl,
amiri: amiriBitmapFontUrl,
'noto-sans-devanagari': devanagariBitmapFontUrl,
'noto-sans-cjk-showcase': notoCjkShowcaseBitmapFontUrl,
'dot-gothic-16': dotGothicBitmapFontUrl,
'font-awesome-free-6.7.2': fontAwesomeBitmapFontUrl,
'source-serif-4': sourceSerifBitmapFontUrl,
'dancing-script': dancingScriptBitmapFontUrl,
};
const bitmapDensityFontUrls: Readonly<Record<BenchmarkFontFixture, string>> = {
inter: bitmapDensityFontUrl,
amiri: amiriBitmapDensityFontUrl,
'noto-sans-devanagari': devanagariBitmapDensityFontUrl,
'noto-sans-cjk-showcase': notoCjkShowcaseBitmapDensityFontUrl,
'dot-gothic-16': dotGothicBitmapDensityFontUrl,
'font-awesome-free-6.7.2': fontAwesomeBitmapDensityFontUrl,
'source-serif-4': sourceSerifBitmapDensityFontUrl,
'dancing-script': dancingScriptBitmapDensityFontUrl,
};

export async function preloadBitmapFontAssets(
fixtures: readonly BenchmarkFontFixture[],
signal?: AbortSignal,
): Promise<void> {
await preloadFontUrls(
fixtures.map((fixture) => bitmapDensityFontUrls[fixture]),
signal,
);
}
export { preloadBitmapFontAssets } from '../workloads/font-assets/bitmap';
export type { BitmapFixtureDensity } from '../workloads/font-assets/bitmap';

interface BitmapTextResources {
readonly backend: RendererBackend;
Expand Down Expand Up @@ -489,50 +439,16 @@ export async function loadBitmapFont(
density: BitmapFixtureDensity = 'conformance',
onProgress?: import('@pmndrs/text').BakeProgressListener,
registry = new FontRegistry(),
): Promise<{
readonly artifactBytes: number;
readonly font: RegisteredFont;
readonly metrics: FontDeliveryMetrics;
readonly raster: ReturnType<typeof bitmap>;
}> {
signal?.throwIfAborted();
const metrics = createFontDeliveryMetrics(delivery);
const raster = density === 'live' ? liveBitmapRequest : bitmapRequest;
if (delivery === 'runtime') {
const loaded = await loadRuntimeFont(fixture, metrics, signal, onProgress, registry);
return {
artifactBytes: metrics.coreArtifactBytes,
font: loaded.font,
metrics,
raster: measuredBitmapRaster(metrics, density, onProgress),
};
}
let font: RegisteredFont | undefined;
try {
const fontResponse = await fetch(
(density === 'live' ? bitmapDensityFontUrls : bitmapFontUrls)[fixture],
signal === undefined ? undefined : { signal },
);
if (!fontResponse.ok) throw new Error(`Unable to load bitmap font fixture (${fontResponse.status})`);
const fontBytes = await fontResponse.arrayBuffer();
signal?.throwIfAborted();
font = await registry.registerAsset(new Uint8Array(fontBytes));
signal?.throwIfAborted();
return { artifactBytes: fontBytes.byteLength, font, metrics, raster };
} catch (error) {
font?.dispose();
throw error;
}
}

async function preloadFontUrls(urls: readonly string[], signal?: AbortSignal): Promise<void> {
await Promise.all(
urls.map(async (url) => {
const response = await fetch(url, signal === undefined ? undefined : { signal });
if (!response.ok) throw new Error(`Unable to preload bitmap font fixture (${response.status})`);
await response.arrayBuffer();
}),
);
): Promise<Awaited<ReturnType<typeof loadBitmapFontAsset>>> {
return loadBitmapFontAsset({
technique: 'bitmap',
fixture,
delivery,
bitmapDensity: density,
...(registry === undefined ? {} : { registry }),
...(signal === undefined ? {} : { signal }),
...(onProgress === undefined ? {} : { onProgress }),
});
}

async function createBitmapLine(
Expand Down
64 changes: 47 additions & 17 deletions apps/benchmarks/src/renderer/comparison-workload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,18 @@ import type {
ComparisonWorkloadId,
} from '../workloads/contracts';
import { committedTextLayout, type ComparisonWorkloadEntry } from '../workloads/factory-contracts';
import { loadBitmapFont, registeredBitmapAtlas, type BitmapTextLiveStats } from './bitmap-text';
import { registeredBitmapAtlas, type BitmapTextLiveStats } from './bitmap-text';
import { createCanvasSurface } from './canvas-surface';
import { createGpuFrameTimer, type GpuFrameTimer } from './gpu-frame-timer';
import { createLiveFrameTelemetry, type LiveFrameTelemetrySnapshot } from './live-frame-telemetry';
import { createTextUpdateTelemetry } from './text-update-telemetry';
import type { FontDeliveryMetrics } from './font-delivery';
import { benchmarkContentWidth } from '../workloads/shared/text-style';
import {
loadMtsdfFont,
registeredMtsdfConfiguration,
type MtsdfRasterConfiguration,
type MtsdfTextLiveStats,
} from './mtsdf-text';
loadBenchmarkFontAsset,
type BakedSlugArtifactSource,
type FontDeliveryMetrics,
} from '../workloads/font-assets';
import { benchmarkContentWidth } from '../workloads/shared/text-style';
import { registeredMtsdfConfiguration, type MtsdfRasterConfiguration, type MtsdfTextLiveStats } from './mtsdf-text';
import type { SlugRasterConfiguration, SlugTextLiveStats } from './slug-text';
import {
createConfiguredRenderer,
Expand Down Expand Up @@ -170,7 +169,7 @@ export interface ComparisonWorkloadPreviewOptions {
readonly showLayoutBounds: boolean;
readonly textLadderExitEnabled: boolean;
readonly signal?: AbortSignal;
readonly slugBakedArtifact?: import('./slug-text').SlugBakedArtifactSource;
readonly slugBakedArtifact?: BakedSlugArtifactSource;
readonly technique: RasterTechnique;
readonly textLadderSpecimen?: RasterConformanceSpecimen;
readonly width: number;
Expand Down Expand Up @@ -1429,12 +1428,20 @@ async function loadTechniqueFont(
delivery: FontDelivery,
signal?: AbortSignal,
onBakeProgress?: import('@pmndrs/text').BakeProgressListener,
slugBakedArtifact?: import('./slug-text').SlugBakedArtifactSource,
slugBakedArtifact?: BakedSlugArtifactSource,
registry?: FontRegistry,
): Promise<LoadedTechniqueFont> {
const startedAt = performance.now();
if (technique === 'bitmap') {
const loaded = await loadBitmapFont(signal, fontFixture, delivery, 'live', onBakeProgress, registry);
const loaded = await loadBenchmarkFontAsset({
technique,
fixture: fontFixture,
delivery,
bitmapDensity: 'live',
...(registry === undefined ? {} : { registry }),
signal,
onProgress: onBakeProgress,
});
const atlas = await registeredBitmapAtlas(loaded.font, 'live');
return {
artifactBytes: loaded.artifactBytes,
Expand All @@ -1448,7 +1455,14 @@ async function loadTechniqueFont(
};
}
if (technique === 'mtsdf') {
const loaded = await loadMtsdfFont(signal, fontFixture, delivery, onBakeProgress, registry);
const loaded = await loadBenchmarkFontAsset({
technique,
fixture: fontFixture,
delivery,
...(registry === undefined ? {} : { registry }),
signal,
onProgress: onBakeProgress,
});
const mtsdfConfiguration = await registeredMtsdfConfiguration(loaded.font, signal);
return {
artifactBytes: loaded.compressedBytes,
Expand All @@ -1462,11 +1476,27 @@ async function loadTechniqueFont(
raster: loaded.raster,
};
}
const { loadSlugBakedArtifact, loadSlugFont, registeredSlugConfiguration } = await import('./slug-text');
const loaded =
slugBakedArtifact === undefined
? await loadSlugFont(signal, fontFixture, delivery, onBakeProgress, registry)
: await loadSlugBakedArtifact(slugBakedArtifact, signal, registry);
const { registeredSlugConfiguration } = await import('./slug-text');
const loaded = await loadBenchmarkFontAsset(
delivery === 'runtime'
? {
technique,
fixture: fontFixture,
delivery,
...(registry === undefined ? {} : { registry }),
signal,
onProgress: onBakeProgress,
}
: {
technique,
fixture: fontFixture,
delivery,
...(slugBakedArtifact === undefined ? {} : { bakedArtifact: slugBakedArtifact }),
...(registry === undefined ? {} : { registry }),
signal,
onProgress: onBakeProgress,
},
);
const slugConfiguration = await registeredSlugConfiguration(loaded.font, signal);
return {
artifactBytes: loaded.compressedBytes,
Expand Down
13 changes: 10 additions & 3 deletions apps/benchmarks/src/renderer/external-raster-proof.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Text } from '@pmndrs/text';
import { FontRegistry, Text } from '@pmndrs/text';
import { glyphExample } from '@pmndrs/text-glyph-example-raster';
import * as THREE from 'three/webgpu';

import type { BenchmarkTarget, TargetRunOutput } from '../benchmark/contracts';
import { compactRgba8Readback } from './tsl-baseline';
import { createFontDeliveryMetrics, loadRuntimeFont } from './font-delivery';
import { loadBenchmarkFontAsset } from '../workloads/font-assets';
import type { PersistentRenderSceneRenderer } from './persistent-render-host';
import { withRendererStateRestored } from './renderer-state-transaction';
import { createConfiguredRenderer, disposeConfiguredRenderer, type RendererBackend } from './webgpu-renderer';
Expand Down Expand Up @@ -97,7 +97,14 @@ async function createResources(
});
target.texture.colorSpace = THREE.NoColorSpace;
target.texture.generateMipmaps = false;
({ font } = await loadRuntimeFont('inter', createFontDeliveryMetrics('runtime'), signal));
({ font } = await loadBenchmarkFontAsset({
technique: 'bitmap',
fixture: 'inter',
delivery: 'runtime',
bitmapDensity: 'conformance',
registry: new FontRegistry(),
signal,
}));
signal?.throwIfAborted();
text = new Text({
text: INITIAL_TEXT,
Expand Down
Loading