From c63809125aa809b72ff3faabc0a73849fee58b56 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Mon, 19 Sep 2011 12:50:18 +0700 Subject: [PATCH] keep as many notes after the receptor on the screen as it could, but limit it to 32. [we may need to fine-tune this number later, but now missed notes no longer disappear on slow / stopped parts using speed/scroll segments] Please note that this code might not work well with negative speeds/scrolls. --- src/NoteField.cpp | 85 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/src/NoteField.cpp b/src/NoteField.cpp index e1a2892a11..affa91454a 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -673,37 +673,80 @@ void NoteField::DrawBGChangeText( const float fBeat, const RString sNewBGName ) m_textMeasureNumber.Draw(); } -// CPU OPTIMIZATION OPPORTUNITY: -// change this probing to binary search +CacheNoteStat GetNumNotesFromBeginning( const PlayerState *pPlayerState, float beat ) +{ + // XXX: I realized that I have copied and pasted my binary search code 3 times already. + // how can we abstract this? + const vector &data = pPlayerState->m_CacheNoteStat; + int max = data.size() - 1; + int l = 0, r = max; + while( l <= r ) + { + int m = ( l + r ) / 2; + if( ( m == 0 || data[m].beat <= beat ) && ( m == max || beat < data[m + 1].beat ) ) + { + return data[m]; + } + else if( data[m].beat <= beat ) + { + l = m + 1; + } + else + { + r = m - 1; + } + } + CacheNoteStat dummy = { 0, 0, 0 }; + return dummy; +} + +int GetNumNotesRange( const PlayerState* pPlayerState, float fLow, float fHigh ) +{ + CacheNoteStat low = GetNumNotesFromBeginning( pPlayerState, fLow ); + CacheNoteStat high = GetNumNotesFromBeginning( pPlayerState, fHigh ); + return high.notesUpper - low.notesLower; +} + float FindFirstDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceAfterTargetsPixels ) { - float fFirstBeatToDraw = pPlayerState->GetDisplayedPosition().m_fSongBeat-4; // Adjust to balance off performance and showing enough notes. - - /* In Boomerang, we'll usually have two sections of notes: before and after - * the peak. We always start drawing before the peak, and end after it, or - * we may falsely detect the off-screen portion as the end (or beginning) - * of the stream. */ - bool bBoomerang; + + float fLow = 0, fHigh = pPlayerState->GetDisplayedPosition().m_fSongBeat; + + bool bHasCache = pPlayerState->m_CacheNoteStat.size() > 0; + + if( !bHasCache ) { - const float* fAccels = pPlayerState->m_PlayerOptions.GetCurrent().m_fAccels; - bBoomerang = (fAccels[PlayerOptions::ACCEL_BOOMERANG] != 0); + fLow = fHigh - 4.0f; } - - while( fFirstBeatToDraw < pPlayerState->GetDisplayedPosition().m_fSongBeat ) + + const int NUM_ITERATIONS = 24; + const int MAX_NOTES_AFTER = 32; + + float fFirstBeatToDraw = fLow; + + for( int i = 0; i < NUM_ITERATIONS; i ++ ) { + + float fMid = (fLow + fHigh) / 2.0f; + bool bIsPastPeakYOffset; float fPeakYOffset; - float fYOffset = ArrowEffects::GetYOffset( pPlayerState, 0, fFirstBeatToDraw, fPeakYOffset, bIsPastPeakYOffset, true ); + float fYOffset = ArrowEffects::GetYOffset( pPlayerState, 0, fMid, fPeakYOffset, bIsPastPeakYOffset, true ); - if( bBoomerang && bIsPastPeakYOffset ) - break; // stop probing - else if( fYOffset < iDrawDistanceAfterTargetsPixels ) // off screen - fFirstBeatToDraw += 0.1f; // move toward fSongBeat - else // on screen - break; // stop probing + if( fYOffset < iDrawDistanceAfterTargetsPixels || ( bHasCache && GetNumNotesRange( pPlayerState, fMid, pPlayerState->GetDisplayedPosition().m_fSongBeat ) > MAX_NOTES_AFTER ) ) // off screen / too many notes + { + fFirstBeatToDraw = fMid; // move towards fSongBeat + fLow = fMid; + } + else // on screen, move away!! + { + fHigh = fMid; + } + } - fFirstBeatToDraw -= 0.1f; // rewind if we intentionally overshot + return fFirstBeatToDraw; + } float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceBeforeTargetsPixels )