PerformanceMetrics.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // Copyright 2022 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/PerformanceMetrics.h"
  4. #include <mutex>
  5. #include <imgui.h>
  6. #include <implot.h>
  7. #include "Core/CoreTiming.h"
  8. #include "Core/HW/VideoInterface.h"
  9. #include "Core/System.h"
  10. #include "VideoCommon/VideoConfig.h"
  11. PerformanceMetrics g_perf_metrics;
  12. void PerformanceMetrics::Reset()
  13. {
  14. m_fps_counter.Reset();
  15. m_vps_counter.Reset();
  16. m_speed_counter.Reset();
  17. m_time_sleeping = DT::zero();
  18. m_real_times.fill(Clock::now());
  19. m_cpu_times.fill(Core::System::GetInstance().GetCoreTiming().GetCPUTimePoint(0));
  20. }
  21. void PerformanceMetrics::CountFrame()
  22. {
  23. m_fps_counter.Count();
  24. }
  25. void PerformanceMetrics::CountVBlank()
  26. {
  27. m_vps_counter.Count();
  28. }
  29. void PerformanceMetrics::CountThrottleSleep(DT sleep)
  30. {
  31. std::unique_lock lock(m_time_lock);
  32. m_time_sleeping += sleep;
  33. }
  34. void PerformanceMetrics::CountPerformanceMarker(Core::System& system, s64 cyclesLate)
  35. {
  36. std::unique_lock lock(m_time_lock);
  37. m_speed_counter.Count();
  38. m_real_times[m_time_index] = Clock::now() - m_time_sleeping;
  39. m_cpu_times[m_time_index] = system.GetCoreTiming().GetCPUTimePoint(cyclesLate);
  40. m_time_index += 1;
  41. }
  42. double PerformanceMetrics::GetFPS() const
  43. {
  44. return m_fps_counter.GetHzAvg();
  45. }
  46. double PerformanceMetrics::GetVPS() const
  47. {
  48. return m_vps_counter.GetHzAvg();
  49. }
  50. double PerformanceMetrics::GetSpeed() const
  51. {
  52. return m_speed_counter.GetHzAvg() / 100.0;
  53. }
  54. double PerformanceMetrics::GetMaxSpeed() const
  55. {
  56. std::shared_lock lock(m_time_lock);
  57. return DT_s(m_cpu_times[u8(m_time_index - 1)] - m_cpu_times[m_time_index]) /
  58. DT_s(m_real_times[u8(m_time_index - 1)] - m_real_times[m_time_index]);
  59. }
  60. double PerformanceMetrics::GetLastSpeedDenominator() const
  61. {
  62. return DT_s(m_speed_counter.GetLastRawDt()).count() *
  63. Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate();
  64. }
  65. void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale)
  66. {
  67. const float bg_alpha = 0.7f;
  68. const auto imgui_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
  69. ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
  70. ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
  71. ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing;
  72. const double fps = GetFPS();
  73. const double vps = GetVPS();
  74. const double speed = GetSpeed();
  75. // Change Color based on % Speed
  76. float r = 0.0f, g = 1.0f, b = 1.0f;
  77. if (g_ActiveConfig.bShowSpeedColors)
  78. {
  79. r = 1.0 - (speed - 0.8) / 0.2;
  80. g = speed / 0.8;
  81. b = (speed - 0.9) / 0.1;
  82. }
  83. const float window_padding = 8.f * backbuffer_scale;
  84. const float window_width = 93.f * backbuffer_scale;
  85. float window_y = window_padding;
  86. float window_x = ImGui::GetIO().DisplaySize.x - window_padding;
  87. const float graph_width = 50.f * backbuffer_scale + 3.f * window_width + 2.f * window_padding;
  88. const float graph_height =
  89. std::min(200.f * backbuffer_scale, ImGui::GetIO().DisplaySize.y - 85.f * backbuffer_scale);
  90. const bool stack_vertically = !g_ActiveConfig.bShowGraphs;
  91. ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
  92. ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 14.f * backbuffer_scale);
  93. if (g_ActiveConfig.bShowGraphs)
  94. {
  95. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 4.f * backbuffer_scale));
  96. // Position in the top-right corner of the screen.
  97. ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
  98. ImGui::SetNextWindowSize(ImVec2(graph_width, graph_height));
  99. ImGui::SetNextWindowBgAlpha(bg_alpha);
  100. window_y += graph_height + window_padding;
  101. if (ImGui::Begin("PerformanceGraphs", nullptr, imgui_flags))
  102. {
  103. static constexpr std::size_t num_ticks = 17;
  104. static constexpr std::array<double, num_ticks> tick_marks = {0.0,
  105. 1000.0 / 360.0,
  106. 1000.0 / 240.0,
  107. 1000.0 / 180.0,
  108. 1000.0 / 120.0,
  109. 1000.0 / 90.00,
  110. 1000.0 / 59.94,
  111. 1000.0 / 40.00,
  112. 1000.0 / 29.97,
  113. 1000.0 / 24.00,
  114. 1000.0 / 20.00,
  115. 1000.0 / 15.00,
  116. 1000.0 / 10.00,
  117. 1000.0 / 5.000,
  118. 1000.0 / 2.000,
  119. 1000.0,
  120. 2000.0};
  121. const DT vblank_time = m_vps_counter.GetDtAvg() + 2 * m_vps_counter.GetDtStd();
  122. const DT frame_time = m_fps_counter.GetDtAvg() + 2 * m_fps_counter.GetDtStd();
  123. const double target_max_time = DT_ms(vblank_time + frame_time).count();
  124. const double a =
  125. std::max(0.0, 1.0 - std::exp(-4.0 * (DT_s(m_fps_counter.GetLastRawDt()) /
  126. DT_s(m_fps_counter.GetSampleWindow()))));
  127. if (std::isfinite(m_graph_max_time))
  128. m_graph_max_time += a * (target_max_time - m_graph_max_time);
  129. else
  130. m_graph_max_time = target_max_time;
  131. const double total_frame_time =
  132. DT_ms(std::max(m_fps_counter.GetSampleWindow(), m_vps_counter.GetSampleWindow())).count();
  133. if (ImPlot::BeginPlot("PerformanceGraphs", ImVec2(-1.0, -1.0),
  134. ImPlotFlags_NoFrame | ImPlotFlags_NoTitle | ImPlotFlags_NoMenus))
  135. {
  136. ImPlot::PushStyleColor(ImPlotCol_PlotBg, {0, 0, 0, 0});
  137. ImPlot::PushStyleColor(ImPlotCol_LegendBg, {0, 0, 0, 0.2f});
  138. ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, ImVec2(0.f, 0.f));
  139. ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 1.5f * backbuffer_scale);
  140. ImPlot::SetupAxes(nullptr, nullptr,
  141. ImPlotAxisFlags_Lock | ImPlotAxisFlags_Invert |
  142. ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight,
  143. ImPlotAxisFlags_Lock | ImPlotAxisFlags_Invert | ImPlotAxisFlags_NoLabel |
  144. ImPlotAxisFlags_NoHighlight);
  145. ImPlot::SetupAxisFormat(ImAxis_Y1, "%.1f");
  146. ImPlot::SetupAxisTicks(ImAxis_Y1, tick_marks.data(), num_ticks);
  147. ImPlot::SetupAxesLimits(0, total_frame_time, 0, m_graph_max_time, ImGuiCond_Always);
  148. ImPlot::SetupLegend(ImPlotLocation_SouthEast, ImPlotLegendFlags_None);
  149. m_vps_counter.ImPlotPlotLines("V-Blank (ms)");
  150. m_fps_counter.ImPlotPlotLines("Frame (ms)");
  151. ImPlot::EndPlot();
  152. ImPlot::PopStyleVar(2);
  153. ImPlot::PopStyleColor(2);
  154. }
  155. ImGui::PopStyleVar();
  156. ImGui::End();
  157. }
  158. }
  159. if (g_ActiveConfig.bShowSpeed)
  160. {
  161. // Position in the top-right corner of the screen.
  162. float window_height = 47.f * backbuffer_scale;
  163. ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
  164. ImGui::SetNextWindowSize(ImVec2(window_width, window_height));
  165. ImGui::SetNextWindowBgAlpha(bg_alpha);
  166. if (stack_vertically)
  167. window_y += window_height + window_padding;
  168. else
  169. window_x -= window_width + window_padding;
  170. if (ImGui::Begin("SpeedStats", nullptr, imgui_flags))
  171. {
  172. ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Speed:%4.0lf%%", 100.0 * speed);
  173. ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Max:%6.0lf%%", 100.0 * GetMaxSpeed());
  174. ImGui::End();
  175. }
  176. }
  177. if (g_ActiveConfig.bShowFPS || g_ActiveConfig.bShowFTimes)
  178. {
  179. int count = g_ActiveConfig.bShowFPS + 2 * g_ActiveConfig.bShowFTimes;
  180. float window_height = (12.f + 17.f * count) * backbuffer_scale;
  181. // Position in the top-right corner of the screen.
  182. ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
  183. ImGui::SetNextWindowSize(ImVec2(window_width, window_height));
  184. ImGui::SetNextWindowBgAlpha(bg_alpha);
  185. if (stack_vertically)
  186. window_y += window_height + window_padding;
  187. else
  188. window_x -= window_width + window_padding;
  189. if (ImGui::Begin("FPSStats", nullptr, imgui_flags))
  190. {
  191. if (g_ActiveConfig.bShowFPS)
  192. ImGui::TextColored(ImVec4(r, g, b, 1.0f), "FPS:%7.2lf", fps);
  193. if (g_ActiveConfig.bShowFTimes)
  194. {
  195. ImGui::TextColored(ImVec4(r, g, b, 1.0f), "dt:%6.2lfms",
  196. DT_ms(m_fps_counter.GetDtAvg()).count());
  197. ImGui::TextColored(ImVec4(r, g, b, 1.0f), " ±:%6.2lfms",
  198. DT_ms(m_fps_counter.GetDtStd()).count());
  199. }
  200. ImGui::End();
  201. }
  202. }
  203. if (g_ActiveConfig.bShowVPS || g_ActiveConfig.bShowVTimes)
  204. {
  205. int count = g_ActiveConfig.bShowVPS + 2 * g_ActiveConfig.bShowVTimes;
  206. float window_height = (12.f + 17.f * count) * backbuffer_scale;
  207. // Position in the top-right corner of the screen.
  208. ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
  209. ImGui::SetNextWindowSize(ImVec2(window_width, (12.f + 17.f * count) * backbuffer_scale));
  210. ImGui::SetNextWindowBgAlpha(bg_alpha);
  211. if (stack_vertically)
  212. window_y += window_height + window_padding;
  213. else
  214. window_x -= window_width + window_padding;
  215. if (ImGui::Begin("VPSStats", nullptr, imgui_flags))
  216. {
  217. if (g_ActiveConfig.bShowVPS)
  218. ImGui::TextColored(ImVec4(r, g, b, 1.0f), "VPS:%7.2lf", vps);
  219. if (g_ActiveConfig.bShowVTimes)
  220. {
  221. ImGui::TextColored(ImVec4(r, g, b, 1.0f), "dt:%6.2lfms",
  222. DT_ms(m_vps_counter.GetDtAvg()).count());
  223. ImGui::TextColored(ImVec4(r, g, b, 1.0f), " ±:%6.2lfms",
  224. DT_ms(m_vps_counter.GetDtStd()).count());
  225. }
  226. ImGui::End();
  227. }
  228. }
  229. ImGui::PopStyleVar(2);
  230. }