Units.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. using System;
  2. using UnityEngine.Assertions;
  3. using UnityEngine;
  4. namespace UnityEditor.Performance.ProfileAnalyzer
  5. {
  6. /// <summary>Unit type identifier</summary>
  7. internal enum Units
  8. {
  9. /// <summary>Time in milliseconds</summary>
  10. Milliseconds,
  11. /// <summary>Time in microseconds</summary>
  12. Microseconds,
  13. /// <summary>Count of number of instances</summary>
  14. Count,
  15. };
  16. internal class DisplayUnits
  17. {
  18. public static readonly string[] UnitNames =
  19. {
  20. "Milliseconds",
  21. "Microseconds",
  22. "Count",
  23. };
  24. public static readonly int[] UnitValues = (int[])Enum.GetValues(typeof(Units));
  25. public readonly Units Units;
  26. const bool kShowFullValueWhenBelowZero = true;
  27. const int kTooltipDigitsNumber = 7;
  28. public DisplayUnits(Units units)
  29. {
  30. Assert.AreEqual(UnitNames.Length, UnitValues.Length, "Number of UnitNames should match number of enum values UnitValues: You probably forgot to update one of them.");
  31. Units = units;
  32. }
  33. public string Postfix()
  34. {
  35. switch (Units)
  36. {
  37. default:
  38. case Units.Milliseconds:
  39. return "ms";
  40. case Units.Microseconds:
  41. return "us";
  42. case Units.Count:
  43. return "";
  44. }
  45. }
  46. int ClampToRange(int value, int min, int max)
  47. {
  48. if (value < min)
  49. value = min;
  50. if (value > max)
  51. value = max;
  52. return value;
  53. }
  54. string RemoveTrailingZeros(string numberStr, int minNumberStringLength)
  55. {
  56. // Find out string length without trailing zeroes.
  57. var strLenWithoutTrailingZeros = numberStr.Length;
  58. while (strLenWithoutTrailingZeros > minNumberStringLength && numberStr[strLenWithoutTrailingZeros - 1] == '0')
  59. strLenWithoutTrailingZeros--;
  60. // Remove hanging '.' in case all zeroes can be omitted.
  61. if (strLenWithoutTrailingZeros > 0 && numberStr[strLenWithoutTrailingZeros - 1] == '.')
  62. strLenWithoutTrailingZeros--;
  63. return strLenWithoutTrailingZeros == numberStr.Length ? numberStr : numberStr.Substring(0, strLenWithoutTrailingZeros);
  64. }
  65. public string ToString(float ms, bool showUnits, int limitToNDigits, bool showFullValueWhenBelowZero = false)
  66. {
  67. float value = ms;
  68. int unitPower = -3;
  69. int minNumberStringLength = -1;
  70. int maxDecimalPlaces = 0;
  71. float minValueShownWhenUsingLimitedDecimalPlaces = 1f;
  72. switch (Units)
  73. {
  74. default:
  75. case Units.Milliseconds:
  76. maxDecimalPlaces = 2;
  77. minValueShownWhenUsingLimitedDecimalPlaces = 0.01f;
  78. break;
  79. case Units.Microseconds:
  80. value *= 1000f;
  81. unitPower -= 3;
  82. if (value < 100)
  83. {
  84. maxDecimalPlaces = 1;
  85. minValueShownWhenUsingLimitedDecimalPlaces = 0.1f;
  86. }
  87. else
  88. {
  89. maxDecimalPlaces = 0;
  90. minValueShownWhenUsingLimitedDecimalPlaces = 1f;
  91. }
  92. break;
  93. case Units.Count:
  94. showUnits = false;
  95. break;
  96. }
  97. int sgn = Math.Sign(value);
  98. if (value < 0)
  99. value = -value;
  100. int numberOfDecimalPlaces = maxDecimalPlaces;
  101. int unitsTextLength = showUnits ? 2 : 0;
  102. int signTextLength = sgn == -1 ? 1 : 0;
  103. if (limitToNDigits > 0 && value > float.Epsilon)
  104. {
  105. int numberOfSignificantFigures = limitToNDigits;
  106. if (!showFullValueWhenBelowZero)
  107. numberOfSignificantFigures -= unitsTextLength + signTextLength;
  108. int valueExp = (int)Math.Log10(value);
  109. // Less than 1 values needs exponent correction as (int) rounds to the upper negative.
  110. if (value < 1)
  111. valueExp -= 1;
  112. int originalUnitPower = unitPower;
  113. float limitRange = (float)Math.Pow(10, numberOfSignificantFigures);
  114. if (limitRange > 0)
  115. {
  116. if (value >= limitRange)
  117. {
  118. while (value >= 1000f && unitPower < 9)
  119. {
  120. value /= 1000f;
  121. unitPower += 3;
  122. valueExp -= 3;
  123. }
  124. }
  125. else if (showFullValueWhenBelowZero) // Only upscale and change unit type if we want to see exact number.
  126. {
  127. while (value < 0.01f && unitPower > -9)
  128. {
  129. value *= 1000f;
  130. unitPower -= 3;
  131. valueExp += 3;
  132. }
  133. }
  134. }
  135. if (unitPower != originalUnitPower)
  136. {
  137. showUnits = true;
  138. unitsTextLength = 2;
  139. numberOfSignificantFigures = limitToNDigits;
  140. if (!showFullValueWhenBelowZero)
  141. numberOfSignificantFigures -= unitsTextLength + signTextLength;
  142. }
  143. // Use all allowed digits to display significant digits if we have any beyond maxDecimalPlaces
  144. int numberOfDigitsBeforeDecimalPoint = 1 + Math.Max(0, valueExp);
  145. if (showFullValueWhenBelowZero)
  146. {
  147. numberOfDecimalPlaces = numberOfSignificantFigures - numberOfDigitsBeforeDecimalPoint;
  148. minNumberStringLength = numberOfDigitsBeforeDecimalPoint + signTextLength + maxDecimalPlaces + 1;
  149. }
  150. else
  151. numberOfDecimalPlaces = ClampToRange(numberOfSignificantFigures - numberOfDigitsBeforeDecimalPoint, 0, maxDecimalPlaces);
  152. }
  153. value *= sgn;
  154. string numberStr;
  155. if (value < minValueShownWhenUsingLimitedDecimalPlaces && showFullValueWhenBelowZero)
  156. {
  157. numberStr = string.Format("{0}", value);
  158. }
  159. else
  160. {
  161. string formatString = string.Concat("{0:f", numberOfDecimalPlaces, "}");
  162. numberStr = string.Format(formatString, value);
  163. }
  164. // Remove trailing 0 if any from string
  165. if (minNumberStringLength > 0 && numberStr.Length > 0)
  166. numberStr = RemoveTrailingZeros(numberStr, minNumberStringLength);
  167. if (!showUnits)
  168. return numberStr;
  169. string siUnitString = GetSIUnitString(unitPower) + "s";
  170. return string.Concat(numberStr, siUnitString);
  171. }
  172. public static string GetSIUnitString(int unitPower)
  173. {
  174. // https://en.wikipedia.org/wiki/Metric_prefix
  175. switch (unitPower)
  176. {
  177. case -9:
  178. return "n";
  179. case -6:
  180. return "u";
  181. case -3:
  182. return "m";
  183. case 0:
  184. return "";
  185. case 3:
  186. return "k";
  187. case 6:
  188. return "M";
  189. case 9:
  190. return "G";
  191. }
  192. return "?";
  193. }
  194. public string ToTooltipString(double ms, bool showUnits, int frameIndex = -1)
  195. {
  196. if (frameIndex >= 0)
  197. return string.Format("{0} on frame {1}", ToString((float)ms, showUnits, kTooltipDigitsNumber, kShowFullValueWhenBelowZero), frameIndex);
  198. return ToString((float)ms, showUnits, kTooltipDigitsNumber, kShowFullValueWhenBelowZero);
  199. }
  200. public GUIContent ToGUIContentWithTooltips(float ms, bool showUnits = false, int limitToNDigits = 5, int frameIndex = -1)
  201. {
  202. return new GUIContent(ToString(ms, showUnits, limitToNDigits), ToTooltipString(ms, true, frameIndex));
  203. }
  204. }
  205. }