luv

Workshop wiki

text.lisp

luvcraft/text.lisp

system luvcraft/core · 19 definitions · on GitHub

HarfBuzz-shaped Slug text as world-owned render geometry.

A world-text-run is the semantic placement boundary. A per-device cache retains HarfBuzz results and normalized outlines. Each distinct glyph set is packed into two device atlases and shared by matching runs.

in-package#:luvcraft

The Slug renderer's tunable values, as knobs. The values themselves live in luv.SLUG (see hal/shader/slug.lisp) and are folded into the text shaders as literals; turning one rebuilds the world-text pipeline at the next frame, so every glyph in the world -- signs, terminal walls, the phone -- shows the change together. None needs a realization: the band texture is not affected by any of these, only the shaders that read it.

define-knobslug-filter-width
:group:text:label"text filter width":unit-label" px":minimum0.25:maximum4.0:step0.05:documentation

The box filter's width in pixels. One is exact pixel coverage; wider softens the edge (and needs the dilation to grow with it, which it does).

luv.slug:*slug-filter-width*
define-knobslug-dilation-pixels
:group:text:label"text quad dilation":unit-label" px":minimum0.0:maximum4.0:step0.1:documentation

How far past its outline each glyph quad grows, in filter widths. Half is the least that keeps the whole filter inside the quad; less clips the edge, more costs fragments.

luv.slug:*slug-dilation-pixels*
define-knobslug-optical-weight
:group:text:label"text optical weight":minimum0.25:maximum2.0:step0.05:documentation

The exponent applied to coverage. One is linear; the reference's SLUG_WEIGHT is one half, which fattens thin strokes.

luv.slug:*slug-optical-weight*
define-knobslug-fill-rule
:group:text:label"text even-odd fill":minimum0.0:maximum1.0:step1.0:documentation

0 fills glyphs by the nonzero winding rule, 1 by even-odd (self- overlapping contours become holes).

luv.slug:*slug-fill-rule*
define-knobslug-footprint-norm
:group:text:label"text footprint fwidth":minimum0.0:maximum1.0:step0.1:documentation

How the pixel's size in em is measured: 0 the gradient length, 1 fwidth as the reference does; between blends.

luv.slug:*slug-footprint-norm*
define-knobslug-early-exit
:group:text:label"text band early exit":minimum0.0:maximum1.0:step1.0:documentation

1 stops walking a band's sorted curves at the first one wholly behind the sample; 0 walks them all. The picture is the same either way.

luv.slug:*slug-early-exit*
define-knobslug-root-epsilon
:group:text:label"text root epsilon":minimum0.000001:maximum0.01:step0.000005:documentation

Below this |a| a curve is solved as a line rather than a quadratic; also the floor under the coverage divisions.

luv.slug:*slug-root-epsilon*
define-knobslug-debug-view
:group:text:label"text band debug view":minimum0.0:maximum1.0:step1.0:documentation

1 paints each glyph quad with its band loads: red the horizontal band's curve count over sixteen, green the vertical's, blue the coverage.

luv.slug:*slug-debug-view*
defclassworld-text-run
string:initarg:string:accessorworld-text-run-string
font-pathname:initarg:font-pathname:readerworld-text-run-font-pathname
shaped-text:initarg:shaped-text:readerworld-text-run-shaped-text
glyphs:initarg:glyphs:accessorworld-text-run-glyphs
atlas:initarg:atlas:accessorworld-text-run-atlas
center:initarg:center:readerworld-text-run-center
world-units-per-em:initarg:world-units-per-em:readerworld-text-run-world-units-per-em
vertex-data:initarg:vertex-data:readerworld-text-run-vertex-data
vertex-buffer:initarg:vertex-buffer:readerworld-text-run-vertex-buffer
instance-data:initarg:instance-data:accessorworld-text-run-instance-data
instance-buffer:initarg:instance-buffer:accessorworld-text-run-instance-buffer
layout:initarg:layout:readerworld-text-run-layout
pipeline:initarg:pipeline:readerworld-text-run-pipeline
resources:initarg:resources:accessorworld-text-run-resources
defunworld-text-point
centerrightupxyscale
make-vec3
+
vec3-xcenter
*scale
+
*x
vec3-xright
*y
vec3-xup
+
vec3-ycenter
*scale
+
*x
vec3-yright
*y
vec3-yup
+
vec3-zcenter
*scale
+
*x
vec3-zright
*y
vec3-zup
defunmake-world-text-quad-vertices
make-array18:element-type'single-float:initial-contents'
0.00.00.01.00.00.01.01.00.00.00.00.01.01.00.00.01.00.0
defunmake-world-text-instances
glyphsatlascenterrightupscalemin-xmin-ymax-xmax-y&key
ink'
0.960.320.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.

let*
middle-x
/
+min-xmax-x
2
middle-y
/
+min-ymax-y
2
data
make-array
*24
lengthglyphs
:element-type'single-float
labels
difference
endstart
make-vec3
-
vec3-xend
vec3-xstart
-
vec3-yend
vec3-ystart
-
vec3-zend
vec3-zstart
write-values
offsetvalues
loopforvalueinvaluesforindexfromoffsetdo
setf
arefdataindex
coercevalue'single-float
loopforglyphinglyphsforbasefrom0by24foroutline-left=
-
luv.slug:slug-glyph-placement-outline-min-xglyph
padding
foroutline-bottom=
-
luv.slug:slug-glyph-placement-outline-min-yglyph
padding
foroutline-right=
+
luv.slug:slug-glyph-placement-outline-max-xglyph
padding
foroutline-top=
+
luv.slug:slug-glyph-placement-outline-max-yglyph
padding
forlayout-left=
+
luv.slug:slug-glyph-placement-origin-xglyph
outline-left
forlayout-bottom=
+
luv.slug:slug-glyph-placement-origin-yglyph
outline-bottom
forlayout-right=
+
luv.slug:slug-glyph-placement-origin-xglyph
outline-right
forlayout-top=
+
luv.slug:slug-glyph-placement-origin-yglyph
outline-top
fororigin=
world-text-pointcenterrightup
-layout-leftmiddle-x
-layout-bottommiddle-y
scale
forright-edge=
world-text-pointcenterrightup
-layout-rightmiddle-x
-layout-bottommiddle-y
scale
fortop-edge=
world-text-pointcenterrightup
-layout-leftmiddle-x
-layout-topmiddle-y
scale
foratlas-location=
gethash
luv.slug:slug-glyph-placement-resourceglyph
luv.slug:slug-glyph-atlas-locationsatlas
forserialized=
luv.slug:slug-device-glyph-serialized
luv.slug:slug-glyph-placement-resourceglyph
do
write-valuesbase
list
vec3-xorigin
vec3-yorigin
vec3-zorigin
vec3-x
differenceright-edgeorigin
vec3-y
differenceright-edgeorigin
vec3-z
differenceright-edgeorigin
vec3-x
differencetop-edgeorigin
vec3-y
differencetop-edgeorigin
vec3-z
differencetop-edgeorigin
outline-leftoutline-bottom
luv.slug:slug-serialized-outline-horizontal-band-countserialized
outline-rightoutline-top
luv.slug:slug-serialized-outline-vertical-band-countserialized
firstatlas-location
secondatlas-location
firstink
luv.slug:slug-glyph-placement-outline-min-xglyph
luv.slug:slug-glyph-placement-outline-min-yglyph
luv.slug:slug-glyph-placement-outline-max-xglyph
luv.slug:slug-glyph-placement-outline-max-yglyph
thirdink
data
defunworld-text-center-before-camera
cameradistancelift
multiple-value-bind
rightupforward
declare
ignoreright
world-text-point
camera-positioncamera
forwardupdistancelift1.0
defunmake-world-text-run-from-instances
devicetarget-formatstringfont-pathnameshapedglyphsatlascenterworld-units-per-eminstance-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.

let
resourcesnil
pipelinenil
completed-pnil
flet
keep
resource
pushresourceresources
resource
unwind-protect
let*
layout
keep
createdevice
make-bind-group-layout-descriptor:label"world Slug text layout":entries'
:binding0:type:texture
:binding1:type:texture
:binding2:type:uniform-buffer
vertex-buffer
keep
createdevice
make-buffer-descriptor:label"world Slug glyph quads":size
*4
lengthvertex-data
:usage'
:vertex:copy-dst
instance-buffer
keep
createdevice
make-buffer-descriptor:label"world Slug glyph instances":size
max4
*4
lengthinstance-data
:usage'
:vertex:copy-dst
setfpipeline
make-live-shader-pipeline:role:slug-world-text:vertex-role:slug-world-text:labellabel:devicedevice:layoutlayout:vertex-buffers'
:array-stride12:attributes
:shader-location0:offset0:format:float32x3
:array-stride96:step-mode:instance:attributes
:shader-location1:offset0:format:float32x3
:shader-location2:offset12:format:float32x3
:shader-location3:offset24:format:float32x3
:shader-location4:offset36:format:float32x3
:shader-location5:offset48:format:float32x3
:shader-location6:offset60:format:float32x3
:shader-location7:offset72:format:float32x3
:shader-location8:offset84:format:float32x3
:target-formattarget-format:target-blend:premultiplied-alpha:primitive'
:topology:triangle-list
:depth-stencil'
:format:depth32-float:depth-write-enablednil:depth-compare:less
write-buffervertex-buffervertex-data
when
plusp
lengthinstance-data
write-bufferinstance-bufferinstance-data
let
run
make-instance'world-text-run:stringstring:font-pathnamefont-pathname:shaped-textshaped:glyphsglyphs:atlasatlas:centercenter:world-units-per-emworld-units-per-em:vertex-datavertex-data:vertex-buffervertex-buffer:instance-datainstance-data:instance-bufferinstance-buffer:layoutlayout:pipelinepipeline:resourcesresources
setfcompleted-pt
run
unlesscompleted-p
whenpipeline
dolist
resourceresources
ignore-errors
destroyresource
defunmake-world-text-run
deviceglyph-cachecameratarget-formatstringfont-pathname&key
lift3.0
world-units-per-em0.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.

let*
shaped
luv.slug:cached-slug-shaped-textglyph-cachefont-pathnamestring
zpb-ttf:with-font-loader
font-loaderfont-pathname
let
glyphs
luv.slug:make-slug-glyph-placementsshapedfont-loaderglyph-cachefont-pathname
unlessglyphs
error'luv.slug:slug-shaping-error:reason:no-drawable-glyphs:detailsstring
multiple-value-bind
min-xmin-ymax-xmax-y
luv.slug:slug-text-extentsglyphsshapedfont-loader
multiple-value-bind
rightupforward
declare
ignoreforward
let*
atlas
instance-data
make-world-text-instancesglyphsatlascenterrightupworld-units-per-emmin-xmin-ymax-xmax-y
make-world-text-run-from-instancesdevicetarget-formatstringfont-pathnameshapedglyphsatlascenterworld-units-per-eminstance-data
defunmake-world-text-frame-bind-groups
rundeviceuniform-buffer

Bind one shared glyph atlas to one drawable-frame uniform.

let*
atlas
world-text-run-atlasrun
groups
make-array1:initial-elementnil
completed-pnil
unwind-protect
progn
setf
arefgroups0
createdevice
make-bind-group-descriptor:label"world glyph atlas frame bindings":layout
world-text-run-layoutrun
:entries`
:binding0:resource,
luv.slug:slug-glyph-atlas-band-viewatlas
:binding1:resource,
luv.slug:slug-glyph-atlas-curve-viewatlas
:binding2:resource,uniform-buffer
setfcompleted-pt
groups
unlesscompleted-p
when
arefgroups0
destroy
arefgroups0
defunworld-text-run-native-pipeline
live-shader-pipeline-native-pipeline
world-text-run-pipelinerun
defunreplace-world-text-run-instances
rundevicestringglyphsatlasinstance-data

Publish one complete replacement instance population into run.

The caller owns the semantic candidate. This function creates and fills its new GPU buffer before installing the new string, glyph, atlas, CPU-data, and buffer cohort. Native destruction of the predecessor remains submission-aware through the backend's ordinary destroy contract.

let
buffernil
completed-pnil
unwind-protect
progn
setfbuffer
createdevice
make-buffer-descriptor:label"world Slug glyph instances":size
max4
*4
lengthinstance-data
:usage'
:vertex:copy-dst
when
plusp
lengthinstance-data
write-bufferbufferinstance-data
let
old-buffer
world-text-run-instance-bufferrun
old-atlas
world-text-run-atlasrun
setf
world-text-run-stringrun
string
world-text-run-glyphsrun
glyphs
world-text-run-atlasrun
atlas
world-text-run-instance-datarun
instance-data
world-text-run-instance-bufferrun
buffer
world-text-run-resourcesrun
consbuffer
deleteold-buffer
world-text-run-resourcesrun
:test#'eq
completed-pt
destroyold-buffer
valuesrun
not
eqold-atlasatlas
unlesscompleted-p
whenbuffer
ignore-errors
destroybuffer
defunrelease-world-text-run
release-live-shader-pipeline
world-text-run-pipelinerun
dolist
resource
world-text-run-resourcesrun
destroyresource
values