perf: faster still

This commit is contained in:
Dan Finch 2026-08-04 19:12:09 +02:00
parent ef5029da1d
commit 67cd54fe33
8 changed files with 280 additions and 203 deletions

View file

@ -59,7 +59,9 @@ rules live in `.agents/rules/*.md`.
AABB test, for chunk culling), `Texture` (nearest/bilinear, wrapping, no
mipmaps), `Sky` (gradient + sun + procedural clouds; renders at 1/`step` res).
- `scene/``Camera` (fps yaw/pitch; far plane reaches the outdoor peaks),
`Mesh` (indexed tris), `Sprite` (Y-axis billboard), `Terrain` (procedural
`Mesh` (indexed tris; verts stored flat: `STRIDE` floats x,y,z,u,v per vertex,
no per-vertex objects — cache-friendly + alloc-free to draw), `Sprite`
(Y-axis billboard), `Terrain` (procedural
heightfield around the room: flat clearing in the center, rolling hills, tall
edge peaks. `Terrain.patch` builds one ground patch over a rectangle -- called
per chunk, aligned so patches weld crack-free, with a hole for the room;
@ -136,19 +138,25 @@ front-out — a culled mesh that renders inside-out has its index order flipped
## Performance / where the frame goes
The world is dense (hundreds of trees + boulders, ~50k tris) but most of it is
off-screen or fogged each frame, so three things keep it cheap:
off-screen or fogged each frame, so several things keep it cheap:
- **Frustum culling** (`Frustum` + per-`Chunk` AABB test in `main`) — skips whole
chunks that fall outside the view. Behind you + off to the sides = free.
- **Backface culling** (`draw(..., true)`) — ~halves fill on solid geometry
(terrain, foliage, rock). See the Rasterizer note re winding.
- **Half-res sky** (`SKY_STEP` in `main`, default 2) — the cloud fbm runs per
pixel and dominated the frame; sampling once per 2×2 block quarters it.
- **Flat geometry + zero-alloc raster**`Mesh` is a flat float array and the
whole per-triangle path uses reused scratch, so a frame allocates ~0 bytes
(measured). This buys frame *consistency* (no GC-pause spikes; worst/mean ~1.3x)
and makes geometry shareable for Web-Worker rasterization later. Note it did
**not** raise mean fps — allocation was never the bottleneck (JSC collects the
churn ~free); the mean is the transform+fill **compute**.
Together ~1.52x over drawing everything full-res every frame, and the win grows
with content since culled chunks cost ~nothing. Next levers if needed: LOD /
impostors for far trees, flat typed-array geometry (kill per-tri allocation),
Web-Worker banded rasterization. `TREE_COUNT`/`BOULDER_COUNT` are the blunt
content dials.
Frustum + backface + half-res sky give ~1.52x, growing with content since culled
chunks cost ~nothing. The remaining bottleneck is raw compute on visible tris, so
the mean-fps levers left are to **do less** (LOD / impostors for far trees —
`TREE_COUNT`/`BOULDER_COUNT` are the blunt content dials) or **use more cores**
(Web-Worker banded rasterization, now unblocked by the flat geometry).
## Clouds