Skip to content

fix: composite the pattern scenes as a handful of layers - #29

Merged
WeekendSuperhero merged 1 commit into
mainfrom
weekendsuperhero/scene_compositing
Oct 21, 2026
Merged

WeekendSuperhero merged 1 commit into
mainfrom
weekendsuperhero/scene_compositing

Conversation

@WeekendSuperhero

Copy link
Copy Markdown
Collaborator

Summary

The horizon scenes were the biggest GPU cost on pages that use them, and the cause was layer count, not drawing.

  • Why: each scene's sun and moon are compositing layers on purpose. Moved on the page's own layer, a masked, filtered disc left half of itself painted behind (the note on .sw-sun). WebKit gives every later sibling that overlaps a composited layer a layer of its own, so the z-order holds. Everything drawn in front of the disc became a separate full-viewport layer, and the window server re-applied each one's mask and blur on every frame that the page above changed. Counted from WebKit's own layer tree, static dune had 52 of them.
  • Fix: everything in front of a disc now sits in one .scene-group (isolation: isolate, same box as the scene). That stacking context paints as one layer, with its masks and blurs drawn once. The same wrapper goes around any still run over a moving part: forest's rays and middle ridge over a fog bank, aurora's sun path and haze over the water, and aurora's sky patch over the curtains. Only what moves gets a layer of its own.
  • Aurora: each curtain moves as a box that carries only its transform and opacity, and an .au-paint child draws the rays with their blur, masks and clip, so those go into the layer once. The sun path, which moves with the disc, gets the disc's treatment the same way.
  • No visual change: nothing is baked into images, so tint, light/dark, disc placement, sand and speed all work as before.

Results

Window-server GPU time in WebKit (macOS, 1400×900, dark, a translucent panel sliding over the scene, only Finder visible, 10 s per run):

Scene Before After
Empty page (floor) 7% 7%
Dune, static 24% 7%
Dune, animated 52% 17%
Aurora, animated 42% 19%
Clouds, animated 14% (20% device busy) 13% (9% device busy)
Forest, static / animated 10% / 11% 7% / 11%
Synthwave, static / animated 10% / 17% 9% / 17%

Extra layers while static, counted from WebKit's layer tree (_caLayerTreeAsText), including clip layers:

Dune Forest Synthwave, moonrise Clouds Aurora
52 → 7 25 → 7 23 → 19 15 → 7 11 → 7

Notes

  • Synthwave and moonrise are unchanged. Their scrolling 3D grid floor needs masked layers of its own. Fixing that would mean redrawing the floor, which is out of scope here.
  • Aurora CPU at small sizes: in a 1400px window, aurora's widest curtain, now drawn with its blur, is just wide enough to cross WebKit's tiling threshold. WebKit then spends about 5% of a core on it while it sways. In wider windows the old build tiles it too, and at 3440px both measure about 6%.
  • Sun path not GPU-measured: it got its own layer and paint child after the GPU run. It's covered by the snapshots and tests below.
  • Overlap with feat: add prebaked glass backdrop for performance #28: both PRs change the generated public/r/background-controller.json. Whichever merges second needs bun run registry:check again.

Test plan

  • 48 WebKit snapshots (every scene, light and dark, both disc sides, animated scenes frozen at the same instant) are pixel-identical to main's build.
  • bun run lint, bun run typecheck, bun run test (311 tests), bun run registry:check
  • New tests in tests/pattern-background.test.tsx pin the group and the paint children. Both fail when either structure is deliberately broken.
  • Look over the scenes on the preview deployment, including moving the disc and switching light/dark on aurora by day.

Each horizon scene's sun and moon are compositing layers on purpose: moved
on the page's own layer, a masked, filtered disc left half of itself painted
behind. WebKit gives every later sibling that overlaps a composited layer a
layer of its own, so everything drawn in front of the disc was a separate
full-viewport layer whose mask and blur the window server re-applied on
every frame the page above it changed. Static dune had 52 of them.

Everything in front of a disc now sits in one .scene-group (an isolated
stacking context, painted as one layer), as does any still run over a
moving part. Aurora's curtains and sun path move as a box that carries only
its transform and opacity, with a paint child that draws the blur, masks
and clip into the layer once.

Window-server GPU time in WebKit (1400x900, a panel sliding over the scene):
static dune 24% -> 7% (an empty page measures 7%), animated dune 52% -> 17%,
animated aurora 42% -> 19%. All 48 WebKit snapshots (every scene, light and
dark, both disc sides, animated scenes frozen at one instant) are pixel
identical to the old build's.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Deploying sistine with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1e8a52c
Status: ✅  Deploy successful!
Preview URL: https://ba955012.sistine.pages.dev
Branch Preview URL: https://weekendsuperhero-scene-compo.sistine.pages.dev

View logs

@WeekendSuperhero
WeekendSuperhero merged commit 44e38df into main Oct 21, 2026
9 checks passed
@WeekendSuperhero
WeekendSuperhero deleted the weekendsuperhero/scene_compositing branch October 21, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant