diff --git a/stepmania/src/ScreenManager.cpp b/stepmania/src/ScreenManager.cpp index d8da1b6684..08b4316a94 100644 --- a/stepmania/src/ScreenManager.cpp +++ b/stepmania/src/ScreenManager.cpp @@ -31,9 +31,11 @@ #include "Screen.h" #include "Foreach.h" #include "ActorUtil.h" +#include "GameLoop.h" ScreenManager* SCREENMAN = NULL; // global and accessable from anywhere in our program +static Preference g_bConcurrentLoading( "ConcurrentLoading", false ); // Screen registration static map *g_pmapRegistrees = NULL; @@ -56,6 +58,7 @@ ScreenManager::ScreenManager() m_bZeroNextUpdate = false; m_PopTopScreen = SM_Invalid; + m_OnDonePreparingScreen = SM_Invalid; } @@ -115,6 +118,8 @@ bool ScreenManager::IsStackedScreen( const Screen *pScreen ) const return false; } +static bool g_bIsConcurrentlyLoading = false; + void ScreenManager::Update( float fDeltaTime ) { // @@ -197,12 +202,41 @@ void ScreenManager::Update( float fDeltaTime ) if( bFirstUpdate ) SOUND->Flush(); - if( m_sDelayedScreen.size() != 0 ) + /* If we're currently inside a background screen load, and m_sDelayedScreen + * is set, then the screen called SetNewScreen before we finished preparing. + * Postpone it until we're finished loading. */ + if( !IsConcurrentlyLoading() && m_sDelayedScreen.size() != 0 ) { LoadDelayedScreen(); } + + if( !m_sDelayedConcurrentPrepare.empty() ) + { + /* Don't call BackgroundPrepareScreen() from within another background load. */ + ASSERT( !IsConcurrentlyLoading() ); + + /* Push rendering into a thread, and prepare the screen. */ + g_bIsConcurrentlyLoading = true; + StartConcurrentRendering(); + PrepareScreen( m_sDelayedConcurrentPrepare ); + FinishConcurrentRendering(); + g_bIsConcurrentlyLoading = false; + + LOG->Trace( "Concurrent prepare of %s finished", m_sDelayedConcurrentPrepare.c_str() ); + + /* We're done. Send the message. The screen is allowed to start + * another concurrent prepare from this message. */ + ScreenMessage SM = m_OnDonePreparingScreen; + m_sDelayedConcurrentPrepare = ""; + m_OnDonePreparingScreen = SM_None; + SendMessageToTopScreen( SM ); + } } +bool ScreenManager::IsConcurrentlyLoading() const +{ + return g_bIsConcurrentlyLoading; +} void ScreenManager::Draw() { @@ -347,6 +381,21 @@ void ScreenManager::PrepareScreen( const CString &sScreenName ) } } +bool ScreenManager::ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage SM ) +{ + ASSERT_M( m_sDelayedConcurrentPrepare == "", m_sDelayedConcurrentPrepare ); + + if( !g_bConcurrentLoading || !DISPLAY->SupportsThreadedRendering() ) + return false; + + LOG->Trace( "ConcurrentlyPrepareScreen(%s)", sScreenName.c_str() ); + ASSERT( !IsConcurrentlyLoading() ); + + m_sDelayedConcurrentPrepare = sScreenName; + m_OnDonePreparingScreen = SM; + return true; +} + void ScreenManager::DeletePreparedScreens() { FOREACH( Screen*, m_vPreparedScreens, s ) @@ -559,7 +608,11 @@ void ScreenManager::RefreshCreditsMessages() void ScreenManager::ZeroNextUpdate() { - m_bZeroNextUpdate = true; + if( !IsConcurrentlyLoading() ) + { + LOG->Trace("ScreenManager::ZeroNextUpdate"); + m_bZeroNextUpdate = true; + } } /* Always play these sounds, even if we're in a silent attract loop. */ diff --git a/stepmania/src/ScreenManager.h b/stepmania/src/ScreenManager.h index 53a8ea685b..324ec419d1 100644 --- a/stepmania/src/ScreenManager.h +++ b/stepmania/src/ScreenManager.h @@ -39,6 +39,8 @@ public: void SetNewScreen( const CString &sName ); void AddNewScreenToTop( const CString &sName ); void PrepareScreen( const CString &sScreenName ); // creates and caches screen so that the next call to SetNewScreen for the prep'd screen will be very quick. + bool ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage send_when_done = SM_None ); + bool IsConcurrentlyLoading() const; void DeletePreparedScreens(); void SetFromNewScreen( Screen *pNewScreen ); void PopTopScreen( ScreenMessage SM ); @@ -82,9 +84,10 @@ private: vector m_vPreparedScreens; vector m_vPreparedBackgrounds; - // Screen loads and removals are delayed until the next update, so the - // screen being removed isn't on the stack. + // Screen loads, removals, and concurrent prepares are delayed until the next update. CString m_sDelayedScreen; + CString m_sDelayedConcurrentPrepare; + ScreenMessage m_OnDonePreparingScreen; ScreenMessage m_PopTopScreen; // Set this to true anywhere we create of delete objects. These