Skip to content

Commit

Permalink
Make calculating stats not cause a noticeable lag spike, and make the…
Browse files Browse the repository at this point in the history
… frame skip information more easily readable. Move over global fDeltaTime, and device time stamps along with Step over to Crono.
  • Loading branch information
xwidghet committed Nov 1, 2016
1 parent 5a5eec8 commit 8bec801
Show file tree
Hide file tree
Showing 17 changed files with 94 additions and 70 deletions.
7 changes: 6 additions & 1 deletion src/GameLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
#include "NetworkSyncManager.h"
#include "RageTimer.h"
#include "RageInput.h"
#include <chrono>

static RageTimer g_GameplayTimer;

static auto g_AccurateGameplayTimer = std::chrono::high_resolution_clock::now();

static Preference<bool> g_bNeverBoostAppPriority( "NeverBoostAppPriority", false );

/* experimental: force a specific update rate. This prevents big animation
Expand Down Expand Up @@ -269,7 +272,9 @@ void GameLoop::RunGameLoop()
}

// Update
float fDeltaTime = g_GameplayTimer.GetDeltaTime();
auto frameStart = std::chrono::high_resolution_clock::now() - g_AccurateGameplayTimer;
float fDeltaTime = std::chrono::duration_cast<std::chrono::microseconds>(frameStart).count() / 1000000.0;
g_AccurateGameplayTimer = std::chrono::high_resolution_clock::now();

if( g_fConstantUpdateDeltaSeconds > 0 )
fDeltaTime = g_fConstantUpdateDeltaSeconds;
Expand Down
41 changes: 24 additions & 17 deletions src/InputFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ struct ButtonState
DeviceInput m_DeviceInput;

// Timestamp of m_BeingHeld changing.
RageTimer m_BeingHeldTime;
std::chrono::steady_clock::time_point m_BeingHeldTime;

// The time that we actually reported the last event (used for debouncing).
RageTimer m_LastReportTime;
std::chrono::steady_clock::time_point m_LastReportTime;

// The timestamp of the last reported change. Unlike m_BeingHeldTime, this
// value is debounced along with the input state. (This is the same as
// m_fSecsHeld, except this isn't affected by Update scaling.)
RageTimer m_LastInputTime;
std::chrono::steady_clock::time_point m_LastInputTime;
};

struct DeviceButtonPair
Expand Down Expand Up @@ -172,8 +172,8 @@ void InputFilter::ResetRepeatRate()
}

ButtonState::ButtonState():
m_BeingHeldTime(RageZeroTimer),
m_LastReportTime(RageZeroTimer)
m_BeingHeldTime(std::chrono::microseconds{ 0 }),
m_LastReportTime(std::chrono::microseconds{ 0 })
{
m_BeingHeld = false;
m_bLastReportedHeld = false;
Expand All @@ -184,8 +184,9 @@ void InputFilter::ButtonPressed( const DeviceInput &di )
{
LockMut(*queuemutex);

if( di.ts.IsZero() )
LOG->Warn( "InputFilter::ButtonPressed: zero timestamp is invalid" );
// Can't happen anymore, but we may want similar validation in the future.
//if( di.ts.IsZero() )
// LOG->Warn( "InputFilter::ButtonPressed: zero timestamp is invalid" );

// Filter out input that is beyond the range of the current system.
if(di.device >= NUM_InputDevice)
Expand All @@ -202,7 +203,7 @@ void InputFilter::ButtonPressed( const DeviceInput &di )
ButtonState &bs = GetButtonState( di );

// Flush any delayed input, like Update() (in case Update() isn't being called).
RageTimer now;
auto now = std::chrono::high_resolution_clock::now();
CheckButtonChange( bs, di, now );

bs.m_DeviceInput = di;
Expand Down Expand Up @@ -230,19 +231,18 @@ void InputFilter::SetButtonComment( const DeviceInput &di, const RString &sComme
void InputFilter::ResetDevice( InputDevice device )
{
LockMut(*queuemutex);
RageTimer now;

const ButtonStateMap ButtonStates( g_ButtonStates );
FOREACHM_CONST( DeviceButtonPair, ButtonState, ButtonStates, b )
{
const DeviceButtonPair &db = b->first;
if( db.device == device )
ButtonPressed( DeviceInput(device, db.button, 0, now) );
ButtonPressed( DeviceInput(device, db.button, 0, std::chrono::high_resolution_clock::now()) );
}
}

/** @brief Check for reportable presses. */
void InputFilter::CheckButtonChange( ButtonState &bs, DeviceInput di, const RageTimer &now )
void InputFilter::CheckButtonChange( ButtonState &bs, DeviceInput di, const std::chrono::steady_clock::time_point &now )
{
if( bs.m_BeingHeld == bs.m_bLastReportedHeld )
return;
Expand All @@ -251,16 +251,18 @@ void InputFilter::CheckButtonChange( ButtonState &bs, DeviceInput di, const Rage

/* Possibly apply debounce,
* If the input was coin, possibly apply distinct coin debounce in the else below. */
auto timeDelta = now - bs.m_LastReportTime;
auto delta = (std::chrono::duration_cast<std::chrono::microseconds>(timeDelta).count() / 1000000.0);
if (! INPUTMAPPER->DeviceToGame(di, gi) || gi.button != GAME_BUTTON_COIN )
{
/* If the last IET_FIRST_PRESS or IET_RELEASE event was sent too recently,
* wait a while before sending it. */
if( now - bs.m_LastReportTime < g_fInputDebounceTime )
if(delta < g_fInputDebounceTime )
{
return;
}
} else {
if( now - bs.m_LastReportTime < PREFSMAN->m_fDebounceCoinInputTime )
if(delta < PREFSMAN->m_fDebounceCoinInputTime )
{
return;
}
Expand Down Expand Up @@ -312,7 +314,7 @@ void InputFilter::MakeButtonStateList( vector<DeviceInput> &aInputOut ) const

void InputFilter::Update( float fDeltaTime )
{
RageTimer now;
auto now = std::chrono::high_resolution_clock::now();

INPUTMAN->Update();

Expand Down Expand Up @@ -340,7 +342,10 @@ void InputFilter::Update( float fDeltaTime )
{
// If the key isn't pressed, and hasn't been pressed for a while
// (so debouncing isn't interested in it), purge the entry.
if( now - bs.m_LastReportTime > g_fInputDebounceTime &&
auto timeAgo = now - bs.m_LastReportTime;
float lastReportTime = (std::chrono::duration_cast<std::chrono::microseconds>(timeAgo).count() / 1000000.0);

if( lastReportTime > g_fInputDebounceTime &&
bs.m_DeviceInput.level == 0.0f )
ButtonsToErase.push_back( b );
continue;
Expand Down Expand Up @@ -374,7 +379,7 @@ void InputFilter::Update( float fDeltaTime )
/* Set the timestamp to the exact time of the repeat. This way, as long
* as tab/` aren't being used, the timestamp will always increase steadily
* during repeats. */
di.ts = bs.m_LastInputTime + fRepeatTime;
di.ts = std::chrono::high_resolution_clock::time_point(bs.m_LastInputTime + std::chrono::microseconds((int)(fRepeatTime * 1000000)));

ReportButtonChange( di, IET_REPEAT );
}
Expand Down Expand Up @@ -410,7 +415,9 @@ float InputFilter::GetSecsHeld( const DeviceInput &di, const DeviceInputList *pB
const DeviceInput *pDI = FindItemBinarySearch( pButtonState->begin(), pButtonState->end(), di );
if( pDI == NULL )
return 0;
return pDI->ts.Ago();

auto inputLength = std::chrono::high_resolution_clock::now() - pDI->ts;
return (std::chrono::duration_cast<std::chrono::microseconds>(inputLength).count() / 1000000.0);
}

float InputFilter::GetLevel( const DeviceInput &di, const DeviceInputList *pButtonState ) const
Expand Down
2 changes: 1 addition & 1 deletion src/InputFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class InputFilter
void PushSelf( lua_State *L );

private:
void CheckButtonChange( ButtonState &bs, DeviceInput di, const RageTimer &now );
void CheckButtonChange( ButtonState &bs, DeviceInput di, const std::chrono::steady_clock::time_point &now );
void ReportButtonChange( const DeviceInput &di, InputEventType t );
void MakeButtonStateList( vector<DeviceInput> &aInputOut ) const;

Expand Down
17 changes: 6 additions & 11 deletions src/InputQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void InputQueue::RememberInput( const InputEventPlus &iep )
m_aQueue[c].push_back( iep );
}

bool InputQueue::WasPressedRecently( GameController c, const GameButton button, const RageTimer &OldestTimeAllowed, InputEventPlus *pIEP )
bool InputQueue::WasPressedRecently( GameController c, const GameButton button, const std::chrono::steady_clock::time_point &OldestTimeAllowed, InputEventPlus *pIEP )
{
for( int queue_index=m_aQueue[c].size()-1; queue_index>=0; queue_index-- ) // iterate newest to oldest
{
Expand Down Expand Up @@ -61,20 +61,15 @@ bool InputQueueCode::EnteredCode( GameController controller ) const
if( m_aPresses.size() == 0 )
return false;

RageTimer OldestTimeAllowed;
if( m_fMaxSecondsBack == -1 )
OldestTimeAllowed.SetZero();
else
OldestTimeAllowed += -m_fMaxSecondsBack;

// iterate newest to oldest
int iSequenceIndex = m_aPresses.size()-1; // count down
const vector<InputEventPlus> &aQueue = INPUTQUEUE->GetQueue( controller );
int iQueueIndex = aQueue.size()-1;
while( iQueueIndex >= 0 )
{
/* If the buttons are too old, stop searching because we're not going to find a match. */
if( !OldestTimeAllowed.IsZero() && aQueue[iQueueIndex].DeviceI.ts < OldestTimeAllowed )
float inputDelta = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - aQueue[iQueueIndex].DeviceI.ts).count() / 1000000.0;
if(m_fMaxSecondsBack != -1 && inputDelta > m_fMaxSecondsBack)
return false;

/* If the last press is an input type we're not interested in, skip it
Expand All @@ -88,8 +83,6 @@ bool InputQueueCode::EnteredCode( GameController controller ) const

/* Search backwards for all of Press.m_aButtonsToPress pressed within g_fTapThreshold seconds
* with m_aButtonsToHold pressed. Start looking at iQueueIndex. */
RageTimer OldestTimeAllowedForTap( aQueue[iQueueIndex].DeviceI.ts );
OldestTimeAllowedForTap += -g_fSimultaneousThreshold;

bool bMatched = false;
int iMinSearchIndexUsed = iQueueIndex;
Expand All @@ -100,7 +93,9 @@ bool InputQueueCode::EnteredCode( GameController controller ) const
for( ; iQueueSearchIndex>=0; --iQueueSearchIndex ) // iterate newest to oldest
{
const InputEventPlus &iep = aQueue[iQueueSearchIndex];
if( iep.DeviceI.ts < OldestTimeAllowedForTap ) // buttons are too old. Stop searching because we're not going to find a match

float simulInputDelta = std::chrono::duration_cast<std::chrono::microseconds>(aQueue[iQueueIndex].DeviceI.ts - iep.DeviceI.ts).count() / 1000000.0;
if( simulInputDelta > g_fSimultaneousThreshold) // buttons are too old. Stop searching because we're not going to find a match
break;

if( !Press.m_InputTypes[iep.type] )
Expand Down
3 changes: 2 additions & 1 deletion src/InputQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "GameInput.h"
#include "InputFilter.h"
#include <chrono>

class InputEventPlus;
class RageTimer;
Expand All @@ -14,7 +15,7 @@ class InputQueue
InputQueue();

void RememberInput( const InputEventPlus &gi );
bool WasPressedRecently( GameController c, const GameButton button, const RageTimer &OldestTimeAllowed, InputEventPlus *pIEP = NULL );
bool WasPressedRecently( GameController c, const GameButton button, const std::chrono::steady_clock::time_point &OldestTimeAllowed, InputEventPlus *pIEP = NULL );
const vector<InputEventPlus> &GetQueue( GameController c ) const { return m_aQueue[c]; }
void ClearQueue( GameController c );

Expand Down
20 changes: 12 additions & 8 deletions src/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,8 @@ void Player::SendComboMessages( unsigned int iOldCombo, unsigned int iOldMissCom

void Player::Update( float fDeltaTime )
{
const RageTimer now;
//const RageTimer now;
const auto now = std::chrono::high_resolution_clock::now();
// Don't update if we haven't been loaded yet.
if( !m_bLoaded )
return;
Expand Down Expand Up @@ -1968,19 +1969,22 @@ void Player::PlayKeysound( const TapNote &tn, TapNoteScore score )
}
}

void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease )
void Player::Step( int col, int row, const std::chrono::steady_clock::time_point &tm, bool bHeld, bool bRelease, float padStickSeconds )
{
if( IsOniDead() )
return;

// TODO: remove use of PlayerNumber
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;

// Do everything that depends on a RageTimer here;
// Do everything that depends on a timer here;
// set your breakpoints somewhere after this block.
auto stepDelta = std::chrono::high_resolution_clock::now() - tm;
auto stepAgo = (std::chrono::duration_cast<std::chrono::microseconds>(stepDelta).count() / 1000000.0) - padStickSeconds;

const float fLastBeatUpdate = m_pPlayerState->m_Position.m_LastBeatUpdate.Ago();
const float fPositionSeconds = m_pPlayerState->m_Position.m_fMusicSeconds - tm.Ago();
const float fTimeSinceStep = tm.Ago();
const float fPositionSeconds = m_pPlayerState->m_Position.m_fMusicSeconds - stepAgo;
const float fTimeSinceStep = stepAgo;

float fSongBeat = m_pPlayerState->m_Position.m_fSongBeat;

Expand Down Expand Up @@ -2705,7 +2709,7 @@ void Player::FlashGhostRow( int iRow )
}
}

void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
void Player::CrossedRows( int iLastRowCrossed, const std::chrono::steady_clock::time_point &now )
{
//LOG->Trace( "Player::CrossedRows %d %d", iFirstRowCrossed, iLastRowCrossed );

Expand Down Expand Up @@ -2734,7 +2738,7 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
float fSecsHeld = INPUTMAPPER->GetSecsHeld(GameI[i], m_pPlayerState->m_mp);
if(fSecsHeld >= PREFSMAN->m_fPadStickSeconds)
{
Step(iTrack, -1, now - PREFSMAN->m_fPadStickSeconds, true, false);
Step(iTrack, -1, now, true, false, PREFSMAN->m_fPadStickSeconds);
}
}
}
Expand Down Expand Up @@ -2762,7 +2766,7 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
float fSecsHeld = INPUTMAPPER->GetSecsHeld(GameI[i], m_pPlayerState->m_mp);
if(fSecsHeld >= PREFSMAN->m_fPadStickSeconds)
{
Step( iTrack, -1, now - PREFSMAN->m_fPadStickSeconds, true, false );
Step( iTrack, -1, now, true, false, PREFSMAN->m_fPadStickSeconds );
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ThemeMetric.h"
#include "InputEventPlus.h"
#include "TimingData.h"
#include <chrono>

class ScoreDisplay;
class LifeMeter;
Expand Down Expand Up @@ -90,7 +91,7 @@ class Player: public ActorFrame
ScoreKeeper* pPrimaryScoreKeeper,
ScoreKeeper* pSecondaryScoreKeeper );
void Load();
void CrossedRows( int iLastRowCrossed, const RageTimer &now );
void CrossedRows( int iLastRowCrossed, const std::chrono::steady_clock::time_point &now );
bool IsOniDead() const;

/**
Expand All @@ -105,8 +106,8 @@ class Player: public ActorFrame

void ScoreAllActiveHoldsLetGo();
void DoTapScoreNone();

void Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease );
void Step( int col, int row, const std::chrono::steady_clock::time_point &tm, bool bHeld, bool bRelease, float padStickSeconds = 0.0f );

void FadeToFail();
void CacheAllUsedNoteSkins();
Expand Down
4 changes: 2 additions & 2 deletions src/RageDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ void RageDisplay::ProcessStatsOnFlip()
{
float fActualTime = g_LastCheckTimer.GetDeltaTime();
g_iNumChecksSinceLastReset++;
g_iFPS = lround(g_iFramesRenderedSinceLastCheck / fActualTime);
g_iFPS = static_cast<int>(g_iFramesRenderedSinceLastCheck / fActualTime);
g_iCFPS = g_iFramesRenderedSinceLastReset / g_iNumChecksSinceLastReset;
g_iCFPS = lround(g_iCFPS / fActualTime);
g_iCFPS = static_cast<int>(g_iCFPS / fActualTime);
g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFramesRenderedSinceLastCheck;
g_iFramesRenderedSinceLastCheck = g_iVertsRenderedSinceLastCheck = 0;
if (LOG_FPS)
Expand Down
11 changes: 6 additions & 5 deletions src/RageInputDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "RageTimer.h"
#include "EnumHelper.h"
#include <chrono>

const int NUM_JOYSTICKS = 32;
const int NUM_PUMPS = 2;
Expand Down Expand Up @@ -331,13 +332,13 @@ struct DeviceInput
* debouncing applied. */
bool bDown;

RageTimer ts;
std::chrono::steady_clock::time_point ts;

DeviceInput(): device(InputDevice_Invalid), button(DeviceButton_Invalid), level(0), z(0), bDown(false), ts(RageZeroTimer) { }
DeviceInput( InputDevice d, DeviceButton b, float l=0 ): device(d), button(b), level(l), z(0), bDown(l > 0.5f), ts(RageZeroTimer) { }
DeviceInput( InputDevice d, DeviceButton b, float l, const RageTimer &t ):
DeviceInput(): device(InputDevice_Invalid), button(DeviceButton_Invalid), level(0), z(0), bDown(false), ts(std::chrono::microseconds{ 0 }) { }
DeviceInput( InputDevice d, DeviceButton b, float l=0 ): device(d), button(b), level(l), z(0), bDown(l > 0.5f), ts(std::chrono::microseconds{ 0 }) { }
DeviceInput( InputDevice d, DeviceButton b, float l, const std::chrono::steady_clock::time_point &t ):
device(d), button(b), level(l), z(0), bDown(level > 0.5f), ts(t) { }
DeviceInput( InputDevice d, DeviceButton b, const RageTimer &t, int zVal=0 ):
DeviceInput( InputDevice d, DeviceButton b, const std::chrono::steady_clock::time_point &t, int zVal=0 ):
device(d), button(b), level(0), z(zVal), bDown(false), ts(t) { }

RString ToString() const;
Expand Down
5 changes: 4 additions & 1 deletion src/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ bool Screen::PassInputToLua(const InputEventPlus& input)
bool handled= false;
Lua* L= LUA->Get();

auto inputDelta = std::chrono::high_resolution_clock::now() - input.DeviceI.ts;
float inputAgo = (std::chrono::duration_cast<std::chrono::microseconds>(inputDelta).count() / 1000000.0);

// Construct the table once, and reuse it.
lua_createtable(L, 0, 7);
{ // This block is meant to improve clarity. A subtable is created for
Expand All @@ -335,7 +338,7 @@ bool Screen::PassInputToLua(const InputEventPlus& input)
lua_setfield(L, -2, "z");
lua_pushboolean(L, input.DeviceI.bDown);
lua_setfield(L, -2, "down");
lua_pushnumber(L, input.DeviceI.ts.Ago());
lua_pushnumber(L, inputAgo);
lua_setfield(L, -2, "ago");
lua_pushboolean(L, input.DeviceI.IsJoystick());
lua_setfield(L, -2, "is_joystick");
Expand Down
Loading

0 comments on commit 8bec801

Please sign in to comment.