From 50341b4a6620409c538b8f93fb36dbcd5e535a2e Mon Sep 17 00:00:00 2001 From: Cyd Date: Mon, 27 Jul 2026 22:43:13 -0500 Subject: [PATCH] BT410 5.3.41: the plane-write hunt -- two more hypotheses dead, defect bounded PlayerStatus (dirty-gated, blits an 8-bit pixmap -- but the widget map for a DIRTY run contains no playerStatus at all; it belongs to cameraInit and never exists in a mech cockpit) and heat-driven redraw (a BT_FORCE_FIRE run came back clean). Seven hypotheses killed by measurement so far. It is a heisenbug: dirty on ~3 runs in 25, and every run since instrumenting has been clean. The pattern tracks STARTUP LOG VOLUME -- the three dirty runs all had the lighter conf, and a chatty trace slows init enough to suppress it. The blit trace is therefore narrowed to fire only for pixmaps landing on a non-palette port, which is near-silent when nothing is wrong. That narrowed trace caught real traffic: 17x17 PixelMap8 blits onto Mfd1/2/3 -- 8-bit pixmaps on single-bit planes. They render correctly, so the library thresholds them; not the culprit but the right bug class. Also banked a hunting recipe, because two detector traps cost runs: Eng1 == 0 means the cockpit is not up (not dirty), and an early capture during mission start caught a full 3-D cockpit frame with the gauges at pod positions (Eng1 62927) that did not recur over the next minute. BTDPL.INI is in the mount and DPLRenderer is linked, so the dPL path may emit a frame during launch even with L4VIDEO=OFF -- first evidence the 3-D path produces anything, and explicitly NOT a claim that btl4vid works. Co-Authored-By: Claude Fable 5 --- .../source410/BT_L4/GAUGE-BLOCK.NOTES.md | 41 +++++++++++++++++++ restoration/source410/MUNGA/GRAPH2D.CPP | 37 +++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md index 6ffd0b04..af209788 100644 --- a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md +++ b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md @@ -1020,3 +1020,44 @@ correctly to 3/5/7). Suspect the tile/strip blit for VertTwoPartBar-family widgets writing tile pixel VALUES rather than using them as a mask with the port colour -- the same widget draws correctly on the MFD heads, so it is specific to this port or this call site. + +-------------------------------------------------------------------------------- +5.3.41 -- the hunt for the plane-write defect: not caught, but bounded +-------------------------------------------------------------------------------- +Two more hypotheses died, and the defect gained a nasty property. + + 5. PlayerStatus blitting `recoloredMech` through DrawPixelMap8 -- it is + dirty-gated (change-driven, matching the intermittency) and DOES blit an + 8-bit pixmap. KILLED: the widget map for a DIRTY run contains no + playerStatus at all. It is built by cameraInit (the observer page) and + never exists in a mech cockpit. + 6. Heat driving the coolant values and thus the change-driven redraw. + KILLED: a run with BT_FORCE_FIRE=1 (weapons firing, heat climbing) came + back clean. + +IT IS A HEISENBUG. Rate across this session: dirty on roughly 3 runs in 25. +Every run since instrumentation was added has been clean -- and the pattern +lines up with STARTUP LOG VOLUME: runs with the attribute log or the blit +trace enabled (hundreds of DEBUG_STREAM lines during init) are clean; the +three dirty runs all had the lighter conf. A chatty trace slows startup +enough to suppress it, which is why the blit trace was narrowed to fire only +for pixmaps landing on a NON-palette port (near-silent in the clean case). +That narrowed trace did catch real traffic -- 17x17 PixelMap8 blits onto +Mfd1/Mfd2/Mfd3, i.e. 8-bit pixmaps on single-bit planes -- but those render +correctly (the MFD heads are within ~150px on clean runs), so the library +evidently thresholds them. Not the culprit, but the right bug class to keep +watching. + +HUNTING RECIPE FOR NEXT TIME (two traps cost runs here): + * Detect dirty as Eng1 in (17981, 25000]. Eng1 == 0 means the cockpit is + not up yet, and my first detector read that as "dirty". + * Require the reading to be STABLE across two captures ~20s apart. An + early capture during mission start caught a completely different frame -- + a full 3-D cockpit view, purple sky, canopy struts, HUD reticle, with the + gauges at POD positions rather than plane-packed (Eng1 read 62927). It + did not recur across 8 samples over the following minute, so it is an + early-run transient, not a persistent render. Worth a look on its own + though: BTDPL.INI is in the mount and the engine's DPLRenderer is linked, + so the dPL path may be producing a frame during the launch sequence even + with L4VIDEO=OFF. That is the first evidence this session that the 3-D + path emits anything at all -- do not confuse it with btl4vid being done. diff --git a/restoration/source410/MUNGA/GRAPH2D.CPP b/restoration/source410/MUNGA/GRAPH2D.CPP index 309033ed..07e586db 100644 --- a/restoration/source410/MUNGA/GRAPH2D.CPP +++ b/restoration/source410/MUNGA/GRAPH2D.CPP @@ -2588,6 +2588,43 @@ void if (ClipImage(&sourceBounds, &position, pixelmap->Data.Size)) { + // + // BLIT TRACE (env BT_VIS_BLIT): every 8-bit pixmap that reaches + // a port, with the port it lands on and the rect it covers. + // Hunting the write that puts raw packed bytes into the + // plane-packed gauge buffer. + // + // + // Log ONLY pixmap blits landing on a port that is NOT one of the + // two palette ports (sec / overlay). An 8-bit pixmap reaching a + // single-bit plane is the thing being hunted, and filtering here + // keeps the trace near-silent -- which matters, because the + // defect is a STARTUP TIMING race and a chatty trace suppresses + // it (six consecutive clean runs once logging was added). + // + if (getenv("BT_VIS_BLIT") != NULL + && graphicsPort != NULL && graphicsPort->name != NULL + && stricmp(graphicsPort->name, "sec") != 0 + && stricmp(graphicsPort->name, "overlay") != 0) + { + static int blitSamples = 0; + if (blitSamples < 400) + { + ++blitSamples; + DEBUG_STREAM << "[blit] port='" + << ((graphicsPort != NULL && graphicsPort->name != NULL) + ? graphicsPort->name : "(null)") + << "' at " << position.x << "," << position.y + << " src " << sourceBounds.bottomLeft.x << "," + << sourceBounds.bottomLeft.y << ".." + << sourceBounds.topRight.x << "," + << sourceBounds.topRight.y + << " size " << pixelmap->Data.Size.x << "x" + << pixelmap->Data.Size.y + << " opaque=" << (opaque ? 1 : 0) + << " rot=" << rotation << "\n" << flush; + } + } graphicsPort-> DrawPixelMap8( currentOperation,