1 // Copyright 2016 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <array>
8 #include <memory>
9 #include <mutex>
10 #include <string>
11 #include <string_view>
12 #include <vector>
13 
14 #include "Common/Analytics.h"
15 #include "Common/CommonTypes.h"
16 
17 #if defined(ANDROID)
18 #include <functional>
19 #endif
20 // Non generic part of the Dolphin Analytics framework. See Common/Analytics.h
21 // for the main documentation.
22 
23 enum class GameQuirk
24 {
25   // Sometimes code run from ICache is different from its mirror in RAM.
26   ICACHE_MATTERS = 0,
27 
28   // The Wii remote hardware makes it possible to bypass normal data reporting and directly
29   // "read" extension or IR data. This would break our current TAS/NetPlay implementation.
30   DIRECTLY_READS_WIIMOTE_INPUT,
31 
32   // Several Wii DI commands that are rarely/never used and not implemented by Dolphin
33   USES_DVD_LOW_STOP_LASER,
34   USES_DVD_LOW_OFFSET,
35   USES_DVD_LOW_READ_DISK_BCA,  // NSMBW known to use this
36   USES_DVD_LOW_REQUEST_DISC_STATUS,
37   USES_DVD_LOW_REQUEST_RETRY_NUMBER,
38   USES_DVD_LOW_SER_MEAS_CONTROL,
39 
40   // Dolphin only implements the simple DVDLowOpenPartition, not any of the variants where some
41   // already-read data is provided
42   USES_DIFFERENT_PARTITION_COMMAND,
43 
44   // IOS has implementations for ioctls 0x85 and 0x89 and a stub for 0x87, but
45   // DVDLowMaskCoverInterrupt/DVDLowUnmaskCoverInterrupt/DVDLowUnmaskStatusInterrupts
46   // are all stubbed on the PPC side so they presumably will never be used.
47   // (DVDLowClearCoverInterrupt is used, though)
48   USES_DI_INTERRUPT_MASK_COMMAND,
49 
50   // Some games configure a mismatched number of texture coordinates or colors between the transform
51   // and TEV/BP stages of the rendering pipeline. Currently, Dolphin just skips over these objects
52   // as the hardware renderers are not equipped to handle the case where the registers between
53   // stages are mismatched.
54   MISMATCHED_GPU_TEXGENS_BETWEEN_XF_AND_BP,
55   MISMATCHED_GPU_COLORS_BETWEEN_XF_AND_BP,
56 
57   COUNT,
58 };
59 
60 class DolphinAnalytics
61 {
62 public:
63   // Performs lazy-initialization of a singleton and returns the instance.
64   static DolphinAnalytics& Instance();
65 
66 #if defined(ANDROID)
67   // Get value from java.
68   static void AndroidSetGetValFunc(std::function<std::string(std::string)> function);
69 #endif
70   // Resets and recreates the analytics system in order to reload
71   // configuration.
72   void ReloadConfig();
73   // Rotates the unique identifier used for this instance of Dolphin and saves
74   // it into the configuration.
75   void GenerateNewIdentity();
76 
77   // Reports a Dolphin start event.
78   void ReportDolphinStart(std::string_view ui_type);
79 
80   // Generates a base report for a "Game start" event. Also preseeds the
81   // per-game base data.
82   void ReportGameStart();
83 
84   // Generates a report for a special condition being hit by a game. This is automatically throttled
85   // to once per game run.
86   void ReportGameQuirk(GameQuirk quirk);
87 
88   struct PerformanceSample
89   {
90     double speed_ratio;  // See SystemTimers::GetEstimatedEmulationPerformance().
91     int num_prims;
92     int num_draw_calls;
93   };
94   // Reports performance information. This method performs its own throttling / aggregation --
95   // calling it does not guarantee when a report will actually be sent.
96   //
97   // This method is NOT thread-safe.
98   void ReportPerformanceInfo(PerformanceSample&& sample);
99 
100   // Forward Send method calls to the reporter.
101   template <typename T>
Send(T report)102   void Send(T report)
103   {
104     std::lock_guard lk{m_reporter_mutex};
105     m_reporter.Send(report);
106   }
107 
108 private:
109   DolphinAnalytics();
110 
111   void MakeBaseBuilder();
112   void MakePerGameBuilder();
113 
114   // Returns a unique ID derived on the global unique ID, hashed with some
115   // report-specific data. This avoid correlation between different types of
116   // events.
117   std::string MakeUniqueId(std::string_view data) const;
118 
119   // Unique ID. This should never leave the application. Only used derived
120   // values created by MakeUniqueId.
121   std::string m_unique_id;
122 
123   // Performance sampling configuration constants.
124   //
125   // 5min after startup + rand(0, 3min) jitter time, collect performance for 100 frames in a row.
126   // Repeat collection after 30min + rand(0, 3min).
127   static constexpr int NUM_PERFORMANCE_SAMPLES_PER_REPORT = 100;
128   static constexpr int PERFORMANCE_SAMPLING_INITIAL_WAIT_TIME_SECS = 300;
129   static constexpr int PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS = 180;
130   static constexpr int PERFORMANCE_SAMPLING_INTERVAL_SECS = 1800;
131 
132   // Performance sampling state & internal helpers.
133   void InitializePerformanceSampling();  // Called on game start / title switch.
134   bool ShouldStartPerformanceSampling();
135   u64 m_sampling_next_start_us;              // Next timestamp (in us) at which to trigger sampling.
136   bool m_sampling_performance_info = false;  // Whether we are currently collecting samples.
137   std::vector<PerformanceSample> m_performance_samples;
138 
139   // What quirks have already been reported about the current game.
140   std::array<bool, static_cast<size_t>(GameQuirk::COUNT)> m_reported_quirks;
141 
142   // Builder that contains all non variable data that should be sent with all
143   // reports.
144   Common::AnalyticsReportBuilder m_base_builder;
145 
146   // Builder that contains per game data and is initialized when a game start
147   // report is sent.
148   Common::AnalyticsReportBuilder m_per_game_builder;
149 
150   std::mutex m_reporter_mutex;
151   Common::AnalyticsReporter m_reporter;
152 };
153