Skip to content

perf(milky-way): draw the far galaxy as 33 merged-copy layers so phones stop flickering - #1588

Merged
apresmoi merged 4 commits into
mainfrom
fix/milky-way-phone-layers
Oct 22, 2026
Merged

apresmoi merged 4 commits into
mainfrom
fix/milky-way-phone-layers

Conversation

@apresmoi

Copy link
Copy Markdown
Contributor

Change

On phones the Milky Way flickered (layers blinking out every other frame) and on the iPad it froze for about 200 ms when crossing into or out of the galaxy. Both came from the far galaxy drawing 390 full-screen layers: 52/52/26 slabs, each drawn three times as optical copies.

  • Phone (Galaxy S23, Chrome): Chrome caps tile memory at 256 MB. While the galaxy turned, it ran short in 827 of 867 tile assignments and drew layers empty in 248 frames of an 8 s turn. That was the flicker.
  • iPad: adding or removing the bank cost 191, 185 and 214 ms composites.

What changes:

  • The far bank is 13/13/7 slabs, and each slab image already holds its three optical copies (alpha 1 − (1 − a)³). It's drawn as one layer per slab at opacity gain / 3, so there are at most 33 layers instead of 390. The bake gets an opt-in --merged-copies flag. The prepared volume declares mergedOpticalCopies: 3 and the bank's prototype records it, so a reused bank keeps it. Volume topology now tells merged and unmerged encodings apart.
  • The far picture leaves as soon as the inside sky covers it (reverting the "wait until the camera stops" deferral from fix(milky-way): stop dropping frames when zooming into the galaxy #1586). Keeping 390 layers under the sky was what made the phone flicker while zooming in.

Body guide: Milky Way volume README.

Known limit, accepted by the owner on the phone: a merged copy is exact only for faint material. Face-on, an opaque pixel draws at a third and a half-opaque one at 0.29 instead of 0.5. That is 31% of the slabs' total alpha face-on and 0.8% at grazing views. The alternatives were measured and rejected: two layers per slab ran the phone out of tile memory again (56 to 62 frames with missing tiles), and fewer, thicker slabs (7/7/4) showed gaps between the sheets.

Prepared assets

  • Baked the object (node packages/bake/authoring/milky-way-dust/prepare-layers.mts --slabs-only). A second bake reproduced the inventory exactly, and CSSEARTH_GALAXY_FULL_REPLAY=1 rebuilds all 33 slabs and 6 sky faces from the restored inputs byte for byte.
  • Published the bytes (node packages/bake/cli/publish-runtime-assets.mts --object=milky-way-volume); check-assets-published verifies every key live.
  • Committed the refreshed inventory.json, and no baked files.

Evidence and limits

Proves the change, same 8 s touch turn of the galaxy on the phone (Chrome traces):

Far galaxy Tile memory short Frames with missing tiles Dropped frames
52/52/26, three copies (main) 827 of 867 248 164
13/13/7, merged copies (this PR) 0 of 536, 0 of 547 9, 4 36, 62
Before #1581 0 of 506 0 43

Clean: pnpm typecheck, eslint on the touched packages, galaxy bake tests with the CI-pinned NumPy 2.5.4 and Pillow 13.0.0, renderer volume and universe tests, objects volume tests (369 passed), check-prepared-delivery, check-no-variables, check-no-hashes, check-color-spelling, typescript-ownership.

The phone still drops about 5% of frames on this view, the same as before #1581. That remaining cost is the star labels: about 240 composited labels restyled every frame. It's separate work.

@apresmoi
apresmoi merged commit 4eae876 into main Oct 22, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant