glass dirty-skip: fold palette generation into the repaint token

Latent flaw in the c9e25e5 dirty-skip (now in master): the token hashes plane
PIXELS + lamp state only, but the ColorMapper gauge family (armor rosette
tints, the adpal/adpal2 damage flash) animates by writing CLUT entries with
ZERO pixel churn -- so a glass window whose plane is pixel-static held stale
colors while the D3D surround (which re-expands every frame) tracked live.

New SVGA16::paletteGeneration, bumped by the palette writers:
  - BuildSecondaryColor: only on a REAL entry change (the flash alternates
    palettes every Execute writing identical RGB at zero damage -- an
    unconditional bump would repaint every palette-expanding window every
    pump for nothing);
  - BuildSecondaryPalette / BuildAuxiliaryPalette / BlankPalette (full
    rebuilds): unconditionally.
GlassWindowToken folds it in for palette-expanding windows (monoTint < 0, or
all windows under BT_GLASS_MFD_PAL); mono-tint windows don't read the palette
and keep their pixel-only token.

KB: glass-cockpit.md gains the repaint-pump section (records f3d27f5 +
c9e25e5 + this).  Companion branch sec-phantom-fix carries the idx-254
translation-table fix -- disjoint hunks, merges in either order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-10 10:56:49 -05:00
co-authored by Claude Opus 4.8
parent 773595b347
commit f65b191181
4 changed files with 75 additions and 0 deletions
+26
View File
@@ -203,6 +203,32 @@ appears in the cfg like any panel and honours `,noframe`. Verified: a drag wrote
at 321,222. NB the plasma window blits directly every frame (`GetDC`+`StretchDIBits`), so unlike
the panels it has no `WM_TIMER` focus-throttle to worry about.
## Glass-panel repaint pump — perf + dirty-skip (2026-08-09/10) [T2 measured]
The exploded per-display windows are pure CPU/GDI (`ExpandPlaneToBGRA` +
`StretchDIBits`), repainted synchronously on the MAIN render thread by
`BTGlassPanels_Tick` (~16 Hz). Three layered changes after a playtester reported
~20 fps in panels mode vs ~130 fps in the surround:
1. **HALFTONE → COLORONCOLOR** (`f3d27f5`, in master): the `HALFTONE` stretch
(GDI's per-output-pixel resample) × 7 windows per pump was the primary sink;
nearest is crisper for the low-res MFD pixels anyway. `BT_GLASS_SMOOTH=1` restores.
2. **Per-window dirty-skip** (`c9e25e5`, in master): each window carries a change
token = FNV over `SVGA16::PlaneChecksum(mask)` (the shared pixelBuffer masked to
every port that can feed the window) + each button's RENDERED lamp brightness +
held/latch. The pump re-blits only windows whose token moved: ~31 pumps/2s →
4-15 window repaints vs 217-224 always-on. `BT_GLASS_DIRTY=1` logs the tally.
3. **Palette generation in the token** (branch `glass-palette-token`): the
ColorMapper family (armor rosette tints, adpal/adpal2 damage flash) animates by
CLUT writes with ZERO pixel churn — invisible to a pixel checksum.
`SVGA16::paletteGeneration` is bumped by the palette writers
(`BuildSecondaryColor` only on a REAL entry change, since the flash alternates
palettes every Execute writing identical RGB at zero damage; full rebuilds
unconditionally) and folded into the token for palette-expanding windows
(`monoTint < 0`, or all under `BT_GLASS_MFD_PAL`). Without it the glass radar
held stale armor tints between pixel repaints while the D3D surround (which
re-expands every frame) tracked live.
**Turning it OFF: `L4PLASMA=NONE` (also `OFF`/`0`, 2026-08-06) [T2].** `L4GREND` creates a
marquee whenever `L4PLASMA` is set at all (`SCREEN` → the desktop window, anything else → a real
`PlasmaDisplay` on that serial port), and the GLASS profile force-defaults it to `SCREEN` — so on