Author SHA1 Message Date
CydandClaude Opus 4.8 5737ed65d8 plasma window: initialize the display area (junk shown until the callsigns)
The desktop plasma marquee showed heap garbage from boot until the gauge
renderer first drew the callsigns.  Root cause [T0]: PixelMap8(w,h) allocates
its pixel bytes UNCLEARED (GRAPH2D.cpp `new Byte[w*h]`, no memset) and
Video8BitBuffered never clears them either -- only changedLine[] is zeroed.
The real pod never showed it: the serial PlasmaDisplay transfers only CHANGED
lines, so the garbage never left the buffer and the hardware marquee sat at
its power-on blank.  PlasmaWindow::Update however blits the WHOLE buffer
every frame, exposing the junk for the window-up-to-first-draw gap (seconds
on slower rigs).

Fix: PlasmaWindow ctor memsets the pixelBuffer to index 0 (the near-black
background) -- the window comes up blank like the hardware did.  Glass-layer
only; the serial pod path is untouched.  Verified: glass boot shows a clean
marquee from the first visible frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-10 21:54:00 -05:00
5 changed files with 16 additions and 75 deletions
-26
View File
@@ -203,32 +203,6 @@ 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 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. 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 **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 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 `PlasmaDisplay` on that serial port), and the GLASS profile force-defaults it to `SCREEN` — so on
-12
View File
@@ -2104,19 +2104,7 @@ static unsigned long
if (svga == NULL) svga = static_cast<SVGA16*>(p->graphicsDisplay); if (svga == NULL) svga = static_cast<SVGA16*>(p->graphicsDisplay);
} }
if (svga != NULL && combined != 0) if (svga != NULL && combined != 0)
{
token ^= svga->PlaneChecksum(combined); 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 // Lamps: each button's RENDERED brightness + held/latched, so a flash toggle or a
+16
View File
@@ -54,6 +54,22 @@ PlasmaWindow::PlasmaWindow():
} }
blitBuffer = new unsigned long[plasmaWidth * plasmaHeight]; blitBuffer = new unsigned long[plasmaWidth * plasmaHeight];
memset(blitBuffer, 0, plasmaWidth * plasmaHeight * sizeof(unsigned long)); memset(blitBuffer, 0, plasmaWidth * plasmaHeight * sizeof(unsigned long));
//
// INITIALIZE THE DISPLAY AREA (2026-08-10). PixelMap8(w,h) allocates its
// pixel bytes UNCLEARED (GRAPH2D.cpp: `new Byte[w*h]`, no memset), so the
// 128x32 plasma buffer starts as heap garbage. The real pod never showed
// it: the serial PlasmaDisplay transfers only CHANGED lines (changedLine[]
// is zeroed by the base ctor), so the garbage never left the buffer and
// the hardware marquee sat at its power-on blank. This desktop window
// blits the WHOLE buffer every frame, so the junk was visible from boot
// until the gauge renderer first drew the callsigns. Clear to index 0
// (the near-black background) -- the window comes up blank like the
// hardware did.
//
if (pixelBuffer != NULL && pixelBuffer->Data.MapPointer != NULL)
memset(pixelBuffer->Data.MapPointer, 0, plasmaWidth * plasmaHeight);
DEBUG_STREAM << "[plasmawin] desktop plasma display up (scale x" DEBUG_STREAM << "[plasmawin] desktop plasma display up (scale x"
<< scale << ")\n" << std::flush; << scale << ")\n" << std::flush;
} }
-23
View File
@@ -5445,7 +5445,6 @@ SVGA16::SVGA16(
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index); BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw
mDevSurfaceTex[_i] = NULL; mDevSurfaceTex[_i] = NULL;
paletteGeneration = 0; // GLASS dirty-skip palette tracking
//STUBBED: VIDEO RB 1/15/07 //STUBBED: VIDEO RB 1/15/07
# if defined(DEBUG) # if defined(DEBUG)
Tell("SVGA16::SVGA16()\n"); Tell("SVGA16::SVGA16()\n");
@@ -7191,7 +7190,6 @@ void
svga_palette->paletteData.Valid = True; svga_palette->paletteData.Valid = True;
svga_palette->modified = True; svga_palette->modified = True;
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild)
Check_Fpu(); Check_Fpu();
} }
@@ -7236,13 +7234,6 @@ void
SVGA16Palette SVGA16Palette
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]); *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, // If any of the ...TransparentZero modes are used,
// leave color zero undefined for this bit group by // leave color zero undefined for this bit group by
@@ -7284,31 +7275,21 @@ void
{ {
case RedChannel: case RedChannel:
case RedChannelTransparentZero: case RedChannelTransparentZero:
if (destination_triplet->Red != source_triplet->Red)
palette_changed = 1;
destination_triplet->Red = source_triplet->Red; destination_triplet->Red = source_triplet->Red;
break; break;
case GreenChannel: case GreenChannel:
case GreenChannelTransparentZero: case GreenChannelTransparentZero:
if (destination_triplet->Green != source_triplet->Green)
palette_changed = 1;
destination_triplet->Green = source_triplet->Green; destination_triplet->Green = source_triplet->Green;
break; break;
case BlueChannel: case BlueChannel:
case BlueChannelTransparentZero: case BlueChannelTransparentZero:
if (destination_triplet->Blue != source_triplet->Blue)
palette_changed = 1;
destination_triplet->Blue = source_triplet->Blue; destination_triplet->Blue = source_triplet->Blue;
break; break;
case AllChannels: case AllChannels:
case AllChannelsTransparentZero: 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; *destination_triplet = *source_triplet;
break; break;
} }
@@ -7329,8 +7310,6 @@ void
svga_palette->paletteData.Valid = True; svga_palette->paletteData.Valid = True;
svga_palette->modified = True; svga_palette->modified = True;
if (palette_changed)
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip
Check_Fpu(); Check_Fpu();
} }
@@ -7434,7 +7413,6 @@ void
svga_palette->paletteData.Valid = True; svga_palette->paletteData.Valid = True;
svga_palette->modified = True; svga_palette->modified = True;
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild)
# if defined(TESTPALETTE) # if defined(TESTPALETTE)
std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " << std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " <<
@@ -7545,7 +7523,6 @@ void
svga_palette->paletteData.Valid = True; svga_palette->paletteData.Valid = True;
svga_palette->modified = True; svga_palette->modified = True;
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild)
Check_Fpu(); Check_Fpu();
} }
-14
View File
@@ -382,15 +382,6 @@ protected:
SVGA16Palette SVGA16Palette
palette[PaletteCount]; 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: private:
int NUMGAUGEWINDOWS; int NUMGAUGEWINDOWS;
HWND *gaugeWindows; HWND *gaugeWindows;
@@ -434,11 +425,6 @@ public:
// glass repaint pump re-blit ONLY the windows whose plane actually changed // glass repaint pump re-blit ONLY the windows whose plane actually changed
// (idle MFDs / static panels skip; the sweeping radar keeps updating). // (idle MFDs / static panels skip; the sweeping radar keeps updating).
unsigned long PlaneChecksum(int mask) const; 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; }
}; };
//######################################################################## //########################################################################