From 47648d61be59a83339030f9ffa8a00936be14540 Mon Sep 17 00:00:00 2001 From: Cyd Date: Mon, 10 Aug 2026 10:56:49 -0500 Subject: [PATCH] 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 --- context/glass-cockpit.md | 26 ++++++++++++++++++++++++++ engine/MUNGA_L4/L4GLASSWIN.cpp | 12 ++++++++++++ engine/MUNGA_L4/L4VB16.cpp | 23 +++++++++++++++++++++++ engine/MUNGA_L4/L4VB16.h | 14 ++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/context/glass-cockpit.md b/context/glass-cockpit.md index 6254292..53e75a2 100644 --- a/context/glass-cockpit.md +++ b/context/glass-cockpit.md @@ -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 diff --git a/engine/MUNGA_L4/L4GLASSWIN.cpp b/engine/MUNGA_L4/L4GLASSWIN.cpp index de29d73..fc3f8df 100644 --- a/engine/MUNGA_L4/L4GLASSWIN.cpp +++ b/engine/MUNGA_L4/L4GLASSWIN.cpp @@ -2104,7 +2104,19 @@ static unsigned long if (svga == NULL) svga = static_cast(p->graphicsDisplay); } if (svga != NULL && combined != 0) + { token ^= svga->PlaneChecksum(combined); + // PALETTE-ANIMATED content (2026-08-10): a palette-expanding window + // (the radar; MFDs under BT_GLASS_MFD_PAL) changes colour with ZERO + // pixel writes -- the ColorMapper family (armor rosette tints, the + // adpal/adpal2 damage flash) writes CLUT entries. Fold the palette + // write generation in so those changes repaint; mono-tint windows + // don't read the palette and keep their pixel-only token. + static int sPalTok = -1; + if (sPalTok < 0) sPalTok = getenv("BT_GLASS_MFD_PAL") ? 1 : 0; + if (w->monoTint < 0 || sPalTok) + token = (token ^ svga->PaletteGeneration()) * 16777619UL; + } } // Lamps: each button's RENDERED brightness + held/latched, so a flash toggle or a diff --git a/engine/MUNGA_L4/L4VB16.cpp b/engine/MUNGA_L4/L4VB16.cpp index cd108c4..6e5808d 100644 --- a/engine/MUNGA_L4/L4VB16.cpp +++ b/engine/MUNGA_L4/L4VB16.cpp @@ -5445,6 +5445,7 @@ SVGA16::SVGA16( BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index); for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw mDevSurfaceTex[_i] = NULL; + paletteGeneration = 0; // GLASS dirty-skip palette tracking //STUBBED: VIDEO RB 1/15/07 # if defined(DEBUG) Tell("SVGA16::SVGA16()\n"); @@ -7190,6 +7191,7 @@ void svga_palette->paletteData.Valid = True; svga_palette->modified = True; + ((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild) Check_Fpu(); } @@ -7234,6 +7236,13 @@ void SVGA16Palette *svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]); + // GLASS dirty-skip: track whether this write CHANGES anything -- the + // ColorMapper family calls this every Execute (the adpal/adpal2 flash + // alternates palettes even at zero damage, usually writing identical RGB), + // so bump the generation only on a real change or every glass window that + // palette-expands would repaint every pump for nothing. + int palette_changed = 0; + //------------------------------------------- // If any of the ...TransparentZero modes are used, // leave color zero undefined for this bit group by @@ -7275,21 +7284,31 @@ void { case RedChannel: case RedChannelTransparentZero: + if (destination_triplet->Red != source_triplet->Red) + palette_changed = 1; destination_triplet->Red = source_triplet->Red; break; case GreenChannel: case GreenChannelTransparentZero: + if (destination_triplet->Green != source_triplet->Green) + palette_changed = 1; destination_triplet->Green = source_triplet->Green; break; case BlueChannel: case BlueChannelTransparentZero: + if (destination_triplet->Blue != source_triplet->Blue) + palette_changed = 1; destination_triplet->Blue = source_triplet->Blue; break; case AllChannels: case AllChannelsTransparentZero: + if (destination_triplet->Red != source_triplet->Red + || destination_triplet->Green != source_triplet->Green + || destination_triplet->Blue != source_triplet->Blue) + palette_changed = 1; *destination_triplet = *source_triplet; break; } @@ -7310,6 +7329,8 @@ void svga_palette->paletteData.Valid = True; svga_palette->modified = True; + if (palette_changed) + ((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip Check_Fpu(); } @@ -7413,6 +7434,7 @@ void svga_palette->paletteData.Valid = True; svga_palette->modified = True; + ((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild) # if defined(TESTPALETTE) std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " << @@ -7523,6 +7545,7 @@ void svga_palette->paletteData.Valid = True; svga_palette->modified = True; + ((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild) Check_Fpu(); } diff --git a/engine/MUNGA_L4/L4VB16.h b/engine/MUNGA_L4/L4VB16.h index bf7d6e1..1a99a9f 100644 --- a/engine/MUNGA_L4/L4VB16.h +++ b/engine/MUNGA_L4/L4VB16.h @@ -382,6 +382,15 @@ protected: SVGA16Palette palette[PaletteCount]; + // GLASS dirty-skip (2026-08-10): bumped by the palette writers + // (L4GraphicsPort::BuildSecondaryColor on a REAL entry change; the full + // palette rebuilds unconditionally) so the glass repaint token can see + // palette-only animation -- the ColorMapper family (armor rosette tints, + // damage flash) changes COLORS with zero pixel writes, which the pixel + // checksum alone can never catch. + unsigned long + paletteGeneration; + private: int NUMGAUGEWINDOWS; HWND *gaugeWindows; @@ -425,6 +434,11 @@ public: // glass repaint pump re-blit ONLY the windows whose plane actually changed // (idle MFDs / static panels skip; the sweeping radar keeps updating). unsigned long PlaneChecksum(int mask) const; + + // GLASS dirty-skip: the palette write generation (see paletteGeneration). + // Folded into the repaint token of palette-expanding windows so ColorMapper + // palette animation (armor tints / damage flash) repaints without pixel churn. + unsigned long PaletteGeneration() const { return paletteGeneration; } }; //########################################################################