luv

Workshop wiki

clim-in-the-world.org

McCLIM surfaces in the world

Luvcraft has grown a family of McCLIM instruments that live inside the block world: the hotbar, the inventory, the film chooser, a Telegram terminal, a sheet of PDF paper. They began with one implementation path, and that path had a property nobody chose deliberately and everybody could see: the text on them was soft, while the text on a terminal wall standing beside them was not.

This page records what that path actually is, why the difference exists, and what redoing it would mean. It is a design note about presentation, not about any one instrument; the instruments themselves are described where they live, and the interaction seam they share is #0U22KN.

The first McCLIM surfaces reached the world as rasters #AX9ZTA

The first embedded frames were made against the session's own canvas, context, and device, and each mirror owned a texture. Everything the frame drew -- draw-text*, draw-analytic-rounded-rectangle*, a decoded photograph as a pattern -- is rasterized into that texture at the size the frame was laid out at. The compositor then samples the texture across a quad placed in the world.

So the whole surface is a picture with a resolution, chosen once, at frame construction. Slug is involved, but only inside the picture: it rasterizes glyphs into the texture, and after that they are pixels like any other. The sampler is created with default options over a texture with a single mip level, so what happens next is an ordinary bilinear resample: minification aliases, magnification blurs, and the thin stems of a serif face are the first thing either destroys.

None of this is wrong for what the path was built to do. It is exactly right for a photograph, for a gradient, for a rounded panel edge -- content that is a raster already, or that has no detail finer than the texel. It is wrong for type.

A terminal is sharp at any distance because its glyphs are geometry #GJUPMQ

The terminal wall does not rasterize anything into a texture. It places one instanced quad per glyph directly in world space -- see make-terminal-display-glyph-instances and the world text run it feeds -- and Slug evaluates the outline analytically in the fragment shader, at whatever resolution that pixel happens to have. The glyph has no resolution of its own to lose.

That is the whole difference. A wall read from across the map and a wall read from arm's length are both drawn from the same Bézier contours at the sampling rate of the frame being drawn, which is what #I9U71F means by the grid remaining authoritative through Slug, and what the atlas and instancing work #AT7L3S built the machinery for.

#0U22KN, written before any of these instruments existed, already says it: reusing the world-overlay relationship is the architectural result, while sharing the CPU raster machinery would preserve precisely the indirection this experiment is meant to remove. The instruments built since took the raster machinery, and inherited the indirection.

defclass agent-terminal-display wall.lisp:20

The agent's wall: the transcript on a terminal wall, drawn the way the phone and the shell walls are drawn -- Ghostty cells as Slug glyph geometry, sharp at any distance (#GJUPMQ) -- rather than through a McCLIM mirror.

The wall is a terminal-display with no PTY. What it shows is VT bytes the agent writes into its Ghostty terminal: the prompt the player types at it, the thought as it streams, and each tool call as an ANSI cassette (#9K823O) once it has finished. The terminal does the scrolling and the wrapping; we do the colours.

Two threads meet here and one thing crosses between them, the way the Telegram wall already does it: the turn's thread formats text and posts it to an outbox; the canvas thread drains the outbox into the terminal at the frame boundary, where it also reads the terminal to draw it. Nothing writes the terminal from anywhere else.

defclassagent-terminal-display
agent:initarg:agent:accessoragent-wall-agent
outbox:initform
sb-concurrency:make-mailbox:name"agent wall outbox"
:readeragent-wall-outbox
draft:initform"":accessoragent-wall-draft:documentation"What the player has typed at the wall so far."
line-open-p:initformnil:accessoragent-wall-line-open-p:documentation"Whether streamed text has left the cursor mid-line."
printed:initform
make-hash-table:test#'eq
:readeragent-wall-printed:documentation"Per turn, (THOUGHT-LENGTH . TEXT-LENGTH) already written."
observer:initformnil:accessoragent-wall-observer
:documentation

A terminal wall showing one agent's transcript.

The PDF sheet made the difference measurable #I3G0S7

A sheet of paper showing a real document is a good probe, because a page is mostly small type and a reader knows immediately whether it is right.

MuPDF hands back each typeset line with its box, size, and font, and the sheet drew those with draw-text* at positions scaled from PDF points. At two and a half metres the body text was soft and broken; at reading distance it was clean. That looks like a legibility limit and is not one, because a terminal wall in the same shot at the same distance is crisp.

The measurement that settles it: widening the page texture from 500 to 1400 pixels did not improve the distant view, and neither did narrowing it to 780. A resolution that is merely wrong gets better when corrected. A resolution that exists at all is the defect, and no value of it helps.

Two lesser findings came out of the same probe, both symptoms of drawing a document through a toolkit's font mapping rather than through its own metrics:

Neither would arise if the page were set from the document's own glyph outlines at the document's own metrics, which is what the geometry path would allow.

A backing texture is a fallback, not a surface #S8LIJP

The mirror texture was never part of McCLIM's semantics. It was one backend's way to preserve painter order before mounting the result somewhere else. Once the backend retains an ordered semantic command stream, the destination can replay that stream directly: solids, analytic edges, gradients, images, and Slug glyphs each select a pipeline while their order and clip rectangles stay unchanged.

A decoded photograph still owns a texture, because pixels are its source representation. That does not imply a pane-sized texture: the image command samples the photograph's own cached texture directly. A pane-wide raster is now appropriate only for an unsupported operation, an intentional cache layer, readback, or another explicit fallback. The pane itself is its CLIM geometry, command stream, clipping, and destination transform.

DONE Draw McCLIM text as world geometry #6XLC0P

Intent. Give an embedded surface a way to emit Slug glyph runs in world space instead of rasterizing its text into the mirror texture, so that a panel's type is as sharp as a terminal wall's at every distance.

Evidence. #I3G0S7 for why texture size cannot fix it, #GJUPMQ for the path that already works, and the instancing and outline-cache machinery at #AT7L3S and #U5X6DY, which is the resource ownership a second caller would share. The PDF sheet is the readiest probe: one page, many sizes, and an obvious right answer.

Done when. A sheet of paper and a terminal wall placed side by side are equally legible at the same distance, and the page's type no longer needs condensing to fit its measure, because it is set from the document's own glyph metrics rather than a substituted face's.

McCLIM gadgets first retained the backend's prepared semantic command stream and replayed only already-shaped 72-byte Slug vertices into their final pass. Physical surfaces used their center/right/up world transform in the depth-tested scene pass; HUD panes used the same affine shader against their screen-space transform in the depthless presentation pass. That established the destination-level replay seam later generalized by #HN9MI5. The PDF sheet keeps its analogous document-specific world text run because its glyphs and metrics come from MuPDF rather than a McCLIM text command.

DONE Replay the complete McCLIM stream at its destination #HN9MI5

Intent. Remove the pane-sized backing texture from embedded GPU mirrors and preserve painter order while drawing every supported command family directly into either the world or HUD pass.

Evidence. The GPU medium already retained one ordered command stream plus dense family-specific vertex buffers. The text-only replay proved that the same local coordinates and clip rectangles could be interpreted through a destination affine transform. The black hotbar corners and resampled selection halo showed that leaving analytic paint in a raster layer remained visible.

Done when. An embedded mirror owns no backing texture; solid, analytic, relief, gradient, image, and Slug commands replay in their original order; images sample their own source textures; both world and HUD destinations are visually healthy; and standalone McCLIM windows retain their native drawable path.

Embedded GPU mirrors now publish prepared commands and six dense uploaded buffers without beginning an offscreen render pass or allocating a pane-sized texture. The destination compositor selects the matching affine vertex and paint fragment pipeline for each command in order. HUD corners therefore have no rectangle to expose, and rounded borders and selection halos evaluate their analytic coverage at presentation resolution. Cached image paints remain textures owned by the images themselves. Embedded repaint publishes its copied command stream and all six matching buffers synchronously as one revision; it does not compile the standalone mirror's unused raster pipelines. A standalone mirror still renders the same stream into its acquired canvas drawable.