diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 1d1300480..3dbd0e83e 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -410,6 +410,8 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) // We still run the playback queue even when FrameExistsAtTime returns false because we might be // playing backwards and about to start showing frames, so the queue should be prepared for // that. + bool popped = false; + while (!playback_queue_.empty()) { const ViewerPlaybackFrame& pf = playback_queue_.front(); @@ -424,6 +426,11 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) // Skip this frame PopOldestFrameFromPlaybackQueue(); + if (popped) { + // We've already popped a frame in this loop, meaning a frame has been skipped + display_widget_->SetSkippedFrames(display_widget_->GetSkippedFrames()+1); + } + popped = true; } } @@ -485,6 +492,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) playback_speed_ = speed; play_in_to_out_only_ = in_to_out_only; + display_widget_->SetSkippedFrames(0); playback_queue_next_frame_ = ruler()->GetTime(); @@ -651,6 +659,7 @@ void ViewerWidget::FinishPlayPreprocess() playback_speed_); playback_timer_.Start(playback_start_time, playback_speed_, timebase_dbl()); + display_widget_->ResetFPSTimer(); foreach (ViewerWindow* window, windows_) { window->Play(playback_start_time, playback_speed_, timebase()); @@ -947,6 +956,13 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) connect(show_waveform_action, &QAction::triggered, this, &ViewerWidget::ManualSwitchToWaveform); } + { + QAction* show_fps_action = menu.addAction(tr("Show FPS")); + show_fps_action->setCheckable(true); + show_fps_action->setChecked(display_widget_->GetShowFPS()); + connect(show_fps_action, &QAction::triggered, display_widget_, &ViewerDisplayWidget::SetShowFPS); + } + menu.exec(static_cast(sender())->mapToGlobal(pos)); } diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index e246453a8..96e95f03f 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -45,7 +45,9 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : gizmo_click_(false), last_loaded_buffer_(nullptr), hand_dragging_(false), - deinterlace_(false) + deinterlace_(false), + show_fps_(false), + frames_skipped_(0) { connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::UpdateCursor); @@ -181,6 +183,12 @@ QPoint ViewerDisplayWidget::TransformViewerSpaceToBufferSpace(QPoint pos) return pos * GenerateGizmoTransform().inverted(); } +void ViewerDisplayWidget::ResetFPSTimer() +{ + fps_timer_start_ = QDateTime::currentMSecsSinceEpoch(); + fps_timer_update_count_ = 0; +} + void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && gizmos_ @@ -348,6 +356,25 @@ void ViewerDisplayWidget::OnPaint() p.drawLines(lines, 2); } + + if (show_fps_) { + QPainter p(inner_widget()); + fps_timer_update_count_++; + qint64 now = QDateTime::currentMSecsSinceEpoch(); + double frame_rate; + if (now == fps_timer_start_) { + // This will cause a divide by zero, so we do nothing here + frame_rate = 0; + } else { + frame_rate = double(fps_timer_update_count_) / double((now - fps_timer_start_)/1000.0); + } + DrawTextWithCrudeShadow(&p, inner_widget()->rect(), tr("%1 FPS").arg(QString::number(frame_rate, 'f', 2))); + + if (frames_skipped_ > 0) { + DrawTextWithCrudeShadow(&p, inner_widget()->rect().adjusted(0, p.fontMetrics().height(), 0, 0), + tr("%1 frames skipped").arg(frames_skipped_)); + } + } } void ViewerDisplayWidget::OnDestroy() @@ -373,6 +400,14 @@ QPointF ViewerDisplayWidget::GetTexturePosition(const double &x, const double &y y / gizmo_params_.height()); } +void ViewerDisplayWidget::DrawTextWithCrudeShadow(QPainter *painter, const QRect &rect, const QString &text) +{ + painter->setPen(Qt::black); + painter->drawText(rect.adjusted(1, 1, 0, 0), text); + painter->setPen(Qt::white); + painter->drawText(rect, text); +} + rational ViewerDisplayWidget::GetGizmoTime() { return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, true); @@ -444,4 +479,11 @@ void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e) } } +void ViewerDisplayWidget::SetShowFPS(bool e) +{ + show_fps_ = e; + + update(); +} + } diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 3c3c6ad98..20b75a06f 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -84,6 +84,23 @@ public: return deinterlace_; } + void ResetFPSTimer(); + + bool GetShowFPS() const + { + return show_fps_; + } + + int GetSkippedFrames() const + { + return frames_skipped_; + } + + void SetSkippedFrames(int i) + { + frames_skipped_ = i; + } + public slots: /** * @brief Set the transformation matrix to draw with @@ -125,6 +142,8 @@ public slots: */ void SetDeinterlacing(bool e); + void SetShowFPS(bool e); + signals: /** * @brief Signal emitted when the user starts dragging from the viewer @@ -167,6 +186,7 @@ protected: */ virtual void mouseReleaseEvent(QMouseEvent* event) override; +protected slots: /** * @brief Paint function to display the texture (received in SetTexture()) on screen. * @@ -181,6 +201,8 @@ private: QPointF GetTexturePosition(const QSize& size); QPointF GetTexturePosition(const double& x, const double& y); + static void DrawTextWithCrudeShadow(QPainter* painter, const QRect& rect, const QString& text); + rational GetGizmoTime(); bool IsHandDrag(QMouseEvent* event) const; @@ -245,6 +267,12 @@ private: bool deinterlace_; + qint64 fps_timer_start_; + qint64 fps_timer_update_count_; + + bool show_fps_; + int frames_skipped_; + private slots: void EmitColorAtCursor(QMouseEvent* e);