#include "munga.h" #pragma hdrstop #include "appmgr.h" HWND ghWnd = 0; ApplicationManager* ApplicationManager::CurrentAppManager = NULL; ApplicationManager::ApplicationManager(HINSTANCE hInstance, HWND hWnd, Scalar frame_rate) : Node(ApplicationManagerClassID), runningApplications(this) { frameRate = frame_rate; frameDuration = 1.0f/frameRate; mHInstance = hInstance; ghWnd = hWnd; ApplicationManager::CurrentAppManager = this; Check_Fpu(); } ApplicationManager::~ApplicationManager() { } void ApplicationManager::StartApplication(Application *new_app) { Check(this); Check(new_app); runningApplications.Add(new_app); application = new_app; new_app->Initialize(); } // //############################################################################# // RunMissions //############################################################################# // void ApplicationManager::RunMissions() { Check(this); int backgroundTasksRun = 0; int foregroundTasksRun = 0; Time beginFrameTimestamp, lastFrameTimestamp; MSG msg; SChainIteratorOf current_application(runningApplications); SChainOf endedApplications(NULL); SChainIteratorOf endingApplication(endedApplications); Start_Of_Frame: backgroundTasksRun = 0; foregroundTasksRun = 0; beginFrameTimestamp = Now(); current_application.First(); lastFrameTimestamp = Now(); Time end_of_frame = Now(); end_of_frame += frameDuration; #if defined(LAB_ONLY) int bad_count = 0; #endif // //---------------------------------- // Run all relevant foreground loops //---------------------------------- // Time startForeground = Now(); while ((application = current_application.ReadAndNext()) != NULL) { Check(application); foregroundTasksRun++; if (!application->ExecuteForeground(end_of_frame, frameDuration)) { if (!application->Shutdown(current_application.GetSize())) { endedApplications.Add(application); if (current_application.GetCurrent() == NULL) { current_application.Last(); } else { current_application.Previous(); } current_application.Remove(); } } } Time endForeground = Now(); // //----------------------------------------------------------------------- // Check the Windows message queue for messages to be processed //----------------------------------------------------------------------- // //InvalidateRect(ghWnd, NULL, false); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { endingApplication.First(); while (application = endingApplication.ReadAndNext()) { application->Terminate(); delete application; } return; } TranslateMessage(&msg); DispatchMessage(&msg); } // //----------------------------------------------------------------------- // Run all relevant background loops until it is time for the next frame. // If no applications remain, exit the whole loop //----------------------------------------------------------------------- // current_application.First(); application = current_application.GetCurrent(); if (!application) { endingApplication.First(); while (application = endingApplication.ReadAndNext()) { application->Terminate(); delete application; } return; } do { while (application->GetNetworkManager()->RoutePacket()) ; current_application.Next(); application = current_application.GetCurrent(); } while (application); current_application.First(); application = current_application.GetCurrent(); if (!application) { endingApplication.First(); while (application = endingApplication.ReadAndNext()) { application->Terminate(); delete application; } return; } // //------------------------------------------------------------------ // Give each application a chance to do at least one background task //------------------------------------------------------------------ // Time startBackground = Now(); Background_Loop: do { Check(application); application->ExecuteBackgroundTask(); backgroundTasksRun++; // // Move to the next application // current_application.Next(); application = current_application.GetCurrent(); } while (application); // //--------------------------------------------------------------------- // If time remains before the next frame is due, do another pass on the // background tasks //--------------------------------------------------------------------- // Time t2 = Now(); Time lateTime = Now(); lateTime += 15L; current_application.First(); application = current_application.GetCurrent(); // // BT MINIMUM BACKGROUND FLOOR (#45 root cause, 2026-07-30). Authentically // the background tasks run ONLY in the frame's leftover time, with a floor // of a single pump per frame -- fine on a 1995 pod (one app, designed // slack), but on a busy MP mission the foreground eats the whole frame // budget, the floor becomes the norm, and the GAUGE renderer (1 of ~7 // round-robin tasks, ONE gauge advanced per turn) starves: every cockpit // instrument -- the comms-panel K/D columns, weapon recharge tickers -- // freezes at its last paint while the underlying values (and fps!) stay // perfectly healthy. Measured: 4-node bench at 70-90 fps gave the // PilotList ~2 Execute turns in SIX MINUTES. Guarantee a minimum number // of pumps per frame so a full instrument rotation completes in ~1s // regardless of slack; the added worst-case cost is bounded and small. // BT_BG_MIN overrides (0 = authentic slack-only behavior). // { static int s_bgMin = -1; if (s_bgMin < 0) { const char *e = getenv("BT_BG_MIN"); s_bgMin = e ? atoi(e) : 32; if (s_bgMin < 0) s_bgMin = 0; if (s_bgMin > 256) s_bgMin = 256; } if (backgroundTasksRun < s_bgMin) goto Background_Loop; } if (t2 < end_of_frame) { #if defined(LAB_ONLY) if (end_of_frame - t2 > 0.1f) { DEBUG_STREAM << t2 << ',' << end_of_frame << endl << std::flush; if (++bad_count == 1000) { Fail("End-of-frame cannot be correctly calculated!"); } } #endif goto Background_Loop; } Time endBackground = Now(); // BT perf probe (env BT_PERF): 1-Hz frame breakdown. { static int s_perf = -1; if (s_perf < 0) { const char *e = getenv("BT_PERF"); s_perf = (e && *e != '0') ? 1 : 0; } if (s_perf) { static Time s_lastP = Now(); if (Now() - s_lastP >= 1.0f) { s_lastP = Now(); // gauge-executive rotation health (#45): turns = one-gauge // Updates and sweeps = full active-list passes SINCE THE LAST // PRINT (~1s); active = the instrument roster size. A frozen // panel shows here as sweeps=0. extern int gBTGaugeTurns, gBTGaugeSweeps, gBTGaugeActive; DEBUG_STREAM << "[perf] frame=" << (float)(Now() - beginFrameTimestamp) << " fg=" << (float)(startBackground - beginFrameTimestamp) << " bg=" << (float)(endBackground - startBackground) << " bgTasks=" << backgroundTasksRun << " gaugeTurns=" << gBTGaugeTurns << " sweeps=" << gBTGaugeSweeps << " active=" << gBTGaugeActive << "\n" << std::flush; gBTGaugeTurns = 0; gBTGaugeSweeps = 0; } } } //char str[256]; //Scalar lastFrameLength = Now() - beginFrameTimestamp; //sprintf(str, "RPL4 - %.2f FPS", 1.0f / lastFrameLength); //SetWindowTextA(ghWnd, str); goto Start_Of_Frame; }