From 6ebc91d3f7cfc88664288f1a3ff021a816ec098e Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 11 Jun 2019 13:44:59 +0200 Subject: [PATCH] Added mainloop frame notifications Using Emesary Global Transmitter the following events are notified * Frame Begin * Frame End * Mainloop started * Mainloop stopped This integrates with the background threaded Nasal Garbage collection (simgear:nasal/cppbind/NasalEmesaryInterface.hxx) and is also controlled by the properties as follows * /sim/nasal-gc-threaded (true) - use background threaded GC * /sim/nasal-gc-threaded-wait (false) - at the start of the frame wait for the previous GC thread to complete. I initially thought that the wait at the start of the frame would be necessary; however in 100 or so hours of flight without the await for completion at the start of frame no threading problems (or any other problems) were shown; so nasal-gc-threaded-wait is defaulted to false which gives a slight boost in performance. So what this does is to it removes the GC pause of 10-20ms every 4 seconds (test using the F-15). This change doesn't really give much extra performance per frame because normally GC is only performed when needed. --- src/Main/main.cxx | 59 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/Main/main.cxx b/src/Main/main.cxx index b0a0dbcd0..a22c62ffc 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -56,6 +56,8 @@ extern bool global_crashRptEnabled; #include #include #include +#include +#include #include #include
@@ -101,11 +103,52 @@ using std::vector; extern int _bootstrap_OSInit; static SGPropertyNode_ptr frame_signal; +static SGPropertyNode_ptr nasal_gc_threaded; +static SGPropertyNode_ptr nasal_gc_threaded_wait; -// What should we do when we have nothing else to do? Let's get ready -// for the next move and update the display? +#ifdef NASAL_BACKGROUND_GC_THREAD +extern "C" { + extern void startNasalBackgroundGarbageCollection(); + extern void stopNasalBackgroundGarbageCollection(); + extern void performNasalBackgroundGarbageCollection(); + extern void awaitNasalGarbageCollectionComplete(bool can_wait); +} +#endif +static simgear::Notifications::MainLoopNotification mln_begin(simgear::Notifications::MainLoopNotification::Type::Begin); +static simgear::Notifications::MainLoopNotification mln_end(simgear::Notifications::MainLoopNotification::Type::End); +static simgear::Notifications::MainLoopNotification mln_started(simgear::Notifications::MainLoopNotification::Type::Started); +static simgear::Notifications::MainLoopNotification mln_stopped(simgear::Notifications::MainLoopNotification::Type::Stopped); +static simgear::Notifications::NasalGarbageCollectionConfigurationNotification *ngccn = nullptr; +// This method is usually called after OSG has finished rendering a frame in what OSG calls an idle handler and +// is reposonsible for invoking all of the relevant per frame processing; most of which is handled by subsystems. static void fgMainLoop( void ) { + // + // the Nasal GC will automatically run when (during allocation) it discovers that more space is needed. + // This has a cost of between 5ms and 50ms (depending on the amount of currently active Nasal). + // The result is unscheduled and unpredictable pauses during normal operation when the garbage collector + // runs; which typically occurs at intervals between 1sec and 20sec. + // + // The solution to this, which overall increases CPU load, is to use a thread to do this; as Nasal is thread safe + // so what we do is to launch the garbage collection at the end of the main loop and then wait for completion at the start of the + // next main loop. + // So although the overall CPU is increased it has little effect on the frame rate; if anything it is an overall benefit + // as there are no unscheduled long duration frames. + // + // The implementation appears to work fine without waiting for completion at the start of the frame - so + // this wait at the start can be disabled by setting the property /sim/nasal-gc-threaded-wait to false. + + // first we see if the config has changed. The notification will return true from SetActive/SetWait when the + // value has been changed - and thus we notify the Nasal system that it should configure itself accordingly. + auto use_threaded_gc = nasal_gc_threaded->getBoolValue(); + auto threaded_wait = nasal_gc_threaded_wait->getBoolValue(); + bool notify_gc_config = false; + notify_gc_config = ngccn->SetActive(use_threaded_gc); + notify_gc_config |= ngccn->SetWait(threaded_wait); + if (notify_gc_config) + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(*ngccn); + + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_begin); if (sglog().has_popup()) { std::string s = sglog().get_popup(); @@ -126,6 +169,8 @@ static void fgMainLoop( void ) // flush commands waiting in the queue SGCommandMgr::instance()->executedQueuedCommands(); simgear::AtomicChangeListener::fireChangeListeners(); + + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_end); } static void initTerrasync() @@ -240,6 +285,8 @@ static void registerMainLoop() { // stash current frame signal property frame_signal = fgGetNode("/sim/signals/frame", true); + nasal_gc_threaded = fgGetNode("/sim/nasal-gc-threaded", true); + nasal_gc_threaded_wait = fgGetNode("/sim/nasal-gc-threaded-wait", true); fgRegisterIdleHandler( fgMainLoop ); } @@ -376,6 +423,10 @@ static void fgIdleFunction ( void ) { // run the main loop. fgSetBool("sim/sceneryloaded", false); registerMainLoop(); + + ngccn = new simgear::Notifications::NasalGarbageCollectionConfigurationNotification(nasal_gc_threaded->getBoolValue(), nasal_gc_threaded_wait->getBoolValue()); + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(*ngccn); + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_started); } if ( idle_state == 2000 ) { @@ -546,8 +597,8 @@ int fgMainInit( int argc, char **argv ) } #if defined(HAVE_QT) + flightgear::initApp(argc, argv); if (showLauncher) { - flightgear::initApp(argc, argv); flightgear::checkKeyboardModifiersForSettingFGRoot(); if (!flightgear::runLauncherDialog()) { @@ -627,6 +678,8 @@ int fgMainInit( int argc, char **argv ) frame_signal.clear(); fgOSCloseWindow(); + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_stopped); + simgear::clearEffectCache(); // clean up here; ensure we null globals to avoid