diff --git a/stepmania/src/Course.cpp b/stepmania/src/Course.cpp index 2c79830bd1..a887b0f0c7 100644 --- a/stepmania/src/Course.cpp +++ b/stepmania/src/Course.cpp @@ -548,29 +548,42 @@ static vector GetFilteredBestSongs( StepsType nt ) return ret; } +/* This is called by many simple functions, like Course::GetTotalSeconds, and may + * be called on all songs to sort. It can take time to execute, so we cache the + * results. */ void Course::GetCourseInfo( StepsType nt, vector &ci, int Difficult ) const { - vector entries = m_entries; + const InfoParams params( nt, IsDifficult(Difficult) ); + InfoCache::const_iterator it = m_InfoCache.find( params ); + if( it != m_InfoCache.end() ) + { + ci = it->second; + return; + } /* Different seed for each course, but the same for the whole round: */ RandomGen rnd( GAMESTATE->m_iRoundSeed + GetHashForString(m_sName) ); + vector tmp_entries; if( m_bRandomize ) { /* Always randomize the same way per round. Otherwise, the displayed course * will change every time it's viewed, and the displayed order will have no * bearing on what you'll actually play. */ - random_shuffle( entries.begin(), entries.end(), rnd ); + tmp_entries = m_entries; + random_shuffle( tmp_entries.begin(), tmp_entries.end(), rnd ); } + const vector &entries = m_bRandomize? tmp_entries:m_entries; + /* This can take some time, so don't fill it out unless we need it. */ bool bMostPlayedSet = false; vector vSongsByMostPlayed; - vector AllSongsShuffled = SONGMAN->GetAllSongs(); - random_shuffle( AllSongsShuffled.begin(), AllSongsShuffled.end(), rnd ); - int CurSong = 0; /* Current offset into AllSongsShuffled */ + bool bShuffledSet = false; + vector AllSongsShuffled; + int CurSong = 0; /* Current offset into AllSongsShuffled */ ci.clear(); for( unsigned i=0; i &ci, int Difficul case COURSE_ENTRY_RANDOM: case COURSE_ENTRY_RANDOM_WITHIN_GROUP: { + if( !bShuffledSet ) + { + AllSongsShuffled = SONGMAN->GetAllSongs(); + random_shuffle( AllSongsShuffled.begin(), AllSongsShuffled.end(), rnd ); + bShuffledSet = true; + } + // find a song with the notes we want for( unsigned j=0; j &ci, int Difficul cinfo.Difficult = IsDifficult(Difficult); ci.push_back( cinfo ); } + + /* Cache results. */ + m_InfoCache[params] = ci; +} + +void Course::ClearCache() +{ + m_InfoCache.clear(); } RageColor Course::GetColor() const diff --git a/stepmania/src/Course.h b/stepmania/src/Course.h index c65e3ceea6..96d361d8b0 100644 --- a/stepmania/src/Course.h +++ b/stepmania/src/Course.h @@ -14,6 +14,7 @@ #include "PlayerNumber.h" #include "GameConstantsAndTypes.h" #include "Attack.h" +#include struct PlayerOptions; struct SongOptions; @@ -196,8 +197,16 @@ public: void UpdateCourseStats(); + /* Call per-screen, and if song or notes pointers change: */ + void ClearCache(); + private: void GetMeterRange( int stage, int& iMeterLowOut, int& iMeterHighOut, int Difficult = -1 ) const; + + typedef pair InfoParams; + typedef vector InfoData; + typedef map InfoCache; + mutable InfoCache m_InfoCache; }; diff --git a/stepmania/src/ScreenManager.cpp b/stepmania/src/ScreenManager.cpp index d2fe3a6bb3..36efe12038 100644 --- a/stepmania/src/ScreenManager.cpp +++ b/stepmania/src/ScreenManager.cpp @@ -477,6 +477,10 @@ Screen* ScreenManager::MakeNewScreen( CString sClassName ) * screens turn this off in their ctor if they handle timers themselves (gameplay, edit). */ SOUND->HandleSongTimer( true ); + /* Cleanup song data. This can free up a fair bit of memory, so do it before + * creating the new screen, to lower peak memory usage slightly. */ + SONGMAN->Cleanup(); + Screen *ret = Screen::Create( sClassName ); /* Loading probably took a little while. Let's reset stats. This prevents us @@ -485,9 +489,6 @@ Screen* ScreenManager::MakeNewScreen( CString sClassName ) * apparent. */ DISPLAY->ResetStats(); - /* This is a convenient time to clean up our song cache. */ - SONGMAN->CompressSongs(); - return ret; } diff --git a/stepmania/src/Song.cpp b/stepmania/src/Song.cpp index a781ba41fe..a2519ca76a 100644 --- a/stepmania/src/Song.cpp +++ b/stepmania/src/Song.cpp @@ -19,6 +19,7 @@ #include "NoteData.h" #include "RageSound.h" #include "RageException.h" +#include "SongManager.h" #include "SongCacheIndex.h" #include "GameManager.h" #include "PrefsManager.h" @@ -100,6 +101,10 @@ void Song::Reset() Song empty; *this = empty; + + /* Courses cache Notes* pointers. On the off chance that this isn't the last + * thing this screen does, clear that cache. */ + SONGMAN->Cleanup(); } diff --git a/stepmania/src/SongManager.cpp b/stepmania/src/SongManager.cpp index 15a2be70c8..1c3a6676bf 100644 --- a/stepmania/src/SongManager.cpp +++ b/stepmania/src/SongManager.cpp @@ -567,9 +567,10 @@ void SongManager::FreeCourses() /* Called periodically to wipe out cached NoteData. This is called when we change * screens. */ -void SongManager::CompressSongs() +void SongManager::Cleanup() { - for( unsigned i=0; im_apNotes.size(); n++ ) @@ -578,6 +579,10 @@ void SongManager::CompressSongs() pNotes->Compress(); } } + + /* Erase cached course info. */ + for( i=0; i < m_pCourses.size(); i++ ) + m_pCourses[i]->ClearCache(); } void SongManager::GetAllCourses( vector &AddTo, bool bIncludeAutogen ) diff --git a/stepmania/src/SongManager.h b/stepmania/src/SongManager.h index 03503faceb..da4180a2d6 100644 --- a/stepmania/src/SongManager.h +++ b/stepmania/src/SongManager.h @@ -34,7 +34,8 @@ public: void InitSongsFromDisk( LoadingWindow *ld ); void FreeSongs(); - void CompressSongs(); + void Cleanup(); + void LoadGroupSymLinks( CString sDir, CString sGroupFolder ); void InitCoursesFromDisk( LoadingWindow *ld );