Some optimizations with the scroll segments.

Scroll segments seem to cause the lag when there's a lot of segment
in the steps, say, 165. On my Mac, it drops the frame-rate down by about
30 FPS in the end.

This is because the way the the game tries to determine the exact position
of the note, it has to go through all the scroll segments from the
beginning until it finds the currently active scroll segment and keep
track of the beat to display instead.

And each frame, the game needs to determine the position for each note,
and also when figuring out the first beat and last beat to display. This
makes the game slow even with just 165 scroll segments.

The approach is that at every game update, we create an optimized data
structure that can calculate the displayed beat from real song beat
in O(log n) time. So in this approach we iterate through EVERY scroll
segments each frame instead of iterating through the scroll segments
for each note for each frame. This eliminates the lag on this side.

The data structure is recomputed each update because I assume that the
timing segments can change at any time, but not during draws.

A similar problem is: when using Cmods, GetElapsedTimeFromBeat()
is also called very often (say, for each note and also in FindFirst/Last
DisplayedBeat), we may as well have to make these queries faster for songs
that has hundreds of BPM changes or stops or delays to prevent the game
from lagging.
This commit is contained in:
Thai Pangsakulyanont
2011-09-14 23:48:24 +07:00
parent be3094b5af
commit d285c80a72
3 changed files with 66 additions and 1 deletions
+26 -1
View File
@@ -213,6 +213,31 @@ void ArrowEffects::Update()
}
}
static float GetDisplayedBeat( const PlayerState* pPlayerState, float beat )
{
// do a binary search here
const vector<CacheDisplayedBeat> &data = pPlayerState->m_CacheDisplayedBeat;
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].displayedBeat + data[m].velocity * (beat - data[m].beat);
}
else if( data[m].beat <= beat )
{
l = m + 1;
}
else
{
r = m - 1;
}
}
return beat;
}
/* For visibility testing: if bAbsolute is false, random modifiers must return
* the minimum possible scroll speed. */
float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute )
@@ -235,7 +260,7 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
float bShowEffects = !( GAMESTATE->m_bInStepEditor || !GAMESTATE->m_bIsUsingStepTiming );
float fBeatsUntilStep = fNoteBeat - fSongBeat;
if( bShowEffects )
fBeatsUntilStep = pCurSteps->m_Timing.GetDisplayedBeat(fNoteBeat) - pCurSteps->m_Timing.GetDisplayedBeat(fSongBeat);
fBeatsUntilStep = GetDisplayedBeat(pPlayerState, fNoteBeat) - GetDisplayedBeat(pPlayerState, fSongBeat);
float fYOffsetBeatSpacing = fBeatsUntilStep;
float fSpeedMultiplier = bShowEffects ?
pCurSteps->m_Timing.GetDisplayedSpeedPercent(