[desktop] Add basic Frametime/FPS overlay (#3537)
Just displays min, max, avg frametime/fps, alongside a chart of FPS in the last 30 seconds. Notes: - Qt Charts is now required - FPS/frametime collector now runs 2x as often. TODO: keep status bar at 500ms, but put perf overlay at 250ms Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3537 Reviewed-by: Lizzie <lizzie@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
parent
8e373eb714
commit
45c9f9bbb3
|
|
@ -641,7 +641,7 @@ if (ENABLE_QT)
|
||||||
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
|
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Concurrent)
|
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
|
||||||
|
|
||||||
if (YUZU_USE_QT_MULTIMEDIA)
|
if (YUZU_USE_QT_MULTIMEDIA)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
|
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
|
||||||
|
|
@ -680,7 +680,7 @@ if (ENABLE_QT)
|
||||||
## Components ##
|
## Components ##
|
||||||
|
|
||||||
# Best practice is to ask for all components at once, so they are from the same version
|
# Best practice is to ask for all components at once, so they are from the same version
|
||||||
set(YUZU_QT_COMPONENTS Core Widgets Concurrent)
|
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent)
|
||||||
if (PLATFORM_LINUX)
|
if (PLATFORM_LINUX)
|
||||||
list(APPEND YUZU_QT_COMPONENTS DBus)
|
list(APPEND YUZU_QT_COMPONENTS DBus)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,9 @@ struct Values {
|
||||||
QVector<u64> favorited_ids;
|
QVector<u64> favorited_ids;
|
||||||
QMap<u64, QDir> ryujinx_link_paths;
|
QMap<u64, QDir> ryujinx_link_paths;
|
||||||
|
|
||||||
|
// perf overlay
|
||||||
|
Setting<bool> show_perf_overlay{linkage, false, "show_perf_overlay", Category::UiGameList};
|
||||||
|
|
||||||
// Compatibility List
|
// Compatibility List
|
||||||
Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList};
|
Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList};
|
||||||
|
|
||||||
|
|
@ -249,7 +252,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
|
||||||
// This must be in alphabetical order according to action name as it must have the same order as
|
// This must be in alphabetical order according to action name as it must have the same order as
|
||||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::array<Shortcut, 32> default_hotkeys{{
|
const std::array<Shortcut, 33> default_hotkeys{{
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
|
||||||
|
|
@ -282,6 +285,7 @@ const std::array<Shortcut, 32> default_hotkeys{{
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Performance Overlay")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+V"), std::string(""), Qt::WindowShortcut, false}},
|
||||||
}};
|
}};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,8 @@ add_executable(yuzu
|
||||||
configuration/system/new_user_dialog.h configuration/system/new_user_dialog.cpp configuration/system/new_user_dialog.ui
|
configuration/system/new_user_dialog.h configuration/system/new_user_dialog.cpp configuration/system/new_user_dialog.ui
|
||||||
configuration/system/profile_avatar_dialog.h configuration/system/profile_avatar_dialog.cpp
|
configuration/system/profile_avatar_dialog.h configuration/system/profile_avatar_dialog.cpp
|
||||||
configuration/addon/mod_select_dialog.h configuration/addon/mod_select_dialog.cpp configuration/addon/mod_select_dialog.ui
|
configuration/addon/mod_select_dialog.h configuration/addon/mod_select_dialog.cpp configuration/addon/mod_select_dialog.ui
|
||||||
|
|
||||||
|
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
||||||
|
|
@ -399,7 +401,7 @@ endif()
|
||||||
|
|
||||||
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
|
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
|
||||||
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
|
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Concurrent)
|
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Charts Qt6::Concurrent)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,9 @@ ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
|
||||||
connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
|
connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
|
||||||
&ConfigureFilesystem::ResetMetadata);
|
&ConfigureFilesystem::ResetMetadata);
|
||||||
|
|
||||||
connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this,
|
connect(ui->gamecard_inserted, &QCheckBox::STATE_CHANGED, this,
|
||||||
&ConfigureFilesystem::UpdateEnabledControls);
|
&ConfigureFilesystem::UpdateEnabledControls);
|
||||||
connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this,
|
connect(ui->gamecard_current_game, &QCheckBox::STATE_CHANGED, this,
|
||||||
&ConfigureFilesystem::UpdateEnabledControls);
|
&ConfigureFilesystem::UpdateEnabledControls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@
|
||||||
<addaction name="action_Enable_Overlay_Applet"/>
|
<addaction name="action_Enable_Overlay_Applet"/>
|
||||||
<addaction name="action_Show_Filter_Bar"/>
|
<addaction name="action_Show_Filter_Bar"/>
|
||||||
<addaction name="action_Show_Status_Bar"/>
|
<addaction name="action_Show_Status_Bar"/>
|
||||||
|
<addaction name="action_Show_Performance_Overlay"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_Reset_Window_Size"/>
|
<addaction name="menu_Reset_Window_Size"/>
|
||||||
<addaction name="menu_View_Debugging"/>
|
<addaction name="menu_View_Debugging"/>
|
||||||
|
|
@ -611,6 +612,14 @@
|
||||||
<string>Show Game &Name</string>
|
<string>Show Game &Name</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Show_Performance_Overlay">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Show &Performance Overlay</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="yuzu.qrc"/>
|
<include location="yuzu.qrc"/>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
#include "frontend_common/settings_generator.h"
|
#include "frontend_common/settings_generator.h"
|
||||||
#include "qt_common/qt_string_lookup.h"
|
#include "qt_common/qt_string_lookup.h"
|
||||||
|
#include "render/performance_overlay.h"
|
||||||
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
||||||
#undef VMA_IMPLEMENTATION
|
#undef VMA_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1401,6 +1402,7 @@ void MainWindow::InitializeHotkeys() {
|
||||||
LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation"));
|
LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation"));
|
||||||
LinkActionShortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar"));
|
LinkActionShortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar"));
|
||||||
LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
|
LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
|
||||||
|
LinkActionShortcut(ui->action_Show_Performance_Overlay, QStringLiteral("Toggle Performance Overlay"));
|
||||||
LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
|
LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
|
||||||
LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
|
LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
|
||||||
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
||||||
|
|
@ -1511,6 +1513,9 @@ void MainWindow::RestoreUIState() {
|
||||||
|
|
||||||
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
|
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
|
||||||
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
||||||
|
|
||||||
|
ui->action_Show_Performance_Overlay->setChecked(UISettings::values.show_perf_overlay.GetValue());
|
||||||
|
if (perf_overlay) perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
|
||||||
Debugger::ToggleConsole();
|
Debugger::ToggleConsole();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1630,6 +1635,7 @@ void MainWindow::ConnectMenuEvents() {
|
||||||
connect_menu(ui->action_Single_Window_Mode, &MainWindow::ToggleWindowMode);
|
connect_menu(ui->action_Single_Window_Mode, &MainWindow::ToggleWindowMode);
|
||||||
connect_menu(ui->action_Show_Filter_Bar, &MainWindow::OnToggleFilterBar);
|
connect_menu(ui->action_Show_Filter_Bar, &MainWindow::OnToggleFilterBar);
|
||||||
connect_menu(ui->action_Show_Status_Bar, &MainWindow::OnToggleStatusBar);
|
connect_menu(ui->action_Show_Status_Bar, &MainWindow::OnToggleStatusBar);
|
||||||
|
connect_menu(ui->action_Show_Performance_Overlay, &MainWindow::OnTogglePerfOverlay);
|
||||||
|
|
||||||
connect_menu(ui->action_Reset_Window_Size_720, &MainWindow::ResetWindowSize720);
|
connect_menu(ui->action_Reset_Window_Size_720, &MainWindow::ResetWindowSize720);
|
||||||
connect_menu(ui->action_Reset_Window_Size_900, &MainWindow::ResetWindowSize900);
|
connect_menu(ui->action_Reset_Window_Size_900, &MainWindow::ResetWindowSize900);
|
||||||
|
|
@ -2136,7 +2142,7 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
|
||||||
game_list->hide();
|
game_list->hide();
|
||||||
game_list_placeholder->hide();
|
game_list_placeholder->hide();
|
||||||
}
|
}
|
||||||
status_bar_update_timer.start(500);
|
status_bar_update_timer.start(250);
|
||||||
renderer_status_button->setDisabled(true);
|
renderer_status_button->setDisabled(true);
|
||||||
refresh_button->setDisabled(true);
|
refresh_button->setDisabled(true);
|
||||||
|
|
||||||
|
|
@ -2210,6 +2216,10 @@ bool MainWindow::OnShutdownBegin() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf_overlay->hide();
|
||||||
|
perf_overlay->deleteLater();
|
||||||
|
perf_overlay = nullptr;
|
||||||
|
|
||||||
QtCommon::system->SetShuttingDown(true);
|
QtCommon::system->SetShuttingDown(true);
|
||||||
discord_rpc->Pause();
|
discord_rpc->Pause();
|
||||||
|
|
||||||
|
|
@ -3211,6 +3221,13 @@ bool MainWindow::ConfirmShutdownGame() {
|
||||||
|
|
||||||
void MainWindow::OnLoadComplete() {
|
void MainWindow::OnLoadComplete() {
|
||||||
loading_screen->OnLoadComplete();
|
loading_screen->OnLoadComplete();
|
||||||
|
|
||||||
|
perf_overlay = new PerformanceOverlay(this);
|
||||||
|
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
|
||||||
|
|
||||||
|
connect(perf_overlay, &PerformanceOverlay::closed, perf_overlay, [this]() {
|
||||||
|
ui->action_Show_Performance_Overlay->setChecked(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OnExecuteProgram(std::size_t program_index) {
|
void MainWindow::OnExecuteProgram(std::size_t program_index) {
|
||||||
|
|
@ -4032,6 +4049,12 @@ void MainWindow::OnToggleStatusBar() {
|
||||||
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnTogglePerfOverlay() {
|
||||||
|
if (perf_overlay) {
|
||||||
|
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::OnGameListRefresh() {
|
void MainWindow::OnGameListRefresh() {
|
||||||
// Resets metadata cache and reloads
|
// Resets metadata cache and reloads
|
||||||
QtCommon::Game::ResetMetadata(false);
|
QtCommon::Game::ResetMetadata(false);
|
||||||
|
|
@ -4256,6 +4279,8 @@ void MainWindow::UpdateStatusBar() {
|
||||||
auto& shader_notify = QtCommon::system->GPU().ShaderNotify();
|
auto& shader_notify = QtCommon::system->GPU().ShaderNotify();
|
||||||
const int shaders_building = shader_notify.ShadersBuilding();
|
const int shaders_building = shader_notify.ShadersBuilding();
|
||||||
|
|
||||||
|
emit statsUpdated(results, shader_notify);
|
||||||
|
|
||||||
if (shaders_building > 0) {
|
if (shaders_building > 0) {
|
||||||
shader_building_label->setText(tr("Building: %n shader(s)", "", shaders_building));
|
shader_building_label->setText(tr("Building: %n shader(s)", "", shaders_building));
|
||||||
shader_building_label->setVisible(true);
|
shader_building_label->setVisible(true);
|
||||||
|
|
@ -4350,6 +4375,7 @@ void MainWindow::UpdateStatusButtons() {
|
||||||
UpdateVolumeUI();
|
UpdateVolumeUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(crueter): Use this for game list stuff
|
||||||
void MainWindow::UpdateUISettings() {
|
void MainWindow::UpdateUISettings() {
|
||||||
if (!ui->action_Fullscreen->isChecked()) {
|
if (!ui->action_Fullscreen->isChecked()) {
|
||||||
UISettings::values.geometry = saveGeometry();
|
UISettings::values.geometry = saveGeometry();
|
||||||
|
|
@ -4360,6 +4386,8 @@ void MainWindow::UpdateUISettings() {
|
||||||
UISettings::values.fullscreen = ui->action_Fullscreen->isChecked();
|
UISettings::values.fullscreen = ui->action_Fullscreen->isChecked();
|
||||||
UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked();
|
UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked();
|
||||||
UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked();
|
UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked();
|
||||||
|
UISettings::values.show_perf_overlay = ui->action_Show_Performance_Overlay->isChecked();
|
||||||
|
|
||||||
UISettings::values.first_start = false;
|
UISettings::values.first_start = false;
|
||||||
|
|
||||||
Settings::values.enable_overlay = ui->action_Enable_Overlay_Applet->isChecked();
|
Settings::values.enable_overlay = ui->action_Enable_Overlay_Applet->isChecked();
|
||||||
|
|
@ -4588,6 +4616,15 @@ void MainWindow::closeEvent(QCloseEvent* event) {
|
||||||
QWidget::closeEvent(event);
|
QWidget::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::resizeEvent(QResizeEvent* event) {
|
||||||
|
emit sizeChanged(event->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::moveEvent(QMoveEvent* event) {
|
||||||
|
auto window_frame_height = frameGeometry().height() - geometry().height();
|
||||||
|
emit positionChanged(event->pos() - QPoint{0, window_frame_height});
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsSingleFileDropEvent(const QMimeData* mime) {
|
static bool IsSingleFileDropEvent(const QMimeData* mime) {
|
||||||
return mime->hasUrls() && mime->urls().length() == 1;
|
return mime->hasUrls() && mime->urls().length() == 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ class QProgressDialog;
|
||||||
class QSlider;
|
class QSlider;
|
||||||
class QHBoxLayout;
|
class QHBoxLayout;
|
||||||
class WaitTreeWidget;
|
class WaitTreeWidget;
|
||||||
|
class PerformanceOverlay;
|
||||||
enum class GameListOpenTarget;
|
enum class GameListOpenTarget;
|
||||||
enum class DumpRomFSTarget;
|
enum class DumpRomFSTarget;
|
||||||
class GameListPlaceholder;
|
class GameListPlaceholder;
|
||||||
|
|
@ -70,6 +71,9 @@ enum class StartGameType {
|
||||||
Global, // Only uses global configuration
|
Global, // Only uses global configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
class ShaderNotify;
|
||||||
|
}
|
||||||
namespace Core {
|
namespace Core {
|
||||||
enum class SystemResultStatus : u32;
|
enum class SystemResultStatus : u32;
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
@ -214,6 +218,9 @@ signals:
|
||||||
void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url);
|
void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url);
|
||||||
|
|
||||||
void SigInterrupt();
|
void SigInterrupt();
|
||||||
|
void sizeChanged(const QSize &size);
|
||||||
|
void positionChanged(const QPoint &pos);
|
||||||
|
void statsUpdated(const Core::PerfStatsResults &results, const VideoCore::ShaderNotify &shaders);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnLoadComplete();
|
void OnLoadComplete();
|
||||||
|
|
@ -310,6 +317,8 @@ private:
|
||||||
void RequestGameExit();
|
void RequestGameExit();
|
||||||
void changeEvent(QEvent* event) override;
|
void changeEvent(QEvent* event) override;
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
void moveEvent(QMoveEvent *event) override;
|
||||||
|
|
||||||
std::string CreateTASFramesString(
|
std::string CreateTASFramesString(
|
||||||
std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
|
std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
|
||||||
|
|
@ -392,6 +401,7 @@ private slots:
|
||||||
void OnDataDialog();
|
void OnDataDialog();
|
||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnToggleStatusBar();
|
void OnToggleStatusBar();
|
||||||
|
void OnTogglePerfOverlay();
|
||||||
void OnGameListRefresh();
|
void OnGameListRefresh();
|
||||||
void InitializeHotkeys();
|
void InitializeHotkeys();
|
||||||
void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
|
|
@ -493,6 +503,7 @@ private:
|
||||||
LoadingScreen* loading_screen = nullptr;
|
LoadingScreen* loading_screen = nullptr;
|
||||||
QTimer shutdown_timer;
|
QTimer shutdown_timer;
|
||||||
OverlayDialog* shutdown_dialog{};
|
OverlayDialog* shutdown_dialog{};
|
||||||
|
PerformanceOverlay *perf_overlay = nullptr;
|
||||||
|
|
||||||
GameListPlaceholder* game_list_placeholder = nullptr;
|
GameListPlaceholder* game_list_placeholder = nullptr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
#include "performance_overlay.h"
|
||||||
|
#include "ui_performance_overlay.h"
|
||||||
|
|
||||||
|
#include "main_window.h"
|
||||||
|
|
||||||
|
#include <QChart>
|
||||||
|
#include <QChartView>
|
||||||
|
#include <QGraphicsLayout>
|
||||||
|
#include <QLineSeries>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QValueAxis>
|
||||||
|
|
||||||
|
// TODO(crueter): Reset samples when user changes turbo, slow, etc.
|
||||||
|
PerformanceOverlay::PerformanceOverlay(MainWindow* parent)
|
||||||
|
: QWidget(parent), m_mainWindow{parent}, ui(new Ui::PerformanceOverlay) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
|
||||||
|
raise();
|
||||||
|
|
||||||
|
// chart setup
|
||||||
|
m_fpsSeries = new QLineSeries(this);
|
||||||
|
|
||||||
|
QPen pen(Qt::red);
|
||||||
|
pen.setWidth(2);
|
||||||
|
m_fpsSeries->setPen(pen);
|
||||||
|
|
||||||
|
m_fpsChart = new QChart;
|
||||||
|
m_fpsChart->addSeries(m_fpsSeries);
|
||||||
|
m_fpsChart->legend()->hide();
|
||||||
|
m_fpsChart->setBackgroundBrush(Qt::black);
|
||||||
|
m_fpsChart->setBackgroundVisible(true);
|
||||||
|
m_fpsChart->layout()->setContentsMargins(2, 2, 2, 2);
|
||||||
|
m_fpsChart->setMargins(QMargins{4, 4, 4, 4});
|
||||||
|
|
||||||
|
// axes
|
||||||
|
m_fpsX = new QValueAxis(this);
|
||||||
|
m_fpsX->setRange(0, NUM_FPS_SAMPLES);
|
||||||
|
m_fpsX->setVisible(false);
|
||||||
|
|
||||||
|
m_fpsY = new QValueAxis(this);
|
||||||
|
m_fpsY->setRange(0, 60);
|
||||||
|
m_fpsY->setLabelFormat(QStringLiteral("%d"));
|
||||||
|
m_fpsY->setLabelsColor(Qt::white);
|
||||||
|
|
||||||
|
QFont axisFont = m_fpsY->labelsFont();
|
||||||
|
axisFont.setPixelSize(10);
|
||||||
|
m_fpsY->setLabelsFont(axisFont);
|
||||||
|
m_fpsY->setTickCount(3);
|
||||||
|
|
||||||
|
// gray-ish label w/ white lines
|
||||||
|
m_fpsY->setLabelsVisible(true);
|
||||||
|
m_fpsY->setGridLineColor(QColor(50, 50, 50));
|
||||||
|
m_fpsY->setLinePenColor(Qt::white);
|
||||||
|
|
||||||
|
m_fpsChart->addAxis(m_fpsX, Qt::AlignBottom);
|
||||||
|
m_fpsChart->addAxis(m_fpsY, Qt::AlignLeft);
|
||||||
|
m_fpsSeries->attachAxis(m_fpsX);
|
||||||
|
m_fpsSeries->attachAxis(m_fpsY);
|
||||||
|
|
||||||
|
// chart view
|
||||||
|
m_fpsChartView = new QChartView(m_fpsChart, this);
|
||||||
|
m_fpsChartView->setRenderHint(QPainter::Antialiasing);
|
||||||
|
m_fpsChartView->setMinimumHeight(100);
|
||||||
|
|
||||||
|
ui->verticalLayout->addWidget(m_fpsChartView, 1);
|
||||||
|
|
||||||
|
// thanks Debian.
|
||||||
|
QFont font = ui->fps->font();
|
||||||
|
font.setWeight(QFont::DemiBold);
|
||||||
|
|
||||||
|
ui->fps->setFont(font);
|
||||||
|
ui->frametime->setFont(font);
|
||||||
|
|
||||||
|
// pos/stats
|
||||||
|
resetPosition(m_mainWindow->pos());
|
||||||
|
connect(parent, &MainWindow::positionChanged, this, &PerformanceOverlay::resetPosition);
|
||||||
|
connect(m_mainWindow, &MainWindow::statsUpdated, this, &PerformanceOverlay::updateStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceOverlay::~PerformanceOverlay() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceOverlay::resetPosition(const QPoint& _) {
|
||||||
|
auto pos = m_mainWindow->pos();
|
||||||
|
move(pos.x() + m_offset.x(), pos.y() + m_offset.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceOverlay::updateStats(const Core::PerfStatsResults& results,
|
||||||
|
const VideoCore::ShaderNotify& shaders) {
|
||||||
|
auto fps = results.average_game_fps;
|
||||||
|
if (!std::isnan(fps)) {
|
||||||
|
// don't sample measurements < 3 fps because they are probably outliers or freezes
|
||||||
|
static constexpr double FPS_SAMPLE_THRESHOLD = 3.0;
|
||||||
|
|
||||||
|
QString fpsText = tr("%1 fps").arg(std::round(fps), 0, 'f', 0);
|
||||||
|
// if (!m_fpsSuffix.isEmpty()) fpsText = fpsText % QStringLiteral(" (%1)").arg(m_fpsSuffix);
|
||||||
|
ui->fps->setText(fpsText);
|
||||||
|
|
||||||
|
// sampling
|
||||||
|
if (fps > FPS_SAMPLE_THRESHOLD) {
|
||||||
|
m_fpsSamples.push_back(fps);
|
||||||
|
m_fpsPoints.push_back(QPointF{m_xPos++, fps});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_fpsSamples.size() > NUM_FPS_SAMPLES) {
|
||||||
|
m_fpsSamples.pop_front();
|
||||||
|
m_fpsPoints.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the average only go back 10 samples max
|
||||||
|
if (m_fpsSamples.size() >= 2) {
|
||||||
|
const int back_search = std::min(size_t(10), m_fpsSamples.size() - 1);
|
||||||
|
double sum = std::accumulate(m_fpsSamples.end() - back_search, m_fpsSamples.end(), 0.0);
|
||||||
|
double avg = sum / back_search;
|
||||||
|
|
||||||
|
ui->fps_avg->setText(tr("Avg: %1").arg(avg, 0, 'f', 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// chart it :)
|
||||||
|
if (!m_fpsPoints.empty()) {
|
||||||
|
auto [min_it, max_it] = std::minmax_element(m_fpsSamples.begin(), m_fpsSamples.end());
|
||||||
|
double min_fps = *min_it;
|
||||||
|
double max_fps = *max_it;
|
||||||
|
|
||||||
|
ui->fps_min->setText(tr("Min: %1").arg(min_fps, 0, 'f', 0));
|
||||||
|
ui->fps_max->setText(tr("Max: %1").arg(max_fps, 0, 'f', 0));
|
||||||
|
|
||||||
|
m_fpsSeries->replace(QList<QPointF>(m_fpsPoints.begin(), m_fpsPoints.end()));
|
||||||
|
|
||||||
|
qreal x_min = std::max(0.0, m_xPos - NUM_FPS_SAMPLES);
|
||||||
|
qreal x_max = std::max(qreal(10), m_xPos);
|
||||||
|
m_fpsX->setRange(x_min, x_max);
|
||||||
|
m_fpsY->setRange(0.0, max_fps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ft = results.frametime;
|
||||||
|
if (!std::isnan(ft)) {
|
||||||
|
// don't sample measurements > 500 ms because they are probably outliers
|
||||||
|
static constexpr double FT_SAMPLE_THRESHOLD = 500.0;
|
||||||
|
|
||||||
|
double ft_ms = results.frametime * 1000.0;
|
||||||
|
ui->frametime->setText(tr("%1 ms").arg(ft_ms, 0, 'f', 2));
|
||||||
|
|
||||||
|
// sampling
|
||||||
|
if (ft_ms <= FT_SAMPLE_THRESHOLD)
|
||||||
|
m_frametimeSamples.push_back(ft_ms);
|
||||||
|
|
||||||
|
if (m_frametimeSamples.size() > NUM_FRAMETIME_SAMPLES)
|
||||||
|
m_frametimeSamples.pop_front();
|
||||||
|
|
||||||
|
if (!m_frametimeSamples.empty()) {
|
||||||
|
auto [min_it, max_it] =
|
||||||
|
std::minmax_element(m_frametimeSamples.begin(), m_frametimeSamples.end());
|
||||||
|
ui->ft_min->setText(tr("Min: %1").arg(*min_it, 0, 'f', 1));
|
||||||
|
ui->ft_max->setText(tr("Max: %1").arg(*max_it, 0, 'f', 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the average only go back 10 samples max
|
||||||
|
if (m_frametimeSamples.size() >= 2) {
|
||||||
|
const int back_search = std::min(size_t(10), m_frametimeSamples.size() - 1);
|
||||||
|
double sum = std::accumulate(m_frametimeSamples.end() - back_search,
|
||||||
|
m_frametimeSamples.end(), 0.0);
|
||||||
|
double avg = sum / back_search;
|
||||||
|
|
||||||
|
ui->ft_avg->setText(tr("Avg: %1").arg(avg, 0, 'f', 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceOverlay::paintEvent(QPaintEvent* event) {
|
||||||
|
QPainter painter(this);
|
||||||
|
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
painter.setBrush(m_background);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
painter.drawRoundedRect(rect(), 10.0, 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceOverlay::mousePressEvent(QMouseEvent* event) {
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
m_drag_start_pos = event->pos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceOverlay::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
// drag
|
||||||
|
if (event->buttons() & Qt::LeftButton) {
|
||||||
|
QPoint new_global_pos = event->globalPosition().toPoint() - m_drag_start_pos;
|
||||||
|
m_offset = new_global_pos - m_mainWindow->pos();
|
||||||
|
move(new_global_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceOverlay::closeEvent(QCloseEvent* event) {
|
||||||
|
emit closed();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
class ShaderNotify;
|
||||||
|
}
|
||||||
|
namespace Core {
|
||||||
|
struct PerfStatsResults;
|
||||||
|
}
|
||||||
|
namespace Ui {
|
||||||
|
class PerformanceOverlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QLineSeries;
|
||||||
|
class QChart;
|
||||||
|
class QChartView;
|
||||||
|
class QValueAxis;
|
||||||
|
class MainWindow;
|
||||||
|
|
||||||
|
class PerformanceOverlay : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PerformanceOverlay(MainWindow* parent = nullptr);
|
||||||
|
~PerformanceOverlay();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent* event) override;
|
||||||
|
|
||||||
|
void mousePressEvent(QMouseEvent* event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent* event) override;
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resetPosition(const QPoint& pos);
|
||||||
|
void updateStats(const Core::PerfStatsResults &results, const VideoCore::ShaderNotify &shaders);
|
||||||
|
|
||||||
|
MainWindow *m_mainWindow = nullptr;
|
||||||
|
Ui::PerformanceOverlay* ui;
|
||||||
|
|
||||||
|
// colors
|
||||||
|
QColor m_background{127, 127, 127, 190};
|
||||||
|
|
||||||
|
QPoint m_offset{25, 75};
|
||||||
|
|
||||||
|
// frametime
|
||||||
|
const size_t NUM_FRAMETIME_SAMPLES = 300;
|
||||||
|
std::deque<double> m_frametimeSamples;
|
||||||
|
|
||||||
|
// fps
|
||||||
|
const size_t NUM_FPS_SAMPLES = 120;
|
||||||
|
qreal m_xPos = 0;
|
||||||
|
std::deque<double> m_fpsSamples;
|
||||||
|
std::deque<QPointF> m_fpsPoints;
|
||||||
|
|
||||||
|
// drag
|
||||||
|
QPoint m_drag_start_pos;
|
||||||
|
|
||||||
|
// fps chart
|
||||||
|
QLineSeries *m_fpsSeries = nullptr;
|
||||||
|
QChart *m_fpsChart = nullptr;
|
||||||
|
QChartView *m_fpsChartView = nullptr;
|
||||||
|
QValueAxis *m_fpsX = nullptr;
|
||||||
|
QValueAxis *m_fpsY = nullptr;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void closed();
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PerformanceOverlay</class>
|
||||||
|
<widget class="QWidget" name="PerformanceOverlay">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>225</width>
|
||||||
|
<height>250</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="ft_label_layout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: #0000ff;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Frametime</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="frametime">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0 ms</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="ft_layout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="ft_min">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Min: 0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="ft_max">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Max: 0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="ft_avg">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Avg: 0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="fps_label_layout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: #ff0000;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>FPS</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="fps">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0 fps</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="fps_min">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Min: 0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="fps_max">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Max: 0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="fps_avg">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Sans Serif</family>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Avg: 0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
Loading…
Reference in New Issue