fix(ui-map): cut the visibility fog with an inverse stencil mask (Safari pan perf, stage 2)
Tests · UI / test (push) Successful in 1m57s
Tests · UI / test (pull_request) Successful in 1m56s

Stage 1 (render-on-demand) removed the idle / whole-system freeze, but
panning a loaded map with "visible hyperspace" on stayed heavy in Safari:
the fog still cut its visibility holes by opaque overpaint — on KNNTS041
that is ~260 near-world-sized opaque circles blended over the fog every
rendered frame, a fill-rate cliff for Safari's WebGPU / Apple's tile-based
GPU.

Replace the overpaint with an INVERSE stencil mask: setVisibilityFog now
draws the FOG_COLOR rectangle(s) into fogLayer and collects the visibility
circles into one Graphics set as fogLayer.setMask({ mask, inverse: true }),
so the fog shows everywhere except the union of the circles. Per-frame cost
drops from dozens of blended opaque circle fills to one rect fill + a
stencil pass (no colour writes), which Apple's TBDR GPU handles cheaply,
and the fog stays fully vector — crisp at any zoom.

fogPaintOps and its unit tests are unchanged (the circle ops now feed the
mask instead of an overpaint). Verified with a high-contrast screenshot
during development (fog field with a correct circle-union hole) plus the
existing fog / render-on-demand e2e green on chromium + webkit.

Docs: renderer.md fog section + PLAN.md Phase 29 decision 9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-20 16:53:54 +02:00
parent 44c18c3ef4
commit a08f4f55b0
4 changed files with 120 additions and 53 deletions
+20 -3
View File
@@ -3293,9 +3293,26 @@ Decisions:
is removed so a released drag stops instantly and the viewport
goes idle immediately. `RendererHandle.getRenderCount()` (mirrored
on `__galaxyDebug` as `getMapRenderCount`) backs the e2e
assertions. If Safari pan is still heavy after this, stage 2 cuts
the overpaint itself (an inverse stencil mask of the circle union,
kept vector so the map stays crisp at any zoom).
assertions. The owner confirmed this removed the idle / whole-system
freeze, but panning a loaded map with the fog on stayed heavy in
Safari (the overpaint fill-rate was untouched) — addressed in
decision 9.
9. **Inverse stencil mask for the fog (fog perf, stage 2).** The fog's
visibility holes were previously cut by opaque background-coloured
circle overpaint — on a large report dozens of near-world-sized
opaque circles repainted every frame, the fill-rate cliff that kept
Safari's WebGPU pan heavy after stage 1. Stage 2 replaces the
overpaint with an INVERSE stencil mask: `setVisibilityFog` draws the
`FOG_COLOR` rectangle(s) into `fogLayer` and collects the visibility
circles into one `Graphics` set as
`fogLayer.setMask({ mask, inverse: true })`, so the fog shows
everywhere except the union of the circles. Per-frame cost drops to
one rectangle fill plus a stencil pass (no blended colour writes,
friendly to Apple's tile-based GPU), and the fog stays fully vector
— crisp at any zoom. `fogPaintOps` and its unit tests are unchanged
(the circle ops now feed the mask instead of an overpaint); the
rendered result is verified by a high-contrast screenshot during
development plus the existing fog / render-on-demand e2e.
## Phase 30. Calculator Tab