The map keeps drawing when the view gets busy
Two testers reported the map and the countdown clock freezing, one of them only on larger, more complex maps, and one of them until a death. Both details point at the same place. The gauges and the cockpit displays are redrawn in whatever time is left after the 3D view. The background loop is guaranteed a single pass per frame and gets more only while time remains before the frame is due, and one pass drew exactly one gauge. So a full sweep of ninety-odd gauges needed ninety-odd passes - free when there is spare frame, but on a busy map the 3D view eats all of it, the loop drops to its one guaranteed pass, and a sweep takes ninety-odd FRAMES. Seconds. A death makes the renderer skip every static object, the budget frees up, and the backlog drains at once: the display appears to come back to life. Worse, the copy phase that follows ended after a SINGLE display, so the map - one of three - came round only every third sweep. So: draw gauges to a 2ms slice rather than one per pass, which ties the refresh rate to elapsed time instead of to how much spare frame there happened to be; and copy every display before reporting the sweep done. Measured on a deliberately starved frame budget, which reproduces the reported symptom: 0.7 sweeps/s before, 3.1 after. At a normal budget 20/s, against 18-19 before - no cost to the healthy case. RP412GAUGESLICE tunes the slice and 0 restores the old behaviour, which reproduces the 0.7 exactly. RP412GAUGEDIAG=1 logs the rate; watching the screen cannot tell a display that has stopped refreshing from one whose picture simply is not changing, which is what made this hard to see. Also fixes the constructor calling Update() three lines before it initialised mDisplayToUpdate, so the first pass indexed the D3D device and surface arrays with whatever was on the stack. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+94
-4
@@ -4358,6 +4358,15 @@ SVGA16::SVGA16(
|
||||
// Split-view mode: decide before BuildWindows so the packed gauge
|
||||
// windows can stay hidden.
|
||||
//------------------------------------------------------------------
|
||||
//
|
||||
// Before anything else: the constructor calls Update() below, and
|
||||
// both of these are read in there. mDisplayToUpdate was only being
|
||||
// set at the END of the constructor, so that first pass indexed the
|
||||
// display arrays with whatever was on the stack.
|
||||
//
|
||||
mDisplayToUpdate = 0;
|
||||
mDisplaysCopiedThisPass = 0;
|
||||
|
||||
splitViews = False;
|
||||
cockpitViewscreen = NULL;
|
||||
Logical explodedViews = False;
|
||||
@@ -4997,12 +5006,16 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
GaugeRenderer *renderer = application->GetGaugeRenderer();
|
||||
if (!valid || renderer == NULL)
|
||||
{
|
||||
mDisplaysCopiedThisPass = 0;
|
||||
CLEAR_SCREEN_COPY();
|
||||
return False; // Do no more!
|
||||
}
|
||||
|
||||
if (++mDisplayToUpdate >= NUMGAUGEWINDOWS)
|
||||
// one display per call; the rotation steps at the end of the function
|
||||
if (mDisplayToUpdate >= NUMGAUGEWINDOWS)
|
||||
{
|
||||
mDisplayToUpdate = 0;
|
||||
}
|
||||
|
||||
//Top MFD's
|
||||
L4GraphicsPort *UL = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxUL2"));
|
||||
@@ -5045,7 +5058,10 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
lrMask |= (lrMask << 16);
|
||||
} else
|
||||
{
|
||||
//No MFDs to draw, break out early
|
||||
//No MFDs to draw, break out early. The sweep counter resets
|
||||
//too: leaving it part-used would keep the renderer in its
|
||||
//copy phase, and it never draws another gauge while there.
|
||||
mDisplaysCopiedThisPass = 0;
|
||||
return False;
|
||||
}
|
||||
} else
|
||||
@@ -5057,7 +5073,8 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
secPalette = &((SVGA16 *) secPort->graphicsDisplay)->palette[secPort->paletteID];
|
||||
} else
|
||||
{
|
||||
//No secondary, skip
|
||||
//No secondary, skip - and end the sweep, as above.
|
||||
mDisplaysCopiedThisPass = 0;
|
||||
return False;
|
||||
}
|
||||
}
|
||||
@@ -5227,7 +5244,80 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
// if (end.ticks - start.ticks > 100)
|
||||
// end = start;
|
||||
|
||||
return False; // True == 'more to do'
|
||||
//
|
||||
//------------------------------------------------------------------
|
||||
// Step the rotation, and say "more to do" until every display has
|
||||
// had its turn.
|
||||
//
|
||||
// This used to return False unconditionally, which told the gauge
|
||||
// renderer its copy phase was over after a SINGLE display. A full
|
||||
// gauge sweep - one gauge per background pass, so as many passes as
|
||||
// there are active gauges - therefore refreshed one display, and the
|
||||
// map, one of three, came round only every third sweep.
|
||||
//
|
||||
// That is invisible with frame time to spare, because the background
|
||||
// loop keeps running until the frame budget is used up and gets
|
||||
// through several sweeps. On a big map the 3D foreground eats the
|
||||
// whole budget, the loop drops to the one pass per frame it is
|
||||
// guaranteed, and the map goes seconds between refreshes - which is
|
||||
// what the field reports describe, on exactly those maps. A death
|
||||
// makes the renderer skip every static object, the budget frees up,
|
||||
// and the backlog drains at once: the display appears to come back
|
||||
// to life, which is the tell that led here.
|
||||
//------------------------------------------------------------------
|
||||
//
|
||||
mDisplayToUpdate++;
|
||||
if (mDisplayToUpdate >= NUMGAUGEWINDOWS)
|
||||
{
|
||||
mDisplayToUpdate = 0;
|
||||
}
|
||||
|
||||
if (++mDisplaysCopiedThisPass < NUMGAUGEWINDOWS)
|
||||
{
|
||||
return True; // call again - there are displays waiting
|
||||
}
|
||||
mDisplaysCopiedThisPass = 0;
|
||||
|
||||
//
|
||||
// RP412GAUGEDIAG=1 reports how often the displays are actually being
|
||||
// refreshed. Pixel-watching from outside cannot tell a display that
|
||||
// is not refreshing from one whose picture simply is not changing,
|
||||
// and that ambiguity is exactly what makes "my map froze" hard to
|
||||
// pin down. This counts the real thing.
|
||||
//
|
||||
{
|
||||
static int diagnostics = -1;
|
||||
if (diagnostics < 0)
|
||||
{
|
||||
const char *setting = getenv("RP412GAUGEDIAG");
|
||||
diagnostics = (setting != NULL && atoi(setting) != 0) ? 1 : 0;
|
||||
}
|
||||
if (diagnostics)
|
||||
{
|
||||
static unsigned long window_start = 0;
|
||||
static int sweeps = 0;
|
||||
unsigned long now = GetTickCount();
|
||||
++sweeps;
|
||||
if (window_start == 0)
|
||||
{
|
||||
window_start = now;
|
||||
}
|
||||
else if (now - window_start >= 2000)
|
||||
{
|
||||
// tenths, by hand: whole sweeps per second rounds the
|
||||
// interesting cases - a starved pipeline managing two
|
||||
// thirds of a sweep a second reads as a flat "0/s".
|
||||
int tenths = sweeps * 10000 / (int)(now - window_start);
|
||||
DEBUG_STREAM << "GaugeDiag: " << sweeps << " display sweep(s) in "
|
||||
<< (now - window_start) << " ms ("
|
||||
<< (tenths / 10) << '.' << (tenths % 10) << "/s, "
|
||||
<< NUMGAUGEWINDOWS << " displays each)\n" << std::flush;
|
||||
window_start = now;
|
||||
sweeps = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return False; // the sweep is complete
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user