Add performance statistics to status bar
This commit is contained in:
parent
21f4f49c7a
commit
c75ae6c585
|
@ -253,6 +253,8 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||||
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
|
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
|
||||||
SLOT(OnEmulationStarting(EmuThread*)));
|
SLOT(OnEmulationStarting(EmuThread*)));
|
||||||
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
|
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
|
||||||
|
|
||||||
|
connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::ConnectMenuEvents() {
|
void GMainWindow::ConnectMenuEvents() {
|
||||||
|
@ -401,6 +403,8 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||||
if (ui.action_Single_Window_Mode->isChecked()) {
|
if (ui.action_Single_Window_Mode->isChecked()) {
|
||||||
game_list->hide();
|
game_list->hide();
|
||||||
}
|
}
|
||||||
|
status_bar_update_timer.start(1000);
|
||||||
|
|
||||||
render_window->show();
|
render_window->show();
|
||||||
render_window->setFocus();
|
render_window->setFocus();
|
||||||
|
|
||||||
|
@ -435,6 +439,12 @@ void GMainWindow::ShutdownGame() {
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
game_list->show();
|
game_list->show();
|
||||||
|
|
||||||
|
// Disable status bar updates
|
||||||
|
status_bar_update_timer.stop();
|
||||||
|
emu_speed_label->setVisible(false);
|
||||||
|
game_fps_label->setVisible(false);
|
||||||
|
emu_frametime_label->setVisible(false);
|
||||||
|
|
||||||
emulation_running = false;
|
emulation_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,6 +624,23 @@ void GMainWindow::OnCreateGraphicsSurfaceViewer() {
|
||||||
graphicsSurfaceViewerWidget->show();
|
graphicsSurfaceViewerWidget->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::UpdateStatusBar() {
|
||||||
|
if (emu_thread == nullptr) {
|
||||||
|
status_bar_update_timer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto results = Core::System::GetInstance().GetAndResetPerfStats();
|
||||||
|
|
||||||
|
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 2));
|
||||||
|
game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 1));
|
||||||
|
emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
|
||||||
|
|
||||||
|
emu_speed_label->setVisible(true);
|
||||||
|
game_fps_label->setVisible(true);
|
||||||
|
emu_frametime_label->setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
bool GMainWindow::ConfirmClose() {
|
bool GMainWindow::ConfirmClose() {
|
||||||
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -127,6 +127,8 @@ private slots:
|
||||||
void OnCreateGraphicsSurfaceViewer();
|
void OnCreateGraphicsSurfaceViewer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void UpdateStatusBar();
|
||||||
|
|
||||||
Ui::MainWindow ui;
|
Ui::MainWindow ui;
|
||||||
|
|
||||||
GRenderWindow* render_window;
|
GRenderWindow* render_window;
|
||||||
|
@ -136,6 +138,7 @@ private:
|
||||||
QLabel* emu_speed_label = nullptr;
|
QLabel* emu_speed_label = nullptr;
|
||||||
QLabel* game_fps_label = nullptr;
|
QLabel* game_fps_label = nullptr;
|
||||||
QLabel* emu_frametime_label = nullptr;
|
QLabel* emu_frametime_label = nullptr;
|
||||||
|
QTimer status_bar_update_timer;
|
||||||
|
|
||||||
std::unique_ptr<Config> config;
|
std::unique_ptr<Config> config;
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,7 @@ set(SRCS
|
||||||
loader/smdh.cpp
|
loader/smdh.cpp
|
||||||
tracer/recorder.cpp
|
tracer/recorder.cpp
|
||||||
memory.cpp
|
memory.cpp
|
||||||
|
perf_stats.cpp
|
||||||
settings.cpp
|
settings.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -357,6 +358,7 @@ set(HEADERS
|
||||||
memory.h
|
memory.h
|
||||||
memory_setup.h
|
memory_setup.h
|
||||||
mmio.h
|
mmio.h
|
||||||
|
perf_stats.h
|
||||||
settings.h
|
settings.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,11 @@ void System::PrepareReschedule() {
|
||||||
reschedule_pending = true;
|
reschedule_pending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PerfStats::Results System::GetAndResetPerfStats() {
|
||||||
|
auto perf_stats = this->perf_stats.Lock();
|
||||||
|
return perf_stats->GetAndResetStats(CoreTiming::GetGlobalTimeUs());
|
||||||
|
}
|
||||||
|
|
||||||
void System::Reschedule() {
|
void System::Reschedule() {
|
||||||
if (!reschedule_pending) {
|
if (!reschedule_pending) {
|
||||||
return;
|
return;
|
||||||
|
@ -140,6 +145,10 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||||
|
|
||||||
LOG_DEBUG(Core, "Initialized OK");
|
LOG_DEBUG(Core, "Initialized OK");
|
||||||
|
|
||||||
|
// Reset counters and set time origin to current frame
|
||||||
|
GetAndResetPerfStats();
|
||||||
|
perf_stats.Lock()->BeginSystemFrame();
|
||||||
|
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/synchronized_wrapper.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
|
||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
class ARM_Interface;
|
class ARM_Interface;
|
||||||
|
@ -83,6 +84,8 @@ public:
|
||||||
/// Prepare the core emulation for a reschedule
|
/// Prepare the core emulation for a reschedule
|
||||||
void PrepareReschedule();
|
void PrepareReschedule();
|
||||||
|
|
||||||
|
PerfStats::Results GetAndResetPerfStats();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a reference to the emulated CPU.
|
* Gets a reference to the emulated CPU.
|
||||||
* @returns A reference to the emulated CPU.
|
* @returns A reference to the emulated CPU.
|
||||||
|
@ -91,6 +94,8 @@ public:
|
||||||
return *cpu_core;
|
return *cpu_core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::SynchronizedWrapper<PerfStats> perf_stats;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Initialize the emulated system.
|
* Initialize the emulated system.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/event.h"
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
@ -280,6 +281,8 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
|
||||||
|
|
||||||
if (screen_id == 0) {
|
if (screen_id == 0) {
|
||||||
MicroProfileFlip();
|
MicroProfileFlip();
|
||||||
|
auto perf_stats = Core::System::GetInstance().perf_stats.Lock();
|
||||||
|
perf_stats->EndGameFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace GPU {
|
||||||
Regs g_regs;
|
Regs g_regs;
|
||||||
|
|
||||||
/// 268MHz CPU clocks / 60Hz frames per second
|
/// 268MHz CPU clocks / 60Hz frames per second
|
||||||
const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / 60;
|
const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE;
|
||||||
/// Event id for CoreTiming
|
/// Event id for CoreTiming
|
||||||
static int vblank_event;
|
static int vblank_event;
|
||||||
/// Total number of frames drawn
|
/// Total number of frames drawn
|
||||||
|
@ -41,7 +41,7 @@ static u64 frame_count;
|
||||||
static u32 time_point;
|
static u32 time_point;
|
||||||
/// Total delay caused by slow frames
|
/// Total delay caused by slow frames
|
||||||
static float time_delay;
|
static float time_delay;
|
||||||
constexpr float FIXED_FRAME_TIME = 1000.0f / 60;
|
constexpr float FIXED_FRAME_TIME = 1000.0f / SCREEN_REFRESH_RATE;
|
||||||
// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
|
// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
|
||||||
// values increases time needed to limit frame rate after spikes
|
// values increases time needed to limit frame rate after spikes
|
||||||
constexpr float MAX_LAG_TIME = 18;
|
constexpr float MAX_LAG_TIME = 18;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
namespace GPU {
|
namespace GPU {
|
||||||
|
|
||||||
|
constexpr float SCREEN_REFRESH_RATE = 60;
|
||||||
|
|
||||||
// Returns index corresponding to the Regs member labeled by field_name
|
// Returns index corresponding to the Regs member labeled by field_name
|
||||||
// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
|
// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
|
||||||
// when used with array elements (e.g. GPU_REG_INDEX(memory_fill_config[0])).
|
// when used with array elements (e.g. GPU_REG_INDEX(memory_fill_config[0])).
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2017 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include "core/hw/gpu.h"
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
void PerfStats::BeginSystemFrame() {
|
||||||
|
frame_begin = Clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfStats::EndSystemFrame() {
|
||||||
|
auto frame_end = Clock::now();
|
||||||
|
accumulated_frametime += frame_end - frame_begin;
|
||||||
|
system_frames += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfStats::EndGameFrame() {
|
||||||
|
game_frames += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) {
|
||||||
|
using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
|
||||||
|
auto now = Clock::now();
|
||||||
|
// Walltime elapsed since stats were reset
|
||||||
|
auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
|
||||||
|
|
||||||
|
auto system_us_per_second =
|
||||||
|
static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
|
||||||
|
|
||||||
|
Results results{};
|
||||||
|
results.system_fps = static_cast<double>(system_frames) / interval;
|
||||||
|
results.game_fps = static_cast<double>(game_frames) / interval;
|
||||||
|
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
|
||||||
|
static_cast<double>(system_frames);
|
||||||
|
results.emulation_speed = system_us_per_second / 1'000'000.0;
|
||||||
|
|
||||||
|
// Reset counters
|
||||||
|
reset_point = now;
|
||||||
|
reset_point_system_us = current_system_time_us;
|
||||||
|
accumulated_frametime = Clock::duration::zero();
|
||||||
|
system_frames = 0;
|
||||||
|
game_frames = 0;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2017 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class PerfStats {
|
||||||
|
public:
|
||||||
|
using Clock = std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
|
struct Results {
|
||||||
|
/// System FPS (LCD VBlanks) in Hz
|
||||||
|
double system_fps;
|
||||||
|
/// Game FPS (GSP frame submissions) in Hz
|
||||||
|
double game_fps;
|
||||||
|
/// Walltime per system frame, in seconds, excluding any waits
|
||||||
|
double frametime;
|
||||||
|
/// Ratio of walltime / emulated time elapsed
|
||||||
|
double emulation_speed;
|
||||||
|
};
|
||||||
|
|
||||||
|
void BeginSystemFrame();
|
||||||
|
void EndSystemFrame();
|
||||||
|
void EndGameFrame();
|
||||||
|
|
||||||
|
Results GetAndResetStats(u64 current_system_time_us);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Clock::time_point reset_point = Clock::now();
|
||||||
|
|
||||||
|
Clock::time_point frame_begin;
|
||||||
|
Clock::duration accumulated_frametime = Clock::duration::zero();
|
||||||
|
u64 reset_point_system_us = 0;
|
||||||
|
u32 system_frames = 0;
|
||||||
|
u32 game_frames = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/profiler_reporting.h"
|
#include "common/profiler_reporting.h"
|
||||||
#include "common/synchronized_wrapper.h"
|
#include "common/synchronized_wrapper.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/hw/gpu.h"
|
#include "core/hw/gpu.h"
|
||||||
#include "core/hw/hw.h"
|
#include "core/hw/hw.h"
|
||||||
|
@ -151,6 +152,10 @@ void RendererOpenGL::SwapBuffers() {
|
||||||
auto aggregator = Common::Profiling::GetTimingResultsAggregator();
|
auto aggregator = Common::Profiling::GetTimingResultsAggregator();
|
||||||
aggregator->AddFrame(profiler.GetPreviousFrameResults());
|
aggregator->AddFrame(profiler.GetPreviousFrameResults());
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto perf_stats = Core::System::GetInstance().perf_stats.Lock();
|
||||||
|
perf_stats->EndSystemFrame();
|
||||||
|
}
|
||||||
|
|
||||||
// Swap buffers
|
// Swap buffers
|
||||||
render_window->PollEvents();
|
render_window->PollEvents();
|
||||||
|
@ -159,6 +164,10 @@ void RendererOpenGL::SwapBuffers() {
|
||||||
prev_state.Apply();
|
prev_state.Apply();
|
||||||
|
|
||||||
profiler.BeginFrame();
|
profiler.BeginFrame();
|
||||||
|
{
|
||||||
|
auto perf_stats = Core::System::GetInstance().perf_stats.Lock();
|
||||||
|
perf_stats->BeginSystemFrame();
|
||||||
|
}
|
||||||
|
|
||||||
RefreshRasterizerSetting();
|
RefreshRasterizerSetting();
|
||||||
|
|
||||||
|
|
Reference in New Issue