Mission loads 72s -> 4s: the wait screen was GPU-syncing every idle frame
The operator challenged the "inevitable ~minute" load (Task Manager showed idle CPU) and was right. Measured chain: - A/B same box/egg: dev 7s, +glass 30s, +BT_DEV_GAUGES 72s+. - Drain census: identical ~560-event make streams; 190 events/s in dev vs a metronomic 7/s in the console shape -- the loop, not the events. - Stack sampling: 4/6 samples inside ExecuteIdle's wait-screen paint -- GDI GetDC/FillRect on the D3DPOOL_DEFAULT offscreen surface forces a full GPU pipeline sync (~140ms/frame at glass window size). The 2026-07-22 flicker fix traded a cosmetic bug for a 25x load throttle, worst on the operator box (bigger window + gauge-window GPU work), with idle CPU because a GPU-sync stall isn't compute. Fix (ExecuteIdle): the staging surface is D3DPOOL_SYSTEMMEM (GetDC is pure CPU; survives Reset for free) uploaded via UpdateSurface, and a change gate skips the whole idle frame unless the spinner phase (12.5Hz) or state text changed -- between paints the loop runs free. Result: console-shape load 72s+ -> 4.1s (BEGIN -> READY), drain at 345 events/s; every pod benefits (same code), and the all-ready launch gate now waits on a seconds-scale slowest loader. Permanent pre-run diagnostics kept: the drain census + >50ms slow-event lines (EVENT.cpp, stand down at scene-live; slow-event identity captured BEFORE Process -- Receiver::Receive(Event*) deletes the event). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
87ef6a85f9
commit
56ca2f5c13
@@ -756,9 +756,58 @@ Logical
|
||||
AbstractEvent *event=PeekAtNextEvent(min_priority);
|
||||
if (event)
|
||||
{
|
||||
// pre-run slow-event profiler (loadab investigation): name any event
|
||||
// whose Process takes >50ms. Identity captured BEFORE Process --
|
||||
// Receiver::Receive(Event*) DELETES the event (RECEIVER.cpp:114).
|
||||
// Stands down once the scene presents.
|
||||
extern Logical gBTSceneHasPresented;
|
||||
unsigned long slow_t0 = 0;
|
||||
int slow_msg = -1;
|
||||
if (!gBTSceneHasPresented)
|
||||
{
|
||||
slow_t0 = GetTickCount();
|
||||
if (event->messageToSend != NULL)
|
||||
slow_msg = (int)event->messageToSend->messageID;
|
||||
}
|
||||
SET_PROCESS_EVENT();
|
||||
event->Process();
|
||||
CLEAR_PROCESS_EVENT();
|
||||
if (!gBTSceneHasPresented && slow_t0 != 0)
|
||||
{
|
||||
unsigned long took = GetTickCount() - slow_t0;
|
||||
if (took > 50)
|
||||
{
|
||||
DEBUG_STREAM << "[loadphase] SLOW event " << took
|
||||
<< "ms msgID=" << slow_msg << std::endl << std::flush;
|
||||
}
|
||||
}
|
||||
// LOAD-DRAIN RATE (loadab investigation 2026-07-23): pre-run only,
|
||||
// a 1 Hz census of events processed -- distinguishes "more events"
|
||||
// from "slower per-event work" across config layers. Standing down
|
||||
// permanently once the scene presents (zero hot-path cost in-game
|
||||
// beyond one predicted branch).
|
||||
{
|
||||
extern Logical gBTSceneHasPresented;
|
||||
if (!gBTSceneHasPresented)
|
||||
{
|
||||
static unsigned long s_windowStart = 0;
|
||||
static long s_windowCount = 0;
|
||||
static long s_totalCount = 0;
|
||||
++s_windowCount;
|
||||
++s_totalCount;
|
||||
unsigned long now_ms = GetTickCount();
|
||||
if (s_windowStart == 0)
|
||||
s_windowStart = now_ms;
|
||||
if (now_ms - s_windowStart >= 1000)
|
||||
{
|
||||
DEBUG_STREAM << "[loadphase] drained " << s_windowCount
|
||||
<< " events/s (total " << s_totalCount << ")"
|
||||
<< std::endl << std::flush;
|
||||
s_windowStart = now_ms;
|
||||
s_windowCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
|
||||
Reference in New Issue
Block a user