diff --git a/docs/packages/benchmarks.md b/docs/packages/benchmarks.md index 1b72e7a3..c0444042 100644 --- a/docs/packages/benchmarks.md +++ b/docs/packages/benchmarks.md @@ -445,7 +445,7 @@ authored reflows in every case. The committed baseline measured 14.295 ms median that baseline measured 13.615 ms; adding semantic dirty tiers while retaining the same old Rust measured 7.450 ms; the complete candidate measured 6.885 ms. These telemetry histories contain 13–16 settled samples and establish direction and isolation, not a portable frame-time gate. A normal phase capture attributes most changed-frame CPU time to the -single Rust `text_update`; TypeScript preparation, semantic readback, plan application, and renderer submit are smaller. +single Rust `pmndrs_glyph_engine_update`; TypeScript preparation, semantic readback, plan application, and renderer submit are smaller. A symbol-preserving diagnostic did not provide honest finer Rust attribution because LTO inlines most warm work into the export, so internal phase timers are required before claiming a particular Rust loop is dominant. diff --git a/docs/packages/glyph.md b/docs/packages/glyph.md index 2f5be2af..89c713fd 100644 --- a/docs/packages/glyph.md +++ b/docs/packages/glyph.md @@ -95,7 +95,8 @@ The package owns five runtime layers: Runtime Rust and all shared Rust code remain `no_std + alloc` compatible with the package allocator contract. The optional font-baker Wasm alone enables a feature-gated `std` adapter for Fontations subsetting; the same crate continues to pass its `wasm32-unknown-unknown --no-default-features` build. The text engine uses the existing compile-time direct-memory mapping -for font registrations and the single `text_update(requestOffset, requestLength)` export for retained engine sessions. +for font registrations and the single mutating `pmndrs_glyph_engine_update(sessionId, requestOffset, requestLength)` +export for retained engine sessions. TypeScript does not independently shape, lay out, or pack paragraphs. ## Public package surfaces @@ -290,7 +291,7 @@ The semantic values preserve information useful to callers: ## Wasm memory and copying The host pins request/result staging views and re-pins after any `memory.grow()`, because growth detaches existing views. -Growth is permitted only at the `text_update` boundary. Result capacity is negotiated and retried without publishing a +Growth is permitted only at the `pmndrs_glyph_engine_update` boundary. Result capacity is negotiated and retried without publishing a partial revision. Batch and paragraph capacities are intentionally separate. Request/result arenas scale with aggregate `TextGroup` @@ -478,7 +479,7 @@ The migration comparison is checked evidence rather than a reconstructed recolle `feat/three-api` base, was rebuilt in an isolated worktree using its own lockfile and original `glyph:layout-benchmark` workflow on this Darwin arm64 host. At the same eight-warmup/31-sample cadence its retained TypeScript path measured 58.32/12.09/9.15/39.61 ms for cold/font-size/width/suffix-edit medians. The current Bitmap, -MTSDF, and Slug records all use one byte-identical optimized shaper Wasm and the complete `text_update` plus +MTSDF, and Slug records all use one byte-identical optimized shaper Wasm and the complete `pmndrs_glyph_engine_update` plus technique-specific Rust render plan. The base reports 25,515 positioned glyphs; the current plan reports 21,805 renderable instances from the unchanged 22,000-glyph target because it omits non-rendering glyphs from GPU records. @@ -501,7 +502,7 @@ same run — the first width-change lane under the 4 ms p95 objective, recorded The variance collapse follows from what the query skips: no gather, no plan compile, no publication packing, and no revision burn, so the following ordinary frame adopts the speculative layout instead of paying a checkpoint rebuild. -The preceding unchanged 22,000-glyph localized-edit lane measured the complete production `text_update` plus Bitmap render +The preceding unchanged 22,000-glyph localized-edit lane measured the complete production `pmndrs_glyph_engine_update` plus Bitmap render plan at 2.607 ms median / 6.184 ms p95 after 40 warmups over 101 updates. The fast ASCII-letter path reuses Unicode and bidi state and recomposes until the line cursor converges; punctuation and spacing edits deliberately retain the full break-sensitive path, so the 42.4% RSD describes remaining workload classes rather than a completed latency result. The diff --git a/docs/planning/core-api.md b/docs/planning/core-api.md index 8fc243b4..ff150293 100644 --- a/docs/planning/core-api.md +++ b/docs/planning/core-api.md @@ -211,10 +211,10 @@ augmentation—not GPU instance storage. Repeated unchanged queries may reuse th ## Synchronization boundary -There is one engine update export, `text_update(requestOffset, requestLength)`. The TypeScript host writes a complete frame -request into retained Wasm staging memory; Rust applies mutations, shapes and lays out affected paragraphs, packs canonical -instance records, and emits the render-plan command buffer plus coalesced dirty ranges. Renderer policy is compiled data, -not a JavaScript callback executed from Rust. +There is one mutating engine update export, `pmndrs_glyph_engine_update(sessionId, requestOffset, requestLength)`. +The TypeScript host writes a complete frame request into retained Wasm staging memory; Rust applies mutations, shapes +and lays out affected paragraphs, packs canonical instance records, and emits the render-plan command buffer plus +coalesced dirty ranges. Renderer policy is compiled data, not a JavaScript callback executed from Rust. The low-level engine session and wire format are package-internal during this foundation stack. This prevents applications from binding to an unstable ABI while the maintained Three implementation proves the policy and command-buffer model. diff --git a/docs/planning/dirty-range-upload-research.md b/docs/planning/dirty-range-upload-research.md index e5adcf52..a790d6bb 100644 --- a/docs/planning/dirty-range-upload-research.md +++ b/docs/planning/dirty-range-upload-research.md @@ -56,7 +56,7 @@ The useful work is therefore narrower: 2. make the cost decision per physical buffer, including the stable-indirect order buffer, rather than treating every program stream as if it had the same changed-range economics; 3. use a Flatland-style reusable tracker only for renderer-local matrix and presentation-origin edits, which never cross - `text_update`; and + `pmndrs_glyph_engine_update`; and 4. remove avoidable host allocations while forwarding already-coalesced Rust patches to Three. No new policy-program opcode is required. The renderer declares integer capabilities; the renderer-neutral Rust plan @@ -187,7 +187,7 @@ renderer-neutral patch ABI. | cost constants and backend limits | registered renderer capability set | renderer knowledge expressed as validated data, not a callback | | packing math | policy program executed by Rust | existing straight-line data transformation boundary | | byte-range to Three update-range translation | Three executor | backend object and scalar-width knowledge | -| scene matrices and presentation-origin dirty tracking | Three executor | renderer-local data never seen by `text_update` | +| scene matrices and presentation-origin dirty tracking | Three executor | renderer-local data never seen by `pmndrs_glyph_engine_update` | | final GPU command submission | Three WebGPU/WebGL backend | outside the renderer-neutral plan | Adding bucket size or a fixed dirty-bucket count to the public policy now would overfit Flatland. The existing byte-cost @@ -240,7 +240,7 @@ Compare at least these planners: Capture per update: -- `text_update`, range-planning, policy packing, publication, Three apply, render submit, and GPU time; +- `pmndrs_glyph_engine_update`, range-planning, policy packing, publication, Three apply, render submit, and GPU time; - patch count, update-range count, payload bytes, uploaded bytes, and full-live promotions per physical buffer; - retained scratch high-water marks and warm allocations/GC; - draw count and framebuffer/conformance hash; and diff --git a/docs/planning/rust-layout-engine.md b/docs/planning/rust-layout-engine.md index c5fc4863..af8fa05a 100644 --- a/docs/planning/rust-layout-engine.md +++ b/docs/planning/rust-layout-engine.md @@ -272,7 +272,7 @@ It does not decide bidi runs, break lines, position glyphs, synthesize decoratio The hot operation is one mutation transaction: ```text -text_update(session_id: u32, request_offset: u32, request_len: u32) -> u32 +pmndrs_glyph_engine_update(session_id: u32, request_offset: u32, request_len: u32) -> u32 ``` “One crossing” means one call for a dirty session update. It does not mean one call on every `requestAnimationFrame`, @@ -354,7 +354,7 @@ declared settling policy, re-pins all views, and performs no typography. The nor 1. reserve request and result capacity at session creation, or call `text_reserve` before pinning when a later mutation exceeds either watermark; 2. write the next mutation request into the retained staging arena; -3. call `text_update` once; +3. call `pmndrs_glyph_engine_update` once; 4. compare `memory.buffer` identity, re-pin all views if it changed, and validate the result header; 5. synchronously consume the published slot, or copy retained/asynchronous bytes into a worker-owned transfer buffer; 6. transfer that buffer to root with the plan revision and ownership token; and @@ -438,7 +438,7 @@ order and advances the cursor to the next region when its block extent is exhaus retry, redistribution, or implicit balancing solver. A shorter final column is valid output. Sequential overflow through at least two supplied regions is required. Every region and exclusion is supplied before -the one `text_update` call, which completes shaping, band construction, exclusion subtraction, breaking, boundary +the one `pmndrs_glyph_engine_update` call, which completes shaping, band construction, exclusion subtraction, breaking, boundary reshaping, positioning, and plan compilation without a measurement callback or host round trip. The measured envelope may cap how many regions, vertices, exclusions, lines, and clusters one realtime transaction accepts; it may not remove multi-region continuation. Missing the 4 ms gate blocks the milestone until the implementation or supported numeric @@ -615,7 +615,7 @@ availability, not a renderer eligibility decision. The Rust engine cold-registers stack identity as a nonempty, duplicate-free ordered list of already registered shaping-font handles. Equivalent registration is idempotent; conflicting order fails, and a member font cannot be disposed while any registered stack retains it. Technique/resource data is deliberately not duplicated in the stack. -During `text_update`, HarfRust output is collapsed to logical cluster records; only clusters containing an actual glyph +During `pmndrs_glyph_engine_update`, HarfRust output is collapsed to logical cluster records; only clusters containing an actual glyph zero advance to the next registered font. Flat source-ordered spans are reshaped at most once per stack depth and then retained with the final shaped SoA. Sorting by source-run/cluster restores logical order for RTL output before one linear span merge. A compiled Inter-to-Noto-Devanagari test observes two plan constructions in the same update, proving the @@ -726,7 +726,7 @@ but the retained text engine already has a more precise input: exact changed rec The [dirty-range upload research](dirty-range-upload-research.md) therefore keeps range selection in the Rust plan compiler, identifies per-physical-buffer costing and stable-order coalescing as the remaining planner work, and reserves Flatland-style bucket trackers for renderer-local transform and presentation-origin writes that never cross -`text_update`. Its thresholds remain a benchmark candidate, not evidence that three or five ranges are optimal for text. +`pmndrs_glyph_engine_update`. Its thresholds remain a benchmark candidate, not evidence that three or five ranges are optimal for text. V0 does not alias several logical stores into one mutable interleaved byte span. Augmentation instead combines semantic fields into independently bindable `vec2`/`vec4` or integer-vector records, including the existing MSDF and Slug @@ -927,7 +927,7 @@ Three related experiments are reserved for a later stack: Font-local OpenType payloads remain in each baked font or fallback font; applications already control their glyph and font-stack subsets. Data-pack discovery should therefore be driven before font registration by declared Unicode/script coverage. A cold unexpected script may report a stable missing-pack identifier and retry after asynchronous loading, but -steady-state `text_update` remains one synchronous call and never initiates I/O. Native consumers may memory-map the same +steady-state `pmndrs_glyph_engine_update` remains one synchronous call and never initiates I/O. Native consumers may memory-map the same pack format, while browsers retain the initialized bytes in Wasm linear memory. Each experiment must report raw, gzip, Brotli, compile/startup, retained-memory, cold-load, and hot-path results before changing the default package. @@ -939,11 +939,11 @@ That sweep excludes mandatory hard-break controls and emits allocation-reusing s 968,086 / 362,664 / 286,438 raw/gzip/Brotli bytes (+4,067 / +1,899 / -2,304 from retained Unicode). HarfRust fallback shaping has not consumed the runs yet, so plan output and complete-path timing remain open. -Primary-font HarfRust shaping now consumes retained runs during Wasm `text_update`. The legacy batch export and frame +Primary-font HarfRust shaping now consumes retained runs during Wasm `pmndrs_glyph_engine_update`. The legacy batch export and frame engine share one borrowed run view, actual prewarmed `UnicodeBuffer`, UTF-16 context scratch, and reusable 128-feature scratch vector. Frame language/features borrow the retained style arena; glyph IDs, clusters, advances, offsets, flags, and source-run/font records append directly into a pre-reserved A/B shape arena without constructing or serializing a -`ShapeBatchRequest`. A compiled real-Inter test observes plan-cache count 0→1 only after `text_update` and no increase +`ShapeBatchRequest`. A compiled real-Inter test observes plan-cache count 0→1 only after `pmndrs_glyph_engine_update` and no increase after an aborted update. Optimized Wasm is 973,367 / 364,517 / 287,942 raw/gzip/Brotli bytes (+5,281 / +1,853 / +1,504). Ordered fallback is not yet applied, and layout/gather still receive no glyphs, so nonempty plan output and complete-path timing remain open. @@ -1046,7 +1046,7 @@ the existing semantic-glyph records instead of duplicating two float arrays. Six region, flow-thread, transform, and stable-glyph identity. Exact float bits plus all integer and semantic fields determine a monotonic transactional `content_revision`. A unit fixture retains revisions `[1,2]` across an identical rebuild and advances to `[3,4]` after shifting the slot one pixel. A compiled real-Inter -`text_update` publishes nonzero resource/buffer/patch/primitive/draw tables; the identical next call keeps the same Wasm +`pmndrs_glyph_engine_update` publishes nonzero resource/buffer/patch/primitive/draw tables; the identical next call keeps the same Wasm buffer and emits zero patches. Optimized Wasm is 1,057,210 / 400,071 / 311,492 raw/gzip/Brotli bytes (+12,413 / +4,849 / +3,697). This proves plan reachability and minimal no-op updates, not the still-unmeasured 25,515-glyph latency target. @@ -1396,7 +1396,7 @@ After the public Three command-buffer cutover and deletion of its parallel targe `recordCount` values from the published Rust plan and rejects a workload below 95% of the requested target; the former `primitiveCount` label counted primitive-table rows and misleadingly reported one. The unchanged fixture produces 25,515 positioned TypeScript glyphs and 21,805 Rust renderable records. TypeScript cold/font-size/width/text medians are -61.29/11.40/8.33/39.15 ms. Complete Rust `text_update` plus Bitmap plan publication measures +61.29/11.40/8.33/39.15 ms. Complete Rust `pmndrs_glyph_engine_update` plus Bitmap plan publication measures 14.77/4.95/3.78/14.43 ms for cold/font-size/column-resize/suffix-edit, MTSDF measures 15.37/5.35/4.35/14.84 ms, and Slug measures 15.43/6.11/5.08/15.61 ms. All three beat the TypeScript implementation; none closes the required resize p95 below 4 ms (4.16/4.95/5.68 ms). This scope includes request copying into retained @@ -1493,7 +1493,7 @@ not an ellipsis-only attribution. ### Stage 4 — atomic cutover and foundation performance gate -- cut the public hot path to one `text_update` call after byte and semantic parity is established; +- cut the public hot path to one `pmndrs_glyph_engine_update` call after byte and semantic parity is established; - remove TypeScript shaping and layout orchestration and the old analysis/shape/reshape exports together; - apply retained patches through both Three/TSL backends, including WebGL2's required retained PBO copy; and - pass the complete 25,515-glyph target-hardware gate before any additional publishing feature enters the stack. @@ -1536,7 +1536,7 @@ not an ellipsis-only attribution. 111 Vitest cases and 16 isolated headless Chromium targets, with the manifest authoritative rather than these counts; - the mixed-direction Amiri golden and packed-consumer contract remain exact until an explicitly versioned render-plan contract replaces the latter; -- `glyph:rust-layout-benchmark -- --glyphs 22000` measures the packaged release Wasm's complete `text_update` and render +- `glyph:rust-layout-benchmark -- --glyphs 22000` measures the packaged release Wasm's complete `pmndrs_glyph_engine_update` and render plan for every current technique. Historical TypeScript tables remain labeled evidence; deleted code is not rebuilt as a second implementation merely to manufacture a live baseline; - Unicode segmentation and line breaking pass the repository's unchanged official vectors; diff --git a/docs/planning/three-api.md b/docs/planning/three-api.md index cd81da8e..98b2d627 100644 --- a/docs/planning/three-api.md +++ b/docs/planning/three-api.md @@ -144,7 +144,7 @@ Setters change desired state. The nearest `TextGroup` applies all pending descen `updateMatrixWorld()` traversal. Reassigning a value that normalizes to the current state is a no-op. Transform-only changes update the transform buffer and do not reshape or recompose text. -One group traversal performs at most one mutating `text_update` transaction for that group's pending values. Calling a +One group traversal performs at most one mutating `pmndrs_glyph_engine_update` transaction for that group's pending values. Calling a layout query with pending mutations may perform that synchronization earlier; the following traversal observes the committed revision and does not repeat the semantic work.