glass dirty-skip: fold palette generation into the repaint token
Latent flaw in thec9e25e5dirty-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 (recordsf3d27f5+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:
@@ -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
|
||||
|
||||
@@ -2104,7 +2104,19 @@ static unsigned long
|
||||
if (svga == NULL) svga = static_cast<SVGA16*>(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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
//########################################################################
|
||||
|
||||
Reference in New Issue
Block a user