From c38282348891ec81dbc3b346f4d894c850c95ef5 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Fri, 25 Apr 2003 03:41:38 +0000 Subject: [PATCH] Add an option to delay screen loads until after we've deleted the old screen. This means that we may unload resources when we'll need them immediately, but lowers the memory usage peak considerably for very low-memory machines. --- stepmania/src/PrefsManager.cpp | 3 ++ stepmania/src/PrefsManager.h | 1 + stepmania/src/ScreenManager.cpp | 76 ++++++++++++++++++++++++++++----- stepmania/src/ScreenManager.h | 3 ++ stepmania/src/StepMania.cpp | 2 - 5 files changed, 72 insertions(+), 13 deletions(-) diff --git a/stepmania/src/PrefsManager.cpp b/stepmania/src/PrefsManager.cpp index d66040e590..229911938b 100644 --- a/stepmania/src/PrefsManager.cpp +++ b/stepmania/src/PrefsManager.cpp @@ -74,6 +74,7 @@ PrefsManager::PrefsManager() m_bArcadeOptionsNavigation = false; m_bSoloSingle = false; m_bDelayedTextureDelete = true; + m_bDelayedScreenLoad = false; m_MusicWheelUsesSections = ALWAYS; m_iMusicWheelSwitchSpeed = 10; m_bEasterEggs = true; @@ -166,6 +167,7 @@ void PrefsManager::ReadGlobalPrefsFromDisk( bool bSwitchToLastPlayedGame ) ini.GetValueB( "Options", "ArcadeOptionsNavigation", m_bArcadeOptionsNavigation ); ini.GetValue ( "Options", "DWIPath", m_DWIPath ); ini.GetValueB( "Options", "DelayedTextureDelete", m_bDelayedTextureDelete ); + ini.GetValueB( "Options", "DelayedScreenLoad", m_bDelayedScreenLoad ); ini.GetValueI( "Options", "MusicWheelUsesSections", (int&)m_MusicWheelUsesSections ); ini.GetValueI( "Options", "MusicWheelSwitchSpeed", m_iMusicWheelSwitchSpeed ); ini.GetValue ( "Options", "SoundDrivers", m_sSoundDrivers ); @@ -249,6 +251,7 @@ void PrefsManager::SaveGlobalPrefsToDisk() ini.SetValueB( "Options", "ArcadeOptionsNavigation", m_bArcadeOptionsNavigation ); ini.SetValue ( "Options", "DWIPath", m_DWIPath ); ini.SetValueB( "Options", "DelayedTextureDelete", m_bDelayedTextureDelete ); + ini.SetValueB( "Options", "DelayedScreenLoad", m_bDelayedScreenLoad ); ini.SetValueI( "Options", "MusicWheelUsesSections", m_MusicWheelUsesSections ); ini.SetValueI( "Options", "MusicWheelSwitchSpeed", m_iMusicWheelSwitchSpeed ); ini.SetValueB( "Options", "EasterEggs", m_bEasterEggs ); diff --git a/stepmania/src/PrefsManager.h b/stepmania/src/PrefsManager.h index 10932e2299..bc227b2de0 100644 --- a/stepmania/src/PrefsManager.h +++ b/stepmania/src/PrefsManager.h @@ -34,6 +34,7 @@ public: bool m_bHiddenSongs; bool m_bVsync; bool m_bDelayedTextureDelete; + bool m_bDelayedScreenLoad; bool m_bIgnoreJoyAxes; bool m_bOnlyDedicatedMenuButtons; diff --git a/stepmania/src/ScreenManager.cpp b/stepmania/src/ScreenManager.cpp index 68da5e2ad8..f8381b6b90 100644 --- a/stepmania/src/ScreenManager.cpp +++ b/stepmania/src/ScreenManager.cpp @@ -315,6 +315,7 @@ void ScreenManager::EmptyDeleteQueue() * change state!). Only do this when we have to, so we don't double * the number of updates. */ static bool g_TopNeedsNeedsNullUpdate = false; +static bool g_SkipRendering = false; void ScreenManager::Update( float fDeltaTime ) { @@ -331,25 +332,54 @@ void ScreenManager::Update( float fDeltaTime ) * If we cap that large update delta from the screen load, the update * to load the new screen will come after 4 seconds plus the load time. * - * So, let's just drop the first update for every screen. + * So, let's just zero the first update for every screen. + * + * XXX: If a new Screen is set during this Update, that new screen is Drawn + * before it's first Update. */ - ASSERT( !m_ScreenStack.empty() ); // Why play the game if there is nothing showing? - Screen* pScreen = m_ScreenStack[m_ScreenStack.size()-1]; - if( pScreen->IsFirstUpdate() ) - pScreen->Update( 0 ); - else - pScreen->Update( fDeltaTime ); + ASSERT( !m_ScreenStack.empty() || m_DelayedScreen != "" ); // Why play the game if there is nothing showing? + + if( !m_ScreenStack.empty() ) + { + Screen* pScreen = m_ScreenStack[m_ScreenStack.size()-1]; + if( pScreen->IsFirstUpdate() ) + pScreen->Update( 0 ); + else + pScreen->Update( fDeltaTime ); + } - // TODO: If a new Screen is set during this Update, that new screen is Drawn - // before it's first Update. m_SystemLayer->Update( fDeltaTime ); EmptyDeleteQueue(); + + if(m_DelayedScreen.size() != 0) + { + LOG->Trace("loading"); + /* We have a screen to display. Delete the current screens and load it. */ + m_ScreensToDelete.insert(m_ScreensToDelete.end(), m_ScreenStack.begin(), m_ScreenStack.end()); + m_ScreenStack.clear(); + EmptyDeleteQueue(); + LoadDelayedScreen(); + + /* Ack. We can't Update(0), since we want to skip the *next* update + * (which is the one that will have all this load time in it). We + * can't draw it until we've updated it. Let's simply not render + * the next frame, so we'll come around quickly and handle it correctly. */ + g_SkipRendering = true; + } } void ScreenManager::Draw() { + DISPLAY->Clear(); + + if(g_SkipRendering) + { + /* Leave the frame there (if any). */ + g_SkipRendering = false; + return; + } if(g_TopNeedsNeedsNullUpdate) { g_TopNeedsNeedsNullUpdate = false; @@ -362,6 +392,8 @@ void ScreenManager::Draw() for( unsigned i=0; iDraw(); m_SystemLayer->Draw(); + + DISPLAY->Flip(); } @@ -422,18 +454,33 @@ void ScreenManager::SetNewScreen( Screen *pNewScreen ) // move current screen(s) to ScreenToDelete m_ScreensToDelete.insert(m_ScreensToDelete.end(), m_ScreenStack.begin(), m_ScreenStack.end()); - m_ScreenStack.clear(); + m_ScreenStack.push_back( pNewScreen ); } void ScreenManager::SetNewScreen( CString sClassName ) { + m_DelayedScreen = sClassName; + + /* If we're not delaying screen loads, load it now. Otherwise, we'll load + * it on the next iteration. Only delay if we already have a screen + * loaded; otherwise, there's no reason to delay. */ + if(!PREFSMAN->m_bDelayedScreenLoad) // || m_ScreenStack.empty() ) + LoadDelayedScreen(); +} + +void ScreenManager::LoadDelayedScreen() +{ +retry: + CString sClassName = m_DelayedScreen; + m_DelayedScreen = ""; + /* If we prepped a screen but didn't use it, nuke it. */ SAFE_DELETE( m_ScreenBuffered ); RageTimer t; - + Screen* pOldTopScreen = m_ScreenStack.empty() ? NULL : m_ScreenStack.back(); // It makes sense that ScreenManager should allocate memory for a new screen since it @@ -451,6 +498,13 @@ void ScreenManager::SetNewScreen( CString sClassName ) return; } + if( PREFSMAN->m_bDelayedScreenLoad && m_DelayedScreen != "" ) + { + /* Same deal: the ctor called SetNewScreen again. Delete the screen + * we just made, but don't delay again. */ + SAFE_DELETE( pNewScreen ); + goto retry; + } SetNewScreen( pNewScreen ); /* If this is a system menu, don't let the operator key touch it! diff --git a/stepmania/src/ScreenManager.h b/stepmania/src/ScreenManager.h index 24b1f6d19f..112633b9fb 100644 --- a/stepmania/src/ScreenManager.h +++ b/stepmania/src/ScreenManager.h @@ -53,6 +53,8 @@ public: void EmptyDeleteQueue(); + void LoadDelayedScreen(); + private: vector m_ScreenStack; // bottommost to topmost vector m_ScreensToDelete; @@ -61,6 +63,7 @@ private: Screen* MakeNewScreen( CString sClassName ); void SetNewScreen( Screen *pNewScreen ); + CString m_DelayedScreen; }; diff --git a/stepmania/src/StepMania.cpp b/stepmania/src/StepMania.cpp index 193524e06c..e577708b0c 100644 --- a/stepmania/src/StepMania.cpp +++ b/stepmania/src/StepMania.cpp @@ -574,9 +574,7 @@ static void GameLoop() /* * Render */ - DISPLAY->Clear(); SCREENMAN->Draw(); - DISPLAY->Flip(); if(g_bHasFocus) SDL_Delay( 1 ); // give some time to other processes and threads