summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar David2019-09-21 20:09:55 +1000
committerGravatar GitHub2019-09-21 20:09:55 +1000
commit183c445c30ad9fa1f58660ff578ecf1b78e51b25 (patch)
tree045c4a6945449d563ea420fbf5a4916cbde1f8b3
parentMerge pull request #2872 from FernandoS27/mem-gpu-opt (diff)
parentAddress review comments (diff)
downloadyuzu-183c445c30ad9fa1f58660ff578ecf1b78e51b25.tar.gz
yuzu-183c445c30ad9fa1f58660ff578ecf1b78e51b25.tar.xz
yuzu-183c445c30ad9fa1f58660ff578ecf1b78e51b25.zip
Merge pull request #2806 from FearlessTobi/port-4882
Port citra-emu/citra#4882: "Add frametime logging for tracking performance over time"
-rw-r--r--src/core/core.cpp25
-rw-r--r--src/core/perf_stats.cpp47
-rw-r--r--src/core/perf_stats.h21
-rw-r--r--src/core/settings.h1
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h2
7 files changed, 93 insertions, 10 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3d0978cbf..9ab174de2 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -160,10 +160,6 @@ struct System::Impl {
160 160
161 LOG_DEBUG(Core, "Initialized OK"); 161 LOG_DEBUG(Core, "Initialized OK");
162 162
163 // Reset counters and set time origin to current frame
164 GetAndResetPerfStats();
165 perf_stats.BeginSystemFrame();
166
167 return ResultStatus::Success; 163 return ResultStatus::Success;
168 } 164 }
169 165
@@ -206,6 +202,16 @@ struct System::Impl {
206 main_process->Run(load_parameters->main_thread_priority, 202 main_process->Run(load_parameters->main_thread_priority,
207 load_parameters->main_thread_stack_size); 203 load_parameters->main_thread_stack_size);
208 204
205 u64 title_id{0};
206 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
207 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
208 static_cast<u32>(load_result));
209 }
210 perf_stats = std::make_unique<PerfStats>(title_id);
211 // Reset counters and set time origin to current frame
212 GetAndResetPerfStats();
213 perf_stats->BeginSystemFrame();
214
209 status = ResultStatus::Success; 215 status = ResultStatus::Success;
210 return status; 216 return status;
211 } 217 }
@@ -219,6 +225,8 @@ struct System::Impl {
219 perf_results.game_fps); 225 perf_results.game_fps);
220 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 226 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
221 perf_results.frametime * 1000.0); 227 perf_results.frametime * 1000.0);
228 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
229 perf_stats->GetMeanFrametime());
222 230
223 is_powered_on = false; 231 is_powered_on = false;
224 232
@@ -229,6 +237,7 @@ struct System::Impl {
229 service_manager.reset(); 237 service_manager.reset();
230 cheat_engine.reset(); 238 cheat_engine.reset();
231 telemetry_session.reset(); 239 telemetry_session.reset();
240 perf_stats.reset();
232 gpu_core.reset(); 241 gpu_core.reset();
233 242
234 // Close all CPU/threading state 243 // Close all CPU/threading state
@@ -286,7 +295,7 @@ struct System::Impl {
286 } 295 }
287 296
288 PerfStatsResults GetAndResetPerfStats() { 297 PerfStatsResults GetAndResetPerfStats() {
289 return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); 298 return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
290 } 299 }
291 300
292 Timing::CoreTiming core_timing; 301 Timing::CoreTiming core_timing;
@@ -327,7 +336,7 @@ struct System::Impl {
327 ResultStatus status = ResultStatus::Success; 336 ResultStatus status = ResultStatus::Success;
328 std::string status_details = ""; 337 std::string status_details = "";
329 338
330 Core::PerfStats perf_stats; 339 std::unique_ptr<Core::PerfStats> perf_stats;
331 Core::FrameLimiter frame_limiter; 340 Core::FrameLimiter frame_limiter;
332}; 341};
333 342
@@ -480,11 +489,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
480} 489}
481 490
482Core::PerfStats& System::GetPerfStats() { 491Core::PerfStats& System::GetPerfStats() {
483 return impl->perf_stats; 492 return *impl->perf_stats;
484} 493}
485 494
486const Core::PerfStats& System::GetPerfStats() const { 495const Core::PerfStats& System::GetPerfStats() const {
487 return impl->perf_stats; 496 return *impl->perf_stats;
488} 497}
489 498
490Core::FrameLimiter& System::FrameLimiter() { 499Core::FrameLimiter& System::FrameLimiter() {
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 4afd6c8a3..d2c69d1a0 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -4,8 +4,14 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <chrono> 6#include <chrono>
7#include <iterator>
7#include <mutex> 8#include <mutex>
9#include <numeric>
10#include <sstream>
8#include <thread> 11#include <thread>
12#include <fmt/chrono.h>
13#include <fmt/format.h>
14#include "common/file_util.h"
9#include "common/math_util.h" 15#include "common/math_util.h"
10#include "core/perf_stats.h" 16#include "core/perf_stats.h"
11#include "core/settings.h" 17#include "core/settings.h"
@@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
15using std::chrono::duration_cast; 21using std::chrono::duration_cast;
16using std::chrono::microseconds; 22using std::chrono::microseconds;
17 23
24// Purposefully ignore the first five frames, as there's a significant amount of overhead in
25// booting that we shouldn't account for
26constexpr std::size_t IgnoreFrames = 5;
27
18namespace Core { 28namespace Core {
19 29
30PerfStats::PerfStats(u64 title_id) : title_id(title_id) {}
31
32PerfStats::~PerfStats() {
33 if (!Settings::values.record_frame_times || title_id == 0) {
34 return;
35 }
36
37 const std::time_t t = std::time(nullptr);
38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w");
46 file.WriteString(stream.str());
47}
48
20void PerfStats::BeginSystemFrame() { 49void PerfStats::BeginSystemFrame() {
21 std::lock_guard lock{object_mutex}; 50 std::lock_guard lock{object_mutex};
22 51
@@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {
27 std::lock_guard lock{object_mutex}; 56 std::lock_guard lock{object_mutex};
28 57
29 auto frame_end = Clock::now(); 58 auto frame_end = Clock::now();
30 accumulated_frametime += frame_end - frame_begin; 59 const auto frame_time = frame_end - frame_begin;
60 if (current_index < perf_history.size()) {
61 perf_history[current_index++] =
62 std::chrono::duration<double, std::milli>(frame_time).count();
63 }
64 accumulated_frametime += frame_time;
31 system_frames += 1; 65 system_frames += 1;
32 66
33 previous_frame_length = frame_end - previous_frame_end; 67 previous_frame_length = frame_end - previous_frame_end;
@@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() {
40 game_frames += 1; 74 game_frames += 1;
41} 75}
42 76
77double PerfStats::GetMeanFrametime() {
78 std::lock_guard lock{object_mutex};
79
80 if (current_index <= IgnoreFrames) {
81 return 0;
82 }
83 const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
84 perf_history.begin() + current_index, 0);
85 return sum / (current_index - IgnoreFrames);
86}
87
43PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 88PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard lock{object_mutex}; 89 std::lock_guard lock{object_mutex};
45 90
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 222ac1a63..d9a64f072 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -4,7 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <chrono> 8#include <chrono>
9#include <cstddef>
8#include <mutex> 10#include <mutex>
9#include "common/common_types.h" 11#include "common/common_types.h"
10 12
@@ -27,6 +29,10 @@ struct PerfStatsResults {
27 */ 29 */
28class PerfStats { 30class PerfStats {
29public: 31public:
32 explicit PerfStats(u64 title_id);
33
34 ~PerfStats();
35
30 using Clock = std::chrono::high_resolution_clock; 36 using Clock = std::chrono::high_resolution_clock;
31 37
32 void BeginSystemFrame(); 38 void BeginSystemFrame();
@@ -36,13 +42,26 @@ public:
36 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); 42 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
37 43
38 /** 44 /**
45 * Returns the Arthimetic Mean of all frametime values stored in the performance history.
46 */
47 double GetMeanFrametime();
48
49 /**
39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 50 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
40 * useful for scaling inputs or outputs moving between the two time domains. 51 * useful for scaling inputs or outputs moving between the two time domains.
41 */ 52 */
42 double GetLastFrameTimeScale(); 53 double GetLastFrameTimeScale();
43 54
44private: 55private:
45 std::mutex object_mutex; 56 std::mutex object_mutex{};
57
58 /// Title ID for the game that is running. 0 if there is no game running yet
59 u64 title_id{0};
60 /// Current index for writing to the perf_history array
61 std::size_t current_index{0};
62 /// Stores an hour of historical frametime data useful for processing and tracking performance
63 /// regressions with code changes.
64 std::array<double, 216000> perf_history = {};
46 65
47 /// Point when the cumulative counters were reset 66 /// Point when the cumulative counters were reset
48 Clock::time_point reset_point = Clock::now(); 67 Clock::time_point reset_point = Clock::now();
diff --git a/src/core/settings.h b/src/core/settings.h
index 6638ce8f9..d4b70ec4c 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -409,6 +409,7 @@ struct Values {
409 float volume; 409 float volume;
410 410
411 // Debugging 411 // Debugging
412 bool record_frame_times;
412 bool use_gdbstub; 413 bool use_gdbstub;
413 u16 gdbstub_port; 414 u16 gdbstub_port;
414 std::string program_args; 415 std::string program_args;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index f594106bf..3f54f54fb 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -466,6 +466,9 @@ void Config::ReadDataStorageValues() {
466void Config::ReadDebuggingValues() { 466void Config::ReadDebuggingValues() {
467 qt_config->beginGroup(QStringLiteral("Debugging")); 467 qt_config->beginGroup(QStringLiteral("Debugging"));
468 468
469 // Intentionally not using the QT default setting as this is intended to be changed in the ini
470 Settings::values.record_frame_times =
471 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
469 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); 472 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
470 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); 473 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
471 Settings::values.program_args = 474 Settings::values.program_args =
@@ -879,6 +882,8 @@ void Config::SaveDataStorageValues() {
879void Config::SaveDebuggingValues() { 882void Config::SaveDebuggingValues() {
880 qt_config->beginGroup(QStringLiteral("Debugging")); 883 qt_config->beginGroup(QStringLiteral("Debugging"));
881 884
885 // Intentionally not using the QT default setting as this is intended to be changed in the ini
886 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
882 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); 887 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
883 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); 888 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
884 WriteSetting(QStringLiteral("program_args"), 889 WriteSetting(QStringLiteral("program_args"),
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 067d58d80..5cadfd191 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -374,6 +374,8 @@ void Config::ReadValues() {
374 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 374 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
375 375
376 // Debugging 376 // Debugging
377 Settings::values.record_frame_times =
378 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
377 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 379 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
378 Settings::values.gdbstub_port = 380 Settings::values.gdbstub_port =
379 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 381 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 0cfc111a6..f9f244522 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -213,6 +213,8 @@ region_value =
213log_filter = *:Trace 213log_filter = *:Trace
214 214
215[Debugging] 215[Debugging]
216# Record frame time data, can be found in the log directory. Boolean value
217record_frame_times =
216# Port for listening to GDB connections. 218# Port for listening to GDB connections.
217use_gdbstub=false 219use_gdbstub=false
218gdbstub_port=24689 220gdbstub_port=24689