@motionscript/renderer 0.1.0-alpha.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +201 -201
- package/NOTICE +49 -49
- package/README.md +162 -162
- package/THIRD_PARTY_NOTICES.txt +1333 -1333
- package/dist/browser/index.js +10 -10
- package/dist/browser/index.js.map +4 -4
- package/dist/browser/manifest.json +1 -1
- package/dist/index.d.ts +153 -153
- package/dist/index.js +529 -228
- package/dist/index.js.map +1 -1
- package/dist/ms_renderer_bg.wasm +0 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
# @motionscript/renderer
|
|
2
|
-
|
|
3
|
-
A retained-mode 2D/3D renderer in Rust on wgpu/WebGPU, compiled to
|
|
4
|
-
WebAssembly: the renderer `@motionscript/browser` draws every frame with.
|
|
5
|
-
Hosts use browser's `createBrowserEngine()`, which builds this package in; it
|
|
6
|
-
depends on nothing but `@motionscript/core` (a peer) and mediabunny.
|
|
7
|
-
Apache-2.0; the Rust crates compiled into the WASM are listed with their
|
|
8
|
-
licences in [THIRD_PARTY_NOTICES.txt](THIRD_PARTY_NOTICES.txt).
|
|
9
|
-
|
|
10
|
-
Read [DESIGN.md](DESIGN.md) for the design and how each part was built.
|
|
11
|
-
|
|
12
|
-
## Requirements
|
|
13
|
-
|
|
14
|
-
- Rust through rustup: [rust-toolchain.toml](rust-toolchain.toml) pins the
|
|
15
|
-
toolchain and the `wasm32-unknown-unknown` target, which rustup installs on
|
|
16
|
-
first use. `wasm-bindgen-cli` must be the exact version of the
|
|
17
|
-
`wasm-bindgen` crate in `Cargo.lock` (`0.2.130`); the build scripts refuse
|
|
18
|
-
any other:
|
|
19
|
-
```bash
|
|
20
|
-
cargo install wasm-bindgen-cli --version 0.2.130 --locked
|
|
21
|
-
```
|
|
22
|
-
- To regenerate the licence notices, `cargo-about` 0.9.3:
|
|
23
|
-
`cargo install cargo-about --version 0.9.3 --features cli --locked`.
|
|
24
|
-
- Node 24 and pnpm.
|
|
25
|
-
- A browser with WebGPU (Chrome/Edge, recent Safari).
|
|
26
|
-
|
|
27
|
-
## In the workspace pipelines
|
|
28
|
-
|
|
29
|
-
`pnpm build` here builds the WASM (`--locked`, with remapped build paths),
|
|
30
|
-
then `dist/`, then `dist/browser/` with the engine's
|
|
31
|
-
`scripts/build-browser.mjs`; turbo runs it before `@motionscript/browser`'s
|
|
32
|
-
build. `prepack` refuses to pack a package without its `dist/`.
|
|
33
|
-
|
|
34
|
-
`dist/browser/index.js` is the CDN build: `@motionscript/core` stays an import
|
|
35
|
-
(the host's import map names it), the rest is bundled. Its `manifest.json`
|
|
36
|
-
lists the WASM under `assets["ms_renderer_bg.wasm"]` (from
|
|
37
|
-
`motionscriptBrowser.assets` in `package.json`): `dist/ms_renderer_bg.wasm`.
|
|
38
|
-
The module's default `wasmUrl` (beside itself) does not hold there, so a host
|
|
39
|
-
loading the CDN build passes that asset's URL as `wasmUrl`.
|
|
40
|
-
|
|
41
|
-
The root `pnpm lint` and `pnpm typecheck` include it without Rust: the
|
|
42
|
-
`.d.ts` files wasm-bindgen generates (`web/pkg/ms_renderer.d.ts`,
|
|
43
|
-
`web/pkg-text/ms_text.d.ts`, `web/pkg-r3d/ms_render3d.d.ts`) are committed,
|
|
44
|
-
and a WASM build rewrites them; commit the change when the Rust API moves.
|
|
45
|
-
|
|
46
|
-
CI's `renderer` job (`.github/workflows/ci.yml`) installs the pinned
|
|
47
|
-
toolchain and runs `cargo test --locked --workspace` (with
|
|
48
|
-
`crates/renderer/tests/wgsl_modules.rs`, which runs every WGSL module the
|
|
49
|
-
renderer ships through naga's validation), `pnpm notices:check`, the three
|
|
50
|
-
WASM builds, a check that the committed `.d.ts` files are current, lint,
|
|
51
|
-
typecheck, and the e2e wgpu lane (`packages/e2e`, `pnpm test:wgpu`: scenes
|
|
52
|
-
drawn by this renderer on SwiftShader's WebGPU adapter, diffed against
|
|
53
|
-
committed frames).
|
|
54
|
-
|
|
55
|
-
`pnpm notices` regenerates [THIRD_PARTY_NOTICES.txt](THIRD_PARTY_NOTICES.txt)
|
|
56
|
-
with cargo-about ([about.toml](about.toml) lists the accepted licences, so a
|
|
57
|
-
crate under any other fails it); run it whenever `Cargo.lock` changes.
|
|
58
|
-
|
|
59
|
-
## The entry
|
|
60
|
-
|
|
61
|
-
`src/engine` (in `web/src/engine/`) is what `@motionscript/browser` builds in.
|
|
62
|
-
`createWgpuRenderer({ wasmUrl, loadBytes })` loads the WASM and returns a
|
|
63
|
-
`WgpuRenderer`: `createStorageAdapter`, `createRenderContext` and
|
|
64
|
-
`createMeasureContext` for a session, its exports and stills.
|
|
65
|
-
`isWgpuSupported()` says whether the browser has a WebGPU adapter; there is no
|
|
66
|
-
other backend to fall back to.
|
|
67
|
-
|
|
68
|
-
`wasmUrl` is where `@motionscript/renderer/ms_renderer_bg.wasm` is served. It
|
|
69
|
-
defaults to beside the module.
|
|
70
|
-
|
|
71
|
-
- `WgpuStorageAdapter` decodes images to `ImageBitmap`s, video frames with
|
|
72
|
-
mediabunny, and hands font bytes to ms-text. Bytes come through
|
|
73
|
-
`loadBytes` (a plain `fetch` by default), which a platform points at its own
|
|
74
|
-
asset store.
|
|
75
|
-
- `WgpuSurfaceContext` is the adapter's render context mounted on a canvas,
|
|
76
|
-
with the preview knobs a host sets (`pixelRatio`, `view`, `frame`,
|
|
77
|
-
`clipToFrame`, `frameBackground`). Mounting is asynchronous (WebGPU device
|
|
78
|
-
creation); frames recorded before it finishes are drawn when it does. The
|
|
79
|
-
canvas composites premultiplied, so a transparent frame background shows the
|
|
80
|
-
page behind it. What the adapter can't draw yet is logged once per kind
|
|
81
|
-
(`[wgpu] not drawn yet: …`) and returned by `takeRenderIssues()` as a
|
|
82
|
-
`missing` issue per kind and node (a blend mode it lacks, text on a path,
|
|
83
|
-
`rasterize()`, nested or projective booleans, an off-centre motion-blur
|
|
84
|
-
shutter, an effect with no WGSL port, a 3D feature it doesn't draw), beside
|
|
85
|
-
`shader` issues (WGSL that does not compile, a stored SkSL shader) and
|
|
86
|
-
`params` issues (a uniform that names no `Params` member, a member given no
|
|
87
|
-
value). A session lists them in `buildErrors`; an export hands them to
|
|
88
|
-
`onIssue`.
|
|
89
|
-
- `measureText` and `layoutTextBlock` are ms-text (`MsText`, exported from
|
|
90
|
-
the same WASM), so measurement, carets and drawing agree.
|
|
91
|
-
|
|
92
|
-
### GPU faults
|
|
93
|
-
|
|
94
|
-
Every surface on the page shares one device, so a fault is everyone's.
|
|
95
|
-
`onWgpuEvent(listener)` hears each one (`web/src/engine/faults.ts`):
|
|
96
|
-
|
|
97
|
-
- **`device-lost`** (a driver reset, the GPU process restarting,
|
|
98
|
-
`device.destroy()`): the device and its pipelines are forgotten and every
|
|
99
|
-
live surface is mounted again on a new one, which rebuilds pipelines, fonts
|
|
100
|
-
and user-shader programs and draws the last frame; `device-restored` follows.
|
|
101
|
-
Each surface queues a `device-lost` render issue, which fails an export or
|
|
102
|
-
still under way with a `RenderBackendError` (`retryable: true`) rather than
|
|
103
|
-
encoding frames drawn on the dead device. A frame drawn while a surface
|
|
104
|
-
remounts waits for it, as for a compiling shader. Four losses within a
|
|
105
|
-
minute stop the recovery (`crashed`).
|
|
106
|
-
- **`gpu-error`**: a WebGPU validation, out-of-memory or internal error the
|
|
107
|
-
device raised (its `uncapturederror`), also a `gpu` render issue on every
|
|
108
|
-
surface. The first 32 per page are reported.
|
|
109
|
-
- **`crashed`**: the renderer's WASM panicked (it is built `panic = "abort"`;
|
|
110
|
-
unwinding on wasm32 needs a nightly std, and the hook that reports the panic
|
|
111
|
-
costs nothing measurable in size). The instance is not called again: every
|
|
112
|
-
surface draws nothing more, gets a `crashed` issue, and an export fails
|
|
113
|
-
with a `RenderBackendError` that is not retryable. A reload recovers.
|
|
114
|
-
|
|
115
|
-
A user shader can't hang the device: its loops share a budget of 16,384
|
|
116
|
-
iterations per pixel and a compile that takes over 10 s fails
|
|
117
|
-
([DESIGN.md §6.3](DESIGN.md#63-user-shaders-wgsl-built-2026-11-02)).
|
|
118
|
-
|
|
119
|
-
### User shaders (WGSL)
|
|
120
|
-
|
|
121
|
-
Shader fills and shader effects are WGSL, written against the contract in
|
|
122
|
-
[DESIGN.md §6.3](DESIGN.md#63-user-shaders-wgsl-built-2026-11-02). A session
|
|
123
|
-
validates each new source with naga, compiles its pipelines in the background
|
|
124
|
-
and draws it once they are ready (an export, and any settled frame, waits). A
|
|
125
|
-
document stored with an SkSL shader (`language: "sksl"`) draws nothing there
|
|
126
|
-
and gets a `shader` render issue saying to port it to WGSL. For a shader
|
|
127
|
-
editor the package also exports:
|
|
128
|
-
|
|
129
|
-
- `validateShader(scope, source)`: naga's diagnostics (line and column in the
|
|
130
|
-
author's code) and the reflected `struct Params` members.
|
|
131
|
-
- `ShaderPreview`: a fill or an effect drawn on its own canvas over a test
|
|
132
|
-
pattern or an image (`frame`, `split`, `backdrop`), with the browser's
|
|
133
|
-
`getCompilationInfo()` messages in its status and `snapshot()` for
|
|
134
|
-
thumbnails. It redraws by itself when a program finishes compiling.
|
|
135
|
-
- `WGSL_PRELUDE`: what the renderer puts in front of the author's code, to show
|
|
136
|
-
beside the editor.
|
|
137
|
-
|
|
138
|
-
`node scripts/validate-wgsl.mjs fill|effect <files>` runs the same validation
|
|
139
|
-
in Node (after `pnpm build:wasm`), and `pnpm dev` serves `web/shaders.html`,
|
|
140
|
-
a page that drives the preview (http://localhost:5190/shaders.html).
|
|
141
|
-
|
|
142
|
-
### Built-in effects
|
|
143
|
-
|
|
144
|
-
Each built-in effect is a port (`web/src/effects/<type>.ts`, a `definePort`
|
|
145
|
-
naming its parameters against core's effect spec) and a pass
|
|
146
|
-
(`crates/renderer/src/effects/<type>.wgsl`) that reads them through generated
|
|
147
|
-
accessors (`gen/<type>.wgsl`). `pnpm effects:gen` regenerates those after a
|
|
148
|
-
port changes; `pnpm test:effects` checks that they are current and that the
|
|
149
|
-
spec, the port and the pass agree by name. How to add one:
|
|
150
|
-
[docs-effects-porting.md](docs-effects-porting.md).
|
|
151
|
-
|
|
152
|
-
## Text
|
|
153
|
-
|
|
154
|
-
`ms-text` (`crates/text`) shapes, lays out and measures text and builds glyph
|
|
155
|
-
outlines. `cargo test -p ms-text` checks its layout, measurement and outlines
|
|
156
|
-
against the recorded reference numbers in `crates/text/tests/oracle.json`
|
|
157
|
-
(needs the e2e fonts, `../e2e/fonts` or `MS_FONTS_DIR`). See
|
|
158
|
-
[DESIGN.md §5](DESIGN.md#5-phase-2--text-images-video).
|
|
159
|
-
|
|
160
|
-
## Layout
|
|
161
|
-
|
|
162
|
-
See the repo map at the end of [DESIGN.md](DESIGN.md#9-repo-map).
|
|
1
|
+
# @motionscript/renderer
|
|
2
|
+
|
|
3
|
+
A retained-mode 2D/3D renderer in Rust on wgpu/WebGPU, compiled to
|
|
4
|
+
WebAssembly: the renderer `@motionscript/browser` draws every frame with.
|
|
5
|
+
Hosts use browser's `createBrowserEngine()`, which builds this package in; it
|
|
6
|
+
depends on nothing but `@motionscript/core` (a peer) and mediabunny.
|
|
7
|
+
Apache-2.0; the Rust crates compiled into the WASM are listed with their
|
|
8
|
+
licences in [THIRD_PARTY_NOTICES.txt](THIRD_PARTY_NOTICES.txt).
|
|
9
|
+
|
|
10
|
+
Read [DESIGN.md](DESIGN.md) for the design and how each part was built.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Rust through rustup: [rust-toolchain.toml](rust-toolchain.toml) pins the
|
|
15
|
+
toolchain and the `wasm32-unknown-unknown` target, which rustup installs on
|
|
16
|
+
first use. `wasm-bindgen-cli` must be the exact version of the
|
|
17
|
+
`wasm-bindgen` crate in `Cargo.lock` (`0.2.130`); the build scripts refuse
|
|
18
|
+
any other:
|
|
19
|
+
```bash
|
|
20
|
+
cargo install wasm-bindgen-cli --version 0.2.130 --locked
|
|
21
|
+
```
|
|
22
|
+
- To regenerate the licence notices, `cargo-about` 0.9.3:
|
|
23
|
+
`cargo install cargo-about --version 0.9.3 --features cli --locked`.
|
|
24
|
+
- Node 24 and pnpm.
|
|
25
|
+
- A browser with WebGPU (Chrome/Edge, recent Safari).
|
|
26
|
+
|
|
27
|
+
## In the workspace pipelines
|
|
28
|
+
|
|
29
|
+
`pnpm build` here builds the WASM (`--locked`, with remapped build paths),
|
|
30
|
+
then `dist/`, then `dist/browser/` with the engine's
|
|
31
|
+
`scripts/build-browser.mjs`; turbo runs it before `@motionscript/browser`'s
|
|
32
|
+
build. `prepack` refuses to pack a package without its `dist/`.
|
|
33
|
+
|
|
34
|
+
`dist/browser/index.js` is the CDN build: `@motionscript/core` stays an import
|
|
35
|
+
(the host's import map names it), the rest is bundled. Its `manifest.json`
|
|
36
|
+
lists the WASM under `assets["ms_renderer_bg.wasm"]` (from
|
|
37
|
+
`motionscriptBrowser.assets` in `package.json`): `dist/ms_renderer_bg.wasm`.
|
|
38
|
+
The module's default `wasmUrl` (beside itself) does not hold there, so a host
|
|
39
|
+
loading the CDN build passes that asset's URL as `wasmUrl`.
|
|
40
|
+
|
|
41
|
+
The root `pnpm lint` and `pnpm typecheck` include it without Rust: the
|
|
42
|
+
`.d.ts` files wasm-bindgen generates (`web/pkg/ms_renderer.d.ts`,
|
|
43
|
+
`web/pkg-text/ms_text.d.ts`, `web/pkg-r3d/ms_render3d.d.ts`) are committed,
|
|
44
|
+
and a WASM build rewrites them; commit the change when the Rust API moves.
|
|
45
|
+
|
|
46
|
+
CI's `renderer` job (`.github/workflows/ci.yml`) installs the pinned
|
|
47
|
+
toolchain and runs `cargo test --locked --workspace` (with
|
|
48
|
+
`crates/renderer/tests/wgsl_modules.rs`, which runs every WGSL module the
|
|
49
|
+
renderer ships through naga's validation), `pnpm notices:check`, the three
|
|
50
|
+
WASM builds, a check that the committed `.d.ts` files are current, lint,
|
|
51
|
+
typecheck, and the e2e wgpu lane (`packages/e2e`, `pnpm test:wgpu`: scenes
|
|
52
|
+
drawn by this renderer on SwiftShader's WebGPU adapter, diffed against
|
|
53
|
+
committed frames).
|
|
54
|
+
|
|
55
|
+
`pnpm notices` regenerates [THIRD_PARTY_NOTICES.txt](THIRD_PARTY_NOTICES.txt)
|
|
56
|
+
with cargo-about ([about.toml](about.toml) lists the accepted licences, so a
|
|
57
|
+
crate under any other fails it); run it whenever `Cargo.lock` changes.
|
|
58
|
+
|
|
59
|
+
## The entry
|
|
60
|
+
|
|
61
|
+
`src/engine` (in `web/src/engine/`) is what `@motionscript/browser` builds in.
|
|
62
|
+
`createWgpuRenderer({ wasmUrl, loadBytes })` loads the WASM and returns a
|
|
63
|
+
`WgpuRenderer`: `createStorageAdapter`, `createRenderContext` and
|
|
64
|
+
`createMeasureContext` for a session, its exports and stills.
|
|
65
|
+
`isWgpuSupported()` says whether the browser has a WebGPU adapter; there is no
|
|
66
|
+
other backend to fall back to.
|
|
67
|
+
|
|
68
|
+
`wasmUrl` is where `@motionscript/renderer/ms_renderer_bg.wasm` is served. It
|
|
69
|
+
defaults to beside the module.
|
|
70
|
+
|
|
71
|
+
- `WgpuStorageAdapter` decodes images to `ImageBitmap`s, video frames with
|
|
72
|
+
mediabunny, and hands font bytes to ms-text. Bytes come through
|
|
73
|
+
`loadBytes` (a plain `fetch` by default), which a platform points at its own
|
|
74
|
+
asset store.
|
|
75
|
+
- `WgpuSurfaceContext` is the adapter's render context mounted on a canvas,
|
|
76
|
+
with the preview knobs a host sets (`pixelRatio`, `view`, `frame`,
|
|
77
|
+
`clipToFrame`, `frameBackground`). Mounting is asynchronous (WebGPU device
|
|
78
|
+
creation); frames recorded before it finishes are drawn when it does. The
|
|
79
|
+
canvas composites premultiplied, so a transparent frame background shows the
|
|
80
|
+
page behind it. What the adapter can't draw yet is logged once per kind
|
|
81
|
+
(`[wgpu] not drawn yet: …`) and returned by `takeRenderIssues()` as a
|
|
82
|
+
`missing` issue per kind and node (a blend mode it lacks, text on a path,
|
|
83
|
+
`rasterize()`, nested or projective booleans, an off-centre motion-blur
|
|
84
|
+
shutter, an effect with no WGSL port, a 3D feature it doesn't draw), beside
|
|
85
|
+
`shader` issues (WGSL that does not compile, a stored SkSL shader) and
|
|
86
|
+
`params` issues (a uniform that names no `Params` member, a member given no
|
|
87
|
+
value). A session lists them in `buildErrors`; an export hands them to
|
|
88
|
+
`onIssue`.
|
|
89
|
+
- `measureText` and `layoutTextBlock` are ms-text (`MsText`, exported from
|
|
90
|
+
the same WASM), so measurement, carets and drawing agree.
|
|
91
|
+
|
|
92
|
+
### GPU faults
|
|
93
|
+
|
|
94
|
+
Every surface on the page shares one device, so a fault is everyone's.
|
|
95
|
+
`onWgpuEvent(listener)` hears each one (`web/src/engine/faults.ts`):
|
|
96
|
+
|
|
97
|
+
- **`device-lost`** (a driver reset, the GPU process restarting,
|
|
98
|
+
`device.destroy()`): the device and its pipelines are forgotten and every
|
|
99
|
+
live surface is mounted again on a new one, which rebuilds pipelines, fonts
|
|
100
|
+
and user-shader programs and draws the last frame; `device-restored` follows.
|
|
101
|
+
Each surface queues a `device-lost` render issue, which fails an export or
|
|
102
|
+
still under way with a `RenderBackendError` (`retryable: true`) rather than
|
|
103
|
+
encoding frames drawn on the dead device. A frame drawn while a surface
|
|
104
|
+
remounts waits for it, as for a compiling shader. Four losses within a
|
|
105
|
+
minute stop the recovery (`crashed`).
|
|
106
|
+
- **`gpu-error`**: a WebGPU validation, out-of-memory or internal error the
|
|
107
|
+
device raised (its `uncapturederror`), also a `gpu` render issue on every
|
|
108
|
+
surface. The first 32 per page are reported.
|
|
109
|
+
- **`crashed`**: the renderer's WASM panicked (it is built `panic = "abort"`;
|
|
110
|
+
unwinding on wasm32 needs a nightly std, and the hook that reports the panic
|
|
111
|
+
costs nothing measurable in size). The instance is not called again: every
|
|
112
|
+
surface draws nothing more, gets a `crashed` issue, and an export fails
|
|
113
|
+
with a `RenderBackendError` that is not retryable. A reload recovers.
|
|
114
|
+
|
|
115
|
+
A user shader can't hang the device: its loops share a budget of 16,384
|
|
116
|
+
iterations per pixel and a compile that takes over 10 s fails
|
|
117
|
+
([DESIGN.md §6.3](DESIGN.md#63-user-shaders-wgsl-built-2026-11-02)).
|
|
118
|
+
|
|
119
|
+
### User shaders (WGSL)
|
|
120
|
+
|
|
121
|
+
Shader fills and shader effects are WGSL, written against the contract in
|
|
122
|
+
[DESIGN.md §6.3](DESIGN.md#63-user-shaders-wgsl-built-2026-11-02). A session
|
|
123
|
+
validates each new source with naga, compiles its pipelines in the background
|
|
124
|
+
and draws it once they are ready (an export, and any settled frame, waits). A
|
|
125
|
+
document stored with an SkSL shader (`language: "sksl"`) draws nothing there
|
|
126
|
+
and gets a `shader` render issue saying to port it to WGSL. For a shader
|
|
127
|
+
editor the package also exports:
|
|
128
|
+
|
|
129
|
+
- `validateShader(scope, source)`: naga's diagnostics (line and column in the
|
|
130
|
+
author's code) and the reflected `struct Params` members.
|
|
131
|
+
- `ShaderPreview`: a fill or an effect drawn on its own canvas over a test
|
|
132
|
+
pattern or an image (`frame`, `split`, `backdrop`), with the browser's
|
|
133
|
+
`getCompilationInfo()` messages in its status and `snapshot()` for
|
|
134
|
+
thumbnails. It redraws by itself when a program finishes compiling.
|
|
135
|
+
- `WGSL_PRELUDE`: what the renderer puts in front of the author's code, to show
|
|
136
|
+
beside the editor.
|
|
137
|
+
|
|
138
|
+
`node scripts/validate-wgsl.mjs fill|effect <files>` runs the same validation
|
|
139
|
+
in Node (after `pnpm build:wasm`), and `pnpm dev` serves `web/shaders.html`,
|
|
140
|
+
a page that drives the preview (http://localhost:5190/shaders.html).
|
|
141
|
+
|
|
142
|
+
### Built-in effects
|
|
143
|
+
|
|
144
|
+
Each built-in effect is a port (`web/src/effects/<type>.ts`, a `definePort`
|
|
145
|
+
naming its parameters against core's effect spec) and a pass
|
|
146
|
+
(`crates/renderer/src/effects/<type>.wgsl`) that reads them through generated
|
|
147
|
+
accessors (`gen/<type>.wgsl`). `pnpm effects:gen` regenerates those after a
|
|
148
|
+
port changes; `pnpm test:effects` checks that they are current and that the
|
|
149
|
+
spec, the port and the pass agree by name. How to add one:
|
|
150
|
+
[docs-effects-porting.md](docs-effects-porting.md).
|
|
151
|
+
|
|
152
|
+
## Text
|
|
153
|
+
|
|
154
|
+
`ms-text` (`crates/text`) shapes, lays out and measures text and builds glyph
|
|
155
|
+
outlines. `cargo test -p ms-text` checks its layout, measurement and outlines
|
|
156
|
+
against the recorded reference numbers in `crates/text/tests/oracle.json`
|
|
157
|
+
(needs the e2e fonts, `../e2e/fonts` or `MS_FONTS_DIR`). See
|
|
158
|
+
[DESIGN.md §5](DESIGN.md#5-phase-2--text-images-video).
|
|
159
|
+
|
|
160
|
+
## Layout
|
|
161
|
+
|
|
162
|
+
See the repo map at the end of [DESIGN.md](DESIGN.md#9-repo-map).
|