Shaders: replace the performance estimate with measured numbers (#67)
Intel HD 630, 48x48 sprites, 640x360 camera, neutral values. The estimate "fine for dozens, not for thousands" was right in spirit and two orders of magnitude too optimistic at the upper end. Development names the new tool and finally lists e2e step 28.
1 parent
46d30db966
commit
bd26824b01
2 files changed
+17
-4
No files matched your search
+4
-3
@@ -12,13 +12,14 @@ 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 # 727 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 728 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 # 21 checks: the walk demo (the seed uploads its own sheet)
|
||||
node tools/seed-e2e.mjs && node tools/perf-shaders.mjs # what a shader costs per sprite; a measuring tool, not a check
|
||||
```
|
||||
|
||||
**A run needs a working GPU.** An idle editor page keeps rendering a picture that never changes, and
|
||||
@@ -62,7 +63,7 @@ to touch it.
|
||||
The e2e run is a list of steps in `tools/e2e/` (hierarchy, inspector, play, assets, drawing,
|
||||
graph, parenting, colliders, containers, text/enabled, menus, textures, frames, overflow, aspect,
|
||||
container tab, run-in-editor, pause, nested behaviors, key repeat, list tab, struct variable,
|
||||
variable nodes, property nodes, animations, state machines, console) that build on each other's
|
||||
variable nodes, property nodes, animations, state machines, console, shaders) that build on each other's
|
||||
scene state and share the CDP driver in `tools/lib/harness.mjs` — `press`, `undo`, `play`,
|
||||
`object(id)`, `world()`, `mutate(js)` and friends, so a check reads like the interaction it
|
||||
exercises.
|
||||
@@ -150,4 +151,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 727 checks as here.
|
||||
WebGL 2.0, and the same 728 checks as here.
|
||||
+13
-1
@@ -83,7 +83,19 @@ deviations while their shader is not worn.
|
||||
## What it costs, and where it stops
|
||||
|
||||
A shader is a Pixi filter: the sprite is rendered into an intermediate texture and drawn back with
|
||||
the fragment, one pass per sprite wearing one. Fine for dozens, not for thousands. The pass runs at
|
||||
the fragment, one pass per sprite wearing one. Measured with `tools/perf-shaders.mjs` on an Intel
|
||||
HD 630 (the GPU CI uses as well), sprites of 48x48 inside a 640x360 camera, neutral values, three
|
||||
runs agreeing to within 1 fps:
|
||||
|
||||
| shaded sprites | 25 | 50 | 100 | 200 | 400 | 800 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| frames per second | 58 | 56 | 54 | 48 | 34 | 20 |
|
||||
|
||||
The very same scene without shaders holds 60 fps at every one of those counts, 800 included, so the
|
||||
whole drop is the passes: about 20 microseconds each up to 200 sprites, 40 at 800. Sprite size
|
||||
hardly matters — four times the area (96x96) measures the same, so what costs is the pass, not the
|
||||
pixels in it. A few dozen shaded sprites are free in practice; a few hundred cost half the frame
|
||||
rate. The pass runs at
|
||||
screen resolution, so pixel art stays sharp: measured in play at a fractional camera scale (1.43
|
||||
screen pixels per world unit), a *Nearest* texture keeps its hard border pixel for pixel with a
|
||||
neutral shader on, and brightness 0.5 darkens every one of those pixels — the pass runs there, and
|
||||
|
||||
Reference in new issue
Block a user