From d01ee3163dff7927f1c447f69dc22f0b8a3779b9 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 3 Apr 2022 23:46:05 +1000 Subject: [PATCH] Qt: Add performance metrics to status bar --- pcsx2-qt/EmuThread.cpp | 100 +++++++++++++++++++++++++++++++++++ pcsx2-qt/EmuThread.h | 12 +++++ pcsx2-qt/MainWindow.cpp | 49 +++++++++++++++++ pcsx2-qt/MainWindow.h | 7 +++ pcsx2-qt/MainWindow.ui | 12 +++++ pcsx2/GS/GS.cpp | 17 ++++-- pcsx2/PerformanceMetrics.cpp | 10 +++- pcsx2/VMManager.h | 3 ++ 8 files changed, 204 insertions(+), 6 deletions(-) diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index da66be8e0c..68223f236c 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -15,6 +15,8 @@ #include "PrecompiledHeader.h" +#include + #include #include @@ -28,6 +30,7 @@ #include "pcsx2/Frontend/InputManager.h" #include "pcsx2/Frontend/ImGuiManager.h" #include "pcsx2/GS.h" +#include "pcsx2/GS/GS.h" #include "pcsx2/GSDumpReplayer.h" #include "pcsx2/HostDisplay.h" #include "pcsx2/PAD/Host/PAD.h" @@ -104,6 +107,7 @@ void EmuThread::startVM(std::shared_ptr boot_params) } pxAssertRel(!VMManager::HasValidVM(), "VM is shut down"); + loadOurSettings(); emit onVMStarting(); @@ -247,6 +251,11 @@ void EmuThread::run() void EmuThread::destroyVM() { + m_last_speed = 0.0f; + m_last_game_fps = 0.0f; + m_last_video_fps = 0.0f; + m_last_internal_width = 0; + m_last_internal_height = 0; VMManager::Shutdown(); } @@ -372,6 +381,11 @@ void EmuThread::reloadGameSettings() } } +void EmuThread::loadOurSettings() +{ + m_verbose_status = QtHost::GetBaseBoolSettingValue("UI", "VerboseStatusBar", false); +} + void EmuThread::checkForSettingChanges() { if (VMManager::HasValidVM()) @@ -384,6 +398,13 @@ void EmuThread::checkForSettingChanges() GetMTGS().WaitGS(); } } + + const bool last_verbose_status = m_verbose_status; + + loadOurSettings(); + + if (m_verbose_status != last_verbose_status) + updatePerformanceMetrics(true); } void EmuThread::toggleSoftwareRendering() @@ -722,6 +743,85 @@ void Host::OnGameChanged(const std::string& disc_path, const std::string& game_s QString::fromStdString(game_name), game_crc); } +void EmuThread::updatePerformanceMetrics(bool force) +{ + QString fps_stat, gs_stat; + bool changed = force; + + if (m_verbose_status && VMManager::HasValidVM()) + { + std::string gs_stat_str; + GSgetTitleStats(gs_stat_str); + changed = true; + + if (THREAD_VU1) + { + gs_stat = + QStringLiteral("%1 | EE: %2% | VU: %3% | GS: %4%") + .arg(gs_stat_str.c_str()) + .arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0) + .arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0) + .arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0); + } + else + { + gs_stat = QStringLiteral("%1 | EE: %2% | GS: %3%") + .arg(gs_stat_str.c_str()) + .arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0) + .arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0); + } + } + + const float speed = std::round(PerformanceMetrics::GetSpeed()); + const float gfps = std::round(PerformanceMetrics::GetInternalFPS()); + const float vfps = std::round(PerformanceMetrics::GetFPS()); + int iwidth, iheight; + GSgetInternalResolution(&iwidth, &iheight); + + if (iwidth != m_last_internal_width || iheight != m_last_internal_height || + speed != m_last_speed || gfps != m_last_game_fps || vfps != m_last_video_fps || + changed) + { + m_last_internal_width = iwidth; + m_last_internal_height = iheight; + m_last_speed = speed; + m_last_game_fps = gfps; + m_last_video_fps = vfps; + changed = true; + + if (iwidth == 0 && iheight == 0) + { + // if we don't have width/height yet, we're not going to have fps either. + // and we'll probably be <100% due to compiling. so just leave it blank for now. + } + else if (PerformanceMetrics::IsInternalFPSValid()) + { + fps_stat = QStringLiteral("%1x%2 | G: %3 | V: %4 | %5%") + .arg(iwidth) + .arg(iheight) + .arg(gfps, 0, 'f', 0) + .arg(vfps, 0, 'f', 0) + .arg(speed, 0, 'f', 0); + } + else + { + fps_stat = QStringLiteral("%1x%2 | V: %3 | %4%") + .arg(iwidth) + .arg(iheight) + .arg(vfps, 0, 'f', 0) + .arg(speed, 0, 'f', 0); + } + } + + if (changed) + emit onPerformanceMetricsUpdated(fps_stat, gs_stat); +} + +void Host::OnPerformanceMetricsUpdated() +{ + g_emu_thread->updatePerformanceMetrics(false); +} + void Host::OnSaveStateLoading(const std::string_view& filename) { emit g_emu_thread->onSaveStateLoading(QtUtils::StringViewToQString(filename)); diff --git a/pcsx2-qt/EmuThread.h b/pcsx2-qt/EmuThread.h index 0708827ef5..358e1bd72d 100644 --- a/pcsx2-qt/EmuThread.h +++ b/pcsx2-qt/EmuThread.h @@ -52,6 +52,7 @@ public: void startBackgroundControllerPollTimer(); void stopBackgroundControllerPollTimer(); + void updatePerformanceMetrics(bool force); public Q_SLOTS: void startVM(std::shared_ptr boot_params); @@ -100,6 +101,9 @@ Q_SIGNALS: /// Provided by the host; called when the running executable changes. void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc); + /// Called when performance metrics are changed, approx. once a second. + void onPerformanceMetricsUpdated(const QString& fps_stats, const QString& gs_stats); + void onInputDevicesEnumerated(const QList>& devices); void onInputDeviceConnected(const QString& identifier, const QString& device_name); void onInputDeviceDisconnected(const QString& identifier); @@ -129,6 +133,7 @@ private: void createBackgroundControllerPollTimer(); void destroyBackgroundControllerPollTimer(); + void loadOurSettings(); private Q_SLOTS: void stopInThread(); @@ -148,8 +153,15 @@ private: std::atomic_bool m_shutdown_flag{false}; + bool m_verbose_status = false; bool m_is_rendering_to_main = false; bool m_is_fullscreen = false; + + float m_last_speed = 0.0f; + float m_last_game_fps = 0.0f; + float m_last_video_fps = 0.0f; + int m_last_internal_width = 0; + int m_last_internal_height = 0; }; /// diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index b50aa9faeb..df8180e662 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -29,6 +29,7 @@ #include "pcsx2/Frontend/GameList.h" #include "pcsx2/GSDumpReplayer.h" #include "pcsx2/HostDisplay.h" +#include "pcsx2/PerformanceMetrics.h" #include "AboutDialog.h" #include "DisplayWidget.h" @@ -113,6 +114,16 @@ void MainWindow::setupAdditionalUi() m_status_progress_widget->setFixedSize(140, 16); m_status_progress_widget->hide(); + m_status_gs_widget = new QLabel(m_ui.statusBar); + m_status_gs_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_status_gs_widget->setFixedHeight(16); + m_status_gs_widget->hide(); + + m_status_fps_widget = new QLabel(m_ui.statusBar); + m_status_fps_widget->setAlignment(Qt::AlignRight); + m_status_fps_widget->setFixedHeight(16); + m_status_fps_widget->hide(); + for (u32 scale = 0; scale <= 10; scale++) { QAction* action = m_ui.menuWindowSize->addAction((scale == 0) ? tr("Internal Resolution") : tr("%1x Scale").arg(scale)); @@ -180,6 +191,8 @@ void MainWindow::connectSignals() }); connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget, &GameListWidget::refreshGridCovers); + SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionViewStatusBarVerbose, "UI", "VerboseStatusBar", false); + SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableSystemConsole, "Logging", "EnableSystemConsole", false); connect(m_ui.actionEnableSystemConsole, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); #ifndef PCSX2_DEVBUILD @@ -216,6 +229,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread) connect(thread, &EmuThread::onVMResumed, this, &MainWindow::onVMResumed); connect(thread, &EmuThread::onVMStopped, this, &MainWindow::onVMStopped); connect(thread, &EmuThread::onGameChanged, this, &MainWindow::onGameChanged); + connect(thread, &EmuThread::onPerformanceMetricsUpdated, this, &MainWindow::onPerformanceMetricsUpdated); connect(m_ui.actionReset, &QAction::triggered, thread, &EmuThread::resetVM); connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused); @@ -565,6 +579,27 @@ void MainWindow::updateEmulationActions(bool starting, bool running) m_game_list_widget->setDisabled(starting && !running); } +void MainWindow::updateStatusBarWidgetVisibility() +{ + auto Update = [this](QWidget* widget, bool visible, int stretch) + { + if (widget->isVisible()) + { + m_ui.statusBar->removeWidget(widget); + widget->hide(); + } + + if (visible) + { + m_ui.statusBar->addPermanentWidget(widget, stretch); + widget->show(); + } + }; + + Update(m_status_gs_widget, m_vm_valid && !m_vm_paused, 1); + Update(m_status_fps_widget, m_vm_valid, 0); +} + void MainWindow::updateWindowTitle() { QString title; @@ -994,6 +1029,7 @@ void MainWindow::onVMStarted() m_vm_valid = true; updateEmulationActions(true, true); updateWindowTitle(); + updateStatusBarWidgetVisibility(); } void MainWindow::onVMPaused() @@ -1006,6 +1042,8 @@ void MainWindow::onVMPaused() m_vm_paused = true; updateWindowTitle(); + updateStatusBarWidgetVisibility(); + m_status_fps_widget->setText(tr("Paused")); } void MainWindow::onVMResumed() @@ -1018,14 +1056,18 @@ void MainWindow::onVMResumed() m_vm_paused = false; updateWindowTitle(); + updateStatusBarWidgetVisibility(); + m_status_fps_widget->setText(m_last_fps_status); } void MainWindow::onVMStopped() { m_vm_valid = false; m_vm_paused = false; + m_last_fps_status = QString(); updateEmulationActions(false, false); updateWindowTitle(); + updateStatusBarWidgetVisibility(); switchToGameListView(); } @@ -1039,6 +1081,13 @@ void MainWindow::onGameChanged(const QString& path, const QString& serial, const updateSaveStateMenus(path, serial, crc); } +void MainWindow::onPerformanceMetricsUpdated(const QString& fps_stat, const QString& gs_stat) +{ + m_last_fps_status = fps_stat; + m_status_fps_widget->setText(m_last_fps_status); + m_status_gs_widget->setText(gs_stat); +} + void MainWindow::closeEvent(QCloseEvent* event) { if (!requestShutdown(true, true, true)) diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 891601a333..3ea6574061 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -15,6 +15,7 @@ #pragma once +#include #include #include @@ -103,6 +104,7 @@ private Q_SLOTS: void onVMStopped(); void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc); + void onPerformanceMetricsUpdated(const QString& fps_stat, const QString& gs_stat); void recreate(); @@ -124,6 +126,7 @@ private: void restoreStateFromConfig(); void updateEmulationActions(bool starting, bool running); + void updateStatusBarWidgetVisibility(); void updateWindowTitle(); void setProgressBar(int current, int total); void clearProgressBar(); @@ -166,6 +169,8 @@ private: ControllerSettingsDialog* m_controller_settings_dialog = nullptr; QProgressBar* m_status_progress_widget = nullptr; + QLabel* m_status_gs_widget = nullptr; + QLabel* m_status_fps_widget = nullptr; QString m_current_disc_path; QString m_current_game_serial; @@ -175,6 +180,8 @@ private: bool m_vm_paused = false; bool m_save_states_invalidated = false; bool m_was_focused_on_container_switch = false; + + QString m_last_fps_status; }; extern MainWindow* g_main_window; diff --git a/pcsx2-qt/MainWindow.ui b/pcsx2-qt/MainWindow.ui index 7de092f1ac..b43d4a1b1c 100644 --- a/pcsx2-qt/MainWindow.ui +++ b/pcsx2-qt/MainWindow.ui @@ -168,6 +168,7 @@ + @@ -597,6 +598,17 @@ &Status Bar + + + true + + + true + + + Verbose Status + + diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index e214541f82..ae4f40501f 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -36,6 +36,7 @@ #include "common/Console.h" #include "common/StringUtil.h" #include "pcsx2/Config.h" +#include "pcsx2/Counters.h" #include "pcsx2/Host.h" #include "pcsx2/HostDisplay.h" #include "pcsx2/GS.h" @@ -715,14 +716,20 @@ void GSgetStats(std::string& info) void GSgetTitleStats(std::string& info) { + const char* api_name = HostDisplay::RenderAPIToString(s_render_api); + const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW"); + const char* deinterlace_mode = theApp.m_gs_interlace[static_cast(GSConfig.InterlaceMode)].name.c_str(); + +#ifndef PCSX2_CORE int iwidth, iheight; GSgetInternalResolution(&iwidth, &iheight); - const char* api_name = HostDisplay::RenderAPIToString(s_render_api); - const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW"); - const char* interlace_mode = theApp.m_gs_interlace[static_cast(GSConfig.InterlaceMode)].name.c_str(); - - info = format("%s%s | %s | %dx%d", api_name, hw_sw_name, interlace_mode, iwidth, iheight); + info = StringUtil::StdStringFromFormat("%s%s | %s | %dx%d", api_name, hw_sw_name, deinterlace_mode, iwidth, iheight); +#else + const char* interlace_mode = ReportInterlaceMode(); + const char* video_mode = ReportVideoMode(); + info = StringUtil::StdStringFromFormat("%s%s | %s | %s | %s", api_name, hw_sw_name, video_mode, interlace_mode, deinterlace_mode); +#endif } void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config) diff --git a/pcsx2/PerformanceMetrics.cpp b/pcsx2/PerformanceMetrics.cpp index 5e87298e77..8006b32b9d 100644 --- a/pcsx2/PerformanceMetrics.cpp +++ b/pcsx2/PerformanceMetrics.cpp @@ -25,6 +25,10 @@ #include "GS.h" #include "MTVU.h" +#ifdef PCSX2_CORE +#include "VMManager.h" +#endif + static const float UPDATE_INTERVAL = 0.5f; static float s_vertical_frequency = 0.0f; @@ -130,6 +134,7 @@ void PerformanceMetrics::Update(bool gs_register_write, bool fb_blit) if (time < UPDATE_INTERVAL) return; + s_last_update_time.ResetTo(now_ticks); s_worst_frame_time = s_worst_frame_time_accumulator; s_worst_frame_time_accumulator = 0.0f; s_average_frame_time = s_average_frame_time_accumulator / static_cast(s_frames_since_last_update); @@ -191,9 +196,12 @@ void PerformanceMetrics::Update(bool gs_register_write, bool fb_blit) s_last_vu_time = vu_time; s_last_ticks = ticks; - s_last_update_time.ResetTo(now_ticks); s_frames_since_last_update = 0; s_presents_since_last_update = 0; + +#ifdef PCSX2_CORE + Host::OnPerformanceMetricsUpdated(); +#endif } void PerformanceMetrics::OnGPUPresent(float gpu_time) diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index 36c0c7e3c9..fce58d7a2e 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -177,6 +177,9 @@ namespace Host /// Called when the VM is resumed after being paused. void OnVMResumed(); + /// Called when performance metrics are updated, approximately once a second. + void OnPerformanceMetricsUpdated(); + /// Called when a save state is loading, before the file is processed. void OnSaveStateLoading(const std::string_view& filename);