diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/core.cpp | 25 | ||||
| -rw-r--r-- | src/core/perf_stats.cpp | 45 | ||||
| -rw-r--r-- | src/core/perf_stats.h | 20 | ||||
| -rw-r--r-- | src/core/settings.h | 1 | 
4 files changed, 81 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 {          LOG_DEBUG(Core, "Initialized OK"); -        // Reset counters and set time origin to current frame -        GetAndResetPerfStats(); -        perf_stats.BeginSystemFrame(); -          return ResultStatus::Success;      } @@ -206,6 +202,16 @@ struct System::Impl {          main_process->Run(load_parameters->main_thread_priority,                            load_parameters->main_thread_stack_size); +        u64 title_id{0}; +        if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { +            LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", +                      static_cast<u32>(load_result)); +        } +        perf_stats = std::make_unique<PerfStats>(title_id); +        // Reset counters and set time origin to current frame +        GetAndResetPerfStats(); +        perf_stats->BeginSystemFrame(); +          status = ResultStatus::Success;          return status;      } @@ -219,6 +225,8 @@ struct System::Impl {                                      perf_results.game_fps);          telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",                                      perf_results.frametime * 1000.0); +        telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", +                                    perf_stats->GetMeanFrametime());          is_powered_on = false; @@ -229,6 +237,7 @@ struct System::Impl {          service_manager.reset();          cheat_engine.reset();          telemetry_session.reset(); +        perf_stats.reset();          gpu_core.reset();          // Close all CPU/threading state @@ -286,7 +295,7 @@ struct System::Impl {      }      PerfStatsResults GetAndResetPerfStats() { -        return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); +        return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());      }      Timing::CoreTiming core_timing; @@ -327,7 +336,7 @@ struct System::Impl {      ResultStatus status = ResultStatus::Success;      std::string status_details = ""; -    Core::PerfStats perf_stats; +    std::unique_ptr<Core::PerfStats> perf_stats;      Core::FrameLimiter frame_limiter;  }; @@ -480,11 +489,11 @@ const Timing::CoreTiming& System::CoreTiming() const {  }  Core::PerfStats& System::GetPerfStats() { -    return impl->perf_stats; +    return *impl->perf_stats;  }  const Core::PerfStats& System::GetPerfStats() const { -    return impl->perf_stats; +    return *impl->perf_stats;  }  Core::FrameLimiter& System::FrameLimiter() { diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 4afd6c8a3..bfab77abb 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -4,8 +4,14 @@  #include <algorithm>  #include <chrono> +#include <iterator>  #include <mutex> +#include <numeric> +#include <sstream>  #include <thread> +#include <fmt/format.h> +#include <fmt/time.h> +#include "common/file_util.h"  #include "common/math_util.h"  #include "core/perf_stats.h"  #include "core/settings.h" @@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;  using std::chrono::duration_cast;  using std::chrono::microseconds; +// Purposefully ignore the first five frames, as there's a significant amount of overhead in +// booting that we shouldn't account for +constexpr std::size_t IgnoreFrames = 5; +  namespace Core { +PerfStats::PerfStats(u64 title_id) : title_id(title_id) {} + +PerfStats::~PerfStats() { +    if (!Settings::values.record_frame_times || title_id == 0) { +        return; +    } + +    std::time_t t = std::time(nullptr); +    std::ostringstream stream; +    std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, +              std::ostream_iterator<double>(stream, "\n")); +    std::string path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); +    // %F Date format expanded is "%Y-%m-%d" +    std::string filename = +        fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); +    FileUtil::IOFile file(filename, "w"); +    file.WriteString(stream.str()); +} +  void PerfStats::BeginSystemFrame() {      std::lock_guard lock{object_mutex}; @@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {      std::lock_guard lock{object_mutex};      auto frame_end = Clock::now(); -    accumulated_frametime += frame_end - frame_begin; +    const auto frame_time = frame_end - frame_begin; +    if (current_index < perf_history.size()) { +        perf_history[current_index++] = +            std::chrono::duration<double, std::milli>(frame_time).count(); +    } +    accumulated_frametime += frame_time;      system_frames += 1;      previous_frame_length = frame_end - previous_frame_end; @@ -40,6 +74,15 @@ void PerfStats::EndGameFrame() {      game_frames += 1;  } +double PerfStats::GetMeanFrametime() { +    if (current_index <= IgnoreFrames) { +        return 0; +    } +    double sum = std::accumulate(perf_history.begin() + IgnoreFrames, +                                 perf_history.begin() + current_index, 0); +    return sum / (current_index - IgnoreFrames); +} +  PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {      std::lock_guard lock{object_mutex}; diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 222ac1a63..2db290c09 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -4,6 +4,7 @@  #pragma once +#include <array>  #include <chrono>  #include <mutex>  #include "common/common_types.h" @@ -27,6 +28,10 @@ struct PerfStatsResults {   */  class PerfStats {  public: +    explicit PerfStats(u64 title_id); + +    ~PerfStats(); +      using Clock = std::chrono::high_resolution_clock;      void BeginSystemFrame(); @@ -36,13 +41,26 @@ public:      PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);      /** +     * Returns the Arthimetic Mean of all frametime values stored in the performance history. +     */ +    double GetMeanFrametime(); + +    /**       * 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: -    std::mutex object_mutex; +    std::mutex object_mutex{}; + +    /// Title ID for the game that is running. 0 if there is no game running yet +    u64 title_id{0}; +    /// Current index for writing to the perf_history array +    std::size_t current_index{0}; +    /// Stores an hour of historical frametime data useful for processing and tracking performance +    /// regressions with code changes. +    std::array<double, 216000> perf_history = {};      /// Point when the cumulative counters were reset      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 {      float volume;      // Debugging +    bool record_frame_times;      bool use_gdbstub;      u16 gdbstub_port;      std::string program_args; | 
