Qt: Add performance metrics to status bar

This commit is contained in:
Connor McLaughlin 2022-04-03 23:46:05 +10:00 committed by refractionpcsx2
parent 90457e32b6
commit d01ee3163d
8 changed files with 204 additions and 6 deletions

View File

@ -15,6 +15,8 @@
#include "PrecompiledHeader.h"
#include <cmath>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMessageBox>
@ -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<VMBootParameters> 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));

View File

@ -52,6 +52,7 @@ public:
void startBackgroundControllerPollTimer();
void stopBackgroundControllerPollTimer();
void updatePerformanceMetrics(bool force);
public Q_SLOTS:
void startVM(std::shared_ptr<VMBootParameters> 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<QPair<QString, QString>>& 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;
};
/// <summary>

View File

@ -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))

View File

@ -15,6 +15,7 @@
#pragma once
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <optional>
@ -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;

View File

@ -168,6 +168,7 @@
<addaction name="actionViewToolbar"/>
<addaction name="actionViewLockToolbar"/>
<addaction name="actionViewStatusBar"/>
<addaction name="actionViewStatusBarVerbose"/>
<addaction name="separator"/>
<addaction name="actionViewGameList"/>
<addaction name="actionViewGameGrid"/>
@ -597,6 +598,17 @@
<string>&amp;Status Bar</string>
</property>
</action>
<action name="actionViewStatusBarVerbose">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Verbose Status</string>
</property>
</action>
<action name="actionViewGameList">
<property name="icon">
<iconset theme="list-check">

View File

@ -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<int>(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<int>(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)

View File

@ -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<float>(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)

View File

@ -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);