ironing out preloading logic
This commit is contained in:
@@ -12,8 +12,9 @@
|
|||||||
|
|
||||||
#define NEXT_SCREEN THEME->GetMetric (m_sName,"NextScreen")
|
#define NEXT_SCREEN THEME->GetMetric (m_sName,"NextScreen")
|
||||||
#define PREV_SCREEN THEME->GetMetric (m_sName,"PrevScreen")
|
#define PREV_SCREEN THEME->GetMetric (m_sName,"PrevScreen")
|
||||||
#define SCREENS_TO_PREPARE THEME->GetMetric (m_sName,"ScreensToPrepare")
|
#define PREPARE_SCREENS THEME->GetMetric (m_sName,"PrepareScreens")
|
||||||
#define SCREENS_TO_GROUP THEME->GetMetric (m_sName,"ScreensToGroup")
|
#define PERSIST_SCREENS THEME->GetMetric (m_sName,"PersistScreens")
|
||||||
|
#define GROUPED_SCREENS THEME->GetMetric (m_sName,"GroupedScreens")
|
||||||
|
|
||||||
Screen::Screen( CString sName )
|
Screen::Screen( CString sName )
|
||||||
{
|
{
|
||||||
@@ -38,7 +39,7 @@ void Screen::Init()
|
|||||||
this->RunCommands( THEME->GetMetricA(m_sName, "InitCommand") );
|
this->RunCommands( THEME->GetMetricA(m_sName, "InitCommand") );
|
||||||
|
|
||||||
vector<CString> asList;
|
vector<CString> asList;
|
||||||
split( SCREENS_TO_PREPARE, ",", asList );
|
split( PREPARE_SCREENS, ",", asList );
|
||||||
for( unsigned i = 0; i < asList.size(); ++i )
|
for( unsigned i = 0; i < asList.size(); ++i )
|
||||||
{
|
{
|
||||||
LOG->Trace( "Screen %s preparing \"%s\"", asList[i].c_str() );
|
LOG->Trace( "Screen %s preparing \"%s\"", asList[i].c_str() );
|
||||||
@@ -46,9 +47,14 @@ void Screen::Init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
asList.clear();
|
asList.clear();
|
||||||
split( SCREENS_TO_GROUP, ",", asList );
|
split( GROUPED_SCREENS, ",", asList );
|
||||||
for( unsigned i = 0; i < asList.size(); ++i )
|
for( unsigned i = 0; i < asList.size(); ++i )
|
||||||
SCREENMAN->GroupScreen( asList[i] );
|
SCREENMAN->GroupScreen( asList[i] );
|
||||||
|
|
||||||
|
asList.clear();
|
||||||
|
split( PERSIST_SCREENS, ",", asList );
|
||||||
|
for( unsigned i = 0; i < asList.size(); ++i )
|
||||||
|
SCREENMAN->PersistantScreen( asList[i] );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::Update( float fDeltaTime )
|
void Screen::Update( float fDeltaTime )
|
||||||
|
|||||||
@@ -2,20 +2,12 @@
|
|||||||
* We maintain a pool of "prepared" screens, which are screens previously loaded
|
* We maintain a pool of "prepared" screens, which are screens previously loaded
|
||||||
* to be available on demand.
|
* to be available on demand.
|
||||||
*
|
*
|
||||||
* A used screen, when finished, goes to the prepared list. If that screen is
|
* When a screen pops off the stack that's in g_setPersistantScreens, it goes to
|
||||||
* used again before it's deleted, it'll be reused. This behavior works for
|
* the prepared list. If that screen is used again before it's deleted, it'll be
|
||||||
* several models of screen use: pushing screens,
|
* reused.
|
||||||
*
|
*
|
||||||
* Typically, the first screen in a group will prepare all of the nearby screens it
|
* Typically, the first screen in a group will prepare all of the nearby screens it
|
||||||
* may load. (This can happen recursively; a prepared screen can prepare more
|
* may load. When it loads one, the prepared screen will be used.
|
||||||
* screens.) When it loads one, the prepared screen will be used. If the initial
|
|
||||||
* screen is being taken off of the stack, it'll be placed back in the prepared
|
|
||||||
* list; if the new screen then reloads the initial screen, it'll be used out
|
|
||||||
* of the prepared list, and we're back where we started.
|
|
||||||
*
|
|
||||||
* When a screen is used that isn't prepared, it indicates that we've left that
|
|
||||||
* group of screens, and the prepared screens are no longer needed, so we unload
|
|
||||||
* all prepared screens.
|
|
||||||
*
|
*
|
||||||
* A group of screens may only want to preload some screens, and not others,
|
* A group of screens may only want to preload some screens, and not others,
|
||||||
* while still considering those screens part of the same group. For example,
|
* while still considering those screens part of the same group. For example,
|
||||||
@@ -25,6 +17,26 @@
|
|||||||
* to load all at once. By calling GroupScreen(), entering these screens will not
|
* to load all at once. By calling GroupScreen(), entering these screens will not
|
||||||
* trigger cleanup.
|
* trigger cleanup.
|
||||||
*
|
*
|
||||||
|
* Example uses:
|
||||||
|
* - ScreenOptions1 preloads ScreenOptions2, and persists both. Moving from Options1
|
||||||
|
* and Options2 and back is instant and reuses both.
|
||||||
|
* - ScreenMenu groups and persists itself and ScreenSubmenu. ScreenSubmenu
|
||||||
|
* is not preloaded, so it will be loaded on demand the first time it's used,
|
||||||
|
* but will remain loaded if it returns to ScreenMenu.
|
||||||
|
* - ScreenSelectMusic groups itself and ScreenPlayerOptions, and persists only
|
||||||
|
* ScreenSelectMusic. This will cause SSMusic will be loaded once, and SPO to
|
||||||
|
* be loaded on demand.
|
||||||
|
* - ScreenAttract1 preloads and persists ScreenAttract1, 3, 5 and 7, and groups 1
|
||||||
|
* through 7. 1, 3, 5 and 7 will remain in memory; the rest will be loaded on
|
||||||
|
* demand.
|
||||||
|
*
|
||||||
|
* If a screen is added to the screen stack that isn't in the current screen group
|
||||||
|
* (added to by GroupScreen), the screen group is reset: all prepared screens are
|
||||||
|
* unloaded and the persistance list is cleared.
|
||||||
|
*
|
||||||
|
* (For persistance, concurrently preparing a screen is logically equivalent
|
||||||
|
* to SetNewScreen, since it's going to be set after it finishes loading.)
|
||||||
|
*
|
||||||
* Note that not all screens yet support reuse in this way; proper use of BeginScreen
|
* Note that not all screens yet support reuse in this way; proper use of BeginScreen
|
||||||
* is required. This will misbehave if a screen pushes another screen that's already
|
* is required. This will misbehave if a screen pushes another screen that's already
|
||||||
* in use lower on the stack, but that's not useful; it would allow infinite screen
|
* in use lower on the stack, but that's not useful; it would allow infinite screen
|
||||||
@@ -98,6 +110,7 @@ namespace
|
|||||||
vector<LoadedScreen> g_ScreenStack; // bottommost to topmost
|
vector<LoadedScreen> g_ScreenStack; // bottommost to topmost
|
||||||
vector<Screen*> g_OverlayScreens;
|
vector<Screen*> g_OverlayScreens;
|
||||||
set<CString> g_setGroupedScreens;
|
set<CString> g_setGroupedScreens;
|
||||||
|
set<CString> g_setPersistantScreens;
|
||||||
|
|
||||||
vector<LoadedScreen> g_vPreparedScreens;
|
vector<LoadedScreen> g_vPreparedScreens;
|
||||||
vector<Actor*> g_vPreparedBackgrounds;
|
vector<Actor*> g_vPreparedBackgrounds;
|
||||||
@@ -226,19 +239,16 @@ ScreenMessage ScreenManager::PopTopScreenInternal()
|
|||||||
|
|
||||||
ls.m_pScreen->HandleScreenMessage( SM_LoseFocus );
|
ls.m_pScreen->HandleScreenMessage( SM_LoseFocus );
|
||||||
|
|
||||||
if( g_setGroupedScreens.find(ls.m_pScreen->GetName()) != g_setGroupedScreens.end() )
|
if( g_setPersistantScreens.find(ls.m_pScreen->GetName()) != g_setPersistantScreens.end() )
|
||||||
{
|
|
||||||
/* The screen wasn't grouped in with the preloaded screens, not preloaded
|
|
||||||
* itself. It's probably expensive--otherwise we would have preloaded it--so
|
|
||||||
* delete it now, don't move it to g_vPreparedScreens. */
|
|
||||||
if( ls.m_bDeleteWhenDone )
|
|
||||||
SAFE_DELETE( ls.m_pScreen );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* Move the screen back to the prepared list. */
|
/* Move the screen back to the prepared list. */
|
||||||
g_vPreparedScreens.push_back( ls );
|
g_vPreparedScreens.push_back( ls );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( ls.m_bDeleteWhenDone )
|
||||||
|
SAFE_DELETE( ls.m_pScreen );
|
||||||
|
}
|
||||||
|
|
||||||
return ls.m_SendOnPop;
|
return ls.m_SendOnPop;
|
||||||
}
|
}
|
||||||
@@ -343,8 +353,9 @@ void ScreenManager::Update( float fDeltaTime )
|
|||||||
g_bIsConcurrentlyLoading = true;
|
g_bIsConcurrentlyLoading = true;
|
||||||
StartConcurrentRendering();
|
StartConcurrentRendering();
|
||||||
|
|
||||||
AboutToLoadScreen( sScreenName );
|
if( g_setGroupedScreens.find(sScreenName) == g_setGroupedScreens.end() )
|
||||||
PrepareScreenInternal( sScreenName );
|
DeletePreparedScreens();
|
||||||
|
PrepareScreen( sScreenName );
|
||||||
FinishConcurrentRendering();
|
FinishConcurrentRendering();
|
||||||
g_bIsConcurrentlyLoading = false;
|
g_bIsConcurrentlyLoading = false;
|
||||||
|
|
||||||
@@ -432,24 +443,7 @@ Screen* ScreenManager::MakeNewScreen( const CString &sScreenName )
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is called by other code to explicitly preload a screen. */
|
|
||||||
void ScreenManager::PrepareScreen( const CString &sScreenName )
|
void ScreenManager::PrepareScreen( const CString &sScreenName )
|
||||||
{
|
|
||||||
/* Normally, a screen is always either preloaded, or grouped in with the
|
|
||||||
* preloaded screens, not both. If preloading a screen that's also grouped,
|
|
||||||
* warn and remove from g_setGroupedScreens; if we leave it, we'll have
|
|
||||||
* obscure inconsistent screen loading behavior (the preloaded screen won't
|
|
||||||
* be reused). */
|
|
||||||
if( g_setGroupedScreens.find(sScreenName) != g_setGroupedScreens.end() )
|
|
||||||
{
|
|
||||||
LOG->Warn( "PrepareScreen: screen %s was prepared when already grouped", sScreenName.c_str() );
|
|
||||||
g_setGroupedScreens.erase( sScreenName );
|
|
||||||
}
|
|
||||||
|
|
||||||
PrepareScreenInternal( sScreenName );
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScreenManager::PrepareScreenInternal( const CString &sScreenName )
|
|
||||||
{
|
{
|
||||||
// If the screen is already prepared, stop.
|
// If the screen is already prepared, stop.
|
||||||
for( int i = (int)g_vPreparedScreens.size()-1; i>=0; i-- )
|
for( int i = (int)g_vPreparedScreens.size()-1; i>=0; i-- )
|
||||||
@@ -500,19 +494,12 @@ void ScreenManager::PrepareScreenInternal( const CString &sScreenName )
|
|||||||
|
|
||||||
void ScreenManager::GroupScreen( const CString &sScreenName )
|
void ScreenManager::GroupScreen( const CString &sScreenName )
|
||||||
{
|
{
|
||||||
/* If a screen is preloaded, don't add it to g_setGroupedScreens (see
|
g_setGroupedScreens.insert( sScreenName );
|
||||||
* PrepareScreen() comment). */
|
|
||||||
for( int i = (int)g_vPreparedScreens.size()-1; i>=0; i-- )
|
|
||||||
{
|
|
||||||
const Screen *pScreen = g_vPreparedScreens[i].m_pScreen;
|
|
||||||
if( pScreen->GetName() == sScreenName )
|
|
||||||
{
|
|
||||||
LOG->Warn( "GroupScreen: screen %s was grouped when already prepared", sScreenName.c_str() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_setGroupedScreens.insert( sScreenName );
|
void ScreenManager::PersistantScreen( const CString &sScreenName )
|
||||||
|
{
|
||||||
|
g_setPersistantScreens.insert( sScreenName );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenManager::ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage SM )
|
bool ScreenManager::ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage SM )
|
||||||
@@ -542,6 +529,9 @@ void ScreenManager::DeletePreparedScreens()
|
|||||||
SAFE_DELETE( *a );
|
SAFE_DELETE( *a );
|
||||||
g_vPreparedBackgrounds.clear();
|
g_vPreparedBackgrounds.clear();
|
||||||
|
|
||||||
|
g_setGroupedScreens.clear();
|
||||||
|
g_setPersistantScreens.clear();
|
||||||
|
|
||||||
/* Now that we've actually deleted a screen, it makes sense to clear out
|
/* Now that we've actually deleted a screen, it makes sense to clear out
|
||||||
* cached textures. */
|
* cached textures. */
|
||||||
TEXTUREMAN->DeleteCachedTextures();
|
TEXTUREMAN->DeleteCachedTextures();
|
||||||
@@ -570,25 +560,6 @@ void ScreenManager::SetNewScreen( const CString &sScreenName )
|
|||||||
m_sDelayedScreen = sScreenName;
|
m_sDelayedScreen = sScreenName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is called before loading (not preparing) a screen. If the screen isn't
|
|
||||||
* already prepared, then delete all prepared screens and backgrounds. */
|
|
||||||
void ScreenManager::AboutToLoadScreen( const CString &sScreenName )
|
|
||||||
{
|
|
||||||
FOREACH( LoadedScreen, g_vPreparedScreens, s )
|
|
||||||
{
|
|
||||||
if( s->m_pScreen->GetName() == sScreenName )
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( g_setGroupedScreens.find(sScreenName) != g_setGroupedScreens.end() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// We're not loading a screen that was preloaded, or that was listed as part
|
|
||||||
// of the same screen group, so we're entering something new. Clear all preloaded
|
|
||||||
// screens.
|
|
||||||
DeletePreparedScreens();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScreenManager::LoadDelayedScreen()
|
void ScreenManager::LoadDelayedScreen()
|
||||||
{
|
{
|
||||||
const bool bWasOnSystemMenu = !g_ScreenStack.empty() && g_ScreenStack.back().m_pScreen->GetScreenType() == system_menu;
|
const bool bWasOnSystemMenu = !g_ScreenStack.empty() && g_ScreenStack.back().m_pScreen->GetScreenType() == system_menu;
|
||||||
@@ -603,11 +574,12 @@ void ScreenManager::LoadDelayedScreen()
|
|||||||
* If DelayedScreenLoad is true, delete old screens first; this lowers
|
* If DelayedScreenLoad is true, delete old screens first; this lowers
|
||||||
* memory requirements, but results in redundant loads as we unload common
|
* memory requirements, but results in redundant loads as we unload common
|
||||||
* data. */
|
* data. */
|
||||||
if( PREFSMAN->m_bDelayedScreenLoad )
|
bool bTimeToDeleteScreens = (g_setGroupedScreens.find(sScreenName) == g_setGroupedScreens.end());
|
||||||
AboutToLoadScreen( sScreenName );
|
if( bTimeToDeleteScreens && PREFSMAN->m_bDelayedScreenLoad )
|
||||||
|
DeletePreparedScreens();
|
||||||
|
|
||||||
// Load the screen, if it's not already prepared.
|
// Load the screen, if it's not already prepared.
|
||||||
PrepareScreenInternal( sScreenName );
|
PrepareScreen( sScreenName );
|
||||||
|
|
||||||
//
|
//
|
||||||
// Find the prepped screen.
|
// Find the prepped screen.
|
||||||
@@ -664,8 +636,8 @@ void ScreenManager::LoadDelayedScreen()
|
|||||||
if( bWasOnSystemMenu && !bIsOnSystemMenu )
|
if( bWasOnSystemMenu && !bIsOnSystemMenu )
|
||||||
PREFSMAN->SaveGlobalPrefsToDisk();
|
PREFSMAN->SaveGlobalPrefsToDisk();
|
||||||
|
|
||||||
if( !PREFSMAN->m_bDelayedScreenLoad )
|
if( bTimeToDeleteScreens && !PREFSMAN->m_bDelayedScreenLoad )
|
||||||
AboutToLoadScreen( sScreenName );
|
DeletePreparedScreens();
|
||||||
|
|
||||||
TEXTUREMAN->DiagnosticOutput();
|
TEXTUREMAN->DiagnosticOutput();
|
||||||
|
|
||||||
@@ -678,7 +650,7 @@ void ScreenManager::LoadDelayedScreen()
|
|||||||
void ScreenManager::AddNewScreenToTop( const CString &sScreenName, ScreenMessage SendOnPop )
|
void ScreenManager::AddNewScreenToTop( const CString &sScreenName, ScreenMessage SendOnPop )
|
||||||
{
|
{
|
||||||
// Load the screen, if it's not already prepared.
|
// Load the screen, if it's not already prepared.
|
||||||
PrepareScreenInternal( sScreenName );
|
PrepareScreen( sScreenName );
|
||||||
|
|
||||||
// Find the prepped screen.
|
// Find the prepped screen.
|
||||||
LoadedScreen ls;
|
LoadedScreen ls;
|
||||||
@@ -810,6 +782,9 @@ class LunaScreenManager: public Luna<ScreenManager>
|
|||||||
public:
|
public:
|
||||||
LunaScreenManager() { LUA->Register( Register ); }
|
LunaScreenManager() { LUA->Register( Register ); }
|
||||||
|
|
||||||
|
// Note: PrepareScreen binding is not allowed; loading data inside
|
||||||
|
// Lua causes the Lua lock to be held for the duration of the load,
|
||||||
|
// which blocks concurrent rendering
|
||||||
static int SetNewScreen( T* p, lua_State *L ) { p->SetNewScreen( SArg(1) ); return 0; }
|
static int SetNewScreen( T* p, lua_State *L ) { p->SetNewScreen( SArg(1) ); return 0; }
|
||||||
static int GetTopScreen( T* p, lua_State *L )
|
static int GetTopScreen( T* p, lua_State *L )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public:
|
|||||||
void AddNewScreenToTop( const CString &sName, ScreenMessage SendOnPop=SM_None );
|
void AddNewScreenToTop( const CString &sName, ScreenMessage SendOnPop=SM_None );
|
||||||
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.
|
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.
|
||||||
void GroupScreen( const CString &sScreenName );
|
void GroupScreen( const CString &sScreenName );
|
||||||
|
void PersistantScreen( const CString &sScreenName );
|
||||||
bool ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage send_when_done = SM_None );
|
bool ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage send_when_done = SM_None );
|
||||||
bool IsConcurrentlyLoading() const;
|
bool IsConcurrentlyLoading() const;
|
||||||
void DeletePreparedScreens();
|
void DeletePreparedScreens();
|
||||||
@@ -66,8 +67,6 @@ public:
|
|||||||
void PlaySharedBackgroundOffCommand();
|
void PlaySharedBackgroundOffCommand();
|
||||||
void ZeroNextUpdate();
|
void ZeroNextUpdate();
|
||||||
private:
|
private:
|
||||||
void AboutToLoadScreen( const CString &sScreenName );
|
|
||||||
|
|
||||||
Screen *m_pInputFocus; // NULL = top of m_ScreenStack
|
Screen *m_pInputFocus; // NULL = top of m_ScreenStack
|
||||||
|
|
||||||
CString m_sSystemMessage;
|
CString m_sSystemMessage;
|
||||||
@@ -83,7 +82,6 @@ private:
|
|||||||
bool m_bZeroNextUpdate;
|
bool m_bZeroNextUpdate;
|
||||||
|
|
||||||
void LoadDelayedScreen();
|
void LoadDelayedScreen();
|
||||||
void PrepareScreenInternal( const CString &sScreenName );
|
|
||||||
ScreenMessage PopTopScreenInternal();
|
ScreenMessage PopTopScreenInternal();
|
||||||
|
|
||||||
// Keep these sounds always loaded, because they could be
|
// Keep these sounds always loaded, because they could be
|
||||||
|
|||||||
Reference in New Issue
Block a user