Textures: a frame is a texture of its own (#47); 725 checks

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rootandClaude Opus 5 committed 2026-09-13 13:25:57 +02:00
1 parent 7debb2370a
commit c5bc04d969
3 files changed
+11 -7

No files matched your search

+1 -1
@@ -51,7 +51,7 @@ src/core/store/ serialize (untrusted JSON → Project), http (one API cal
src/runtime/ World, Entity, System interface, Input tracker, CollisionSystem, AnimationSystem, BehaviorSystem
src/ui/ widget toolkit (generic, knows nothing about assets)
src/viewport/ view (transforms), items (what to draw), symbols (how to draw it),
textureFill, TextureLibrary, activeCamera — no editor concepts, so tests reach it freely
TextureLibrary, activeCamera — no editor concepts, so tests reach it freely
src/editor/ App → Editor (layout + ticker), PlaySession (the run), ProjectSession (load/create),
EditorRun (run in editor), state, actions, shortcuts, debugHook, autosave, textureImport,
framing (fit/marquee/snap maths), AssetTabEditor (the shared shell of the asset tabs)
+3 -3
@@ -12,13 +12,13 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
# code is lost (tail's is reported), so a red run reads as green
npm run build # writes dist/ and regenerates index.html
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
node tools/seed-e2e.mjs && node tools/e2e.mjs # 723 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 725 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result
node tools/seed-jumprun.mjs && node tools/smoke-jumprun.mjs # 24 checks: the example game, played end to end
node tools/seed-tetris.mjs && node tools/smoke-tetris.mjs # 19 checks: the Tetris example, played end to end
node tools/seed-actor.mjs && node tools/smoke-actor.mjs # 20 checks: the walk demo (the seed uploads its own sheet)
node tools/seed-actor.mjs && node tools/smoke-actor.mjs # 21 checks: the walk demo (the seed uploads its own sheet)
```
**A run needs a working GPU.** An idle editor page keeps rendering a picture that never changes, and
@@ -150,4 +150,4 @@ widths to find click points. The **render device is not in this repo** — `/dev
and a 1 GB `/dev/shm` come from `container.options` in the runner's own `config.yaml`, which
applies to every job on that runner. Without them chromium falls back to llvmpipe, which gets
WebGL 1.0 only. Measured in the job container: `ANGLE (Intel, Vulkan 1.3.230, HD Graphics 630)`,
WebGL 2.0, and the same 723 checks as here.
WebGL 2.0, and the same 725 checks as here.
+7 -3
@@ -78,9 +78,13 @@ from texture* makes a sprite asset that wears the whole picture at its pixel siz
texture (numbered like any name clash, so `Coin` gives `Coin 2`) and selected. It is the same as creating a sprite, picking the texture and clicking *Set to
W x H px*, but in one undo step.
Rendering a frame is a texture matrix on the fill, not a Pixi sub-texture — a Graphics fill
ignores a `Texture`'s `frame` (measured, see `viewport/textureFill.ts`). One consequence is
worth having: a sheet stays **one** GPU upload however many frames of it are on screen.
**A frame is a texture of its own.** The first time a sprite wears it, the editor cuts the
rectangle out of the sheet and uploads that cut. Drawn straight from the sheet, a frame showed a
hairline of the neighbouring cell along any edge that landed inside a screen pixel — the GPU
evaluates a partly covered pixel at its centre, past the edge — and Pixi lets a fill wrap around
the picture, so the top row of a sheet read its bottom row. A cut has no neighbour to show, and
sheets need no padding between cells. The price is one small texture per frame on screen, next
to the sheet itself.
**Nothing ever breaks on a missing picture.** While an image loads, and for a reference that
dangles because the texture was deleted, the sprite draws its plain colour — exactly what it