Write down what the Image family taught, where the next lane will look

The section on what a headless FFI test can and cannot pin was written before
anything CPU-side was bound, so it had no example of the one shape that beats
store-and-return: scalars in and struct fields out, with nothing for a
permuted layout to cancel against.

It also did not say that MeasureText answers 0 without a window, which is the
assumption this lane started with and had to measure its way out of. Two
lanes have now guessed the same thing.
This commit is contained in:
Joseph Ferano 2026-09-11 19:47:08 +07:00
parent 05676f3181
commit cb11fdf272

26
NEXT.md
View File

@ -295,10 +295,36 @@ turned out to check nothing.
computes four numbers from four different field pairs, and the point/rect computes four numbers from four different field pairs, and the point/rect
predicates turn the wrong way when width and height are exchanged. predicates turn the wrong way when width and height are exchanged.
- **Scalars in, fields out is the strongest shape there is**, and the Image
family is where it was finally available. `GenImageColor(4, 2, colour)` is
handed two integers and answers with a struct reading 4, 2, 1, 7 — four
distinct values in four adjacent `i32` slots, with no input struct for a
permutation to cancel against. That pins `Image` completely, including that
`data` is present and first; `Texture2D` could never be pinned that way
because nothing without a GPU reads its width, height or mipmaps at all.
- **A non-square image is an axis discriminator.** `GetImageColor` indexes
`y*width + x`, so on a 4-wide, 2-tall image `(3,0)` exists and its transpose
does not: exchange the wrapper's `x` and `y` and the read goes out of bounds
and answers transparent black. `ImageFlipHorizontal` against
`ImageFlipVertical` says the same thing twice more.
- **A file is external ground truth**, so `ExportImage` then `LoadImage` is not
the symmetric round trip the rest of the package has to avoid — stb's encoder
and decoder agree with each other, not with Flan's field order. Verified
red by the `x`/`y` permutation above.
The rule that falls out: make raylib **compute** something whose answer differs The rule that falls out: make raylib **compute** something whose answer differs
per axis, then verify the test can fail by permuting the fields and watching it per axis, then verify the test can fail by permuting the fields and watching it
go red. A case not verified that way is decoration. go red. A case not verified that way is decoration.
One correction to an assumption that has now cost two lanes a guess:
**`MeasureText` is not headless material.** It measures with the default font,
`LoadFontDefault` is not exported, and nothing but `InitWindow` loads it — so
with no window it answers 0 for every string. Measured against
`libraylib.so.550`, not reasoned about. `GetFrameTime`, `GetTime` and
`GetScreenWidth`/`Height` are all 0 headless for the same kind of reason. All
five are bound, and all five are exercised by running `sand.flan` and looking,
which is the whole of what can be claimed for them.
## Packages ## Packages
`lib/load.ml` resolves `(import rl "vendor:raylib")` before the checker runs. `lib/load.ml` resolves `(import rl "vendor:raylib")` before the checker runs.