diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 224242f81..510fd5a99 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -433,26 +433,43 @@ void HostInterface::UpdateSpeedLimiterState() m_last_throttle_time = 0; } +void HostInterface::OnPerformanceCountersUpdated() {} + +void HostInterface::RunFrame() +{ + m_frame_timer.Reset(); + m_system->RunFrame(); + UpdatePerformanceCounters(); +} + void HostInterface::UpdatePerformanceCounters() { - if (!m_system) - return; + const float frame_time = static_cast(m_frame_timer.GetTimeMilliseconds()); + m_average_frame_time_accumulator += frame_time; + m_worst_frame_time_accumulator = std::max(m_worst_frame_time_accumulator, frame_time); // update fps counter - const double time = m_fps_timer.GetTimeSeconds(); - if (time >= 0.25f) - { - m_vps = static_cast(static_cast(m_system->GetFrameNumber() - m_last_frame_number) / time); - m_last_frame_number = m_system->GetFrameNumber(); - m_fps = - static_cast(static_cast(m_system->GetInternalFrameNumber() - m_last_internal_frame_number) / time); - m_last_internal_frame_number = m_system->GetInternalFrameNumber(); - m_speed = static_cast(static_cast(m_system->GetGlobalTickCounter() - m_last_global_tick_counter) / - (static_cast(MASTER_CLOCK) * time)) * - 100.0f; - m_last_global_tick_counter = m_system->GetGlobalTickCounter(); - m_fps_timer.Reset(); - } + const float time = static_cast(m_fps_timer.GetTimeSeconds()); + if (time < 1.0f) + return; + + const float frames_presented = static_cast(m_system->GetFrameNumber() - m_last_frame_number); + + m_worst_frame_time = m_worst_frame_time_accumulator; + m_worst_frame_time_accumulator = 0.0f; + m_average_frame_time = m_average_frame_time_accumulator / frames_presented; + m_average_frame_time_accumulator = 0.0f; + m_vps = static_cast(frames_presented / time); + m_last_frame_number = m_system->GetFrameNumber(); + m_fps = static_cast(m_system->GetInternalFrameNumber() - m_last_internal_frame_number) / time; + m_last_internal_frame_number = m_system->GetInternalFrameNumber(); + m_speed = static_cast(static_cast(m_system->GetGlobalTickCounter() - m_last_global_tick_counter) / + (static_cast(MASTER_CLOCK) * time)) * + 100.0f; + m_last_global_tick_counter = m_system->GetGlobalTickCounter(); + m_fps_timer.Reset(); + + OnPerformanceCountersUpdated(); } void HostInterface::ResetPerformanceCounters() @@ -469,5 +486,7 @@ void HostInterface::ResetPerformanceCounters() m_last_internal_frame_number = 0; m_last_global_tick_counter = 0; } + m_average_frame_time_accumulator = 0.0f; + m_worst_frame_time_accumulator = 0.0f; m_fps_timer.Reset(); } diff --git a/src/core/host_interface.h b/src/core/host_interface.h index af63a9709..1ba182935 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -72,6 +72,10 @@ protected: float duration; }; + virtual void OnPerformanceCountersUpdated(); + + void RunFrame(); + /// Throttles the system, i.e. sleeps until it's time to execute the next frame. void Throttle(); @@ -95,18 +99,24 @@ protected: Common::Timer m_throttle_timer; Common::Timer m_speed_lost_time_timestamp; + bool m_paused = false; + bool m_speed_limiter_temp_disabled = false; + bool m_speed_limiter_enabled = false; + + float m_average_frame_time_accumulator = 0.0f; + float m_worst_frame_time_accumulator = 0.0f; + float m_vps = 0.0f; float m_fps = 0.0f; float m_speed = 0.0f; + float m_worst_frame_time = 0.0f; + float m_average_frame_time = 0.0f; u32 m_last_frame_number = 0; u32 m_last_internal_frame_number = 0; u32 m_last_global_tick_counter = 0; Common::Timer m_fps_timer; + Common::Timer m_frame_timer; std::deque m_osd_messages; std::mutex m_osd_messages_lock; - - bool m_paused = false; - bool m_speed_limiter_temp_disabled = false; - bool m_speed_limiter_enabled = false; }; diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index b855e3d6b..6c5f483fb 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -6,6 +6,7 @@ #include "qtsettingsinterface.h" #include "settingsdialog.h" #include +#include static constexpr char DISC_IMAGE_FILTER[] = "All File Types (*.bin *.img *.cue *.exe *.psexe);;Single-Track Raw Images (*.bin *.img);;Cue Sheets " @@ -124,6 +125,16 @@ void MainWindow::switchRenderer() } } +void MainWindow::onPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time, + float worst_frame_time) +{ + m_status_speed_widget->setText(QStringLiteral("%1%").arg(speed, 0, 'f', 0)); + m_status_fps_widget->setText( + QStringLiteral("FPS: %1/%2").arg(std::round(fps), 0, 'f', 0).arg(std::round(vps), 0, 'f', 0)); + m_status_frame_time_widget->setText( + QStringLiteral("%1ms average, %2ms worst").arg(average_frame_time, 0, 'f', 2).arg(worst_frame_time, 0, 'f', 2)); +} + void MainWindow::onStartDiscActionTriggered() { QString filename = @@ -183,6 +194,21 @@ void MainWindow::setupAdditionalUi() m_ui.mainContainer->setCurrentIndex(0); + m_status_speed_widget = new QLabel(m_ui.statusBar); + m_status_speed_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_status_speed_widget->setFixedSize(40, 16); + m_status_speed_widget->hide(); + + m_status_fps_widget = new QLabel(m_ui.statusBar); + m_status_fps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_status_fps_widget->setFixedSize(80, 16); + m_status_fps_widget->hide(); + + m_status_frame_time_widget = new QLabel(m_ui.statusBar); + m_status_frame_time_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_status_frame_time_widget->setFixedSize(190, 16); + m_status_frame_time_widget->hide(); + for (u32 i = 0; i < static_cast(GPURenderer::Count); i++) { const GPURenderer renderer = static_cast(i); @@ -215,6 +241,27 @@ void MainWindow::updateEmulationActions(bool starting, bool running) m_ui.actionSaveState->setDisabled(starting); m_ui.actionFullscreen->setDisabled(starting || !running); + + if (running && m_status_speed_widget->isHidden()) + { + m_status_speed_widget->show(); + m_status_fps_widget->show(); + m_status_frame_time_widget->show(); + m_ui.statusBar->addPermanentWidget(m_status_speed_widget); + m_ui.statusBar->addPermanentWidget(m_status_fps_widget); + m_ui.statusBar->addPermanentWidget(m_status_frame_time_widget); + } + else if (!running && m_status_speed_widget->isVisible()) + { + m_ui.statusBar->removeWidget(m_status_speed_widget); + m_ui.statusBar->removeWidget(m_status_fps_widget); + m_ui.statusBar->removeWidget(m_status_frame_time_widget); + m_status_speed_widget->hide(); + m_status_fps_widget->hide(); + m_status_frame_time_widget->hide(); + } + + m_ui.statusBar->clearMessage(); } void MainWindow::switchToGameListView() @@ -259,6 +306,8 @@ void MainWindow::connectSignals() connect(m_host_interface, &QtHostInterface::emulationStopped, this, &MainWindow::onEmulationStopped); connect(m_host_interface, &QtHostInterface::emulationPaused, this, &MainWindow::onEmulationPaused); connect(m_host_interface, &QtHostInterface::toggleFullscreenRequested, this, &MainWindow::toggleFullscreen); + connect(m_host_interface, &QtHostInterface::performanceCountersUpdated, this, + &MainWindow::onPerformanceCountersUpdated); connect(m_game_list_widget, &GameListWidget::bootEntryRequested, [this](const GameList::GameListEntry* entry) { // if we're not running, boot the system, otherwise swap discs @@ -274,6 +323,15 @@ void MainWindow::connectSignals() switchToEmulationView(); } }); + connect(m_game_list_widget, &GameListWidget::entrySelected, [this](const GameList::GameListEntry* entry) { + if (!entry) + { + m_ui.statusBar->clearMessage(); + return; + } + + m_ui.statusBar->showMessage(QString::fromStdString(entry->path)); + }); } void MainWindow::doSettings(SettingsDialog::Category category) @@ -289,4 +347,4 @@ void MainWindow::doSettings(SettingsDialog::Category category) if (category != SettingsDialog::Category::Count) m_settings_dialog->setCategory(category); -} \ No newline at end of file +} diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 9287031c1..b057d5c34 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -7,6 +7,8 @@ #include "settingsdialog.h" #include "ui_mainwindow.h" +class QLabel; + class GameList; class GameListWidget; class QtHostInterface; @@ -26,6 +28,8 @@ private Q_SLOTS: void onEmulationPaused(bool paused); void toggleFullscreen(); void switchRenderer(); + void onPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time, + float worst_frame_time); void onStartDiscActionTriggered(); void onChangeDiscActionTriggered(); @@ -53,6 +57,10 @@ private: GameListWidget* m_game_list_widget = nullptr; QWidget* m_display_widget = nullptr; + QLabel* m_status_speed_widget = nullptr; + QLabel* m_status_fps_widget = nullptr; + QLabel* m_status_frame_time_widget = nullptr; + SettingsDialog* m_settings_dialog = nullptr; bool m_emulation_running = false; diff --git a/src/duckstation-qt/mainwindow.ui b/src/duckstation-qt/mainwindow.ui index 1629b1349..0aced349c 100644 --- a/src/duckstation-qt/mainwindow.ui +++ b/src/duckstation-qt/mainwindow.ui @@ -14,7 +14,7 @@ DuckStation - + :/icons/duck.png:/icons/duck.png @@ -30,7 +30,7 @@ 0 0 754 - 21 + 22 @@ -128,9 +128,10 @@ + - + :/icons/drive-optical.png:/icons/drive-optical.png @@ -139,7 +140,7 @@ - + :/icons/drive-removable-media.png:/icons/drive-removable-media.png @@ -148,7 +149,7 @@ - + :/icons/system-shutdown.png:/icons/system-shutdown.png @@ -157,7 +158,7 @@ - + :/icons/view-refresh.png:/icons/view-refresh.png @@ -169,7 +170,7 @@ true - + :/icons/media-playback-pause.png:/icons/media-playback-pause.png @@ -178,7 +179,7 @@ - + :/icons/document-open.png:/icons/document-open.png @@ -187,7 +188,7 @@ - + :/icons/document-save.png:/icons/document-save.png @@ -201,7 +202,7 @@ - + :/icons/utilities-system-monitor.png:/icons/utilities-system-monitor.png @@ -210,7 +211,7 @@ - + :/icons/input-gaming.png:/icons/input-gaming.png @@ -219,7 +220,7 @@ - + :/icons/applications-other.png:/icons/applications-other.png @@ -228,7 +229,7 @@ - + :/icons/video-display.png:/icons/video-display.png @@ -237,7 +238,7 @@ - + :/icons/view-fullscreen.png:/icons/view-fullscreen.png @@ -290,7 +291,7 @@ - + :/icons/media-optical.png:/icons/media-optical.png @@ -299,7 +300,7 @@ - + :/icons/audio-card.png:/icons/audio-card.png @@ -308,7 +309,7 @@ - + :/icons/folder-open.png:/icons/folder-open.png @@ -317,7 +318,7 @@ - + :/icons/edit-find.png:/icons/edit-find.png @@ -326,7 +327,7 @@ - + :/icons/applications-system.png:/icons/applications-system.png @@ -335,7 +336,7 @@ - + diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index b4a74c1fc..726e18b6b 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -185,6 +185,13 @@ void QtHostInterface::onDisplayWindowResized(int width, int height) m_display_window->onWindowResized(width, height); } +void QtHostInterface::OnPerformanceCountersUpdated() +{ + HostInterface::OnPerformanceCountersUpdated(); + + emit performanceCountersUpdated(m_speed, m_fps, m_vps, m_average_frame_time, m_worst_frame_time); +} + void QtHostInterface::updateInputMap() { if (!isOnWorkerThread()) @@ -508,8 +515,7 @@ void QtHostInterface::threadEntryPoint() // execute the system, polling events inbetween frames // simulate the system if not paused - if (m_system && !m_paused) - m_system->RunFrame(); + RunFrame(); // rendering { @@ -519,7 +525,6 @@ void QtHostInterface::threadEntryPoint() m_system->GetGPU()->ResetGraphicsAPIState(); } - DrawFPSWindow(); DrawOSDMessages(); m_display->Render(); @@ -531,8 +536,6 @@ void QtHostInterface::threadEntryPoint() if (m_speed_limiter_enabled) Throttle(); } - - UpdatePerformanceCounters(); } m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents); diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index c0a677cb4..a1356eed7 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -71,6 +71,7 @@ Q_SIGNALS: void gameListRefreshed(); void toggleFullscreenRequested(); void switchRendererRequested(); + void performanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, float worst_frame_time); public Q_SLOTS: void powerOffSystem(); @@ -87,6 +88,9 @@ private Q_SLOTS: void doHandleKeyEvent(int key, bool pressed); void onDisplayWindowResized(int width, int height); +protected: + void OnPerformanceCountersUpdated() override; + private: using InputButtonHandler = std::function; diff --git a/src/duckstation/sdl_host_interface.cpp b/src/duckstation/sdl_host_interface.cpp index 1451fa4d5..6ea64789f 100644 --- a/src/duckstation/sdl_host_interface.cpp +++ b/src/duckstation/sdl_host_interface.cpp @@ -1614,7 +1614,7 @@ void SDLHostInterface::Run() if (m_system && !m_paused) { - m_system->RunFrame(); + RunFrame(); if (m_frame_step_request) { m_frame_step_request = false; @@ -1644,8 +1644,6 @@ void SDLHostInterface::Run() Throttle(); } } - - UpdatePerformanceCounters(); } // Save state on exit so it can be resumed