Add performance statistics to status bar
This commit is contained in:
		| @@ -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])). | ||||||
|   | |||||||
							
								
								
									
										53
									
								
								src/core/perf_stats.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/core/perf_stats.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										43
									
								
								src/core/perf_stats.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/core/perf_stats.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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
	
	Block a user