Analytics.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor.Analytics;
  4. using UnityEngine;
  5. using UnityEngine.Analytics;
  6. namespace UnityEditor.Performance.ProfileAnalyzer
  7. {
  8. class ProfileAnalyzerAnalytics
  9. {
  10. const int k_MaxEventsPerHour = 100;
  11. const int k_MaxEventItems = 1000;
  12. const string k_VendorKey = "unity.profileanalyzer";
  13. const string k_EventTopicName = "usability";
  14. static bool s_EnableAnalytics = false;
  15. public static void EnableAnalytics()
  16. {
  17. #if UNITY_2018_1_OR_NEWER
  18. AnalyticsResult result = EditorAnalytics.RegisterEventWithLimit(k_EventTopicName, k_MaxEventsPerHour, k_MaxEventItems, k_VendorKey);
  19. if (result == AnalyticsResult.Ok)
  20. s_EnableAnalytics = true;
  21. #endif
  22. }
  23. public enum UIButton
  24. {
  25. Pull,
  26. OpenProfiler,
  27. CloseProfiler,
  28. JumpToFrame,
  29. ExportSingleFrames,
  30. ExportComparisonFrames,
  31. };
  32. public enum UIUsageMode
  33. {
  34. Single,
  35. Comparison,
  36. };
  37. public enum UIVisibility
  38. {
  39. FrameTimeContextMenu,
  40. Filters,
  41. TopTen,
  42. Frames,
  43. Threads,
  44. Markers,
  45. };
  46. public enum UIResizeView
  47. {
  48. Single,
  49. Comparison,
  50. };
  51. [Serializable]
  52. struct ProfileAnalyzerUIButtonEventParameters
  53. {
  54. public string name;
  55. public ProfileAnalyzerUIButtonEventParameters(string name)
  56. {
  57. this.name = name;
  58. }
  59. }
  60. // camelCase since these events get serialized to Json and naming convention in analytics is camelCase
  61. [Serializable]
  62. struct ProfileAnalyzerUIButtonEvent
  63. {
  64. public ProfileAnalyzerUIButtonEvent(string name, float durationInTicks)
  65. {
  66. subtype = "profileAnalyzerUIButton";
  67. // ts is auto added so no need to include it here
  68. //ts = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
  69. this.duration = durationInTicks;
  70. parameters = new ProfileAnalyzerUIButtonEventParameters(name);
  71. }
  72. public string subtype;
  73. //public int ts;
  74. public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  75. public ProfileAnalyzerUIButtonEventParameters parameters;
  76. }
  77. [Serializable]
  78. struct ProfileAnalyzerUIUsageEventParameters
  79. {
  80. public string name;
  81. public ProfileAnalyzerUIUsageEventParameters(string name)
  82. {
  83. this.name = name;
  84. }
  85. }
  86. [Serializable]
  87. struct ProfileAnalyzerUIUsageEvent
  88. {
  89. public ProfileAnalyzerUIUsageEvent(string name, float durationInTicks)
  90. {
  91. subtype = "profileAnalyzerModeUsage";
  92. this.duration = durationInTicks;
  93. parameters = new ProfileAnalyzerUIUsageEventParameters(name);
  94. }
  95. public string subtype;
  96. public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  97. public ProfileAnalyzerUIUsageEventParameters parameters;
  98. }
  99. [Serializable]
  100. struct ProfileAnalyzerUIVisibilityEventParameters
  101. {
  102. public string name;
  103. public bool show;
  104. public ProfileAnalyzerUIVisibilityEventParameters(string name, bool show)
  105. {
  106. this.name = name;
  107. this.show = show;
  108. }
  109. }
  110. [Serializable]
  111. struct ProfileAnalyzerUIVisibilityEvent
  112. {
  113. public ProfileAnalyzerUIVisibilityEvent(string name, float durationInTicks, bool show)
  114. {
  115. subtype = "profileAnalyzerUIVisibility";
  116. this.duration = durationInTicks;
  117. parameters = new ProfileAnalyzerUIVisibilityEventParameters(name, show);
  118. }
  119. public string subtype;
  120. public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  121. public ProfileAnalyzerUIVisibilityEventParameters parameters;
  122. }
  123. [Serializable]
  124. struct ProfileAnalyzerUIResizeEventParameters
  125. {
  126. public string name;
  127. public float width;
  128. public float height;
  129. public float screenWidth;
  130. public float screenHeight;
  131. public bool docked;
  132. public ProfileAnalyzerUIResizeEventParameters(string name, float width, float height, float screenWidth, float screenHeight, bool isDocked)
  133. {
  134. this.name = name;
  135. this.width = width;
  136. this.height = height;
  137. this.screenWidth = screenWidth;
  138. this.screenHeight = screenHeight;
  139. docked = isDocked;
  140. }
  141. }
  142. [Serializable]
  143. struct ProfileAnalyzerUIResizeEvent
  144. {
  145. public ProfileAnalyzerUIResizeEvent(string name, float durationInTicks, float width, float height, float screenWidth, float screenHeight, bool isDocked)
  146. {
  147. subtype = "profileAnalyzerUIResize";
  148. this.duration = durationInTicks;
  149. parameters = new ProfileAnalyzerUIResizeEventParameters(name, width, height, screenWidth, screenHeight, isDocked);
  150. }
  151. public string subtype;
  152. public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  153. public ProfileAnalyzerUIResizeEventParameters parameters;
  154. }
  155. static float SecondsToTicks(float durationInSeconds)
  156. {
  157. return durationInSeconds * 10000;
  158. }
  159. public static bool SendUIButtonEvent(UIButton uiButton, float durationInSeconds)
  160. {
  161. if (!s_EnableAnalytics)
  162. return false;
  163. #if UNITY_2018_1_OR_NEWER
  164. // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  165. float durationInTicks = SecondsToTicks(durationInSeconds);
  166. ProfileAnalyzerUIButtonEvent uiButtonEvent;
  167. switch (uiButton)
  168. {
  169. case UIButton.Pull:
  170. uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerGrab", durationInTicks);
  171. break;
  172. case UIButton.OpenProfiler:
  173. uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerOpenProfiler", durationInTicks);
  174. break;
  175. case UIButton.CloseProfiler:
  176. uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerCloseProfiler", durationInTicks);
  177. break;
  178. case UIButton.JumpToFrame:
  179. uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerJumpToFrame", durationInTicks);
  180. break;
  181. case UIButton.ExportSingleFrames:
  182. uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerExportSingleFrames", durationInTicks);
  183. break;
  184. case UIButton.ExportComparisonFrames:
  185. uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerExportComparisonFrames", durationInTicks);
  186. break;
  187. default:
  188. Debug.LogFormat("SendUIButtonEvent: Unsupported button type : {0}", uiButton);
  189. return false;
  190. }
  191. AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiButtonEvent);
  192. if (result != AnalyticsResult.Ok)
  193. return false;
  194. return true;
  195. #else
  196. return false;
  197. #endif
  198. }
  199. public static bool SendUIUsageModeEvent(UIUsageMode uiUsageMode, float durationInSeconds)
  200. {
  201. if (!s_EnableAnalytics)
  202. return false;
  203. #if UNITY_2018_1_OR_NEWER
  204. // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  205. float durationInTicks = SecondsToTicks(durationInSeconds);
  206. ProfileAnalyzerUIUsageEvent uiUsageEvent;
  207. switch (uiUsageMode)
  208. {
  209. case UIUsageMode.Single:
  210. uiUsageEvent = new ProfileAnalyzerUIUsageEvent("profileAnalyzerSingle", durationInTicks);
  211. break;
  212. case UIUsageMode.Comparison:
  213. uiUsageEvent = new ProfileAnalyzerUIUsageEvent("profileAnalyzerCompare", durationInTicks);
  214. break;
  215. default:
  216. Debug.LogFormat("SendUsageEvent: Unsupported usage mode : {0}", uiUsageMode);
  217. return false;
  218. }
  219. AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiUsageEvent);
  220. if (result != AnalyticsResult.Ok)
  221. return false;
  222. return true;
  223. #else
  224. return false;
  225. #endif
  226. }
  227. public static bool SendUIVisibilityEvent(UIVisibility uiVisibility, float durationInSeconds, bool show)
  228. {
  229. if (!s_EnableAnalytics)
  230. return false;
  231. #if UNITY_2018_1_OR_NEWER
  232. // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  233. float durationInTicks = SecondsToTicks(durationInSeconds);
  234. ProfileAnalyzerUIVisibilityEvent uiUsageEvent;
  235. switch (uiVisibility)
  236. {
  237. case UIVisibility.FrameTimeContextMenu:
  238. uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerFrameTimeContextMenu", durationInTicks, show);
  239. break;
  240. case UIVisibility.Filters:
  241. uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerFilters", durationInTicks, show);
  242. break;
  243. case UIVisibility.TopTen:
  244. uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerTopTen", durationInTicks, show);
  245. break;
  246. case UIVisibility.Frames:
  247. uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerFrames", durationInTicks, show);
  248. break;
  249. case UIVisibility.Threads:
  250. uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerThreads", durationInTicks, show);
  251. break;
  252. case UIVisibility.Markers:
  253. uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerMarkers", durationInTicks, show);
  254. break;
  255. default:
  256. Debug.LogFormat("SendUIVisibilityEvent: Unsupported visibililty item : {0}", uiVisibility);
  257. return false;
  258. }
  259. AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiUsageEvent);
  260. if (result != AnalyticsResult.Ok)
  261. return false;
  262. return true;
  263. #else
  264. return false;
  265. #endif
  266. }
  267. public static bool SendUIResizeEvent(UIResizeView uiResizeView, float durationInSeconds, float width, float height, bool isDocked)
  268. {
  269. if (!s_EnableAnalytics)
  270. return false;
  271. #if UNITY_2018_1_OR_NEWER
  272. // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
  273. float durationInTicks = SecondsToTicks(durationInSeconds);
  274. ProfileAnalyzerUIResizeEvent uiResizeEvent;
  275. switch (uiResizeView)
  276. {
  277. case UIResizeView.Single:
  278. // Screen.width, Screen.height is game view size
  279. uiResizeEvent = new ProfileAnalyzerUIResizeEvent("profileAnalyzerSingle", durationInTicks, width, height, Screen.currentResolution.width, Screen.currentResolution.height, isDocked);
  280. break;
  281. case UIResizeView.Comparison:
  282. uiResizeEvent = new ProfileAnalyzerUIResizeEvent("profileAnalyzerCompare", durationInTicks, width, height, Screen.currentResolution.width, Screen.currentResolution.height, isDocked);
  283. break;
  284. default:
  285. Debug.LogFormat("SendUIResizeEvent: Unsupported view : {0}", uiResizeView);
  286. return false;
  287. }
  288. AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiResizeEvent);
  289. if (result != AnalyticsResult.Ok)
  290. return false;
  291. return true;
  292. #else
  293. return false;
  294. #endif
  295. }
  296. internal class Analytic
  297. {
  298. double m_StartTime;
  299. float m_DurationInSeconds;
  300. public Analytic()
  301. {
  302. m_StartTime = EditorApplication.timeSinceStartup;
  303. m_DurationInSeconds = 0;
  304. }
  305. public void End()
  306. {
  307. m_DurationInSeconds = (float)(EditorApplication.timeSinceStartup - m_StartTime);
  308. }
  309. public float GetDurationInSeconds()
  310. {
  311. return m_DurationInSeconds;
  312. }
  313. }
  314. static public Analytic BeginAnalytic()
  315. {
  316. return new Analytic();
  317. }
  318. static public void SendUIButtonEvent(UIButton uiButton, Analytic instance)
  319. {
  320. instance.End();
  321. SendUIButtonEvent(uiButton, instance.GetDurationInSeconds());
  322. }
  323. static public void SendUIUsageModeEvent(UIUsageMode uiUsageMode, Analytic instance)
  324. {
  325. instance.End();
  326. SendUIUsageModeEvent(uiUsageMode, instance.GetDurationInSeconds());
  327. }
  328. }
  329. }