Buffer rewinds from backward skips in replays (#5141)

* split event processing to own method

* implement rewind throttling

* don't throttle backward skips from clicks

* use the term 'buffering' instead

* remove initial backup logic; just always buffer shortcut backward skips

* prevent segfault

* turns out you can just reuse the same one-shot timer

* try scaling timeout based on event count

* rewrite timeout calculation code

* fix linker error
This commit is contained in:
RickyRister 2024-11-05 09:45:42 -08:00 committed by GitHub
parent 6652012f4c
commit e43a21866c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 13 deletions

View file

@ -10,6 +10,10 @@ ReplayTimelineWidget::ReplayTimelineWidget(QWidget *parent)
{
replayTimer = new QTimer(this);
connect(replayTimer, SIGNAL(timeout()), this, SLOT(replayTimerTimeout()));
rewindBufferingTimer = new QTimer(this);
rewindBufferingTimer->setSingleShot(true);
connect(rewindBufferingTimer, &QTimer::timeout, this, &ReplayTimelineWidget::processRewind);
}
const int ReplayTimelineWidget::binLength = 5000;
@ -67,10 +71,11 @@ void ReplayTimelineWidget::mousePressEvent(QMouseEvent *event)
#else
int newTime = static_cast<int>((qint64)maxTime * (qint64)event->x() / width());
#endif
skipToTime(newTime);
// don't buffer rewinds from clicks, since clicks usually don't happen fast enough to require buffering
skipToTime(newTime, false);
}
void ReplayTimelineWidget::skipToTime(int newTime)
void ReplayTimelineWidget::skipToTime(int newTime, bool doRewindBuffering)
{
// check boundary conditions
if (newTime < 0) {
@ -81,16 +86,53 @@ void ReplayTimelineWidget::skipToTime(int newTime)
}
newTime -= newTime % 200; // Time should always be a multiple of 200
if (newTime < currentTime) {
currentTime = 0;
currentEvent = 0;
emit rewound();
const bool isBackwardsSkip = newTime < currentTime;
currentTime = newTime;
if (isBackwardsSkip) {
handleBackwardsSkip(doRewindBuffering);
} else {
processNewEvents();
}
currentTime = newTime - 200; // 200 is added back in replayTimerTimeout
replayTimerTimeout();
update();
}
/// @param doRewindBuffering When true, if multiple backward skips are made in quick succession, only a single rewind
/// is processed at the end. When false, the backwards skip will always cause an immediate rewind
void ReplayTimelineWidget::handleBackwardsSkip(bool doRewindBuffering)
{
if (doRewindBuffering) {
// We use a one-shot timer to implement the rewind buffering.
// The rewind only happens once the timer runs out.
// If another backwards skip happens, the timer will just get reset instead of rewinding.
rewindBufferingTimer->stop();
rewindBufferingTimer->start(calcRewindBufferingTimeout());
} else {
// otherwise, process the rewind immediately
processRewind();
}
}
/// The timeout scales based on the current event number, up to a limit
int ReplayTimelineWidget::calcRewindBufferingTimeout() const
{
int extraTime = currentEvent / 100;
return std::min(BASE_REWIND_BUFFERING_TIMEOUT_MS + extraTime, MAX_REWIND_BUFFERING_TIMEOUT_MS);
}
void ReplayTimelineWidget::processRewind()
{
// stop any queued-up rewinds
rewindBufferingTimer->stop();
// process the rewind
currentEvent = 0;
emit rewound();
processNewEvents();
}
QSize ReplayTimelineWidget::sizeHint() const
{
return {-1, 50};
@ -104,6 +146,16 @@ QSize ReplayTimelineWidget::minimumSizeHint() const
void ReplayTimelineWidget::replayTimerTimeout()
{
currentTime += 200;
processNewEvents();
if (!(currentTime % 1000))
update();
}
/// Processes all unprocessed events up to the current time.
void ReplayTimelineWidget::processNewEvents()
{
while ((currentEvent < replayTimeline.size()) && (replayTimeline[currentEvent] < currentTime)) {
emit processNextEvent();
++currentEvent;
@ -112,9 +164,6 @@ void ReplayTimelineWidget::replayTimerTimeout()
emit replayFinished();
replayTimer->stop();
}
if (!(currentTime % 1000))
update();
}
void ReplayTimelineWidget::setTimeScaleFactor(qreal _timeScaleFactor)
@ -135,5 +184,5 @@ void ReplayTimelineWidget::stopReplay()
void ReplayTimelineWidget::skipByAmount(int amount)
{
skipToTime(currentTime + amount);
skipToTime(currentTime + amount, true);
}