From 5bef54618a2850e42f7bab45120dab9166468a25 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sat, 15 May 2021 20:34:20 -0400 Subject: [PATCH] perf_stats: Rework FPS counter to be more accurate The FPS counter was based on metrics in the nvdisp swapbuffers call. This metric would be accurate if the gpu thread/renderer were synchronous with the nvdisp service, but that's no longer the case. This commit moves the frame counting responsibility onto the concrete renderers after their frame draw calls. Resulting in more meaningful metrics. The displayed FPS is now made up of the average framerate between the previous and most recent update, in order to avoid distracting FPS counter updates when framerate is oscillating between close values. The status bar update frequency was also changed from 2 seconds to 500ms. --- src/core/core.cpp | 3 ++- src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | 1 - src/core/perf_stats.cpp | 12 ++++++------ src/core/perf_stats.h | 9 ++++++--- src/video_core/gpu.cpp | 5 +++++ src/video_core/gpu.h | 2 ++ src/video_core/renderer_opengl/renderer_opengl.cpp | 1 + src/video_core/renderer_vulkan/renderer_vulkan.cpp | 1 + src/yuzu/main.cpp | 4 ++-- src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 2 +- 10 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 434bf3262..47e70c157 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -289,7 +289,8 @@ struct System::Impl { telemetry_session->AddField(performance, "Shutdown_EmulationSpeed", perf_results.emulation_speed * 100.0); - telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps); + telemetry_session->AddField(performance, "Shutdown_Framerate", + perf_results.average_game_fps); telemetry_session->AddField(performance, "Shutdown_Frametime", perf_results.frametime * 1000.0); telemetry_session->AddField(performance, "Mean_Frametime_MS", diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index bbef04a29..2cc0da124 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -52,7 +52,6 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, static_cast(format), transform, crop_rect}; - system.GetPerfStats().EndGameFrame(); system.GetPerfStats().EndSystemFrame(); system.GPU().SwapBuffers(&framebuffer); system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs()); diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index c92337079..526c017cd 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -69,9 +69,7 @@ void PerfStats::EndSystemFrame() { } void PerfStats::EndGameFrame() { - std::lock_guard lock{object_mutex}; - - game_frames += 1; + game_frames.fetch_add(1, std::memory_order_relaxed); } double PerfStats::GetMeanFrametime() const { @@ -94,10 +92,11 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us const auto interval = duration_cast(now - reset_point).count(); const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; - + const auto current_frames = static_cast(game_frames.load(std::memory_order_relaxed)); + const auto current_fps = current_frames / interval; const PerfStatsResults results{ .system_fps = static_cast(system_frames) / interval, - .game_fps = static_cast(game_frames) / interval, + .average_game_fps = (current_fps + previous_fps) / 2.0, .frametime = duration_cast(accumulated_frametime).count() / static_cast(system_frames), .emulation_speed = system_us_per_second.count() / 1'000'000.0, @@ -108,7 +107,8 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us reset_point_system_us = current_system_time_us; accumulated_frametime = Clock::duration::zero(); system_frames = 0; - game_frames = 0; + game_frames.store(0, std::memory_order_relaxed); + previous_fps = current_fps; return results; } diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 69256b960..90f9cc048 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -15,8 +16,8 @@ namespace Core { struct PerfStatsResults { /// System FPS (LCD VBlanks) in Hz double system_fps; - /// Game FPS (GSP frame submissions) in Hz - double game_fps; + /// Average game FPS (GPU frame renders) in Hz + double average_game_fps; /// Walltime per system frame, in seconds, excluding any waits double frametime; /// Ratio of walltime / emulated time elapsed @@ -72,7 +73,7 @@ private: /// 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; + std::atomic game_frames = 0; /// Point when the previous system frame ended Clock::time_point previous_frame_end = reset_point; @@ -80,6 +81,8 @@ private: 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(); + /// Previously computed fps + double previous_fps = 0; }; class FrameLimiter { diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index a38024242..37f7b24e1 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -13,6 +13,7 @@ #include "core/frontend/emu_window.h" #include "core/hardware_interrupt_manager.h" #include "core/memory.h" +#include "core/perf_stats.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_memory.h" @@ -191,6 +192,10 @@ u64 GPU::GetTicks() const { return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; } +void GPU::RendererFrameEndNotify() { + system.GetPerfStats().EndGameFrame(); +} + void GPU::FlushCommands() { rasterizer->FlushCommands(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 8669e9940..29a867863 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -247,6 +247,8 @@ public: return use_nvdec; } + void RendererFrameEndNotify(); + enum class FenceOperation : u32 { Acquire = 0, Increment = 1, diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index cc2e499f9..a718bff7a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -155,6 +155,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { ++m_current_frame; + gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); context->SwapBuffers(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 2e0cf4232..3986eb172 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -154,6 +154,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { if (swapchain.Present(render_semaphore)) { blit_screen.Recreate(); } + gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9e72acbf7..0eb1cd7ee 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1377,7 +1377,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { game_list->hide(); game_list_placeholder->hide(); } - status_bar_update_timer.start(2000); + status_bar_update_timer.start(500); async_status_button->setDisabled(true); multicore_status_button->setDisabled(true); renderer_status_button->setDisabled(true); @@ -2797,7 +2797,7 @@ void GMainWindow::UpdateStatusBar() { } else { emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); } - game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); + game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index d64f81106..06b20c975 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -215,7 +215,7 @@ void EmuWindow_SDL2::WaitEvent() { const auto results = Core::System::GetInstance().GetAndResetPerfStats(); const auto title = fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, - Common::g_scm_branch, Common::g_scm_desc, results.game_fps, + Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, results.emulation_speed * 100.0); SDL_SetWindowTitle(render_window, title.c_str()); last_time = current_time;