BT410 5.3.83: MFD-bleed hardening -- the uninitialised half of the 6-bit colour head's translation table
Operator report: "artifacts bleeding over from the radar display into the
MFDs, I have noticed this in the BT411 builds as well, but does not happen
in the original."
MECHANISM (proven from the archive source, not inferred):
The ten cockpit heads are not ten framebuffers -- L4GAUGE.CFG gives each
port a BIT MASK into ONE 16-bit buffer. The colour head is six bits
(sec, 0x003F); the MFDs are bits 8-15 of the same words. A colour-head
defect can therefore only ever surface as MFD garbage, which is exactly
the reported shape.
The two translation-table builders in L4VB16.CPP are asymmetric:
BuildSecondaryTranslation (:5419) writes ONLY the active-bit
combinations -- 1<<numberOfBits entries, so 64 of int
translationTable[256] for this port. Entries 64..255 are never
written.
BuildAuxiliaryTranslation (:5443) nests IncrementInactive inside
IncrementActive and so fills all 256 -- which is why the mono MFD
ports are safe by construction and must NOT be touched.
DrawPixelMap8 (:2708) indexes that table with an 8-BIT source pixel, and
DrawPoint ORs the result in UNMASKED. An out-of-range index therefore
ORs whatever the heap left at translationTable[n] straight into other
ports' planes. The colour head's own art carries out-of-range
sentinels: AVACRIT.PCC is 94% <= 63 with 6% at exactly 231, BTSEC1.PCX
99.8% <= 63 with 0.2% at 254. Transparent draws skip them; OPAQUE draws
put every one through the table.
That it depends on heap CONTENTS rather than drawing logic explains both
the intermittency and why BT411 shows it too: both reconstructions
inherit the same latent read from the archive.
THE CHANGE, via the sanctioned source410-shadows-CODE seam (CODE/ is
untouched): a verbatim copy of CODE/RP/MUNGA_L4/L4GREND.CPP with one
difference -- port construction builds a BTL4GraphicsPort, whose ctor
zeroes translationTable[1<<numberOfBits .. 255] for SECONDARY ports only.
The guard (bit_mask & 0xFF) != 0 is literally the test
BuildSecondaryTranslation itself asserts on. numberOfBits is a popcount
set in the base ctor, so the subclass body sees 6 and the later
SetSecondaryPalette -> BuildSecondaryTranslation refills 0..63 over the
top without disturbing the zeroed tail.
WHAT IS AND IS NOT PROVEN -- the honest scoreboard:
PROVEN non-regressive. Gauge A/B, three boots: unfixed x2 and fixed x1
score identically on every head (Mfd1 132/0, Mfd2 37/7, Mfd3 23/0,
Eng1/2/3 and Comm exact), against a ~6px across-boot noise floor
established by re-booting the same binary.
NOT PROVEN to cure the symptom. The A/B rig captures the boot/attract
cockpit, where the MFD heads already show ZERO extra pixels -- there is
no bleed there to remove. The operator's report is in-mission with the
radar live. This is a hardening change against a real uninitialised
read that matches the symptom exactly; the cure needs a mission-state
capture to confirm.
METHOD NOTE: the first "fixed" capture appeared to regress the MFDs by
~2700px and nearly got the change reverted. It was a bad grab caught
mid-draw -- rebuild-and-rerun matched the unfixed numbers exactly. The
two-agreeing-runs rule earned its keep; a single run would have thrown
away a correct change. Worth noting the noise floor that mattered was
ACROSS boots, not the within-boot floor measured first.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,958 @@
|
||||
//===========================================================================//
|
||||
// File: l4grend.cc //
|
||||
// Project: MUNGA Brick: LBE4 Gauge Renderer //
|
||||
// Contents: //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// 02/10/95 CPB Initial coding. //
|
||||
// 04/12/95 CPB Moved BuildGraphicsPorts in from RP/BTL4GRND.CPP. //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. All rights reserved //
|
||||
// PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#include <mungal4.hpp>
|
||||
#pragma hdrstop
|
||||
|
||||
#if !defined(L4APP_HPP)
|
||||
# include <l4app.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(TIME_HPP)
|
||||
# include <time.hpp> // for profiling
|
||||
#endif
|
||||
|
||||
#if !defined(L4GREND_HPP)
|
||||
# include <l4grend.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(L4GAUGE_HPP)
|
||||
# include <l4gauge.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MISSION_HPP)
|
||||
# include <mission.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(L4PLASMA_HPP)
|
||||
# include <l4plasma.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(NOTATION_HPP)
|
||||
# include <notation.hpp>
|
||||
#endif
|
||||
|
||||
// #define LOCAL_TEST
|
||||
|
||||
#if defined(LOCAL_TEST)
|
||||
# define Test_Tell(n) cout << n
|
||||
#else
|
||||
# define Test_Tell(n)
|
||||
#endif
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// L4MethodDescription list
|
||||
//#############################################################################
|
||||
//
|
||||
MethodDescription
|
||||
ChainToPrevious = METHOD_DESCRIPTION_CHAIN(NULL); // Null:end of chain
|
||||
|
||||
MethodDescription
|
||||
*L4MethodDescription[] =
|
||||
{
|
||||
&MakeConfigMethodDescription,
|
||||
&MakeExternConfigMethodDescription,
|
||||
&BackgroundReconfig::methodDescription,
|
||||
&BackgroundLine::methodDescription,
|
||||
&BackgroundRect::methodDescription,
|
||||
&BackgroundFilledRect::methodDescription,
|
||||
&BackgroundPixelmap::methodDescription,
|
||||
&BackgroundBitmap::methodDescription,
|
||||
&NumericDisplayScalar::methodDescription,
|
||||
&NumericDisplaySpeed::methodDescription,
|
||||
&NumericDisplayInteger::methodDescription,
|
||||
&DigitalClock::methodDescription,
|
||||
&RankAndScore::methodDescription,
|
||||
&BarGraphSolidScalar::methodDescription,
|
||||
&BarGraphPixelMapScalar::methodDescription,
|
||||
&ColorState::methodDescription,
|
||||
&TwoState::methodDescription,
|
||||
&SegmentArcNormalized::methodDescription,
|
||||
&ChainToPrevious
|
||||
};
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// L4GaugeRenderer
|
||||
//#############################################################################
|
||||
//
|
||||
L4GaugeRenderer::L4GaugeRenderer():
|
||||
GaugeRenderer()
|
||||
{
|
||||
//
|
||||
//---------------------------------------------------------------------
|
||||
// Ensure that graphicsDisplay is NULL
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
graphicsDisplay = NULL;
|
||||
externalDisplay = NULL;
|
||||
|
||||
palettesAreActive = True;
|
||||
scrambleVideoFlag = False;
|
||||
|
||||
char
|
||||
*mode_string = getenv("L4GAUGE");
|
||||
//
|
||||
//---------------------------------------------------------------------
|
||||
// NOTE that L4Application::Initialize() searches for the environment
|
||||
// variable "L4GAUGE" to determine whether or not to create
|
||||
// a gauge renderer.
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
if (mode_string != NULL)
|
||||
{
|
||||
//
|
||||
//---------------------------------------------------------------------
|
||||
// Attempt to initialize external display
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
char
|
||||
*plasma_string = getenv("L4PLASMA");
|
||||
|
||||
if (plasma_string != NULL)
|
||||
{
|
||||
if (strnicmp(plasma_string, "COM2", 4) == 0)
|
||||
{
|
||||
Tell("Plasma display created on COM2\n");
|
||||
externalDisplay = new PlasmaDisplay(PCS_COM2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Tell("Plasma display created on COM1\n");
|
||||
externalDisplay = new PlasmaDisplay(PCS_COM1);
|
||||
}
|
||||
|
||||
if (externalDisplay != NULL)
|
||||
{
|
||||
Register_Object(externalDisplay);
|
||||
}
|
||||
}
|
||||
//
|
||||
//---------------------------------------------------------------------
|
||||
// Attempt to initialize main display
|
||||
//---------------------------------------------------------------------
|
||||
//-------------------------------------------------
|
||||
// Search notation file for specified resolution
|
||||
//-------------------------------------------------
|
||||
//
|
||||
NotationFile
|
||||
notation_file("GAUGE\\L4GAUGE.INI");
|
||||
|
||||
if (!notation_file.PageExists(mode_string))
|
||||
{
|
||||
Tell("L4GaugeRenderer::L4GaugeRenderer - '" <<
|
||||
mode_string <<
|
||||
"' not found in L4GAUGE.INI!\n"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
//-------------------------------------------------
|
||||
// Requested resolution exists, read parameters
|
||||
//-------------------------------------------------
|
||||
//
|
||||
int
|
||||
mode(0),
|
||||
width(640),
|
||||
height(480),
|
||||
page_size(64),
|
||||
gran(64),
|
||||
bpl(640*2),
|
||||
pfp(0),
|
||||
special(0),
|
||||
q(0);
|
||||
|
||||
q += notation_file.GetEntry(mode_string, "mode", &mode);
|
||||
q += notation_file.GetEntry(mode_string, "width", &width);
|
||||
q += notation_file.GetEntry(mode_string, "height", &height);
|
||||
q += notation_file.GetEntry(mode_string, "sizeInKB", &page_size);
|
||||
q += notation_file.GetEntry(mode_string, "granularityInKB", &gran);
|
||||
q += notation_file.GetEntry(mode_string, "bytesPerLine", &bpl);
|
||||
q += notation_file.GetEntry(mode_string, "pageFcnPtr", &pfp);
|
||||
q += notation_file.GetEntry(mode_string, "special", &special);
|
||||
|
||||
if (q == 8)
|
||||
{
|
||||
graphicsDisplay = new
|
||||
SVGA16(
|
||||
mode,
|
||||
width,
|
||||
height,
|
||||
page_size,
|
||||
gran,
|
||||
bpl,
|
||||
pfp,
|
||||
special
|
||||
);
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Register_Object(graphicsDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ~L4GaugeRenderer
|
||||
//#############################################################################
|
||||
//
|
||||
L4GaugeRenderer::~L4GaugeRenderer()
|
||||
{
|
||||
# if defined (LOCAL_TEST)
|
||||
Tell("L4GaugeRenderer::~L4GaugeRenderer()\n");
|
||||
# endif
|
||||
Check(this);
|
||||
|
||||
int i;
|
||||
|
||||
//----------------------------------------------------
|
||||
// Make sure all gauges removed first
|
||||
//----------------------------------------------------
|
||||
Remove(0);
|
||||
//----------------------------------------------------
|
||||
// Delete the graphics ports
|
||||
//----------------------------------------------------
|
||||
for(i=0; i<maximumGraphicsPorts; ++i)
|
||||
{
|
||||
if (graphicsPort[i] != NULL)
|
||||
{
|
||||
Check(graphicsPort[i]);
|
||||
Unregister_Object(graphicsPort[i]);
|
||||
delete graphicsPort[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Check(graphicsDisplay);
|
||||
Unregister_Object(graphicsDisplay);
|
||||
delete graphicsDisplay;
|
||||
}
|
||||
|
||||
if (externalDisplay != NULL)
|
||||
{
|
||||
Check(externalDisplay);
|
||||
Unregister_Object(externalDisplay);
|
||||
delete externalDisplay;
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// LocalEmergencyShutdown - restore the ABSOLUTE MINIMUM to get back
|
||||
// to the system!
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::LocalEmergencyShutdown()
|
||||
{
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
// It's an EMERGENCY: a 'verify' somewhere may have already failed.
|
||||
// Therefore don't check it, just kill it!
|
||||
delete graphicsDisplay;
|
||||
}
|
||||
|
||||
GaugeRenderer::LocalEmergencyShutdown();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// TestInstance
|
||||
//#############################################################################
|
||||
//
|
||||
Logical
|
||||
L4GaugeRenderer::TestInstance() const
|
||||
{
|
||||
return GaugeRenderer::TestInstance();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// StartImplementation
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::LoadMissionImplementation(Mission *mission)
|
||||
{
|
||||
Check(this);
|
||||
# if defined (LOCAL_TEST)
|
||||
Tell("L4GaugeRenderer::LoadMissionImplementation()\n");
|
||||
# endif
|
||||
|
||||
GaugeRenderer::LoadMissionImplementation(mission);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// StopImplementation
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::ShutdownImplementation()
|
||||
{
|
||||
Check(this);
|
||||
# if defined (LOCAL_TEST)
|
||||
Tell("L4GaugeRenderer::ShutdownImplementation()\n");
|
||||
# endif
|
||||
|
||||
// EVENTUALLY kill all interrupt-driven processes (if they exist)
|
||||
|
||||
GaugeRenderer::ShutdownImplementation();
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// SuspendImplementation
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::SuspendImplementation()
|
||||
{
|
||||
Check(this);
|
||||
# if defined (LOCAL_TEST)
|
||||
Tell("L4GaugeRenderer::SuspendImplementation()\n");
|
||||
# endif
|
||||
|
||||
GaugeRenderer::SuspendImplementation();
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ResumeImplementation
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::ResumeImplementation()
|
||||
{
|
||||
Check(this);
|
||||
# if defined (LOCAL_TEST)
|
||||
Tell("L4GaugeRenderer::ResumeImplementation()\n");
|
||||
# endif
|
||||
|
||||
GaugeRenderer::ResumeImplementation();
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ExecuteForeground
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::ExecuteForeground()
|
||||
{
|
||||
Check(this);
|
||||
//---------------------------------------
|
||||
// We must call the ancestral method!
|
||||
//---------------------------------------
|
||||
GaugeRenderer::ExecuteForeground();
|
||||
//---------------------------------------
|
||||
// Process special effects
|
||||
//---------------------------------------
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Check(graphicsDisplay);
|
||||
|
||||
ProcessVideoEffects();
|
||||
((SVGA16 *) graphicsDisplay)->UpdatePalette();
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ExecuteImplementation
|
||||
//#############################################################################
|
||||
//
|
||||
Logical
|
||||
L4GaugeRenderer::ExecuteBackgroundDisplayUpdate()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Logical
|
||||
result = False; // False == 'all done'
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Update external annunciator
|
||||
//
|
||||
// Allow the plasma display to continue if there's
|
||||
// more background time available.
|
||||
//
|
||||
// This method is still called if taskmode is set to
|
||||
// 'foreground'. If it's called in 'foreground' mode,
|
||||
// it really means that we're still waiting for foreground
|
||||
// to begin processing, so we might as well update the
|
||||
// external display (if needed).
|
||||
//--------------------------------------------------------
|
||||
if (externalDisplay != NULL)
|
||||
{
|
||||
Check(externalDisplay);
|
||||
result = externalDisplay->Update(False); // returns 'False' if done
|
||||
}
|
||||
//-------------------------------------------------------
|
||||
// Update display ONLY in 'copy' mode
|
||||
//--------------------------------------------------------
|
||||
if (taskMode == copy)
|
||||
{
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Check(graphicsDisplay);
|
||||
|
||||
if (graphicsDisplay->Update(False) == False)
|
||||
{
|
||||
//--------------------------------------------------------
|
||||
// Tell executionImplementation that we're basically done.
|
||||
//--------------------------------------------------------
|
||||
taskMode = foreground;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = True; // We're not done yet!
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// NotifyOfNewInterestingEntity
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::NotifyOfNewInterestingEntity(
|
||||
Entity *interesting_entity
|
||||
)
|
||||
{
|
||||
SET_GAUGE_RENDERER();
|
||||
Check(this);
|
||||
Check(interesting_entity);
|
||||
//----------------------------------------------
|
||||
// Make sure gaugeImage exists for entity
|
||||
//----------------------------------------------
|
||||
Check(warehousePointer);
|
||||
|
||||
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
|
||||
interesting_entity->GetResourceID()
|
||||
);
|
||||
//----------------------------------------------
|
||||
// Update gaugeRenderer
|
||||
//----------------------------------------------
|
||||
GaugeRenderer::NotifyOfNewInterestingEntity(interesting_entity);
|
||||
Check_Fpu();
|
||||
CLEAR_GAUGE_RENDERER();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// NotifyOfBecomingUninterestingEntity
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::NotifyOfBecomingUninterestingEntity(
|
||||
Entity *uninteresting_entity
|
||||
)
|
||||
{
|
||||
SET_GAUGE_RENDERER();
|
||||
Check(this);
|
||||
Check(uninteresting_entity);
|
||||
//----------------------------------------------
|
||||
// Remove gaugeImage if no longer used
|
||||
//----------------------------------------------
|
||||
Check(warehousePointer);
|
||||
|
||||
((L4Warehouse*)warehousePointer)->gaugeImageBin.Release(
|
||||
uninteresting_entity->GetResourceID()
|
||||
);
|
||||
//----------------------------------------------
|
||||
// Update gaugeRenderer
|
||||
//----------------------------------------------
|
||||
GaugeRenderer::NotifyOfBecomingUninterestingEntity(uninteresting_entity);
|
||||
Check_Fpu();
|
||||
CLEAR_GAUGE_RENDERER();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// BuildGraphicsPort
|
||||
//#############################################################################
|
||||
|
||||
//===========================================================================//
|
||||
// RECONSTRUCTION SHADOW (source410 overrides CODE per build410.sh srcfor).
|
||||
//
|
||||
// Verbatim copy of CODE/RP/MUNGA_L4/L4GREND.CPP with ONE change: the port
|
||||
// construction below builds a BTL4GraphicsPort instead of an L4GraphicsPort,
|
||||
// to close the MFD-bleed artifact. Everything else is the archive's.
|
||||
//
|
||||
// THE ARTIFACT (operator report 2026-07-29: "artifacts bleeding over from the
|
||||
// radar display into the MFDs ... I have noticed this in the BT411 builds as
|
||||
// well, but does not happen in the original"):
|
||||
//
|
||||
// * The colour head is SIX BITS (L4GAUGE.CFG:4395, sec mask 0x003F) and the
|
||||
// MFDs are bits 8-15 of the SAME 16-bit words -- one plane-packed
|
||||
// framebuffer, so a colour-head defect can only surface as MFD garbage.
|
||||
// * L4GraphicsPort::BuildSecondaryTranslation (L4VB16.CPP:5419) enumerates
|
||||
// only the ACTIVE-bit combinations, filling 1<<numberOfBits entries of
|
||||
// int translationTable[256] -- 64 for this port. Entries 64..255 are
|
||||
// never written. Note the asymmetry: BuildAuxiliaryTranslation nests an
|
||||
// IncrementInactive loop inside IncrementActive and so fills all 256,
|
||||
// which is why the MONO MFD ports are safe by construction.
|
||||
// * DrawPixelMap8 (L4VB16.CPP:2708) indexes that table with an 8-BIT source
|
||||
// pixel and DrawPoint ORs the result in UNMASKED, so an out-of-range
|
||||
// entry puts arbitrary bits into other ports' planes.
|
||||
// * The colour head's own art carries out-of-range SENTINELS: AVACRIT.PCC
|
||||
// is 94% <= 63 with 6% at exactly 231; BTSEC1.PCX is 99.8% <= 63 with
|
||||
// 0.2% at 254. Drawn transparent they are skipped; drawn OPAQUE every
|
||||
// pixel goes through the table.
|
||||
//
|
||||
// Why the original is PROBABLY clean (hypothesis, not proven): the port is
|
||||
// heap-allocated, and an allocation that happens to arrive zeroed makes those
|
||||
// reads colour 0 -- black, and invisible on a black head. Our heap has a
|
||||
// different history. That would also explain why the artifact is
|
||||
// INTERMITTENT and why the operator sees it in the BT411 builds too: it
|
||||
// depends on heap CONTENTS, not on any drawing logic, and both reconstructions
|
||||
// inherit the same latent read from the archive.
|
||||
//
|
||||
// The fix does not change drawing behaviour -- it only guarantees the
|
||||
// out-of-range case draws colour 0, i.e. contributes no bits under DrawPoint's
|
||||
// OR. That is the most conservative possible outcome and matches what the
|
||||
// shipped binary is observed to do.
|
||||
//
|
||||
// VERIFICATION STATUS (2026-07-29) -- read this before believing the fix:
|
||||
//
|
||||
// * The MECHANISM is proven from the archive source (the two builders'
|
||||
// asymmetric loops, above). That part is solid.
|
||||
// * The FIX IS PROVEN NON-REGRESSIVE on the gauge A/B rig: three boots
|
||||
// (unfixed x2, fixed x1) score identically on every head, Mfd1 132/0,
|
||||
// Mfd2 37/7, Mfd3 23/0, within a ~6px across-boot noise floor.
|
||||
// * The FIX IS NOT YET PROVEN TO CURE THE REPORTED SYMPTOM. The A/B rig's
|
||||
// capture point is the boot/attract cockpit, where the MFD heads already
|
||||
// show ZERO extra pixels -- there is no bleed there to remove. The
|
||||
// operator's report is from an in-mission cockpit with the radar live.
|
||||
// Confirming the cure needs a mission-state capture; until then this is a
|
||||
// hardening change against a real uninitialised read, not a demonstrated
|
||||
// cure.
|
||||
//
|
||||
// A first "fixed" capture appeared to REGRESS the MFDs by ~2700px. It was a
|
||||
// bad grab (caught mid-draw); the rebuild-and-rerun matched the unfixed
|
||||
// numbers exactly. Recorded here because the two-runs rule is what caught it.
|
||||
//===========================================================================//
|
||||
|
||||
class BTL4GraphicsPort :
|
||||
public L4GraphicsPort
|
||||
{
|
||||
public:
|
||||
BTL4GraphicsPort(
|
||||
Video16BitBuffered *graphics_display,
|
||||
const char *name,
|
||||
int rotation,
|
||||
int bit_mask,
|
||||
SVGA16::PaletteID palette_ID,
|
||||
L4GraphicsPort::ChannelEnableID channel_enable
|
||||
):
|
||||
L4GraphicsPort(
|
||||
graphics_display, name, rotation, bit_mask,
|
||||
palette_ID, channel_enable
|
||||
)
|
||||
{
|
||||
//
|
||||
// SECONDARY ports only. The low byte non-zero is exactly the
|
||||
// test BuildSecondaryTranslation asserts on
|
||||
// (Verify((bitMask & 0xFF) != 0)); an AUXILIARY port fills all
|
||||
// 256 entries itself and must not be touched.
|
||||
//
|
||||
if ((bit_mask & 0xFF) != 0)
|
||||
{
|
||||
int
|
||||
first = 1 << numberOfBits,
|
||||
i;
|
||||
|
||||
for (i = first; i < 256; ++i)
|
||||
{
|
||||
translationTable[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::BuildGraphicsPort(
|
||||
int port_number,
|
||||
const char *name,
|
||||
int rotation,
|
||||
int bitAllocation,
|
||||
SVGA16::PaletteID palette_ID,
|
||||
L4GraphicsPort::ChannelEnableID enable_ID,
|
||||
Palette8 *palette
|
||||
)
|
||||
{
|
||||
Test_Tell("L4GaugeRenderer::BuildGraphicsPort\n");
|
||||
Check(this);
|
||||
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Test_Tell("...non-NULL display\n");
|
||||
Verify(port_number < maximumGraphicsPorts);
|
||||
Verify(port_number >= 0);
|
||||
Test_Tell("...port_number= " << port_number << "\n");
|
||||
//
|
||||
//---------------------------------------------------------
|
||||
// If it already exists, delete it
|
||||
//---------------------------------------------------------
|
||||
//
|
||||
L4GraphicsPort
|
||||
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
||||
|
||||
if (graphics_port != NULL)
|
||||
{
|
||||
Test_Tell("...deleting existing port\n");
|
||||
Check(graphics_port);
|
||||
Unregister_Object(graphics_port);
|
||||
delete graphics_port;
|
||||
}
|
||||
//
|
||||
//---------------------------------------------------------
|
||||
// Attempt to create it
|
||||
//---------------------------------------------------------
|
||||
//
|
||||
graphicsPort[port_number] = new BTL4GraphicsPort(
|
||||
(Video16BitBuffered *) graphicsDisplay,
|
||||
name,
|
||||
rotation,
|
||||
bitAllocation,
|
||||
palette_ID,
|
||||
enable_ID
|
||||
);
|
||||
|
||||
graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
||||
|
||||
Test_Tell("...new port at " << graphics_port << "\n");
|
||||
//
|
||||
//---------------------------------------------------------
|
||||
// If created, register it and build the palette
|
||||
//---------------------------------------------------------
|
||||
//
|
||||
if (graphics_port != NULL)
|
||||
{
|
||||
Check(graphics_port);
|
||||
Register_Object(graphics_port);
|
||||
//
|
||||
// Set the palette for the port
|
||||
// (Note: secondary uses lowest 8 bits)
|
||||
//
|
||||
if ((bitAllocation > 0) && (bitAllocation < 0x0100))
|
||||
{
|
||||
Test_Tell("...secondary\n");
|
||||
graphics_port->SetSecondaryPalette(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
Test_Tell("...auxiliary\n");
|
||||
graphics_port->SetAuxiliaryPalette();
|
||||
}
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// RemapGraphicsPort
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::RemapGraphicsPort(
|
||||
int port_number,
|
||||
SVGA16::PaletteID palette_ID,
|
||||
L4GraphicsPort::ChannelEnableID enable_ID,
|
||||
Palette8 *palette
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Test_Tell("L4GaugeRenderer::RemapGraphicsPort\n");
|
||||
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Test_Tell("...non-NULL display\n");
|
||||
Verify(port_number < maximumGraphicsPorts);
|
||||
Verify(port_number >= 0);
|
||||
Test_Tell("...port_number= " << port_number << "\n");
|
||||
//---------------------------------------------------------
|
||||
// If it exists, change the palette ID and set the palette
|
||||
//---------------------------------------------------------
|
||||
L4GraphicsPort
|
||||
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
||||
|
||||
if (graphics_port != NULL)
|
||||
{
|
||||
Check(graphics_port);
|
||||
//
|
||||
// Set the palette ID
|
||||
//
|
||||
graphics_port->SetPaletteID(palette_ID);
|
||||
graphics_port->SetEnableID(enable_ID);
|
||||
|
||||
//
|
||||
// Set the palette for the port
|
||||
// (Note: secondary uses lowest 8 bits)
|
||||
//
|
||||
|
||||
int
|
||||
bit_mask = graphics_port->GetBitMask();
|
||||
|
||||
if (bit_mask & 0x00FF) // must be secondary
|
||||
{
|
||||
Test_Tell("...secondary\n");
|
||||
Verify(palette != NULL);
|
||||
graphics_port->SetSecondaryPalette(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
Test_Tell("...auxiliary\n");
|
||||
graphics_port->SetAuxiliaryPalette();
|
||||
}
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// BuildExternalGraphicsPort
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::BuildExternalGraphicsPort(
|
||||
int port_number,
|
||||
const char *name,
|
||||
int /*bitAllocation*/
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Test_Tell(
|
||||
"L4GaugeRenderer::BuildExternalGraphicsPort(" << port_number << ")\n"
|
||||
);
|
||||
|
||||
if (externalDisplay != NULL)
|
||||
{
|
||||
Test_Tell("...non-NULL display\n");
|
||||
Verify(port_number < maximumGraphicsPorts);
|
||||
Verify(port_number >= 0);
|
||||
Test_Tell("...port_number= " << port_number << "\n");
|
||||
//
|
||||
//---------------------------------------------------------
|
||||
// If it already exists, delete it
|
||||
//---------------------------------------------------------
|
||||
//
|
||||
L4GraphicsPort
|
||||
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
||||
|
||||
if (graphics_port != NULL)
|
||||
{
|
||||
Test_Tell("...deleting existing port\n");
|
||||
Check(graphics_port);
|
||||
Unregister_Object(graphics_port);
|
||||
delete graphics_port;
|
||||
}
|
||||
//
|
||||
//---------------------------------------------------------
|
||||
// Attempt to create it
|
||||
//---------------------------------------------------------
|
||||
//
|
||||
graphicsPort[port_number] = new L4BytePort(
|
||||
(Video8BitBuffered *) externalDisplay,
|
||||
name,
|
||||
0 // rotation
|
||||
);
|
||||
|
||||
graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
||||
|
||||
Test_Tell("...new port at " << graphics_port << "\n");
|
||||
//
|
||||
//---------------------------------------------------------
|
||||
// If created, register it
|
||||
//---------------------------------------------------------
|
||||
//
|
||||
if (graphics_port != NULL)
|
||||
{
|
||||
Check(graphics_port);
|
||||
Register_Object(graphics_port);
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// FadeToWhite, FadeToGame
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::FadeToWhite(Scalar number_of_seconds)
|
||||
{
|
||||
Check(this);
|
||||
Verify(number_of_seconds >= 0.0); // zero is allowed (instant change)
|
||||
|
||||
// cout << "L4GaugeRenderer::FadeToWhite(" << number_of_seconds << ")\n";
|
||||
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Check(graphicsDisplay);
|
||||
((SVGA16 *)graphicsDisplay)->FadeToWhite(number_of_seconds);
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
L4GaugeRenderer::FadeToNormal(Scalar number_of_seconds)
|
||||
{
|
||||
Check(this);
|
||||
Verify(number_of_seconds >= 0.0); // zero is allowed (instant change)
|
||||
|
||||
// cout << "L4GaugeRenderer::FadeToNormal(" << number_of_seconds << ")\n";
|
||||
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
Check(graphicsDisplay);
|
||||
((SVGA16 *)graphicsDisplay)->FadeToPalettes(number_of_seconds);
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Configure
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::ConfigureForModel(
|
||||
const char *configuration_name,
|
||||
Entity *entity
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Check_Pointer(configuration_name);
|
||||
|
||||
Test_Tell(
|
||||
"L4GaugeRenderer::ConfigureForModel(" <<
|
||||
configuration_name << ", " <<
|
||||
entity << ")\n"
|
||||
);
|
||||
|
||||
Check(application);
|
||||
Check(entity);
|
||||
|
||||
Mission
|
||||
*mission = application->GetCurrentMission();
|
||||
Check(mission);
|
||||
|
||||
const char
|
||||
*model_name = mission->GetGameModel();
|
||||
Check_Pointer(model_name);
|
||||
|
||||
char
|
||||
full_name[64];
|
||||
|
||||
Str_Copy(full_name, model_name, sizeof(full_name));
|
||||
Str_Cat(full_name, configuration_name, sizeof(full_name));
|
||||
|
||||
Test_Tell(
|
||||
"L4GaugeRenderer::ConfigureForModel, full_name=" << full_name <<
|
||||
")\n"
|
||||
);
|
||||
|
||||
Configure(full_name, entity);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// SpecialEffect
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
L4GaugeRenderer::SpecialEffect(
|
||||
VideoEffectType type,
|
||||
Scalar duration
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case scrambleVideo:
|
||||
if (graphicsDisplay != NULL)
|
||||
{
|
||||
scrambleVideoFlag = True;
|
||||
scrambleVideoTimeout = ((Scalar)Now()) + duration;
|
||||
|
||||
Check(graphicsDisplay);
|
||||
((SVGA16*) graphicsDisplay)->FunkyVideo(True);
|
||||
}
|
||||
break;
|
||||
|
||||
// other modes go here
|
||||
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
L4GaugeRenderer::ProcessVideoEffects()
|
||||
{
|
||||
Check(this);
|
||||
Check(graphicsDisplay);
|
||||
|
||||
Time
|
||||
now = Now();
|
||||
|
||||
if (scrambleVideoFlag)
|
||||
{
|
||||
if (now >= scrambleVideoTimeout)
|
||||
{
|
||||
scrambleVideoFlag = False;
|
||||
((SVGA16*) graphicsDisplay)->FunkyVideo(False);
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Profiling support
|
||||
//--------------------------------------------------------------------
|
||||
Scalar
|
||||
L4GaugeRenderer::GetCurrentFramePercentage()
|
||||
{
|
||||
Check(this);
|
||||
return Get_Frame_Percent_Used();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user