From cdea531d95750d6ed444cd2542fabd52d7215bea Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 27 Apr 2021 01:22:58 +1000 Subject: [PATCH] viewer: fix timer issues Should address various viewer playback lag issues. --- app/panel/viewer/viewerbase.cpp | 2 +- app/widget/viewer/viewer.cpp | 71 +++++++++++++++++------------ app/widget/viewer/viewer.h | 6 +-- app/widget/viewer/viewerdisplay.cpp | 23 ++++++++++ app/widget/viewer/viewerdisplay.h | 11 +++-- 5 files changed, 76 insertions(+), 37 deletions(-) diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index 0a40a106c..28d38f7fd 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -113,7 +113,7 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) p->SetColorManager(vw->color_manager()); - vw->ForceUpdate(); + vw->UpdateTextureFromNode(); } void ViewerPanelBase::closeEvent(QCloseEvent *e) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index fbbd89b80..8b539f518 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -47,7 +47,7 @@ namespace olive { QVector ViewerWidget::instances_; -const int kMaxPreQueueSize = 16; +const int kMaxPreQueueSize = QThread::idealThreadCount(); ViewerWidget::ViewerWidget(QWidget *parent) : super(false, true, parent), @@ -81,6 +81,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(display_widget_, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); connect(display_widget_, &ViewerDisplayWidget::DragEntered, this, &ViewerWidget::DragEntered); connect(display_widget_, &ViewerDisplayWidget::Dropped, this, &ViewerWidget::Dropped); + connect(display_widget_, &ViewerDisplayWidget::VisibilityChanged, this, &ViewerWidget::Pause); connect(sizer_, &ViewerSizer::RequestScale, display_widget_, &ViewerDisplayWidget::SetMatrixZoom); connect(sizer_, &ViewerSizer::RequestTranslate, display_widget_, &ViewerDisplayWidget::SetMatrixTranslate); connect(display_widget_, &ViewerDisplayWidget::HandDragMoved, sizer_, &ViewerSizer::HandDragMove); @@ -168,7 +169,7 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i) rational time_set = Timecode::timestamp_to_time(i, timebase()); if (!IsPlaying()) { - UpdateTextureFromNode(time_set); + UpdateTextureFromNode(); PushScrubbedAudio(); } @@ -224,7 +225,7 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n) UpdateRendererAudioParameters(); // Set texture to new texture (or null if no viewer node is available) - ForceUpdate(); + UpdateTextureFromNode(); } void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n) @@ -347,12 +348,6 @@ void ViewerWidget::SetFullScreen(QScreen *screen) windows_.insert(screen, vw); } -void ViewerWidget::ForceUpdate() -{ - // Hack that forces the viewer to update - UpdateTextureFromNode(GetTime()); -} - void ViewerWidget::SetAutoCacheEnabled(bool e) { auto_cacher_.SetPaused(!e); @@ -452,8 +447,9 @@ void ViewerWidget::StartAudioOutput() } } -void ViewerWidget::UpdateTextureFromNode(const rational& time) +void ViewerWidget::UpdateTextureFromNode() { + rational time = GetTime(); bool frame_exists_at_time = FrameExistsAtTime(time); bool frame_might_be_still = GetConnectedNode() && GetConnectedNode()->GetConnectedTextureOutput().IsValid() && GetConnectedNode()->GetVideoLength().isNull(); @@ -496,14 +492,32 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) qWarning() << "Playback queue failed to keep up"; } + } else { + + // We don't clear the FPS timer on pause in case users want to see it immediately after, but by + // the time a new texture is drawn, assume that the FPS no longer needs to be shown. + display_widget_->ResetFPSTimer(); + } if (frame_exists_at_time || frame_might_be_still) { // Frame was not in queue, will require rendering or decoding from cache - RenderTicketWatcher* watcher = new RenderTicketWatcher(); - connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame); - nonqueue_watchers_.append(watcher); - watcher->SetTicket(GetFrame(time, true)); + if (IsPlaying()) { + // Is playing, yet the queue above failed to retrieve the frame. We effectively do a quick + // reboot of the queue here, assuming the above loop has emptied it so far. + playback_queue_next_frame_ = GetTimestamp() + playback_speed_; + int new_queue_length = DeterminePlaybackQueueSize(); + for (int i=0; iupdate(); + } else { + // Not playing, run a task to get the frame either from the cache or the renderer + RenderTicketWatcher* watcher = new RenderTicketWatcher(); + connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame); + nonqueue_watchers_.append(watcher); + watcher->SetTicket(GetFrame(time, true)); + } } else { // There is definitely no frame here, we can immediately flip to showing nothing nonqueue_watchers_.clear(); @@ -554,7 +568,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) controls_->ShowPauseButton(); // Attempt to fill playback queue - if (stack_->currentWidget() == sizer_) { + if (display_widget_->isVisible()) { prequeue_length_ = DeterminePlaybackQueueSize(); if (prequeue_length_ > 0) { @@ -578,7 +592,7 @@ void ViewerWidget::PauseInternal() playback_speed_ = 0; controls_->ShowPlayButton(); - disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::ForceUpdate); + disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); foreach (ViewerWindow* window, windows_) { window->Pause(); @@ -719,10 +733,12 @@ void ViewerWidget::FinishPlayPreprocess() window->Play(playback_start_time, playback_speed_, timebase()); } - connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::ForceUpdate); - - playback_backup_timer_.setInterval(qFloor(timebase_dbl())); - playback_backup_timer_.start(); + if (display_widget_->isVisible()) { + connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + } else { + playback_backup_timer_.setInterval(qFloor(timebase_dbl())); + playback_backup_timer_.start(); + } PlaybackTimerUpdate(); } @@ -738,7 +754,7 @@ int ViewerWidget::DeterminePlaybackQueueSize() end_ts = 0; } - int remaining_frames = (end_ts - GetTimestamp()) * playback_speed_; + int remaining_frames = (end_ts - GetTimestamp()) / playback_speed_; return qMin(kMaxPreQueueSize, remaining_frames); } @@ -1182,10 +1198,9 @@ void ViewerWidget::PlaybackTimerUpdate() } - if (!isVisible()) { - while (!playback_queue_.empty() && playback_queue_.front().timestamp != GetTime()) { - PopOldestFrameFromPlaybackQueue(); - } + if (display_widget_->isVisible()) { + // Updating display widget + UpdateTextureFromNode(); } } @@ -1214,7 +1229,7 @@ void ViewerWidget::LengthChangedSlot(const rational &length) UpdateMinimumScale(); if (length < last_length_ && GetTime() >= length) { - ForceUpdate(); + UpdateTextureFromNode(); } last_length_ = length; @@ -1254,7 +1269,7 @@ void ViewerWidget::ViewerInvalidatedVideoRange(const TimeRange &range) { // If our current frame is within this range, we need to update if (GetTime() >= range.in() && (GetTime() < range.out() || range.in() == range.out())) { - QMetaObject::invokeMethod(this, "ForceUpdate", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "UpdateTextureFromNode", Qt::QueuedConnection); } } @@ -1280,7 +1295,7 @@ void ViewerWidget::TimeChangedFromWaveform(qint64 t) void ViewerWidget::ViewerShiftedRange(const rational &from, const rational &to) { if (GetTime() >= qMin(from, to)) { - QMetaObject::invokeMethod(this, "ForceUpdate", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "UpdateTextureFromNode", Qt::QueuedConnection); } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 21ef2a33e..5b8ce36fa 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -107,8 +107,6 @@ public slots: */ void SetSignalCursorColorEnabled(bool e); - void ForceUpdate(); - void SetAutoCacheEnabled(bool e); void CacheEntireSequence(); @@ -119,6 +117,8 @@ public slots: void SetViewerPixelAspect(const rational& ratio); + void UpdateTextureFromNode(); + signals: /** * @brief Wrapper for ViewerGLWidget::CursorColor() @@ -169,8 +169,6 @@ protected: private: void UpdateTimeInternal(int64_t i); - void UpdateTextureFromNode(const rational &time); - void PlayInternal(int speed, bool in_to_out_only); void PauseInternal(); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 7f17b11ee..a8a3b9f5d 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -185,6 +185,15 @@ void ViewerDisplayWidget::ResetFPSTimer() fps_timer_update_count_ = 0; frames_skipped_ = 0; frame_rate_average_count_ = 0; + + Core::instance()->ClearStatusBarMessage(); +} + +void ViewerDisplayWidget::IncrementSkippedFrames() +{ + frames_skipped_++; + + Core::instance()->ShowStatusBarMessage(tr("%n skipped frame(s) detected during playback", nullptr, frames_skipped_), 10000); } void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) @@ -294,6 +303,20 @@ void ViewerDisplayWidget::dropEvent(QDropEvent *event) } } +void ViewerDisplayWidget::showEvent(QShowEvent *event) +{ + emit VisibilityChanged(true); + + super::showEvent(event); +} + +void ViewerDisplayWidget::hideEvent(QHideEvent *event) +{ + emit VisibilityChanged(false); + + super::hideEvent(event); +} + void ViewerDisplayWidget::OnPaint() { // Clear background to empty diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 7f649399d..de80290c0 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -95,10 +95,7 @@ public: return show_fps_; } - void IncrementSkippedFrames() - { - frames_skipped_++; - } + void IncrementSkippedFrames(); void IncrementFrameCount() { @@ -180,6 +177,8 @@ signals: void Dropped(QDropEvent* event); + void VisibilityChanged(bool visible); + protected: /** * @brief Override the mouse press event for the DragStarted() signal and gizmos @@ -202,6 +201,10 @@ protected: virtual void dropEvent(QDropEvent* event) override; + virtual void showEvent(QShowEvent* event) override; + + virtual void hideEvent(QHideEvent* event) override; + protected slots: /** * @brief Paint function to display the texture (received in SetTexture()) on screen.