Slug: Bézier outlines in the pixel shader
The 2026 release removes two different restrictions #VDGHWX
Slug is Eric Lengyel's method for evaluating quadratic outline curves directly
for each covered pixel. It does not rasterize a glyph into a bitmap or a
signed-distance field first. The original description is the 2017 JCGT paper
“GPU-Centered Font Rendering Directly from Glyph Outlines”. The current
reference is the shader repository at commit
be3c13eb7d63f9e8aa5c583e42d92c374cb91d98 (15 April 2026), not the
paper's sample code.
There are two separate freedom claims:
- In “A Decade of Slug”, Lengyel says that US patent 10,373,352 was irrevocably dedicated to the public domain by filing USPTO form SB/43 and paying its fee, effective 17 March 2026.
- The released shader source is dual-licensed under MIT or Apache-2.0. Its NOTICE identifies Eric Lengyel as copyright holder, and the README requires credit in distributed software.
These are the patent owner's announcement and the repository's license terms, not legal advice. The Google Patents snapshot for US10373352B1 still labels the patent “Active”, but that site also says its legal-status field is an unverified assumption and shows no 2026 event. It is therefore a lagging aggregator, not evidence against the later dedication.
A pixel asks the outline for its winding #3T0JD9
For a quadratic curve with control points p1, p2, and p3, Slug writes
C(t) = a t^2 - 2 b t + p1 a = p1 - 2 p2 + p3 b = p1 - p2
At a pixel, the control points are translated so that the pixel is the origin.
For a horizontal ray, the shader solves C_y(t) = 0 and accumulates a signed
contribution from the x coordinate of each eligible intersection. It repeats
the calculation with x and y exchanged for a vertical ray. The magnitude of
the resulting winding quantity distinguishes the inside from the outside,
while the continuous intersection coordinate supplies antialiasing across the
boundary.
For one axis, the quadratic roots are
t1 = (b - sqrt(max(b^2 - a p1, 0))) / a t2 = (b + sqrt(max(b^2 - a p1, 0))) / a
where a, b, and p1 above mean the selected scalar components. The
current reference treats abs(a) < 1/65536 as linear and uses
t = p1 / (2 b). Clamping the discriminant to zero keeps the square root
defined in finite-precision arithmetic.
Root eligibility, not a floating-point range test, supplies robustness #S2F8SA
Testing whether a computed root lies in [0,1] is numerically unstable at
shared endpoints. Slug instead classifies the signs of the three control
coordinates before calculating roots. Let s1, s2, and s3 be one only
when the corresponding coordinate is strictly positive; zero is non-positive.
The exact eligibility table from the paper is:
| s3 | s2 | s1 | root 2 | root 1 |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 1 | 1 |
| 1 | 1 | 0 | 1 | 0 |
| 1 | 1 | 1 | 0 | 0 |
The reference shader extracts the two bits from the constant 0x2E74. Luv's
proof emits the same table as float arithmetic so that it needs no integer
bit operators. In particular,
e1 = s1 (1 - s2 s3) + (1 - s1) s2 (1 - s3) e2 = s3 (1 - s1 s2) + (1 - s3) s2 (1 - s1)
The source-level signum operator lowers to GLSL.std.450 FSign for SPIR-V
and sign for MSL. Its zero result preserves the table's strict inequality
at an on-curve endpoint.
Mentioned in: The reference's shape, with its constants as knobs
Referenced from code: Return the two Slug root-eligibility bits as numeric masks. Only strict positivity matters. The arithmetic form is Table 1 of Lengyel's
2017 paper without an integer lookup, and is the form generated into the proof
pixel shader. #S2F8SAdefun slug-root-eligibility slug.lisp:118 ↗
Coverage turns intersections into antialiasing #P9FDUS
If r is an intersection's coordinate along the ray in pixel units, its
coverage contribution is saturate(r + 0.5), multiplied by its eligibility
bit and added or subtracted according to which root it is. This gives a
one-pixel-wide linear transition without supersampling.
The current 2026 pixel shader combines horizontal and vertical results instead of trusting one ray direction:
w(r) = saturate(1 - 2 abs(r))
coverage = max(abs(xcov*xweight + ycov*yweight)
/ max(xweight + yweight, 1/65536),
min(abs(xcov), abs(ycov)))Each axis weight is the maximum w of its eligible intersections. The
weighted term prefers the ray whose crossing is closest to the pixel centre;
the minimum term preserves coverage when both weights vanish. The reference
then clamps for nonzero fill, or applies abs(fmod(coverage + 1, 2) - 1) for
even-odd fill. An optional square root supplies optical-weight adjustment.
Bands make exact curves affordable #8FJF9U
The full algorithm does not test every outline curve at every pixel. A glyph is divided into equal-thickness horizontal and vertical bands, each pointing to the subset of curves that could cross rays in that band. Adjacent bands may share an identical list or a contiguous subrange.
The current reference has two textures:
- an RGBA16F curve texture. One texel stores
(x1,y1,x2,y2)and the next texel's first two channels providep3; connected curves reuse their shared endpoint; - an RG16U band texture whose two channels identify curve-list data. A glyph may have any number of bands, chosen to minimize the largest per-band list.
The reference fixes the texture row width at 4096 texels. Curves in a
horizontal band are sorted by descending maximum x; vertical bands use
descending maximum y. The pixel shader can therefore stop once that maximum,
in pixel units, is less than -0.5. The README recommends a 1/1024 em
overlap between bands. Horizontal straight lines must be omitted from
horizontal bands and vertical straight lines from vertical bands because a
ray parallel to such a line has no winding contribution. A straight segment
from p1 to p2 is encoded as {p1,p2,p2}.
The 2026 reference is not the 2017 sample #45K8GR
The decade retrospective records several material changes:
- Slug still uses no precomputed or cached glyph images.
- A former subdivision of each band into left and right curve lists was removed, halving the band texture from four 16-bit channels to two.
- The original supersampling layer was removed after the analytic coverage calculation improved.
- Colour emoji are drawn as multiple ordinary outline layers, one solid colour per layer, and SVG gradient meshes are a separate path.
- The vertex shader now performs dynamic dilation so the rasterized primitive conservatively reaches every pixel whose centre can receive coverage.
The repository's shader comments and data contracts should therefore be the implementation reference; the paper remains the clearest derivation and historical measurement. Its benchmark and storage numbers describe 2017 hardware and the older layout, not current performance promises.
Mentioned in: The reference's shape, with its constants as knobs
Dynamic dilation is a rasterization guarantee #J0H590
The reference vertex shader expands the quad by half a pixel in screen space. It transforms a per-vertex dilation normal through the inverse of the local-to-device Jacobian, while the model-view-projection matrix and viewport size determine the em-space size of that half pixel. This is not decorative boldening: without conservative geometry, rasterization can omit a pixel whose centre lies just outside the undilated quad even though the analytic filter would assign it nonzero coverage.
The full vertex record is five float4 values carrying position plus dilation
normal, texture coordinate plus packed glyph location and maxima, inverse
Jacobian, band transform, and colour. Band and glyph data are marked
non-interpolated; only the render coordinate is interpolated.
Mentioned in: The reference's shape, with its constants as knobs
DONE The reference's shape, with its constants as knobs #3YHNO3
Read against the 2026 reference shaders and the retrospective, luv's Slug had the algorithm -- eligibility, two rays, weighted combination, sorted bands -- but not the reference's shape around it. This figure closes the gaps and names what the reference hard-codes.
- Early exit. The reference leaves a band's loop at the first curve whose
largest x (or y) is more than half a pixel behind the sample; the band is
sorted descending for exactly that. luv's serializer had sorted, and its
shader had walked every curve. The shared language gained a termination
test on its fold --
(counted-fold (index count state initial :until test) update)-- lowered toif (...) break;in Metal and to aLogicalAndof the counter test in the SPIR-V loop header, and the band folds now carry a thirddonelane that slug-horizontal-band-step sets from the curve's reach. #8RQOF0's fold thereby learned what #45K8GR's loops always had. - Fill rule and optical weight.
SLUG_EVENODDandSLUG_WEIGHTare compile-time switches in the reference; slug-finish-coverage holds both as values, an even-odd blend and an exponent (one half is the reference's square root). - Footprint. The reference measures the pixel with
fwidth; luv measured the gradient length. Both are available and blend. - Dynamic dilation. #J0H590 is now real for world text: the vertex stage in block-world-text-vertex-specification knows a vertex's depth and, from two spare frame lanes, the target's height in pixels, so it sizes each em edge in pixels and slides the corner outward by the dilation over that length, moving the em coordinate with it. The 0.035-em constant padding of #9G0Z19 became *slug-static-padding*, zero by default; the flat McCLIM path, which knows its pixel size when it lays out, takes slug-dilation-em instead.
- Band data. The band count per axis is now chosen by
choose-slug-band-count as the reference advises -- the fewest bands
that minimize the fullest band -- and serialize-slug-outline lets a
band whose sorted list is a contiguous run of one already written point
into it, which the reference recommends for cache and size (a DejaVu
Wfalls from 157 to 79 band texels, anOfrom 172 to 122). The overlap epsilon and the maximum band count are values too. - Cap-height alignment. slug-cap-height-aligned-size reads
sCapHeightfrom the OS/2 table (which ZPB-TTF does not) and returns the size nearest a request at which capitals land on whole pixels: the reference's substitute for hinting. DejaVu's OS/2 table is version 1 and carries none, so it answers the request unchanged there.
The values live in luv.slug as specials -- *slug-filter-width*,
*slug-fill-rule*, *slug-optical-weight*, *slug-footprint-norm*,
*slug-early-exit*, *slug-root-epsilon*, *slug-debug-view*,
*slug-dilation-pixels* -- and stand in shader source by their unstarred
names, folded through shader-source-value. For that to be live, the two
band shaders are define-live-shader forms, reparsed on every pipeline
build, where define-shader parses once at load; luvcraft wraps each value
in a :text knob in make-world-text-run's file, so the metabar turns
them and the terminal walls rebuild at the next frame. *slug-debug-view*
paints every quad with its two bands' loads, red and green over sixteen,
which is the quickest way to see what a band count buys.
What was studied and left alone: the reference's packed vertex record and
its 8-bit band maxima (luv's instance record already carries the counts and
bounds it needs), and the multi-layer emoji path, which luv has no outlines
for yet. The reference's CalcRootCode bit trick and luv's arithmetic
eligibility (#S2F8SA) are the same table.
Referenced from code: Fold one horizontal-band curve into STATE = (xcov, xweight, done). CURVE holds p1 and p2, NEXT's first two lanes p3. DONE becomes one when the
curve lies wholly more than half a filter width left of the sample: the
band is sorted by descending maximum x, so nothing after it can contribute
and the fold's :UNTIL leaves the loop. #3YHNO3define-shader-function slug-horizontal-band-step slug.lisp:305 ↗
DONE One fixed contour proves luv's pixel-stage mathematics #OWR8OZ
Intent: prove that luv's shader DSL can state, lower, compile, and actually draw the numerically important pixel-stage core before designing texture and font-data machinery.
Evidence:
slug-quadratic-outlineis now a typed shader function over the eight points of a connected four-curve contour. It composes ordinary typedslug-axis-contribution, root-eligibility, and coverage functions with lexicallet*; no Slug helper constructs or returns shader source forms. The calculation still implements the eight-class eligibility table, quadratic/linear roots, both ray directions, intersection weights, and the current coverage combination. #RO74NL records the language distinction.- The ordinary Lisp function
slug-root-eligibilityexposes the same table to focused tests. All eight classes and the strict treatment of zero are checked. - The DSL's open operator protocol now admits Common Lisp
signumand lowers it directly to SPIR-VFSignand MSLsign. Both generated shaders passspirv-valand Apple's Metal 4 compiler. - Refactoring from form-generating source abstraction to typed function calls leaves the actual 256 by 256 Metal readback byte-identical.
- The cold Metal probe draws a two-triangle quad into a 256 by 256 RGBA8 texture, reads the real GPU result back, and writes this image:

Done when (met): a fixed four-quadratic heart crosses the normal mathematical shader path, both backend compilers accept it, root classification has an executable table oracle, and a Metal pixel shader produces the expected antialiased outline from Bézier data rather than a sampled image.
Mentioned in: One distance function draws the roundrect family analytically
Referenced from code: Render one connected four-quadratic contour with Slug's two-ray pixel math. This is a typed shader function: its LET* bindings and nested calls are parsed
directly into shader objects. The fixed curve count remains an atelier proof,
not the band-texture font renderer. #OWR8OZdefine-shader-function slug-quadratic-outline slug.lisp:359 ↗
The fixed proof delineates the first milestone #LB46YZ
The proof bakes four curves and 204.8 pixels per em into one shader. It has
no curve or band textures, variable-length band loop, font parser, glyph
layout, dynamic dilation, fill-rule flag, or optical-weight flag. The quad
already includes ample margin, so it does not test the reference vertex
dilation. SPIR-V validation proves the portable module, while the executed
offscreen probe is presently Metal-specific because that backend already has
a no-resource mathematical-pipeline seam.
This boundary matters historically: that image proves the hard per-pixel mathematics and the DSL capability, not Slug's data-scale performance or a usable text API. The later data-driven and font proofs below cross this boundary without removing the small fixed oracle.
Full Slug is a structured fold over indexed outline data #ZXTZ5J
The 2026 reference pixel shader makes the next language requirements concrete. For each horizontal and vertical band it loads a curve count and list offset, iterates the indexed curves, loads two curve texels, exits when the sorted maximum coordinate cannot reach the pixel, conditionally accumulates the two eligible roots, and carries coverage and weight forward. The required source vocabulary is therefore:
- signed and unsigned scalar/vector values, conversions, comparisons, and the small bit operations used by packed glyph metadata;
- exact indexed texture or buffer loads rather than filtered sampling;
- fragment derivatives for pixels-per-em and flat glyph metadata;
- a counted loop carrying several named values, conditional updates, and an early termination condition; and
- the existing floating-point quadratic and coverage functions.
This is not a reason to embed MSL statements or SPIR-V branches in the source language. The loop is a value-producing fold in #8RQOF0. Its result is the four-value coverage state; target lowering chooses mutable locals in MSL and structured loop/header/continue/merge blocks with phi values in SPIR-V.
McCLIM already exposes the TrueType contour boundary #7IVPIE
McCLIM's native TrueType renderer is a useful source path even though luv will
not use its software rasterizer. mcclim-native-ttf.lisp obtains a glyph with
zpb-ttf:find-glyph and walks it with do-contours and
do-contour-segments. Each visit supplies start point, optional quadratic
control point, and end point; consecutive off-curve TrueType points have already
acquired their implicit on-curve midpoint. McCLIM then turns those segments
into cl-vectors paths and sweeps them into an alpha bitmap. Luv can branch at
the earlier segment boundary and pack the exact quadratic outline instead.
TrueType glyf contours therefore need no curve fitting. Lines use the Slug
encoding {p1,p2,p2}. Cubic outlines, such as CFF/CFF2 or arbitrary vector
paths, are a preprocessing concern: subdivide and approximate them with
quadratics under an explicit font-unit error bound. Current HarfBuzz GPU code
uses exactly this shape, adapting FontTools cu2qu with a default half-font-unit
tolerance before its Slug encoder. HarfBuzz 14's experimental
libharfbuzz-gpu is consequently a valuable format and pixel oracle, but the
first luv path can remain Lisp-native through zpb-ttf.
DONE Teach the shared language Slug's structured band fold #HMNMBU
Intent: add the smallest shared control-flow and indexed-data vocabulary that states one Slug band traversal without burying its concrete data contract in a generic scene abstraction.
Evidence needed: after shared function application #YZS04W, add represented integer/index values, exact loads, comparison and conditional expressions, and a counted value-producing loop with early termination. Execute a CPU realization over a small curve array, lower the same body to valid MSL and SPIR-V structured control flow, and keep the current fixed shader as the pixel oracle.
The neutral loop skeleton is now present: counted-fold accepts a runtime
count and one carried scalar or vector state, executes as dotimes on the CPU,
as an ordinary for loop in direct Metal, and as validated structured SPIR-V
with phi-carried index and state. Its update is an expression body rather than
an artificially single call: a leading let* produces per-iteration lexical
bindings in the common AST, an inner Lisp let*, loop-local SSA values, and
named locals inside the emitted Metal loop.
Conditional accumulation is shared too. if is a first-class value node
in the common expression graph, with checked scalar comparisons <, <=,
>, >=, and ===. Lisp retains ordinary branch semantics; SPIR-V emits the
ordered comparison plus OpSelect; and direct Metal emits a ternary inside the
same for loop. A bounded triangular fold executes on the CPU and compiles
from the same shape on both GPU targets.
The shader representation layer now supplies uint and uvec2/3/4 values plus
an exact texel-load extension. A count loaded from a uint-texture-2d
selects an unsigned fold index automatically; address addition, unsigned
division and the common mod operation stay typed throughout. The same probe
emits OpImageFetch, OpULessThan, OpUDiv, OpUMod, OpIAdd, and
OpConvertUToF in SPIR-V, while Metal emits texture2d<uint>.read(uint2) and a
for (uint ...) loop with readable address locals. Both spirv-val and the
Metal 4 compiler accept those generated products. mod itself belongs to the
shared arithmetic vocabulary and executes through Common Lisp's ordinary
mod; the texture resource and exact load remain properly shader-specific.
slug-banded-fragment-specification now supplies the Slug-shaped traversal.
Two runtime unsigned curve counts control two counted-fold values. Each
iteration reads its RG16U curve location and two adjacent RGBA16F curve texels,
creates ordinary lexical let* bindings for the reusable axis-contribution
function, and carries coverage plus maximum weight. There is no host unrolling
and no form-generating Slug macro.
This exercised one subtle language rule that the scalar loop probes did not:
function-local bindings called from a fold belong to that iteration. Direct
MSL lowering now materializes them inside the for body, while SPIR-V only
reuses a cached resource load in the basic block that owns it. The latter rule
prevents a value defined inside one loop from being reused in another loop
where it does not dominate. Regression tests inspect both generated products;
spirv-val and Apple's Metal 4 compiler accept them.
The first correctness shader intentionally uses one complete band per axis. Sorted-coordinate early exit and multiple spatial bands are culling optimizations over this same value-producing traversal, not prerequisites for rendering an arbitrary outline.
Done when: a runtime curve count rather than source unrolling controls the number of evaluated quadratics; CPU and GPU results agree at edge, shared-endpoint, empty-band, and early-exit cases; and source/lowering tools retain the loop, carried values, and emitted block provenance.
DONE Pack arbitrary quadratic contours into Slug bands #TTE5JG
Intent: turn arbitrary connected quadratic contours into dense curve and band data suitable for the shared Slug fold.
Evidence needed: a Lisp outline model preserving contour boundaries and orientation, line-to-quadratic encoding, conservative band overlap, omission of parallel straight lines, per-axis descending sort, and visible band-length statistics. Exercise an outer contour plus an inner contour before involving a font loader.
The CPU-owned model and the first preprocessing boundary are now concrete.
slug-outline keeps closed contours, validates every curve junction, computes
quadratic contour orientation from the exact area integral, and flattens curves
in contour order. Lines use {p1,p2,p2}. pack-slug-outline builds
horizontal and vertical bands with the reference 1/1024 conservative overlap,
omits curves parallel to the relevant ray, and retains both descending-maximum
and ascending-minimum orders. Tests exercise a counterclockwise outer square
with a clockwise inner hole and reject a disconnected contour.
The serialization boundary now preserves the released shader's exact texture
contract. Luv first rounds every point through binary16, decodes those actual
shader-visible values, and only then recomputes bounds, bands, and sort keys.
The 4096-wide RGBA16F curve texture stores one {p1,p2} texel per quadratic;
the following texel supplies the shared p3, with one endpoint sentinel after
each contour. The 4096-wide RG16U band texture stores all horizontal headers,
then all vertical headers, followed by their descending-maximum
(curve-texture-x, curve-texture-y) lists. Tests deliberately use a one-third
coordinate to prove that packing observes the half-quantized value rather than
the original Lisp rational.
The portable GPU vocabulary now names the exact storage formats too:
:rg16-uint is four bytes per texel and :rgba16-float is eight. The Metal
backend maps those names to MTLPixelFormatRG16Uint 63 and
MTLPixelFormatRGBA16Float 115; Vulkan maps them to
VK_FORMAT_R16G16_UINT 81 and VK_FORMAT_R16G16B16A16_SFLOAT 97. Both upload
paths accept the serializer's two-dimensional specialized arrays directly and
compute row length, byte size, alignment, and foreign element width from the
format instead of assuming RGBA8.
The first serializer still describes one glyph beginning at texture origin zero. Atlas allocation and relocation remain open, but the complete one-glyph data is now uploaded and rendered from those resources while preserving the fixed-width addressing assumed by the released pixel shader.
Done when: several differently sized and holed quadratic outlines render from uploaded data without changing shader source, and a CPU winding oracle agrees with GPU pixels around extrema, shared endpoints, and contour holes.
HarfBuzz places a run; Slug draws indexed outlines #4G7064
Text shaping and outline rendering meet at a deliberately narrow seam.
shape-slug-text gives HarfBuzz the UTF-8 run and font bytes, then
returns dense records containing glyph IDs, byte clusters, advances, and
offsets in font units. It does not ask HarfBuzz to rasterize. The Slug side
uses each glyph ID with zpb-ttf:index-glyph, normalizes that outline to em
units, and places its quad from the corresponding HarfBuzz record.
The first run proof keeps resource ownership literal rather than anticipating
an atlas. One shared vertex buffer contains six vertices per drawable glyph;
each slice carries clip position, outline coordinate, and pixels per em. Each
glyph temporarily owns its own serialized band texture, curve texture, views,
and bind group. The render pass switches bind groups and uses first-vertex
to draw each six-vertex slice. This is intentionally expensive but makes
glyph selection, placement, outline data, and draw ownership visible before a
packing or caching policy hides them.
DejaVu Sans shapes office affinity into 11 glyphs rather than 14 drawable
characters: each ffi sequence becomes one ligature glyph. The regression
test checks the office clusters (0 1 4 5), proving that the three UTF-8
characters beginning at cluster 1 became one selected glyph. The cold Metal
probe then renders the complete shaped run through the same public Slug vertex
and band-fragment specifications used by the earlier “O” proof:
The image also forced a useful correction that the symmetric “O” could not
show: target Y had been inverted. Visual inspection of letters with ascenders
and descenders caught it, and the fitted text vertices now map font-up to
image-up. Its blue background is also a composition check: the Slug fragment
emits premultiplied color and coverage, and the render target explicitly uses
one / one-minus-source-alpha blending instead of relying on the black
background that concealed transparent quad pixels in the first proof.
HarfBuzz 13.2.1 is part of the owned Nix runtime rather than an ambient host
dependency.
Mentioned in: The terminal grid remains authoritative through Slug, Shape and draw a HarfBuzz text run through Slug, Put a shaped run into the moving world
Referenced from code: Shape The proof target is 768 by 256 pixels. HarfBuzz owns glyph selection,
ligatures, clusters, advances, and offsets; ZPB-TTF supplies outlines by the
resulting glyph IDs. Return pixels, width, height, format, and shaped text.
#4G7064 Shape defun render-metal-slug-text examples.lisp:875 ↗
string with HarfBuzz and render one Slug quad per drawable glyph.defun shape-slug-text slug-harfbuzz.lisp:108 ↗
string with HarfBuzz, returning glyph IDs and font-unit placements.direction may be :LTR, :RTL, :TTB, or :BTT. With no direction HarfBuzz
guesses the segment properties from the Unicode text. #4G7064
DONE Shape and draw a HarfBuzz text run through Slug #DTBI99
Intent: join HarfBuzz shaping to the proven zpb-ttf outline boundary and
render a run as placed Slug glyph quads without a software rasterizer.
Evidence: #4G7064 records the owned HarfBuzz binding, ffi ligature and
cluster regression, glyph-ID-to-outline join, one-quad-per-glyph Metal draw,
and visually inspected office affinity image. The complete luvcraft suite
passes with the per-vertex pixels-per-em shader input.
The font boundary is now working before any rasterizer. The shader module in
:luv depends directly on zpb-ttf and converts its resolved
(start,control,end) segments into luv contours; a nil control becomes the
standard Slug line quadratic. A slug-glyph also retains advance width, left
side bearing, and units per em, and the converter accepts an already selected
zpb glyph so a later HarfBuzz glyph index need not be turned back into a
character. The reproducible test font is cl-dejavu's DejaVu Sans: its “O”
produces two opposite-winding contours, 16 quadratics, a 1612-unit advance, and
packs through the same arbitrary-outline path. No cl-vectors, cl-aa, or
McCLIM raster object enters this system.
The Metal mark now crosses the complete boundary. normalize-slug-glyph-outline
converts the font-unit points to em units while preserving the TrueType origin.
The cold glyph probe loads DejaVu Sans “O”, serializes its 16 quadratics and
two contours, uploads its exact band and curve arrays, binds the two texture
views, draws the em-square quad through the public mathematical shader
specifications, reads back the GPU target, and writes this image:
Changing the outline changes only the uploaded arrays. The fragment shader
contains no “O” points, contour count, or source-unrolled curve calls. The
generated SPIR-V has two structured loops and at least six exact image fetches;
the generated MSL has two unsigned for loops with the reusable Slug
function's lexical locals scoped inside them.
Done when: HarfBuzz-selected glyph IDs and placements render as a legible run through indexed Slug outlines, including a ligature that cannot be reproduced by one cmap lookup per character.
DONE Put a shaped run into the moving world #QW7P96
Intent: cross #4G7064 from an isolated target into the actual luvcraft scene, with world placement, camera projection, scene depth, and alpha composition all participating in the same frame as terrain.
make-world-text-run is now a semantic owner around dense HarfBuzz
placement and vertex data. Its initial model transform fixes the text plane in
world space from the camera's initial right and up basis. The
:slug-world-text vertex stage then applies the scene frame uniform's camera
view and perspective projection; the existing Slug fragment stage still owns
coverage. The pipeline uses :less depth testing with depth writes disabled
and premultiplied-alpha blending. It is encoded after opaque chunks and before
the always-visible crosshair.
The fragment stage now derives pixels-per-em from the horizontal and vertical screen derivatives of the interpolated outline coordinate. Perspective, rotation, and camera distance therefore participate at the actual fragment; there is no run-center approximation or per-frame instance-buffer rewrite.
Evidence: the Metal validation layer accepts the complete hidden luvcraft
frame, including its pipeline-layout transitions and teardown. This capture
is the ordinary world smoke path, not an isolated text target:
The label's transparent quad exterior preserves sky and blocks; the pipeline's
:less comparison puts terrain in front whenever their projections overlap.
Frame metrics count six unit-quad vertices instanced across the run in one
draw. The cache and atlas iterations below remove the original literal
bind-and-draw-per-glyph baseline.
Done when: a HarfBuzz-shaped run is legible in the real scene, changes screen position and scale with its camera relationship, obeys terrain depth, and survives a full Metal-validation frame without a bind-state or lifetime error.
Mentioned in: A terminal is a world-native interactive surface, Bridge styled Ghostty render-state rows into the block wall
Referenced from code: Shape The run owns dense placement/model data and its live pipeline; defun make-world-text-run text.lisp:287 ↗
string once and create a depth-tested world text run on device.glyph-cache owns
font-and-glyph device resources reusable across runs. See #QW7P96.
DONE Cache shaping and device glyph outlines #U5X6DY
Intent: make text runs cheap to reconstruct without hiding GPU lifetime inside glyph occurrences.
make-world-text-glyph-cache is owned by the luvcraft session and tied to one device. It retains HarfBuzz results by canonical font path and exact string, and retains normalized, serialized Slug outlines by canonical font path and HarfBuzz glyph ID. Atlas objects own the uploaded textures; a run owns placement, dense instance data, layout, and pipeline. Stopping the session destroys frame bind groups, then runs, then atlas textures, then the device.
Repeated occurrences share the same serialized resource object. The
"hello, world" proof has 11 drawable occurrences but only 8 cached outlines.
That banner is a proof rather than scenery, so a caller asks for it by passing
:world-text-string; the ordinary game sky carries no text at all.
A second identical run reuses the slug-shaped-text, all eight outlines, and
the matching atlas. Metal regressions construct both runs and exercise the
ownership-order teardown.
Done when: duplicate glyph IDs within a run and across runs resolve to the same per-device outline, identical shaping is not repeated, and Metal teardown is clean.
Mentioned in: A terminal is a world-native interactive surface, The terminal grid remains authoritative through Slug, Draw McCLIM text as world geometry
DONE Pack one atlas and issue one instanced text draw #AT7L3S
Intent: remove the command and allocation multiplication caused by treating each glyph occurrence as an independently bound texture pair.
create-world-text-glyph-atlas concatenates each distinct serialized
outline into one RG16U band texture and one RGBA16F curve texture. Its location
table maps the cached outline object to linear band and curve bases. Each run
has a six-vertex unit quad and one 96-byte instance record per drawable glyph:
world origin, right/up edge vectors, outline bounds and band counts, and atlas
bases. The final two lanes separately retain the exact packed band bounds;
quad geometry can therefore stay padded without changing spatial band
selection. The vertex stage expands these records; the render pass binds the
two buffers and one atlas group, then calls draw once with the glyph count as
the instance count.
The exact proof-run comparison is deliberately about owned objects and encoded commands, not a noisy whole-frame timing claim:
text cost, "hello, world" | cached glyph pairs | atlas instances |
|---|---|---|
| drawable / unique glyphs | 11 / 8 | 11 / 8 |
| text draws per frame | 11 | 1 |
| text bind calls per frame | 11 | 1 |
| text encode calls | 24 | 5 |
| per-frame text buffer writes | 1 | 0 |
| glyph textures / views | 16 / 16 | 2 / 2 |
| frame text bind groups | 8 | 1 |
| allocated glyph texture data | 384 KiB | 48 KiB |
| text vertex-buffer data | 2,376 B | 1,128 B |
The 24-to-5 encode count includes pipeline, vertex-buffer, bind-group, and draw calls. Texture allocation falls by 87.5 percent because every former glyph pair paid the 4096-texel minimum row; vertex data falls by 52.5 percent. The new spatial bands intentionally increase used band payload from 202 to 996 texels while curve payload remains 121 texels: roughly 4.8 KiB of useful data inside the 48 KiB allocated atlas. A 30-frame Apple M2 Pro steady run records 165 whole-scene draws, 0.173 ms median scene encoding, and 0.011 ms median uniform update. It is a current-path health measurement, not an A/B timing, because upstream terrain, particles, overlays, and lighting changed across the saved pre-atlas commit.
Done when: repeated glyphs share atlas storage, one run emits one instanced draw and one bind call, the backend validation layer accepts the frame, and the ordinary world capture remains legible.
Mentioned in: The terminal grid remains authoritative through Slug, A terminal is sharp at any distance because its glyphs are geometry, Draw McCLIM text as world geometry
DONE Select spatial bands and scale coverage from fragment derivatives #D4R1VX
The shared shader language now carries derivative-x and derivative-y
through direct Metal dfdx/dfdy and SPIR-V OpDPdx/OpDPdy. The fragment
shader forms a screen gradient for each em axis and takes its reciprocal length
as pixels-per-em. The same fragment normalizes its x/y outline coordinate,
chooses one of the serialized horizontal and vertical bands, and relocates
that band's local curve addresses through the atlas bases. Default outlines
use min(curve-count, 16) bands per axis rather than the one-band correctness
fallback.
The generated MSL and SPIR-V are compiled and validated in the normal build, unit tests require derivative instructions and selected header addressing, and the Metal debug layer renders the multi-band world capture above. The default eight outlines use band counts from 4 through 16; the inspected atlas contains 996 band texels and 121 curve texels.
The remaining efficiency goblins are now explicit. Atlases and live pipelines are shared only by runs with the same exact glyph set, so many overlapping labels can duplicate storage and pipeline state. Tiny sets still pay one 4096-wide texture row. The sorted band lists do not yet early-exit their GPU fold, and multiple runs are not coalesced into a world-wide draw or culled as a batch. A terminal-scale client should next move atlas pages and pipeline ownership into the per-device cache, then measure whether page locality or world-wide instance aggregation wins before adding a generic text manager.
Done when: projected scale has no CPU estimate or frame upload, fragments select real spatial bands, both shader backends validate the derivative path, and an ordinary-distance debug-layer world frame is visually intact. The first close-up in #9G0Z19 subsequently falsified the bounds crossing; its corrected close-up now verifies that fragment selection agrees with preprocessing at the enlarged proof scale.
Mentioned in: The terminal grid remains authoritative through Slug
DONE One distance function draws the roundrect family analytically #LDAHBP
Intent: reuse the proof discipline learned from Slug without pretending that a fixed GUI primitive is an arbitrary outline.
The distinction is now executable. Slug's first proof #OWR8OZ puts reusable pixel mathematics in a typed function, exposes it through tiny portable shader specifications, inspects both generated targets, and finally demands a real GPU readback. The roundrect proof follows exactly that path while omitting curve textures, bands, winding traversal, and device outline identity.
roundrect-signed-distance is one shared arithmetic definition. It
executes as an ordinary Lisp function for the scalar oracle and lowers into the
same mathematical shader graph for both backends. A radius of zero is a
rectangle; radius equal to the shorter half-extent yields capsules and circles.
The shared arithmetic vocabulary gained raw abs and sqrt realization on
this evidence, without claiming quantity semantics for either operation.
roundrect-coverage evaluates that distance in the fragment shader,
uses derivative-x and derivative-y to measure its screen-space gradient,
and maps the signed result through a one-pixel coverage ramp. One padded quad
record carries clip position, local coordinate, half-size plus radius, opacity,
and premultiplied colour. The cold Metal proof sends four such records through
one resource-free pipeline: a roundrect, a capsule, a circle, and a rotated
affine ellipse.
Evidence: the Lisp distance oracle checks straight edges, circle points, and
radius normalization. The generated SPIR-V contains both fragment derivative
instructions and validates under spirv-val. Generated MSL contains direct
dfdx, dfdy, and sqrt calls and compiles under Metal 4. The image above is
a 768 by 480 native Metal render and readback, not a software reconstruction.
The complete luv, luvcraft, and wiki suites pass.
Done when (met): one shared distance definition produces exact scalar oracle values, both backend products compile, and a real GPU draw preserves smooth coverage across a card, capsule, circle, and non-uniform rotated transform.
NEXT Compare Slug coverage at glyph edges #9G0Z19
Intent: distinguish “a shaped run renders” from evidence that Slug's edge coverage and geometry expansion are correct across backends.
Evidence needed: exercise the portable draw on Vulkan, compare boundary pixels with a small CPU winding oracle, and test dynamic dilation on geometry whose undilated quad actually loses edge coverage. Atlas allocation and outline reuse are separate performance work and should follow measurement.
capture-hidden-luvcraft-text-closeup renders a 1280 by 360 logical
Metal frame with the text enlarged in world space, producing a 2560 by 720
Retina image without post-render scaling. The elevated camera leaves enough
world context to exercise the real depth path while giving the outline an
unobstructed sky background:
The first native close-up was falsifying evidence: horizontal cuts through
h and e and vertical slivers in h, r, and d coincided with band
transitions, not font extrema or terrain edges. The instance record had sent
the quad's 0.035-em padded outline bounds to the fragment shader, while
pack-slug-outline partitioned the unpadded packed-outline bounds. The
shader consequently mapped fragments into different spatial partitions than
the serializer used to build their curve lists.
The corrected record carries both meanings. Padded low/high values still
expand and parameterize the quad, while two additional attributes carry the
exact packed min/max used only for band selection. The image above is the
same close-up after that change: all transition-aligned cuts have disappeared,
including the former horizontal break across h/e and the vertical breaks in
h/r/d. A focused regression requires those padded and exact lanes to remain
distinct. Metal validation accepts the enlarged Retina world render, and a
cold Vulkan capture through the same shared specification produces the same
continuous glyphs at its native 1280 by 360 drawable extent.
The world terminal #NMAD2U is a particularly demanding next client: repeated grid-aligned glyphs make edge quality, outline reuse, and batching visible, but the terminal should consume this evidence rather than silently becoming its oracle.
Done when: representative extrema, shared endpoints, holes, and clipped edges agree with the oracle on Metal and Vulkan, and dilation demonstrably recovers coverage rather than merely enlarging an already sufficient quad.
Mentioned in: The reference's shape, with its constants as knobs, Select spatial bands and scale coverage from fragment derivatives
Referenced from code: Render a native-resolution close-up of the world Slug text proof. The wide viewport and enlarged world-space text make outline, band-selection,
and coverage defects visible without scaling up a smaller raster afterward.
#9G0Z19defun capture-hidden-luvcraft-text-closeup capture.lisp:228 ↗
(y1 y2 y3)Return the two Slug root-eligibility bits as numeric masks. Only strict positivity matters. The arithmetic form is Table 1 of Lengyel's 2017 paper without an integer lookup, and is the form generated into the proof pixel shader. #S2F8SA
(name parameters &body body)Define a reusable typed shader expression with ordinary source syntax. The body is parsed at each call site, so argument types and quantity meanings flow through the same operator protocol as handwritten shader expressions. LET* is lexical inside the function. The definition macro records source; its body does not…
Fold one horizontal-band curve into STATE = (xcov, xweight, done). CURVE holds p1 and p2, NEXT's first two lanes p3. DONE becomes one when the curve lies wholly more than half a filter width left of the sample: the band is sorted by descending maximum x, so nothing after it can contribute and the fold's :UNTIL…
Subtraction or unary negation.
Select and reorder vector components by a designator such as :XYZ or :RGB.
Multiplication and scalar scaling.
The maximum of compatible quantities.
Addition over compatible quantities.
Compare compatible quantities and produce dimensionless values.
Render one connected four-quadratic contour with Slug's two-ray pixel math. This is a typed shader function: its LET* bindings and nested calls are parsed directly into shader objects. The fixed curve count remains an atelier proof, not the band-texture font renderer. #OWR8OZ
(provider string font-pathname)Shape STRING with HarfBuzz and render one Slug quad per drawable glyph. The proof target is 768 by 256 pixels. HarfBuzz owns glyph selection, ligatures, clusters, advances, and offsets; ZPB-TTF supplies outlines by the resulting glyph IDs. Return pixels, width, height, format, and shaped text. #4G7064
How the children of a list are arranged.
(string font-pathname &key direction)Shape STRING with HarfBuzz, returning glyph IDs and font-unit placements. DIRECTION may be :LTR, :RTL, :TTB, or :BTT. With no direction HarfBuzz guesses the segment properties from the Unicode text. #4G7064
(shaped font-loader)(draws shaped font-loader)(draws width height min-x min-y max-x max-y)(provider &optional descriptor)(device descriptor)Asks the DEVICE for a handle to newly created instance of some object fulfilling the DESCRIPTOR.
(pass-encoder vertex-count
&optional (instance-count 1) (first-vertex 0) (first-instance 0))(device layout draw resources)(encoder descriptor)(pass-encoder pipeline)(pass-encoder slot buffer &key (offset 0))(pass-encoder index bind-group)(pass-encoder)(queue work)Schedule some command buffers on the QUEUE. Submission is asynchronous: returning does not mean the GPU has finished the work, only that the implementation retains everything the work depends on until it completes. Use SUBMITTED-WORK-DONE to wait.
(buffer &key offset size)Wait for BUFFER's device queue and copy mapped bytes back to the host.
(handle)Logically invalidate HANDLE immediately. Native teardown may be deferred until submitted work which captured HANDLE has completed.
(pathname)(direction)(buffer)(device glyph-cache camera target-format string font-pathname
&key (distance 8.0) (lift 3.0) (world-units-per-em 0.55))Shape STRING once and create a depth-tested world text run on DEVICE. The run owns dense placement/model data and its live pipeline; GLYPH-CACHE owns font-and-glyph device resources reusable across runs. See #QW7P96.
(cache font-pathname string &key direction)(camera distance lift)(shaped font-loader cache font-pathname)(camera)(cache glyphs)(glyphs atlas center right up scale min-x min-y max-x max-y
&key (ink '(0.96 0.32 0.48)))Build one dense model-and-atlas record per drawable glyph occurrence. INK is the linear RGB carried in the record's three spare lanes.
(device target-format string font-pathname shaped glyphs atlas center
world-units-per-em instance-data &key (label "world HarfBuzz Slug text"))Create one owned Slug draw batch from caller-positioned GLYPHS. The caller owns the semantic placement policy and supplies the dense instance records. This is the shared GPU boundary for ordinary shaped runs and the unified terminal surface in #7ZM22R.
(pathname &key (provider *gpu-provider*))Render a native-resolution close-up of the world Slug text proof. The wide viewport and enlarged world-space text make outline, band-selection, and coverage defects visible without scaling up a smaller raster afterward. #9G0Z19
If you're lucky, someone has bound this to a working GPU-PROVIDER.
(pathname &key
(title "luv hidden block world")
(width 960) (height 640)
(world (make-empty-little-block-world))
(mesher (make-instance 'exposed-face-mesher))
(camera (make-instance 'fly-camera))
(critters (make-instance 'critter-population))
(provider *gpu-provider*)
(world-text-string nil)
(world-text-distance 8.0)
(world-text-lift 3.0)
(world-text-units-per-em 0.55)
(sky-clock (make-instance 'sky-clock
:pinned-day-fraction 0.5))
(sky-profile (make-default-sky-profile)))Open a hidden SDL canvas, render one block-world frame, and save it. The sky clock arrives pinned at noon so captures stay byte-deterministic; pass an unpinned clock to photograph another time of day. A capture never runs the frame simulation, so any animals it wants in shot arrive already placed in CRITTERS rather…
(x y z)The extracted arithmetic frontend already owns literals, references, calls, lexical bindings, quantity boundaries, unit conversion, and a checked arithmetic-function-definition. Shader expressions subclass those common objects. The first define-shader-function implementation nevertheless introduced a second…
The decade retrospective records several material changes: – Slug still uses no precomputed or cached glyph images. – A former subdivision of each band into left and right curve lists was removed, halving the band texture from four 16-bit channels to two. – The original supersampling layer was removed after the…
The reference vertex shader expands the quad by half a pixel in screen space. It transforms a per-vertex dilation normal through the inverse of the local-to-device Jacobian, while the model-view-projection matrix and viewport size determine the em-space size of that half pixel. This is not decorative boldening:…
Slug exposed a missing layer in the language. The core operators were typed objects and a complete shader could have top-level let* bindings, but the only reusable source vocabulary was define-shader-abstraction. Its method ran as host Lisp before parsing and returned a newly constructed S-expression. That is…
Intent: make one portable arithmetic definition callable both by compiled Lisp and from a shader body, sharing source identity, parameter normalization, arity, lexical bindings, recursion rejection, and application provenance. Evidence needed: move Slug's two scalar root-eligibility functions to the common definition…
The luv terminal should be an object in the little world, not a conventional terminal window captured into a texture. Its rectangular cell grid has a position, orientation, depth relationship, and input surface in the scene. Ghostty supplies terminal semantics; luv's Slug path supplies glyph outlines; the ordinary…
Testing whether a computed root lies in [0,1] is numerically unstable at shared endpoints. Slug instead classifies the signs of the three control coordinates before calculating roots. Let s1, s2, and s3 be one only when the corresponding coordinate is strictly positive; zero is non-positive. The exact eligibility…
Read against the 2026 reference shaders and the retrospective, luv's Slug had the algorithm -- eligibility, two rays, weighted combination, sorted bands -- but not the reference's shape around it. This figure closes the gaps and names what the reference hard-codes. – Early exit. The reference leaves a band's loop at…
Intent: prove that luv's shader DSL can state, lower, compile, and actually draw the numerically important pixel-stage core before designing texture and font-data machinery. Evidence: – slug-quadratic-outline is now a typed shader function over the eight points of a connected four-curve contour. It composes ordinary…
Text shaping and outline rendering meet at a deliberately narrow seam. shape-slug-text gives HarfBuzz the UTF-8 run and font bytes, then returns dense records containing glyph IDs, byte clusters, advances, and offsets in font units. It does not ask HarfBuzz to rasterize. The Slug side uses each glyph ID with…
Intent: cross #4G7064 from an isolated target into the actual luvcraft scene, with world placement, camera projection, scene depth, and alpha composition all participating in the same frame as terrain. make-world-text-run is now a semantic owner around dense HarfBuzz placement and vertex data. Its initial model…
Intent: distinguish “a shaped run renders” from evidence that Slug's edge coverage and geometry expansion are correct across backends. Evidence needed: exercise the portable draw on Vulkan, compare boundary pixels with a small CPU winding oracle, and test dynamic dilation on geometry whose undilated quad actually…
HB_MEMORY_MODE_DUPLICATE copies the bytes before this dynamic pointer leaves scope.