From 9fbe9ced44e8cc7033971a44efb1794bd15e7152 Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Sun, 24 Jan 2021 19:42:53 -0800 Subject: [PATCH 01/12] DolphinAnalytics: Remove redundant #include STL header was included from both Analytics.h and Analytics.cpp on Android. --- Source/Core/Core/DolphinAnalytics.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 936cd7191b..6c80d79b6a 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -17,10 +17,6 @@ #include "Common/WindowsRegistry.h" #endif -#if defined(ANDROID) -#include -#endif - #if defined(__APPLE__) #include "Common/CommonFuncs.h" #endif From e2728d5f8c363df304af8c6f4100993726b186ef Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Mon, 25 Jan 2021 09:25:02 -0800 Subject: [PATCH 02/12] DolphinAnalytics: Refactor variable declaration Move endpoint declaration from anonymous namespace to the only function it's used in. --- Source/Core/Core/DolphinAnalytics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 6c80d79b6a..ccc3821528 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -43,7 +43,6 @@ namespace { -constexpr char ANALYTICS_ENDPOINT[] = "https://analytics.dolphin-emu.org/report"; } // namespace #if defined(ANDROID) @@ -90,7 +89,8 @@ void DolphinAnalytics::ReloadConfig() std::unique_ptr new_backend; if (m_last_analytics_enabled) { - new_backend = std::make_unique(ANALYTICS_ENDPOINT); + constexpr char analytics_endpoint[] = "https://analytics.dolphin-emu.org/report"; + new_backend = std::make_unique(analytics_endpoint); } m_reporter.SetBackend(std::move(new_backend)); From f95d9c9fe1e055155b168992b9c42022772da8da Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Mon, 25 Jan 2021 11:36:21 -0800 Subject: [PATCH 03/12] DolphinAnalytics: Extract ReportBuilder functions --- Source/Core/Core/DolphinAnalytics.cpp | 130 +++++++++++++++----------- 1 file changed, 78 insertions(+), 52 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index ccc3821528..493b0985d6 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -41,10 +41,6 @@ #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" -namespace -{ -} // namespace - #if defined(ANDROID) static std::function s_get_val_func; void DolphinAnalytics::AndroidSetGetValFunc(std::function func) @@ -53,6 +49,79 @@ void DolphinAnalytics::AndroidSetGetValFunc(std::functionAddData("version-desc", Common::GetScmDescStr()); + builder->AddData("version-hash", Common::GetScmRevGitStr()); + builder->AddData("version-branch", Common::GetScmBranchStr()); + builder->AddData("version-dist", Common::GetScmDistributorStr()); +} + +void AddAutoUpdateInformationToReportBuilder(Common::AnalyticsReportBuilder* builder) +{ + builder->AddData("update-track", Config::Get(Config::MAIN_AUTOUPDATE_UPDATE_TRACK)); +} + +void AddCPUInformationToReportBuilder(Common::AnalyticsReportBuilder* builder) +{ + builder->AddData("cpu-summary", cpu_info.Summarize()); +} + +#if defined(_WIN32) +void AddWindowsInformationToReportBuilder(Common::AnalyticsReportBuilder* builder) +{ + const auto winver = WindowsRegistry::GetOSVersion(); + builder->AddData("win-ver-major", static_cast(winver.dwMajorVersion)); + builder->AddData("win-ver-minor", static_cast(winver.dwMinorVersion)); + builder->AddData("win-ver-build", static_cast(winver.dwBuildNumber)); +} +#elif defined(ANDROID) +void AddAndroidInformationToReportBuilder(Common::AnalyticsReportBuilder* builder) +{ + builder->AddData("android-manufacturer", s_get_val_func("DEVICE_MANUFACTURER")); + builder->AddData("android-model", s_get_val_func("DEVICE_MODEL")); + builder->AddData("android-version", s_get_val_func("DEVICE_OS")); +} +#elif defined(__APPLE__) +void AddMacOSInformationToReportBuilder(Common::AnalyticsReportBuilder* builder) +{ + Common::MacOSVersion version = Common::GetMacOSVersion(); + builder->AddData("osx-ver-major", version.major); + builder->AddData("osx-ver-minor", version.minor); + builder->AddData("osx-ver-bugfix", version.patch); +} +#endif + +void AddPlatformInformationToReportBuilder(Common::AnalyticsReportBuilder* builder) +{ +#if defined(_WIN32) + builder->AddData("os-type", "windows"); + AddWindowsInformationToReportBuilder(builder); +#elif defined(ANDROID) + builder->AddData("os-type", "android"); + AddAndroidInformationToReportBuilder(builder); +#elif defined(__APPLE__) + builder->AddData("os-type", "osx"); + AddMacOSInformationToReportBuilder(builder); +#elif defined(__linux__) + builder->AddData("os-type", "linux"); +#elif defined(__FreeBSD__) + builder->AddData("os-type", "freebsd"); +#elif defined(__OpenBSD__) + builder->AddData("os-type", "openbsd"); +#elif defined(__NetBSD__) + builder->AddData("os-type", "netbsd"); +#elif defined(__HAIKU__) + builder->AddData("os-type", "haiku"); +#else + builder->AddData("os-type", "unknown"); +#endif +} +} // namespace + + // Under arm64, we need to call objc_msgSend to receive a struct. DolphinAnalytics::DolphinAnalytics() { m_last_analytics_enabled = Config::Get(Config::MAIN_ANALYTICS_ENABLED); @@ -268,55 +337,12 @@ bool DolphinAnalytics::ShouldStartPerformanceSampling() void DolphinAnalytics::MakeBaseBuilder() { - Common::AnalyticsReportBuilder builder; + m_base_builder = Common::AnalyticsReportBuilder(); - // Version information. - builder.AddData("version-desc", Common::GetScmDescStr()); - builder.AddData("version-hash", Common::GetScmRevGitStr()); - builder.AddData("version-branch", Common::GetScmBranchStr()); - builder.AddData("version-dist", Common::GetScmDistributorStr()); - - // Auto-Update information. - builder.AddData("update-track", Config::Get(Config::MAIN_AUTOUPDATE_UPDATE_TRACK)); - - // CPU information. - builder.AddData("cpu-summary", cpu_info.Summarize()); - -// OS information. -#if defined(_WIN32) - builder.AddData("os-type", "windows"); - - const auto winver = WindowsRegistry::GetOSVersion(); - builder.AddData("win-ver-major", static_cast(winver.dwMajorVersion)); - builder.AddData("win-ver-minor", static_cast(winver.dwMinorVersion)); - builder.AddData("win-ver-build", static_cast(winver.dwBuildNumber)); -#elif defined(ANDROID) - builder.AddData("os-type", "android"); - builder.AddData("android-manufacturer", s_get_val_func("DEVICE_MANUFACTURER")); - builder.AddData("android-model", s_get_val_func("DEVICE_MODEL")); - builder.AddData("android-version", s_get_val_func("DEVICE_OS")); -#elif defined(__APPLE__) - builder.AddData("os-type", "osx"); - - Common::MacOSVersion version = Common::GetMacOSVersion(); - builder.AddData("osx-ver-major", version.major); - builder.AddData("osx-ver-minor", version.minor); - builder.AddData("osx-ver-bugfix", version.patch); -#elif defined(__linux__) - builder.AddData("os-type", "linux"); -#elif defined(__FreeBSD__) - builder.AddData("os-type", "freebsd"); -#elif defined(__OpenBSD__) - builder.AddData("os-type", "openbsd"); -#elif defined(__NetBSD__) - builder.AddData("os-type", "netbsd"); -#elif defined(__HAIKU__) - builder.AddData("os-type", "haiku"); -#else - builder.AddData("os-type", "unknown"); -#endif - - m_base_builder = builder; + AddVersionInformationToReportBuilder(&m_base_builder); + AddAutoUpdateInformationToReportBuilder(&m_base_builder); + AddCPUInformationToReportBuilder(&m_base_builder); + AddPlatformInformationToReportBuilder(&m_base_builder); } static const char* GetShaderCompilationMode() From ed14921ee565995ebb06da4ee996bca7f9f2ff4a Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Mon, 25 Jan 2021 12:03:04 -0800 Subject: [PATCH 04/12] DolphinAnalytics: Make assorted variables const --- Source/Core/Core/DolphinAnalytics.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 493b0985d6..a5c5423cd7 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -152,7 +152,7 @@ DolphinAnalytics& DolphinAnalytics::Instance() void DolphinAnalytics::ReloadConfig() { - std::lock_guard lk{m_reporter_mutex}; + const std::lock_guard lk{m_reporter_mutex}; // Install the HTTP backend if analytics support is enabled. std::unique_ptr new_backend; @@ -259,7 +259,7 @@ static_assert(GAME_QUIRKS_NAMES.size() == static_cast(GameQuirk::Count), void DolphinAnalytics::ReportGameQuirk(GameQuirk quirk) { - u32 quirk_idx = static_cast(quirk); + const u32 quirk_idx = static_cast(quirk); // Only report once per run. if (m_reported_quirks[quirk_idx]) @@ -317,7 +317,7 @@ void DolphinAnalytics::InitializePerformanceSampling() m_performance_samples.clear(); m_sampling_performance_info = false; - u64 wait_us = + const u64 wait_us = PERFORMANCE_SAMPLING_INITIAL_WAIT_TIME_SECS * 1000000 + Common::Random::GenerateValue() % (PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS * 1000000); m_sampling_next_start_us = Common::Timer::NowUs() + wait_us; @@ -328,7 +328,7 @@ bool DolphinAnalytics::ShouldStartPerformanceSampling() if (Common::Timer::NowUs() < m_sampling_next_start_us) return false; - u64 wait_us = + const u64 wait_us = PERFORMANCE_SAMPLING_INTERVAL_SECS * 1000000 + Common::Random::GenerateValue() % (PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS * 1000000); m_sampling_next_start_us = Common::Timer::NowUs() + wait_us; From d2414125a9c474d58f05c09973cfcc9beffecd1c Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Mon, 25 Jan 2021 12:09:28 -0800 Subject: [PATCH 05/12] DolphinAnalytics: Refactor redundant data accesses --- Source/Core/Core/DolphinAnalytics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index a5c5423cd7..54ceaf0a21 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -369,12 +369,13 @@ static bool UseVertexRounding() void DolphinAnalytics::MakePerGameBuilder() { Common::AnalyticsReportBuilder builder(m_base_builder); + const SConfig& config = SConfig::GetInstance(); // Gameid. - builder.AddData("gameid", SConfig::GetInstance().GetGameID()); + builder.AddData("gameid", config.GetGameID()); // Unique id bound to the gameid. - builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetGameID())); + builder.AddData("id", MakeUniqueId(config.GetGameID())); // Configuration. builder.AddData("cfg-dsp-hle", Config::Get(Config::MAIN_DSP_HLE)); From 5a263b652476991076f392bb9a040f5c56be9277 Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Mon, 25 Jan 2021 12:11:56 -0800 Subject: [PATCH 06/12] DolphinAnalytics: Reformat comments --- Source/Core/Core/DolphinAnalytics.cpp | 2 +- Source/Core/Core/DolphinAnalytics.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 54ceaf0a21..ef5ace951e 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -121,7 +121,7 @@ void AddPlatformInformationToReportBuilder(Common::AnalyticsReportBuilder* build } } // namespace - // Under arm64, we need to call objc_msgSend to receive a struct. +// Under arm64, we need to call objc_msgSend to receive a struct. DolphinAnalytics::DolphinAnalytics() { m_last_analytics_enabled = Config::Get(Config::MAIN_ANALYTICS_ENABLED); diff --git a/Source/Core/Core/DolphinAnalytics.h b/Source/Core/Core/DolphinAnalytics.h index 252207e2d1..711168c81d 100644 --- a/Source/Core/Core/DolphinAnalytics.h +++ b/Source/Core/Core/DolphinAnalytics.h @@ -185,7 +185,7 @@ private: // Performance sampling state & internal helpers. void InitializePerformanceSampling(); // Called on game start / title switch. bool ShouldStartPerformanceSampling(); - u64 m_sampling_next_start_us; // Next timestamp (in us) at which to trigger sampling. + u64 m_sampling_next_start_us; // Next timestamp (in us) at which to trigger sampling. bool m_sampling_performance_info = false; // Whether we are currently collecting samples. std::vector m_performance_samples; From e86c9607fe36c3ef7a3da5977876187f38d61750 Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Thu, 28 Jan 2021 09:22:40 -0800 Subject: [PATCH 07/12] DolphinAnalytics: Move PerformanceSample to header Avoid circular dependencies or forward declarations in upcoming commits. --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/DolphinAnalytics.h | 8 ++------ Source/Core/Core/PerformanceSample.h | 12 ++++++++++++ Source/Core/DolphinLib.props | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 Source/Core/Core/PerformanceSample.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index e411239d17..789ded01fd 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -512,6 +512,7 @@ add_library(core NetworkCaptureLogger.h PatchEngine.cpp PatchEngine.h + PerformanceSample.h PowerPC/BreakPoints.cpp PowerPC/BreakPoints.h PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp diff --git a/Source/Core/Core/DolphinAnalytics.h b/Source/Core/Core/DolphinAnalytics.h index 711168c81d..c62d97e0dd 100644 --- a/Source/Core/Core/DolphinAnalytics.h +++ b/Source/Core/Core/DolphinAnalytics.h @@ -13,6 +13,8 @@ #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Core/PerformanceSample.h" + #if defined(ANDROID) #include #endif @@ -138,12 +140,6 @@ public: // Get the base builder for building a report const Common::AnalyticsReportBuilder& BaseBuilder() const { return m_base_builder; } - struct PerformanceSample - { - double speed_ratio; // See SystemTimers::GetEstimatedEmulationPerformance(). - int num_prims; - int num_draw_calls; - }; // Reports performance information. This method performs its own throttling / aggregation -- // calling it does not guarantee when a report will actually be sent. // diff --git a/Source/Core/Core/PerformanceSample.h b/Source/Core/Core/PerformanceSample.h new file mode 100644 index 0000000000..34e289408a --- /dev/null +++ b/Source/Core/Core/PerformanceSample.h @@ -0,0 +1,12 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +struct PerformanceSample +{ + double speed_ratio; // See SystemTimers::GetEstimatedEmulationPerformance(). + int num_prims; + int num_draw_calls; +}; diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 9959caf60c..3083663531 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -459,6 +459,7 @@ + From bd303aa572a1ee2f98330812d2395a280fcf1aa0 Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Thu, 28 Jan 2021 09:36:37 -0800 Subject: [PATCH 08/12] Create PerformanceSampleAggregator --- Source/Core/Core/CMakeLists.txt | 2 ++ Source/Core/Core/DolphinAnalytics.cpp | 3 +++ Source/Core/Core/PerformanceSampleAggregator.cpp | 5 +++++ Source/Core/Core/PerformanceSampleAggregator.h | 11 +++++++++++ Source/Core/DolphinLib.props | 2 ++ 5 files changed, 23 insertions(+) create mode 100644 Source/Core/Core/PerformanceSampleAggregator.cpp create mode 100644 Source/Core/Core/PerformanceSampleAggregator.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 789ded01fd..463736ac08 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -513,6 +513,8 @@ add_library(core PatchEngine.cpp PatchEngine.h PerformanceSample.h + PerformanceSampleAggregator.cpp + PerformanceSampleAggregator.h PowerPC/BreakPoints.cpp PowerPC/BreakPoints.h PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index ef5ace951e..f105ad1499 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -35,9 +35,12 @@ #include "Core/HW/GCPad.h" #include "Core/Movie.h" #include "Core/NetPlayProto.h" +#include "Core/PerformanceSampleAggregator.h" + #include "Core/System.h" #include "InputCommon/GCAdapter.h" #include "InputCommon/InputConfig.h" + #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" diff --git a/Source/Core/Core/PerformanceSampleAggregator.cpp b/Source/Core/Core/PerformanceSampleAggregator.cpp new file mode 100644 index 0000000000..1e0b8ddf4b --- /dev/null +++ b/Source/Core/Core/PerformanceSampleAggregator.cpp @@ -0,0 +1,5 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/PerformanceSampleAggregator.h" diff --git a/Source/Core/Core/PerformanceSampleAggregator.h b/Source/Core/Core/PerformanceSampleAggregator.h new file mode 100644 index 0000000000..59f5c86484 --- /dev/null +++ b/Source/Core/Core/PerformanceSampleAggregator.h @@ -0,0 +1,11 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +class PerformanceSampleAggregator +{ +public: + PerformanceSampleAggregator() = default; +}; diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 3083663531..c1e0c663fd 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -460,6 +460,7 @@ + @@ -1165,6 +1166,7 @@ + From 8c1a667b8b058d5ff0fa39a908aa7551f855f5fa Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Tue, 26 Jan 2021 16:36:12 -0800 Subject: [PATCH 09/12] DolphinAnalytics: Extract timestamp functions Moves functions that generate starting timestamps for performance sampling to PerformanceSampleAggregator. --- Source/Core/Core/DolphinAnalytics.cpp | 18 ++------- Source/Core/Core/DolphinAnalytics.h | 14 +++---- .../Core/Core/PerformanceSampleAggregator.cpp | 39 +++++++++++++++++++ .../Core/Core/PerformanceSampleAggregator.h | 6 +++ 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index f105ad1499..f8a35fb9d9 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -27,7 +27,6 @@ #include "Common/Config/Config.h" #include "Common/Crypto/SHA1.h" #include "Common/Random.h" -#include "Common/Timer.h" #include "Common/Version.h" #include "Core/Config/GraphicsSettings.h" #include "Core/Config/MainSettings.h" @@ -35,7 +34,6 @@ #include "Core/HW/GCPad.h" #include "Core/Movie.h" #include "Core/NetPlayProto.h" -#include "Core/PerformanceSampleAggregator.h" #include "Core/System.h" #include "InputCommon/GCAdapter.h" @@ -312,6 +310,7 @@ void DolphinAnalytics::ReportPerformanceInfo(PerformanceSample sample) // Clear up and stop sampling until next time ShouldStartPerformanceSampling() says so. m_performance_samples.clear(); m_sampling_performance_info = false; + m_sampling_next_start_us = m_sample_aggregator.GetRepeatSamplingStartTimestamp().count(); } } @@ -320,22 +319,13 @@ void DolphinAnalytics::InitializePerformanceSampling() m_performance_samples.clear(); m_sampling_performance_info = false; - const u64 wait_us = - PERFORMANCE_SAMPLING_INITIAL_WAIT_TIME_SECS * 1000000 + - Common::Random::GenerateValue() % (PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS * 1000000); - m_sampling_next_start_us = Common::Timer::NowUs() + wait_us; + m_sampling_next_start_us = m_sample_aggregator.GetInitialSamplingStartTimestamp().count(); } bool DolphinAnalytics::ShouldStartPerformanceSampling() { - if (Common::Timer::NowUs() < m_sampling_next_start_us) - return false; - - const u64 wait_us = - PERFORMANCE_SAMPLING_INTERVAL_SECS * 1000000 + - Common::Random::GenerateValue() % (PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS * 1000000); - m_sampling_next_start_us = Common::Timer::NowUs() + wait_us; - return true; + return static_cast(m_sample_aggregator.GetCurrentMicroseconds().count()) >= + m_sampling_next_start_us; } void DolphinAnalytics::MakeBaseBuilder() diff --git a/Source/Core/Core/DolphinAnalytics.h b/Source/Core/Core/DolphinAnalytics.h index c62d97e0dd..3e38dc6943 100644 --- a/Source/Core/Core/DolphinAnalytics.h +++ b/Source/Core/Core/DolphinAnalytics.h @@ -14,6 +14,7 @@ #include "Common/Config/Config.h" #include "Core/PerformanceSample.h" +#include "Core/PerformanceSampleAggregator.h" #if defined(ANDROID) #include @@ -142,6 +143,10 @@ public: // Reports performance information. This method performs its own throttling / aggregation -- // calling it does not guarantee when a report will actually be sent. + // Performance reports are generated using data from 100 consecutive frames. + // Report starting times are randomized to obtain a wider range of sample data. + // The first report begins 5-8 minutes after a game is launched. + // Successive reports begin 30-33 minutes after the previous report finishes. // // This method is NOT thread-safe. void ReportPerformanceInfo(PerformanceSample sample); @@ -169,21 +174,16 @@ private: // values created by MakeUniqueId. std::string m_unique_id; - // Performance sampling configuration constants. - // - // 5min after startup + rand(0, 3min) jitter time, collect performance for 100 frames in a row. - // Repeat collection after 30min + rand(0, 3min). static constexpr int NUM_PERFORMANCE_SAMPLES_PER_REPORT = 100; - static constexpr int PERFORMANCE_SAMPLING_INITIAL_WAIT_TIME_SECS = 300; - static constexpr int PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS = 180; - static constexpr int PERFORMANCE_SAMPLING_INTERVAL_SECS = 1800; // Performance sampling state & internal helpers. void InitializePerformanceSampling(); // Called on game start / title switch. bool ShouldStartPerformanceSampling(); + u64 m_sampling_next_start_us; // Next timestamp (in us) at which to trigger sampling. bool m_sampling_performance_info = false; // Whether we are currently collecting samples. std::vector m_performance_samples; + PerformanceSampleAggregator m_sample_aggregator; // What quirks have already been reported about the current game. std::array(GameQuirk::Count)> m_reported_quirks; diff --git a/Source/Core/Core/PerformanceSampleAggregator.cpp b/Source/Core/Core/PerformanceSampleAggregator.cpp index 1e0b8ddf4b..f4ec11924a 100644 --- a/Source/Core/Core/PerformanceSampleAggregator.cpp +++ b/Source/Core/Core/PerformanceSampleAggregator.cpp @@ -3,3 +3,42 @@ // Refer to the license.txt file included. #include "Core/PerformanceSampleAggregator.h" + +#include "Common/Random.h" + +namespace +{ +std::chrono::microseconds GetSamplingStartTimeJitter() +{ + constexpr long long max_delay = std::chrono::microseconds(std::chrono::minutes(3)).count(); + return std::chrono::microseconds(Common::Random::GenerateValue() % max_delay); +} + +std::chrono::microseconds +GetSamplingStartTimestampUsingBaseDelay(const std::chrono::microseconds base_delay) +{ + const std::chrono::microseconds now = PerformanceSampleAggregator::GetCurrentMicroseconds(); + const std::chrono::microseconds jitter = GetSamplingStartTimeJitter(); + const std::chrono::microseconds sampling_start_timestamp = now + base_delay + jitter; + return sampling_start_timestamp; +} +} // namespace + +std::chrono::microseconds PerformanceSampleAggregator::GetInitialSamplingStartTimestamp() +{ + constexpr std::chrono::microseconds base_initial_delay = std::chrono::minutes(5); + return GetSamplingStartTimestampUsingBaseDelay(base_initial_delay); +} + +std::chrono::microseconds PerformanceSampleAggregator::GetRepeatSamplingStartTimestamp() +{ + constexpr std::chrono::microseconds base_repeat_delay = std::chrono::minutes(30); + return GetSamplingStartTimestampUsingBaseDelay(base_repeat_delay); +} + +std::chrono::microseconds PerformanceSampleAggregator::GetCurrentMicroseconds() +{ + const std::chrono::high_resolution_clock::duration time_since_epoch = + std::chrono::high_resolution_clock::now().time_since_epoch(); + return std::chrono::duration_cast(time_since_epoch); +} diff --git a/Source/Core/Core/PerformanceSampleAggregator.h b/Source/Core/Core/PerformanceSampleAggregator.h index 59f5c86484..a3a8bc4e4a 100644 --- a/Source/Core/Core/PerformanceSampleAggregator.h +++ b/Source/Core/Core/PerformanceSampleAggregator.h @@ -4,8 +4,14 @@ #pragma once +#include + class PerformanceSampleAggregator { public: PerformanceSampleAggregator() = default; + + static std::chrono::microseconds GetInitialSamplingStartTimestamp(); + static std::chrono::microseconds GetRepeatSamplingStartTimestamp(); + static std::chrono::microseconds GetCurrentMicroseconds(); }; From f0d2d0f5bef5283ff57abe215a67248b0c07582b Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Sat, 30 Jan 2021 10:55:25 -0800 Subject: [PATCH 10/12] DolphinAnalytics: Extract report generation Move generation of completed performance sample report to PerformanceSampleAggregator. --- Source/Core/Core/DolphinAnalytics.cpp | 17 +++++------------ .../Core/Core/PerformanceSampleAggregator.cpp | 19 +++++++++++++++++++ .../Core/Core/PerformanceSampleAggregator.h | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index f8a35fb9d9..d8f9cc6466 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -287,23 +287,16 @@ void DolphinAnalytics::ReportPerformanceInfo(PerformanceSample sample) if (m_performance_samples.size() >= NUM_PERFORMANCE_SAMPLES_PER_REPORT) { - std::vector speed_times_1000(m_performance_samples.size()); - std::vector num_prims(m_performance_samples.size()); - std::vector num_draw_calls(m_performance_samples.size()); - for (size_t i = 0; i < m_performance_samples.size(); ++i) - { - speed_times_1000[i] = static_cast(m_performance_samples[i].speed_ratio * 1000); - num_prims[i] = m_performance_samples[i].num_prims; - num_draw_calls[i] = m_performance_samples[i].num_draw_calls; - } + PerformanceSampleAggregator::CompletedReport report = + PerformanceSampleAggregator::GetReportFromSamples(m_performance_samples); // The per game builder should already exist -- there is no way we can be reporting performance // info without a game start event having been generated. Common::AnalyticsReportBuilder builder(m_per_game_builder); builder.AddData("type", "performance"); - builder.AddData("speed", speed_times_1000); - builder.AddData("prims", num_prims); - builder.AddData("draw-calls", num_draw_calls); + builder.AddData("speed", report.speed); + builder.AddData("prims", report.primitives); + builder.AddData("draw-calls", report.draw_calls); Send(builder); diff --git a/Source/Core/Core/PerformanceSampleAggregator.cpp b/Source/Core/Core/PerformanceSampleAggregator.cpp index f4ec11924a..7d59a2cd75 100644 --- a/Source/Core/Core/PerformanceSampleAggregator.cpp +++ b/Source/Core/Core/PerformanceSampleAggregator.cpp @@ -42,3 +42,22 @@ std::chrono::microseconds PerformanceSampleAggregator::GetCurrentMicroseconds() std::chrono::high_resolution_clock::now().time_since_epoch(); return std::chrono::duration_cast(time_since_epoch); } + +PerformanceSampleAggregator::CompletedReport +PerformanceSampleAggregator::GetReportFromSamples(const std::vector& samples) +{ + CompletedReport report; + const size_t num_samples = samples.size(); + report.speed.resize(num_samples); + report.primitives.resize(num_samples); + report.draw_calls.resize(num_samples); + + for (size_t i = 0; i < num_samples; ++i) + { + const PerformanceSample& sample = samples[i]; + report.speed[i] = static_cast(sample.speed_ratio * 1'000); + report.primitives[i] = sample.num_prims; + report.draw_calls[i] = sample.num_draw_calls; + } + return report; +} diff --git a/Source/Core/Core/PerformanceSampleAggregator.h b/Source/Core/Core/PerformanceSampleAggregator.h index a3a8bc4e4a..7b50d6e171 100644 --- a/Source/Core/Core/PerformanceSampleAggregator.h +++ b/Source/Core/Core/PerformanceSampleAggregator.h @@ -5,6 +5,11 @@ #pragma once #include +#include + +#include "Common/CommonTypes.h" + +#include "Core/PerformanceSample.h" class PerformanceSampleAggregator { @@ -14,4 +19,13 @@ public: static std::chrono::microseconds GetInitialSamplingStartTimestamp(); static std::chrono::microseconds GetRepeatSamplingStartTimestamp(); static std::chrono::microseconds GetCurrentMicroseconds(); + + struct CompletedReport + { + std::vector speed; + std::vector primitives; + std::vector draw_calls; + }; + + static CompletedReport GetReportFromSamples(const std::vector& samples); }; From 346a4570b40d40350f3b213da86e9bfd9654c883 Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Sat, 30 Jan 2021 12:19:09 -0800 Subject: [PATCH 11/12] DolphinAnalytics: Extract sample aggregation Moves remaining sample aggregation logic to PerformanceSampleAggregation. --- Source/Core/Core/DolphinAnalytics.cpp | 56 ++++----------- Source/Core/Core/DolphinAnalytics.h | 9 --- .../Core/Core/PerformanceSampleAggregator.cpp | 68 +++++++++++++++---- .../Core/Core/PerformanceSampleAggregator.h | 19 ++++-- 4 files changed, 83 insertions(+), 69 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index d8f9cc6466..4ef783ff49 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -216,7 +216,7 @@ void DolphinAnalytics::ReportGameStart() // Reset per-game state. m_reported_quirks.fill(false); - InitializePerformanceSampling(); + m_sample_aggregator.InitializePerformanceSampling(); } // Keep in sync with enum class GameQuirk definition. @@ -275,50 +275,24 @@ void DolphinAnalytics::ReportGameQuirk(GameQuirk quirk) void DolphinAnalytics::ReportPerformanceInfo(PerformanceSample sample) { - if (ShouldStartPerformanceSampling()) + m_sample_aggregator.AddSampleIfSamplingInProgress(sample); + const std::optional report_optional = + m_sample_aggregator.PopReportIfComplete(); + if (!report_optional) { - m_sampling_performance_info = true; + return; } + const PerformanceSampleAggregator::CompletedReport& report = *report_optional; - if (m_sampling_performance_info) - { - m_performance_samples.emplace_back(sample); - } + // The per game builder should already exist -- there is no way we can be reporting performance + // info without a game start event having been generated. + Common::AnalyticsReportBuilder builder(m_per_game_builder); + builder.AddData("type", "performance"); + builder.AddData("speed", report.speed); + builder.AddData("prims", report.primitives); + builder.AddData("draw-calls", report.draw_calls); - if (m_performance_samples.size() >= NUM_PERFORMANCE_SAMPLES_PER_REPORT) - { - PerformanceSampleAggregator::CompletedReport report = - PerformanceSampleAggregator::GetReportFromSamples(m_performance_samples); - - // The per game builder should already exist -- there is no way we can be reporting performance - // info without a game start event having been generated. - Common::AnalyticsReportBuilder builder(m_per_game_builder); - builder.AddData("type", "performance"); - builder.AddData("speed", report.speed); - builder.AddData("prims", report.primitives); - builder.AddData("draw-calls", report.draw_calls); - - Send(builder); - - // Clear up and stop sampling until next time ShouldStartPerformanceSampling() says so. - m_performance_samples.clear(); - m_sampling_performance_info = false; - m_sampling_next_start_us = m_sample_aggregator.GetRepeatSamplingStartTimestamp().count(); - } -} - -void DolphinAnalytics::InitializePerformanceSampling() -{ - m_performance_samples.clear(); - m_sampling_performance_info = false; - - m_sampling_next_start_us = m_sample_aggregator.GetInitialSamplingStartTimestamp().count(); -} - -bool DolphinAnalytics::ShouldStartPerformanceSampling() -{ - return static_cast(m_sample_aggregator.GetCurrentMicroseconds().count()) >= - m_sampling_next_start_us; + Send(builder); } void DolphinAnalytics::MakeBaseBuilder() diff --git a/Source/Core/Core/DolphinAnalytics.h b/Source/Core/Core/DolphinAnalytics.h index 3e38dc6943..1bbf0aff3d 100644 --- a/Source/Core/Core/DolphinAnalytics.h +++ b/Source/Core/Core/DolphinAnalytics.h @@ -174,15 +174,6 @@ private: // values created by MakeUniqueId. std::string m_unique_id; - static constexpr int NUM_PERFORMANCE_SAMPLES_PER_REPORT = 100; - - // Performance sampling state & internal helpers. - void InitializePerformanceSampling(); // Called on game start / title switch. - bool ShouldStartPerformanceSampling(); - - u64 m_sampling_next_start_us; // Next timestamp (in us) at which to trigger sampling. - bool m_sampling_performance_info = false; // Whether we are currently collecting samples. - std::vector m_performance_samples; PerformanceSampleAggregator m_sample_aggregator; // What quirks have already been reported about the current game. diff --git a/Source/Core/Core/PerformanceSampleAggregator.cpp b/Source/Core/Core/PerformanceSampleAggregator.cpp index 7d59a2cd75..4328f066e4 100644 --- a/Source/Core/Core/PerformanceSampleAggregator.cpp +++ b/Source/Core/Core/PerformanceSampleAggregator.cpp @@ -4,10 +4,21 @@ #include "Core/PerformanceSampleAggregator.h" +#include + #include "Common/Random.h" namespace { +constexpr size_t s_samples_per_report = 100; + +std::chrono::microseconds GetCurrentMicroseconds() +{ + const std::chrono::high_resolution_clock::duration time_since_epoch = + std::chrono::high_resolution_clock::now().time_since_epoch(); + return std::chrono::duration_cast(time_since_epoch); +} + std::chrono::microseconds GetSamplingStartTimeJitter() { constexpr long long max_delay = std::chrono::microseconds(std::chrono::minutes(3)).count(); @@ -17,36 +28,28 @@ std::chrono::microseconds GetSamplingStartTimeJitter() std::chrono::microseconds GetSamplingStartTimestampUsingBaseDelay(const std::chrono::microseconds base_delay) { - const std::chrono::microseconds now = PerformanceSampleAggregator::GetCurrentMicroseconds(); + const std::chrono::microseconds now = GetCurrentMicroseconds(); const std::chrono::microseconds jitter = GetSamplingStartTimeJitter(); const std::chrono::microseconds sampling_start_timestamp = now + base_delay + jitter; return sampling_start_timestamp; } -} // namespace -std::chrono::microseconds PerformanceSampleAggregator::GetInitialSamplingStartTimestamp() +std::chrono::microseconds GetInitialSamplingStartTimestamp() { constexpr std::chrono::microseconds base_initial_delay = std::chrono::minutes(5); return GetSamplingStartTimestampUsingBaseDelay(base_initial_delay); } -std::chrono::microseconds PerformanceSampleAggregator::GetRepeatSamplingStartTimestamp() +std::chrono::microseconds GetRepeatSamplingStartTimestamp() { constexpr std::chrono::microseconds base_repeat_delay = std::chrono::minutes(30); return GetSamplingStartTimestampUsingBaseDelay(base_repeat_delay); } -std::chrono::microseconds PerformanceSampleAggregator::GetCurrentMicroseconds() -{ - const std::chrono::high_resolution_clock::duration time_since_epoch = - std::chrono::high_resolution_clock::now().time_since_epoch(); - return std::chrono::duration_cast(time_since_epoch); -} - PerformanceSampleAggregator::CompletedReport -PerformanceSampleAggregator::GetReportFromSamples(const std::vector& samples) +GetCompletedReport(const std::vector& samples) { - CompletedReport report; + PerformanceSampleAggregator::CompletedReport report; const size_t num_samples = samples.size(); report.speed.resize(num_samples); report.primitives.resize(num_samples); @@ -61,3 +64,42 @@ PerformanceSampleAggregator::GetReportFromSamples(const std::vector(s_samples_per_report)), + m_next_starting_timestamp(std::numeric_limits::max()) +{ +} + +void PerformanceSampleAggregator::InitializePerformanceSampling() +{ + m_samples.clear(); + m_next_starting_timestamp = GetInitialSamplingStartTimestamp(); +} + +void PerformanceSampleAggregator::ResetPerformanceSampling() +{ + m_samples.clear(); + m_next_starting_timestamp = GetRepeatSamplingStartTimestamp(); +} + +void PerformanceSampleAggregator::AddSampleIfSamplingInProgress(PerformanceSample&& sample) +{ + if (GetCurrentMicroseconds() >= m_next_starting_timestamp) + { + m_samples.push_back(sample); + } +} + +std::optional +PerformanceSampleAggregator::PopReportIfComplete() +{ + if (m_samples.size() < s_samples_per_report) + { + return std::nullopt; + } + const CompletedReport report = GetCompletedReport(m_samples); + ResetPerformanceSampling(); + return report; +} diff --git a/Source/Core/Core/PerformanceSampleAggregator.h b/Source/Core/Core/PerformanceSampleAggregator.h index 7b50d6e171..27354ff2c6 100644 --- a/Source/Core/Core/PerformanceSampleAggregator.h +++ b/Source/Core/Core/PerformanceSampleAggregator.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "Common/CommonTypes.h" @@ -14,11 +15,7 @@ class PerformanceSampleAggregator { public: - PerformanceSampleAggregator() = default; - - static std::chrono::microseconds GetInitialSamplingStartTimestamp(); - static std::chrono::microseconds GetRepeatSamplingStartTimestamp(); - static std::chrono::microseconds GetCurrentMicroseconds(); + PerformanceSampleAggregator(); struct CompletedReport { @@ -27,5 +24,15 @@ public: std::vector draw_calls; }; - static CompletedReport GetReportFromSamples(const std::vector& samples); + // Called on game start / title switch. + void InitializePerformanceSampling(); + void AddSampleIfSamplingInProgress(PerformanceSample&& sample); + std::optional PopReportIfComplete(); + +private: + // Called after sampling report is completed + void ResetPerformanceSampling(); + + std::vector m_samples; + std::chrono::microseconds m_next_starting_timestamp; }; From fff0a19e08b80d92658d6dc8455507a542664723 Mon Sep 17 00:00:00 2001 From: "Dr. Dystopia" Date: Sun, 31 Jan 2021 10:41:09 -0800 Subject: [PATCH 12/12] DolphinAnalytics: Remove unused headers --- Source/Core/Core/DolphinAnalytics.cpp | 1 - Source/Core/Core/DolphinAnalytics.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 4ef783ff49..ec12be3cbc 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include diff --git a/Source/Core/Core/DolphinAnalytics.h b/Source/Core/Core/DolphinAnalytics.h index 1bbf0aff3d..d72d24b2a1 100644 --- a/Source/Core/Core/DolphinAnalytics.h +++ b/Source/Core/Core/DolphinAnalytics.h @@ -7,10 +7,8 @@ #include #include #include -#include #include "Common/Analytics.h" -#include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "Core/PerformanceSample.h"