From c75ae6c585f651a1b7c162c2e1ecccd22a1c587d Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Sun, 19 Feb 2017 14:34:47 -0800 Subject: Add performance statistics to status bar --- src/core/perf_stats.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/core/perf_stats.h (limited to 'src/core/perf_stats.h') diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h new file mode 100644 index 000000000..566a1419a --- /dev/null +++ b/src/core/perf_stats.h @@ -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 +#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 -- cgit v1.2.3 From 92c8bd4b1f650438274e50303a6d3f668924d071 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Sun, 19 Feb 2017 18:18:26 -0800 Subject: PerfStats: Add method to get the instantaneous time ratio --- src/core/frontend/emu_window.cpp | 5 ++--- src/core/perf_stats.cpp | 14 +++++++++++--- src/core/perf_stats.h | 10 +++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) (limited to 'src/core/perf_stats.h') diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 6b4637741..b65d6ff58 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -5,7 +5,7 @@ #include #include #include "common/assert.h" -#include "common/profiler_reporting.h" +#include "core/core.h" #include "core/frontend/emu_window.h" #include "core/frontend/key_map.h" #include "video_core/video_core.h" @@ -104,8 +104,7 @@ void EmuWindow::AccelerometerChanged(float x, float y, float z) { void EmuWindow::GyroscopeChanged(float x, float y, float z) { constexpr float FULL_FPS = 60; float coef = GetGyroscopeRawToDpsCoefficient(); - float stretch = - FULL_FPS / Common::Profiling::GetTimingResultsAggregator()->GetAggregatedResults().fps; + float stretch = Core::System::GetInstance().perf_stats.Lock()->GetLastFrameTimeScale(); std::lock_guard lock(gyro_mutex); gyro_x = static_cast(x * coef * stretch); gyro_y = static_cast(y * coef * stretch); diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 6d9e603e4..8d9e521a3 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -6,6 +6,9 @@ #include "core/hw/gpu.h" #include "core/perf_stats.h" +using DoubleSecs = std::chrono::duration; +using std::chrono::duration_cast; + namespace Core { void PerfStats::BeginSystemFrame() { @@ -16,6 +19,9 @@ void PerfStats::EndSystemFrame() { auto frame_end = Clock::now(); accumulated_frametime += frame_end - frame_begin; system_frames += 1; + + previous_frame_length = frame_end - previous_frame_end; + previous_frame_end = frame_end; } void PerfStats::EndGameFrame() { @@ -23,9 +29,6 @@ void PerfStats::EndGameFrame() { } PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { - using DoubleSecs = std::chrono::duration; - using std::chrono::duration_cast; - auto now = Clock::now(); // Walltime elapsed since stats were reset auto interval = duration_cast(now - reset_point).count(); @@ -50,4 +53,9 @@ PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { return results; } +double PerfStats::GetLastFrameTimeScale() { + constexpr double FRAME_LENGTH = 1.0 / GPU::SCREEN_REFRESH_RATE; + return duration_cast(previous_frame_length).count() / FRAME_LENGTH; +} + } // namespace Core diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 566a1419a..8a03c511a 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -30,11 +30,19 @@ public: Results GetAndResetStats(u64 current_system_time_us); + /** + * Gets the ratio between walltime and the emulated time of the previous system frame. This is + * useful for scaling inputs or outputs moving between the two time domains. + */ + double GetLastFrameTimeScale(); + private: Clock::time_point reset_point = Clock::now(); - Clock::time_point frame_begin; + Clock::time_point frame_begin = reset_point; + Clock::time_point previous_frame_end = reset_point; Clock::duration accumulated_frametime = Clock::duration::zero(); + Clock::duration previous_frame_length = Clock::duration::zero(); u64 reset_point_system_us = 0; u32 system_frames = 0; u32 game_frames = 0; -- cgit v1.2.3 From b285c2a4ed29a126b5bcfe46e2784bd1870bdf82 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 20 Feb 2017 13:56:58 -0800 Subject: Core: Make PerfStats internally locked More ergonomic to use and will be required for upcoming changes. --- src/core/core.cpp | 5 ++--- src/core/core.h | 3 +-- src/core/frontend/emu_window.cpp | 2 +- src/core/hle/service/gsp_gpu.cpp | 3 +-- src/core/perf_stats.cpp | 11 +++++++++++ src/core/perf_stats.h | 7 +++++++ src/video_core/renderer_opengl/renderer_opengl.cpp | 10 ++-------- 7 files changed, 25 insertions(+), 16 deletions(-) (limited to 'src/core/perf_stats.h') diff --git a/src/core/core.cpp b/src/core/core.cpp index ca2c28ce4..140ff6451 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -110,8 +110,7 @@ void System::PrepareReschedule() { } PerfStats::Results System::GetAndResetPerfStats() { - auto perf_stats = this->perf_stats.Lock(); - return perf_stats->GetAndResetStats(CoreTiming::GetGlobalTimeUs()); + return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); } void System::Reschedule() { @@ -147,7 +146,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { // Reset counters and set time origin to current frame GetAndResetPerfStats(); - perf_stats.Lock()->BeginSystemFrame(); + perf_stats.BeginSystemFrame(); return ResultStatus::Success; } diff --git a/src/core/core.h b/src/core/core.h index 3efc20c3d..db3b98a05 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -7,7 +7,6 @@ #include #include #include "common/common_types.h" -#include "common/synchronized_wrapper.h" #include "core/memory.h" #include "core/perf_stats.h" @@ -94,7 +93,7 @@ public: return *cpu_core; } - Common::SynchronizedWrapper perf_stats; + PerfStats perf_stats; private: /** diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index b65d6ff58..a155b657d 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -104,7 +104,7 @@ void EmuWindow::AccelerometerChanged(float x, float y, float z) { void EmuWindow::GyroscopeChanged(float x, float y, float z) { constexpr float FULL_FPS = 60; float coef = GetGyroscopeRawToDpsCoefficient(); - float stretch = Core::System::GetInstance().perf_stats.Lock()->GetLastFrameTimeScale(); + float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); std::lock_guard lock(gyro_mutex); gyro_x = static_cast(x * coef * stretch); gyro_y = static_cast(y * coef * stretch); diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 67bab38da..097ed87e4 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -281,8 +281,7 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { if (screen_id == 0) { MicroProfileFlip(); - auto perf_stats = Core::System::GetInstance().perf_stats.Lock(); - perf_stats->EndGameFrame(); + Core::System::GetInstance().perf_stats.EndGameFrame(); } return RESULT_SUCCESS; diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 8d9e521a3..06bc788bd 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include "core/hw/gpu.h" #include "core/perf_stats.h" @@ -12,10 +13,14 @@ using std::chrono::duration_cast; namespace Core { void PerfStats::BeginSystemFrame() { + std::lock_guard lock(object_mutex); + frame_begin = Clock::now(); } void PerfStats::EndSystemFrame() { + std::lock_guard lock(object_mutex); + auto frame_end = Clock::now(); accumulated_frametime += frame_end - frame_begin; system_frames += 1; @@ -25,10 +30,14 @@ void PerfStats::EndSystemFrame() { } void PerfStats::EndGameFrame() { + std::lock_guard lock(object_mutex); + game_frames += 1; } PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { + std::lock_guard lock(object_mutex); + auto now = Clock::now(); // Walltime elapsed since stats were reset auto interval = duration_cast(now - reset_point).count(); @@ -54,6 +63,8 @@ PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { } double PerfStats::GetLastFrameTimeScale() { + std::lock_guard lock(object_mutex); + constexpr double FRAME_LENGTH = 1.0 / GPU::SCREEN_REFRESH_RATE; return duration_cast(previous_frame_length).count() / FRAME_LENGTH; } diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 8a03c511a..4098fc1f2 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -5,10 +5,15 @@ #pragma once #include +#include #include "common/common_types.h" namespace Core { +/** + * Class to manage and query performance/timing statistics. All public functions of this class are + * thread-safe unless stated otherwise. + */ class PerfStats { public: using Clock = std::chrono::high_resolution_clock; @@ -37,6 +42,8 @@ public: double GetLastFrameTimeScale(); private: + std::mutex object_mutex; + Clock::time_point reset_point = Clock::now(); Clock::time_point frame_begin = reset_point; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 6bc142148..b3604106c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -145,10 +145,7 @@ void RendererOpenGL::SwapBuffers() { DrawScreens(); - { - auto perf_stats = Core::System::GetInstance().perf_stats.Lock(); - perf_stats->EndSystemFrame(); - } + Core::System::GetInstance().perf_stats.EndSystemFrame(); // Swap buffers render_window->PollEvents(); @@ -156,10 +153,7 @@ void RendererOpenGL::SwapBuffers() { prev_state.Apply(); - { - auto perf_stats = Core::System::GetInstance().perf_stats.Lock(); - perf_stats->BeginSystemFrame(); - } + Core::System::GetInstance().perf_stats.BeginSystemFrame(); RefreshRasterizerSetting(); -- cgit v1.2.3 From fb1979d7e26c20fe2b8d2c3d3dc998e5e00f2f61 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 20 Feb 2017 16:31:59 -0800 Subject: Core: Re-write frame limiter Now based on std::chrono, and also works in terms of emulated time instead of frames, so we can in the future frame-limit even when the display is disabled, etc. The frame limiter can also be enabled along with v-sync now, which should be useful for those with displays running at more than 60 Hz. --- src/core/core.h | 1 + src/core/hw/gpu.cpp | 39 ---------------------- src/core/perf_stats.cpp | 33 ++++++++++++++++++ src/core/perf_stats.h | 16 +++++++++ src/video_core/renderer_opengl/renderer_opengl.cpp | 6 ++-- 5 files changed, 53 insertions(+), 42 deletions(-) (limited to 'src/core/perf_stats.h') diff --git a/src/core/core.h b/src/core/core.h index db3b98a05..6c9c936b5 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -94,6 +94,7 @@ public: } PerfStats perf_stats; + FrameLimiter frame_limiter; private: /** diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7cf081aad..42809c731 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -8,17 +8,13 @@ #include "common/color.h" #include "common/common_types.h" #include "common/logging/log.h" -#include "common/math_util.h" #include "common/microprofile.h" -#include "common/thread.h" -#include "common/timer.h" #include "common/vector_math.h" #include "core/core_timing.h" #include "core/hle/service/gsp_gpu.h" #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/memory.h" -#include "core/settings.h" #include "core/tracer/recorder.h" #include "video_core/command_processor.h" #include "video_core/debug_utils/debug_utils.h" @@ -35,16 +31,6 @@ Regs g_regs; const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE; /// Event id for CoreTiming static int vblank_event; -/// Total number of frames drawn -static u64 frame_count; -/// Start clock for frame limiter -static u32 time_point; -/// Total delay caused by slow frames -static float time_delay; -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 -// values increases time needed to limit frame rate after spikes -constexpr float MAX_LAG_TIME = 18; template inline void Read(T& var, const u32 raw_addr) { @@ -522,24 +508,8 @@ template void Write(u32 addr, const u32 data); template void Write(u32 addr, const u16 data); template void Write(u32 addr, const u8 data); -static void FrameLimiter() { - time_delay += FIXED_FRAME_TIME; - time_delay = MathUtil::Clamp(time_delay, -MAX_LAG_TIME, MAX_LAG_TIME); - s32 desired_time = static_cast(time_delay); - s32 elapsed_time = static_cast(Common::Timer::GetTimeMs() - time_point); - - if (elapsed_time < desired_time) { - Common::SleepCurrentThread(desired_time - elapsed_time); - } - - u32 frame_time = Common::Timer::GetTimeMs() - time_point; - - time_delay -= frame_time; -} - /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { - frame_count++; VideoCore::g_renderer->SwapBuffers(); // Signal to GSP that GPU interrupt has occurred @@ -550,12 +520,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) { Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); - if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) { - FrameLimiter(); - } - - time_point = Common::Timer::GetTimeMs(); - // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); } @@ -590,9 +554,6 @@ void Init() { framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8); framebuffer_sub.active_fb = 0; - frame_count = 0; - time_point = Common::Timer::GetTimeMs(); - vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); CoreTiming::ScheduleEvent(frame_ticks, vblank_event); diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 06bc788bd..eb59a1332 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -4,11 +4,16 @@ #include #include +#include +#include "common/math_util.h" #include "core/hw/gpu.h" #include "core/perf_stats.h" +#include "core/settings.h" +using namespace std::chrono_literals; using DoubleSecs = std::chrono::duration; using std::chrono::duration_cast; +using std::chrono::microseconds; namespace Core { @@ -69,4 +74,32 @@ double PerfStats::GetLastFrameTimeScale() { return duration_cast(previous_frame_length).count() / FRAME_LENGTH; } +void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { + // 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. + constexpr microseconds MAX_LAG_TIME_US = 25ms; + + if (!Settings::values.toggle_framelimit) { + return; + } + + auto now = Clock::now(); + + frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); + frame_limiting_delta_err -= duration_cast(now - previous_walltime); + frame_limiting_delta_err = + MathUtil::Clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); + + if (frame_limiting_delta_err > microseconds::zero()) { + std::this_thread::sleep_for(frame_limiting_delta_err); + + auto now_after_sleep = Clock::now(); + frame_limiting_delta_err -= duration_cast(now_after_sleep - now); + now = now_after_sleep; + } + + previous_system_time_us = current_system_time_us; + previous_walltime = now; +} + } // namespace Core diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 4098fc1f2..b03adab68 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -55,4 +55,20 @@ private: u32 game_frames = 0; }; +class FrameLimiter { +public: + using Clock = std::chrono::high_resolution_clock; + + void DoFrameLimiting(u64 current_system_time_us); + +private: + /// Emulated system time (in microseconds) at the last limiter invocation + u64 previous_system_time_us = 0; + /// Walltime at the last limiter invocation + Clock::time_point previous_walltime = Clock::now(); + + /// Accumulated difference between walltime and emulated time + std::chrono::microseconds frame_limiting_delta_err{0}; +}; + } // namespace Core diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index b3604106c..e19375466 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -10,8 +10,8 @@ #include "common/assert.h" #include "common/bit_field.h" #include "common/logging/log.h" -#include "common/synchronized_wrapper.h" #include "core/core.h" +#include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/hw/gpu.h" #include "core/hw/hw.h" @@ -151,10 +151,10 @@ void RendererOpenGL::SwapBuffers() { render_window->PollEvents(); render_window->SwapBuffers(); - prev_state.Apply(); - + Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs()); Core::System::GetInstance().perf_stats.BeginSystemFrame(); + prev_state.Apply(); RefreshRasterizerSetting(); if (Pica::g_debug_context && Pica::g_debug_context->recorder) { -- cgit v1.2.3 From 174464a87f2e1709597bc1e0cb08c877487a771b Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Sun, 26 Feb 2017 17:13:12 -0800 Subject: PerfStats: Re-order and document members better --- src/core/perf_stats.cpp | 2 +- src/core/perf_stats.h | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'src/core/perf_stats.h') diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index eb59a1332..2cdfb9ded 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -76,7 +76,7 @@ double PerfStats::GetLastFrameTimeScale() { void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { // 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 increase the time needed to recover and limit framerate again after spikes. constexpr microseconds MAX_LAG_TIME_US = 25ms; if (!Settings::values.toggle_framelimit) { diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index b03adab68..362b205c8 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -44,15 +44,24 @@ public: private: std::mutex object_mutex; + /// Point when the cumulative counters were reset Clock::time_point reset_point = Clock::now(); + /// System time when the cumulative counters were reset + u64 reset_point_system_us = 0; - Clock::time_point frame_begin = reset_point; - Clock::time_point previous_frame_end = reset_point; + /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset Clock::duration accumulated_frametime = Clock::duration::zero(); - Clock::duration previous_frame_length = Clock::duration::zero(); - u64 reset_point_system_us = 0; + /// Cumulative number of system frames (LCD VBlanks) presented since last reset u32 system_frames = 0; + /// Cumulative number of game frames (GSP frame submissions) since last reset u32 game_frames = 0; + + /// Point when the previous system frame ended + Clock::time_point previous_frame_end = reset_point; + /// Point when the current system frame began + Clock::time_point frame_begin = reset_point; + /// Total visible duration (including frame-limiting, etc.) of the previous system frame + Clock::duration previous_frame_length = Clock::duration::zero(); }; class FrameLimiter { -- cgit v1.2.3