Web frontend and supporting services for lance.blue
headquarters plan render-performance.md
3.7 kB


id: render-performance title: Input-to-pixels stays under the tail status: continuous repos: [arena] dependsOn: [] exitCriterion: > A player interacting with the board does not wait on the renderer, and any regression is measured rather than argued about. #

render-performance #

arena owns this one. The task list is arena's TODO under "Render performance", and the record of what shipped and what each fix measured is PERFORMANCE.md in that repo. This file is here so the register is complete and so the epic can be cited from a commit — it is not a second copy, and it should not become one.

Why it is an epic #

Rendering was sluggish and the shipped fixes roughly halved median downpress-to-pixels, with the p90 tail falling from ~470ms to ~330ms. The proxy was ruled out early: the api host stayed under 5% CPU during a match.

What makes it worth naming rather than leaving as a pile of arena tickets is that it is the one number the whole architecture is arranged to protect. arena-image's SOCI option is argued against because lazy loading would move a delay into the frame path. The "Princess in its own process" proposal exists because bot search steals cores from the EDT. Task sizing is anchored on where a client JVM starts stalling. All three are render-performance decisions made in other epics.

The state of it, as of 2026-08-07 #

The first real profile exists. async-profiler 4.5 ships in the image and loads into both JVMs when ARENA_PROFILE=1, dumping collapsed stacks that exit/finalize.sh uploads under a diagnostics key.

Of the EDT samples that were running rather than waiting, 85% are under BoardView painting, 76% under DirectDraw and 72% under PNG encode and pixel hashing — libzpng alone is 43% of the EDT's whole working time. That confirms by measurement what had until then been deduced from source.

Two caveats on that run, both recorded in arena: the match never left the lobby, so those are ambient rather than interaction costs, and the host was busy with an unrelated build. A run that plays is what settles the largest open items, and arena.sh up --spectate exists for exactly that.

Where the work is #

Everything open lives in arena: the frame tearing discriminator, the 1008px tiled board background, DirectDraw's re-encode on every draw, capping the client canvas resolution, the game-summary images, server-side text rasterization, and the load-time batch. Each has its own evidence and its own argument, and several record things that were tried and rejected — repainting by hex, memoising by image identity — which is the part that would be lost by summarising them here.

Done #

arena holds the detail; this is the shape of what landed.