Skip to content
Closed
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
2 changes: 1 addition & 1 deletion docs/packages/glyph.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Implements portable font loading, retained Rust shaping and layout,
resource: ../../packages/glyph
workspace_package: '@pmndrs/glyph'
documentation_type: reference
source_digest: 'sha256:d2329e42da8b6e4584a04b887d90bf2f51e0bc18b9460206b38465ce6dbeb868'
source_digest: 'sha256:14200b3f0f4025d2113c4595fa39cd39755699e3a2904d0e45ccd5b6c753bc9f'
tags: [package, public-api, rust, wasm, threejs, typography]
sources:
- id: manifest
Expand Down
2 changes: 1 addition & 1 deletion docs/planning/typegpu-first-shader-authority.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ sources:
resource: ../../packages/glyph/src/raster/slug-technique.ts
title: Merged v0 Slug TSL implementation
- id: slug-texture-v0
resource: ../../packages/glyph/src/internal/slug-shaders/slug-texture.ts
resource: ../../packages/glyph/src/tsl/slug-shaders/slug-texture.ts
title: Merged v0 Slug texture access
- id: gpucat
resource: https://github.com/isaac-mason/gpucat/tree/11cf91b5172cc5143f68ff6ebf01c5e815de4e94
Expand Down
4 changes: 4 additions & 0 deletions packages/glyph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"types": "./dist/core.d.ts",
"import": "./dist/core.js"
},
"./tsl": {
"types": "./dist/tsl.d.ts",
"import": "./dist/tsl.js"
},
"./three": {
"types": "./dist/three.d.ts",
"import": "./dist/three.js"
Expand Down
16 changes: 0 additions & 16 deletions packages/glyph/src/three.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ export type {
} from './formatted-text.js';
export type { FontSelection, FontStack, LoadedFont } from './loaded-font.js';
export type { GlyphBufferCapacity, ParagraphContentBox, ParagraphStyle } from './text-properties.js';
export { bitmapShader } from './three/bitmap-shader.js';
export type {
ThreeBitmapInstanceNodes,
ThreeBitmapShaderOutput,
ThreeBitmapShaderResources,
} from './three/bitmap-shader.js';
export { FontLoader } from './three/font-loader.js';
export { defineTextMaterial } from './three/material.js';
export type { ThreeTextMaterial, ThreeTextMaterialContext } from './three/material.js';
Expand All @@ -26,20 +20,10 @@ export type {
ThreePlanProgramMaterialContext,
ThreeRasterPlanProgram,
} from './three/plan-program-registry.js';
export { msdfShader } from './three/msdf-shader.js';
export type { ThreeMsdfInstanceNodes, ThreeMsdfShaderOutput, ThreeMsdfShaderResources } from './three/msdf-shader.js';
export type {
ThreeFontLoaderOptions as FontLoaderOptions,
ThreeLoadedFontRequest as LoadedFontRequest,
} from './three/font-loader.js';
export { slugShader } from './three/slug-shader.js';
export type {
ThreeSlugFillRule,
ThreeSlugInstanceNodes,
ThreeSlugPageResources,
ThreeSlugShaderOutput,
ThreeSlugShaderResources,
} from './three/slug-shader.js';
export { Text, TextGroup } from './three/text.js';
export type {
StandaloneTextProperties,
Expand Down
10 changes: 5 additions & 5 deletions packages/glyph/src/three/engine-plan-target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { TextEngineRenderPlanView, type RenderPlanTable } from '../core/plan-vie
import { bitmap, type BitmapStrikeData } from '../raster/bitmap-technique.js';
import { msdf, type MsdfData } from '../raster/msdf.js';
import { slug, type SlugPageData } from '../raster/slug-technique.js';
import { decorationShader } from './decoration-shader.js';
import { bitmapShader } from './bitmap-shader.js';
import { decorationShader } from '../tsl/decoration-shader.js';
import { bitmapShader } from '../tsl/bitmap-shader.js';
import type { ThreeTextEngineCoordinator, ThreeTextEngineResource } from './engine-runtime.js';
import { msdfShader } from './msdf-shader.js';
import { slugShader, type ThreeSlugPageResources } from './slug-shader.js';
import { msdfShader } from '../tsl/msdf-shader.js';
import { slugShader, type TslSlugPageResources } from '../tsl/slug-shader.js';
import type { ThreeTextMaterialContext } from './material.js';
import type { ThreePlanProgramBuffer } from './plan-program-registry.js';

Expand All @@ -36,7 +36,7 @@ interface RetainedResource {
readonly referenceId: number;
}

interface RetainedSlugPage extends ThreeSlugPageResources {
interface RetainedSlugPage extends TslSlugPageResources {
readonly byteLength: number;
dispose(): void;
}
Expand Down
12 changes: 6 additions & 6 deletions packages/glyph/src/three/material.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import type { Node, NodeMaterial } from 'three/webgpu';

import type { ThreeBitmapShaderOutput } from './bitmap-shader.js';
import type { ThreeMsdfShaderOutput } from './msdf-shader.js';
import type { ThreeSlugShaderOutput } from './slug-shader.js';
import type { TslBitmapShaderOutput } from '../tsl/bitmap-shader.js';
import type { TslMsdfShaderOutput } from '../tsl/msdf-shader.js';
import type { TslSlugShaderOutput } from '../tsl/slug-shader.js';

export type ThreeTextMaterialContext =
| Readonly<{
technique: 'pmndrs.bitmap';
shader: ThreeBitmapShaderOutput;
shader: TslBitmapShaderOutput;
/** Final renderer-local position including policy-selected transform indirection. */
position: Node<'vec3'>;
createDefaultMaterial(): NodeMaterial;
}>
| Readonly<{
technique: 'pmndrs.msdf';
shader: ThreeMsdfShaderOutput;
shader: TslMsdfShaderOutput;
position: Node<'vec3'>;
createDefaultMaterial(): NodeMaterial;
}>
| Readonly<{
technique: 'pmndrs.slug';
shader: ThreeSlugShaderOutput;
shader: TslSlugShaderOutput;
position: Node<'vec3'>;
createDefaultMaterial(): NodeMaterial;
}>;
Expand Down
32 changes: 32 additions & 0 deletions packages/glyph/src/tsl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* The technique shader library: TSL node graphs for the Bitmap, MSDF, Slug, and
* decoration techniques, importable without the Three integration so any renderer
* that consumes TSL — including future TypeGPU integrations — reuses one canonical
* shading implementation per technique.
*/
export {
bitmapShader,
type TslBitmapInstanceNodes,
type TslBitmapShaderOptions,
type TslBitmapShaderOutput,
type TslBitmapShaderResources,
} from './tsl/bitmap-shader.js';
export {
msdfShader,
type TslMsdfInstanceNodes,
type TslMsdfShaderOutput,
type TslMsdfShaderResources,
} from './tsl/msdf-shader.js';
export {
slugShader,
type TslSlugFillRule,
type TslSlugInstanceNodes,
type TslSlugPageResources,
type TslSlugShaderOutput,
type TslSlugShaderResources,
} from './tsl/slug-shader.js';
export {
decorationShader,
type TslDecorationInstanceNodes,
type TslDecorationShaderOutput,
} from './tsl/decoration-shader.js';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Node, Texture } from 'three/webgpu';
* One glyph instance's canonical Bitmap fields, already resolved to nodes. Core owns what each field means; how a
* program addresses it — storage buffers, instanced attributes, or a texture — stays the program's own choice.
*/
export interface ThreeBitmapInstanceNodes {
export interface TslBitmapInstanceNodes {
/** Paragraph-local glyph origin, in layout units, with y measured downward. */
readonly origin: Node<'vec2'>;
/** Glyph quad extent in layout units. */
Expand All @@ -21,18 +21,18 @@ export interface ThreeBitmapInstanceNodes {
}

/** The GPU resources one Bitmap glyph batch binds: the single-channel coverage page its strike binding selected. */
export interface ThreeBitmapShaderResources {
export interface TslBitmapShaderResources {
/** Coverage page uploaded in the atlas's own top-down row order, so `flipY` must stay disabled. */
readonly page: Texture;
}

export interface ThreeBitmapShaderOptions {
export interface TslBitmapShaderOptions {
/** Snap projected vertices to physical pixels. Disabled by default so animated transforms retain subpixel motion. */
readonly pixelSnapping?: boolean;
}

/** Everything the canonical Bitmap graph produces, so a program can consume a stage or compose over its final output. */
export interface ThreeBitmapShaderOutput {
export interface TslBitmapShaderOutput {
readonly position: Node<'vec3'>;
/**
* Clip-space vertex position selected by the shader options. Pixel snapping is opt-in because it preserves strike
Expand All @@ -57,10 +57,10 @@ export interface ThreeBitmapShaderOutput {
* the glyph's upper-left corner. A program supplying different geometry owns that correspondence.
*/
export function bitmapShader(
instance: ThreeBitmapInstanceNodes,
resources: ThreeBitmapShaderResources,
options: ThreeBitmapShaderOptions = {},
): ThreeBitmapShaderOutput {
instance: TslBitmapInstanceNodes,
resources: TslBitmapShaderResources,
options: TslBitmapShaderOptions = {},
): TslBitmapShaderOutput {
const atlasUv = TSL.vec2(
instance.uvOrigin.x.add(TSL.uv().x.mul(instance.uvSize.x)),
instance.uvOrigin.y.add(TSL.uv().y.mul(instance.uvSize.y)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as TSL from 'three/tsl';
import type { Node } from 'three/webgpu';

export interface ThreeDecorationInstanceNodes {
export interface TslDecorationInstanceNodes {
/** Decoration rectangle: inline start, block start, inline extent, block extent. */
readonly rect: Node<'vec4'>;
/** Packed decoration lanes: x carries little-endian RGBA color, y carries flags and line style. */
readonly packed: Node<'uvec2'>;
}

export interface ThreeDecorationShaderOutput {
export interface TslDecorationShaderOutput {
readonly position: Node<'vec3'>;
readonly color: Node<'vec3'>;
readonly opacity: Node<'float'>;
Expand All @@ -30,7 +30,7 @@ const srgbTransferEotf = TSL.sRGBTransferEOTF as unknown as (color: Node<'vec3'>
* the Rust gather decodes through its sRGB-to-linear table — so the color channels pass
* through the sRGB EOTF into the renderer's linear working space. Alpha stays linear.
*/
export function decorationShader(instance: ThreeDecorationInstanceNodes): ThreeDecorationShaderOutput {
export function decorationShader(instance: TslDecorationInstanceNodes): TslDecorationShaderOutput {
const byte = TSL.float(1 / 255);
const color = srgbTransferEotf(
TSL.vec3(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Node, Texture } from 'three/webgpu';
* One glyph instance's canonical MSDF fields, already resolved to nodes. Core owns what each field means; how a
* program packs them — the first-party target interleaves them into seven `vec4` storage buffers — stays its own choice.
*/
export interface ThreeMsdfInstanceNodes {
export interface TslMsdfInstanceNodes {
/** Paragraph-local glyph origin, in layout units, with y measured downward. */
readonly origin: Node<'vec2'>;
/** Glyph quad extent in layout units. */
Expand All @@ -28,7 +28,7 @@ export interface ThreeMsdfInstanceNodes {
}

/** The GPU resources one MSDF glyph batch binds, plus the baked constants its distance field was generated with. */
export interface ThreeMsdfShaderResources {
export interface TslMsdfShaderResources {
/** Layered atlas whose RGB channels carry the multi-channel field and whose alpha carries the true distance. */
readonly atlas: Texture;
readonly atlasWidth: number;
Expand All @@ -44,7 +44,7 @@ export interface ThreeMsdfShaderResources {
* gradient, so it is correct at any subpixel placement and must keep the default projection rather than snap to the
* physical pixel grid.
*/
export interface ThreeMsdfShaderOutput {
export interface TslMsdfShaderOutput {
readonly position: Node<'vec3'>;
/** Unclamped atlas coordinate the glyph cell is sampled at. */
readonly atlasUv: Node<'vec2'>;
Expand All @@ -64,10 +64,7 @@ export interface ThreeMsdfShaderOutput {
* The graph reads `positionLocal` and `uv()` from the technique's unit quad: both must span `[0, 1]` with the origin at
* the glyph's upper-left corner. A program supplying different geometry owns that correspondence.
*/
export function msdfShader(
instance: ThreeMsdfInstanceNodes,
resources: ThreeMsdfShaderResources,
): ThreeMsdfShaderOutput {
export function msdfShader(instance: TslMsdfInstanceNodes, resources: TslMsdfShaderResources): TslMsdfShaderOutput {
const atlasU = instance.uvOrigin.x.add(TSL.uv().x.mul(instance.uvSize.x));
const atlasV = instance.uvOrigin.y.add(TSL.uv().y.mul(instance.uvSize.y));
const minimumU = instance.uvBounds.x.add(0.5 / resources.atlasWidth);
Expand Down Expand Up @@ -127,7 +124,7 @@ function median3(value: Node<'vec3'>): Node<'float'> {
function screenPixelRange(
atlasU: Node<'float'>,
atlasV: Node<'float'>,
resources: ThreeMsdfShaderResources,
resources: TslMsdfShaderResources,
): Node<'float'> {
const screenTexelsU = TSL.float(1).div(TSL.max(TSL.fwidth(atlasU), 1e-6));
const screenTexelsV = TSL.float(1).div(TSL.max(TSL.fwidth(atlasV), 1e-6));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as TSL from 'three/tsl';
import type { DataTexture, Node } from 'three/webgpu';

import { slugDilate, slugDilateMatrix, slugRender, type SlugRenderOptions } from '../internal/slug-shaders/index.js';
import { slugDilate, slugDilateMatrix, slugRender, type SlugRenderOptions } from './slug-shaders/index.js';

/**
* One glyph instance's canonical Slug fields, already resolved to nodes. The address and count fields locate the
* glyph's band tables inside the shared page; core owns their meaning, and a program owns how it stores them.
*/
export interface ThreeSlugInstanceNodes {
export interface TslSlugInstanceNodes {
/** Paragraph-local glyph origin, in layout units, with y measured downward. */
readonly origin: Node<'vec2'>;
/** Glyph quad extent in layout units. */
Expand All @@ -31,7 +31,7 @@ export interface ThreeSlugInstanceNodes {
}

/** The three integer textures one decoded Slug page publishes, plus the row widths that address them. */
export interface ThreeSlugPageResources {
export interface TslSlugPageResources {
readonly curveTexture: DataTexture;
readonly curveWidth: number;
readonly headerTexture: DataTexture;
Expand All @@ -41,7 +41,7 @@ export interface ThreeSlugPageResources {
}

/** Optional coverage controls. Omitted fields keep the canonical non-zero winding rule with no weight compensation. */
export interface ThreeSlugFillRule {
export interface TslSlugFillRule {
readonly evenOdd?: Node<'bool'>;
readonly weightBoost?: Node<'bool'>;
readonly stemDarken?: Node<'float'>;
Expand All @@ -53,10 +53,10 @@ export interface ThreeSlugFillRule {
* dilation, so they must describe the same draw the returned position node feeds.
*/
interface ThreeSlugShaderResourceBase {
readonly page: ThreeSlugPageResources;
readonly page: TslSlugPageResources;
/** Drawing-buffer size in device pixels. */
readonly viewport: Node<'vec2'>;
readonly fillRule?: ThreeSlugFillRule;
readonly fillRule?: TslSlugFillRule;
}

interface ThreeSlugShaderRowResources extends ThreeSlugShaderResourceBase {
Expand All @@ -74,15 +74,15 @@ interface ThreeSlugShaderMatrixResources extends ThreeSlugShaderResourceBase {
readonly modelViewProjectionRow3?: never;
}

export type ThreeSlugShaderResources = ThreeSlugShaderRowResources | ThreeSlugShaderMatrixResources;
export type TslSlugShaderResources = ThreeSlugShaderRowResources | ThreeSlugShaderMatrixResources;

/**
* Everything the canonical Slug graph produces, so a program can consume a stage or compose over its final output.
*
* Unlike Bitmap this output publishes no `clipPosition`: Slug integrates coverage analytically from outlines, so it is
* correct at any subpixel placement and must keep the default projection rather than snap to the physical pixel grid.
*/
export interface ThreeSlugShaderOutput {
export interface TslSlugShaderOutput {
/** Dilated glyph-quad position. Reading it from a vertex node is what publishes `renderCoordinate`. */
readonly position: Node<'vec3'>;
/** Interpolated em-space coordinate the coverage integral is evaluated at. */
Expand All @@ -103,10 +103,7 @@ export interface ThreeSlugShaderOutput {
* The graph reads `positionLocal` from the technique's unit quad, which must span `[0, 1]` with the origin at the
* glyph's upper-left corner. A program supplying different geometry owns that correspondence.
*/
export function slugShader(
instance: ThreeSlugInstanceNodes,
resources: ThreeSlugShaderResources,
): ThreeSlugShaderOutput {
export function slugShader(instance: TslSlugInstanceNodes, resources: TslSlugShaderResources): TslSlugShaderOutput {
const renderCoordinate = TSL.varyingProperty('vec2', 'pmndrsSlugRenderCoordinate');
const position = TSL.Fn(() => {
const localPosition = TSL.vec2(
Expand Down Expand Up @@ -170,7 +167,7 @@ export function slugShader(
};
}

function renderOptions(rule: ThreeSlugFillRule | undefined): SlugRenderOptions {
function renderOptions(rule: TslSlugFillRule | undefined): SlugRenderOptions {
return {
evenOdd: rule?.evenOdd ?? TSL.bool(false),
weightBoost: rule?.weightBoost ?? TSL.bool(false),
Expand Down
4 changes: 3 additions & 1 deletion packages/glyph/tests/integration/three-shader.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import test from 'node:test';

import { createTextRuntime, FontRegistry } from '@pmndrs/glyph';
import { bitmap } from '@pmndrs/glyph/three/bitmap';
import { bitmapShader, defineTextMaterial, msdfShader, slugShader, Text } from '@pmndrs/glyph/three';
import { defineTextMaterial, Text } from '@pmndrs/glyph/three';
import { bitmapShader, decorationShader, msdfShader, slugShader } from '@pmndrs/glyph/tsl';
import * as TSL from 'three/tsl';
import * as THREE from 'three/webgpu';

Expand All @@ -14,6 +15,7 @@ test('the canonical technique shaders are exported as callable node builders', (
assert.equal(typeof bitmapShader, 'function');
assert.equal(typeof msdfShader, 'function');
assert.equal(typeof slugShader, 'function');
assert.equal(typeof decorationShader, 'function');
});

test('a custom Three material composes over the Bitmap shader in the Rust command-buffer draw path', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
referenceVerticalIntersections,
resolveSlugCurveTexel,
slugGridCoordinate,
} from '../../dist/internal/slug-shaders/reference.js';
} from '../../dist/tsl/slug-shaders/reference.js';

test('V0 header decoding preserves exact unsigned count and glyph-local offset', () => {
assert.deepEqual(decodeSlugHeader(0xabcd_fffe), {
Expand Down
Loading
Loading