Search Unity

FpsToText :: Free FPS / Framerate calculator with options

Discussion in 'Scripting' started by LaneFox, Mar 30, 2017.

  1. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    I was curious about options related to framerate reporting and interested in making a script with a tiny footprint and lots of options so I whipped one up.

    Features
    • Target UI.Text components
    • Sample lots of frames
    • Sample the current frame
    • Alternate between smoothDeltaTime and deltaTime
    • Apply thresholds to change text color if values fall below them
    • Specify the max number of characters in the resulting string.
    • Specify the update frequency (frames)
    Note: This reports fps for the current environment, so the build is accurate, but in the editor it includes time spent rendering the entire editor. You will see lower fps in the Editor but the stats window will show a higher framerate since it supposedly does not include time spent on Editor stuff.

    The Sample Size is how many frames to average across. You won't see a value until this many frames has pass to populate valid data. If you just want the last frame's data, turn off Group Sampling.

    fps.png

    fps2.png

    Code (csharp):
    1.  
    2. using System.Globalization;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. namespace Performance
    7. {
    8.     /// <summary>
    9.     /// <para>Pushes the Framerate value to a Text component.</para>
    10.     /// </summary>
    11.     public class FpsToText : MonoBehaviour
    12.     {
    13.         [Header("// Sample Groups of Data ")]
    14.         public bool GroupSampling = true;
    15.         public int SampleSize = 20;
    16.  
    17.         [Header("// Config ")]
    18.         public Text TargetText;
    19.         public int UpdateTextEvery = 1;
    20.         public int MaxTextLength = 5;
    21.         public bool Smoothed = true;
    22.         public bool ForceIntResult = true;
    23.  
    24.         [Header("// System FPS (updates once/sec)")]
    25.         public bool UseSystemTick = false;
    26.  
    27.         [Header("// Color Config ")]
    28.         public bool UseColors = true;
    29.         public Color Good = Color.green;
    30.         public Color Okay = Color.yellow;
    31.         public Color Bad = Color.red;
    32.         public int OkayBelow = 60;
    33.         public int BadBelow = 30;
    34.  
    35.         public float Framerate { get { return _fps; } }
    36.  
    37.         protected float[] FpsSamples;
    38.         protected int SampleIndex;
    39.         protected int TextUpdateIndex;
    40.         private float _fps;
    41.  
    42.         private int _sysLastSysTick;
    43.         private int _sysLastFrameRate;
    44.         private int _sysFrameRate;
    45.  
    46.         protected virtual void Reset()
    47.         {
    48.             SampleSize = 20;
    49.             UpdateTextEvery = 1;
    50.             MaxTextLength = 5;
    51.             Smoothed = true;
    52.             UseColors = true;
    53.             Good = Color.green;
    54.             Okay = Color.yellow;
    55.             Bad = Color.red;
    56.             OkayBelow = 60;
    57.             BadBelow = 30;
    58.             UseSystemTick = false;
    59.             ForceIntResult = true;
    60.         }
    61.  
    62.         protected virtual void Start()
    63.         {
    64.             FpsSamples = new float[SampleSize];
    65.             for (int i = 0; i < FpsSamples.Length; i++) FpsSamples[i] = 0.001f;
    66.             if (!TargetText) enabled = false;
    67.         }
    68.  
    69.         protected virtual void Update()
    70.         {
    71.             if (GroupSampling) Group();
    72.             else SingleFrame();
    73.  
    74.             string fps = _fps.ToString(CultureInfo.CurrentCulture);
    75.  
    76.             SampleIndex = SampleIndex < SampleSize-1 ? SampleIndex + 1 : 0;
    77.             TextUpdateIndex = TextUpdateIndex > UpdateTextEvery ? 0 : TextUpdateIndex + 1;
    78.             if (TextUpdateIndex == UpdateTextEvery) TargetText.text = fps.Substring(0, fps.Length < 5 ? fps.Length : 5);
    79.  
    80.             if (!UseColors) return;
    81.             if (_fps < BadBelow)
    82.             {
    83.                 TargetText.color = Bad;
    84.                 return;
    85.             }
    86.             TargetText.color = _fps < OkayBelow ? Okay : Good;
    87.         }
    88.  
    89.         protected virtual void SingleFrame()
    90.         {
    91.             _fps = UseSystemTick
    92.                 ? GetSystemFramerate()
    93.                 : Smoothed ? 1 / Time.smoothDeltaTime : 1 / Time.deltaTime;
    94.             if (ForceIntResult) _fps = (int) _fps;
    95.         }
    96.  
    97.         protected virtual void Group()
    98.         {
    99.             FpsSamples[SampleIndex] = UseSystemTick
    100.                 ? GetSystemFramerate()
    101.                 : Smoothed ? 1 / Time.smoothDeltaTime : 1 / Time.deltaTime;
    102.  
    103.             _fps = 0;
    104.             bool loop = true;
    105.             int i = 0;
    106.             while (loop)
    107.             {
    108.                 if (i == SampleSize - 1) loop = false;
    109.                 _fps += FpsSamples[i];
    110.                 i++;
    111.             }
    112.             _fps /= FpsSamples.Length;
    113.             if (ForceIntResult) _fps = (int)_fps;
    114.         }
    115.  
    116.         protected virtual int GetSystemFramerate()
    117.         {
    118.             if (System.Environment.TickCount - _sysLastSysTick >= 1000)
    119.             {
    120.                 _sysLastFrameRate = _sysFrameRate;
    121.                 _sysFrameRate = 0;
    122.                 _sysLastSysTick = System.Environment.TickCount;
    123.             }
    124.             _sysFrameRate++;
    125.             return _sysLastFrameRate;
    126.         }
    127.     }
    128. }
    With Optional Zero Alloc toggle
    Code (csharp):
    1.  
    2. using System.Globalization;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. namespace Performance
    7. {
    8.     /// <summary>
    9.     /// <para>Pushes the Framerate value to a Text component.</para>
    10.     /// </summary>
    11.     public class FpsToText : MonoBehaviour
    12.     {
    13.         [Header("// Sample Groups of Data ")]
    14.         public bool GroupSampling = true;
    15.         public int SampleSize = 20;
    16.  
    17.         [Header("// Config ")]
    18.         public Text TargetText;
    19.         public int UpdateTextEvery = 1;
    20.         public int MaxTextLength = 5;
    21.         public bool Smoothed = true;
    22.         public bool ForceIntResult = true;
    23.         public bool ZeroAllocOnly = false;
    24.  
    25.         [Header("// System FPS (updates once/sec)")]
    26.         public bool UseSystemTick = false;
    27.  
    28.         [Header("// Color Config ")]
    29.         public bool UseColors = true;
    30.         public Color Good = Color.green;
    31.         public Color Okay = Color.yellow;
    32.         public Color Bad = Color.red;
    33.         public int OkayBelow = 60;
    34.         public int BadBelow = 30;
    35.  
    36.         public float Framerate { get { return _fps; } }
    37.  
    38.         protected float[] FpsSamples;
    39.         protected int SampleIndex;
    40.         protected int TextUpdateIndex;
    41.         private float _fps;
    42.  
    43.         private int _sysLastSysTick;
    44.         private int _sysLastFrameRate;
    45.         private int _sysFrameRate;
    46.         private string _localfps;
    47.  
    48.         private static readonly string[] FpsStringMap = {
    49.             "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
    50.             "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
    51.             "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
    52.             "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
    53.             "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
    54.             "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
    55.             "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
    56.             "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
    57.             "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
    58.             "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
    59.             "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
    60.             "110", "111", "122", "113", "114", "115", "116", "117", "118", "119",
    61.             "120", "121", "132", "123", "124", "125", "126", "127", "128", "129",
    62.             "130", "131", "142", "133", "134", "135", "136", "137", "138", "139",
    63.             "140", "141", "152", "143", "144", "145", "146", "147", "148", "149",
    64.             "150", "151", "162", "153", "154", "155", "156", "157", "158", "159",
    65.             "160", "161", "172", "163", "164", "165", "166", "167", "168", "169",
    66.             "170", "171", "182", "173", "174", "175", "176", "177", "178", "179",
    67.             "180", "181", "192", "183", "184", "185", "186", "187", "188", "189",
    68.             "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
    69.             "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
    70.             "210", "211", "222", "213", "214", "215", "216", "217", "218", "219",
    71.             "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
    72.             "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
    73.             "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
    74.             "250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
    75.             "260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
    76.             "270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
    77.             "280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
    78.             "290", "291", "292", "293", "294", "295", "296", "297", "298", "299+"
    79.         };
    80.  
    81.         protected virtual void Reset()
    82.         {
    83.             SampleSize = 20;
    84.             UpdateTextEvery = 1;
    85.             MaxTextLength = 5;
    86.             Smoothed = true;
    87.             UseColors = true;
    88.             Good = Color.green;
    89.             Okay = Color.yellow;
    90.             Bad = Color.red;
    91.             OkayBelow = 60;
    92.             BadBelow = 30;
    93.             UseSystemTick = false;
    94.             ForceIntResult = true;
    95.         }
    96.  
    97.         protected virtual void Start()
    98.         {
    99.             FpsSamples = new float[SampleSize];
    100.             for (int i = 0; i < FpsSamples.Length; i++) FpsSamples[i] = 0.001f;
    101.             if (!TargetText) enabled = false;
    102.         }
    103.  
    104.         protected virtual void Update()
    105.         {
    106.             if (GroupSampling) Group();
    107.             else SingleFrame();
    108.  
    109.             _localfps = ZeroAllocOnly ? FpsStringMap[Mathf.Clamp((int)_fps, 0, 299)] : _fps.ToString(CultureInfo.CurrentCulture);
    110.  
    111.             SampleIndex = SampleIndex < SampleSize-1 ? SampleIndex + 1 : 0;
    112.             TextUpdateIndex = TextUpdateIndex > UpdateTextEvery ? 0 : TextUpdateIndex + 1;
    113.             if (TextUpdateIndex == UpdateTextEvery) TargetText.text = _localfps.Substring(0, _localfps.Length < 5 ? _localfps.Length : 5);
    114.  
    115.             if (!UseColors) return;
    116.             if (_fps < BadBelow)
    117.             {
    118.                 TargetText.color = Bad;
    119.                 return;
    120.             }
    121.             TargetText.color = _fps < OkayBelow ? Okay : Good;
    122.         }
    123.  
    124.         protected virtual void SingleFrame()
    125.         {
    126.             _fps = UseSystemTick
    127.                 ? GetSystemFramerate()
    128.                 : Smoothed ? 1 / Time.smoothDeltaTime : 1 / Time.deltaTime;
    129.             if (ForceIntResult) _fps = (int) _fps;
    130.         }
    131.  
    132.         protected virtual void Group()
    133.         {
    134.             FpsSamples[SampleIndex] = UseSystemTick
    135.                 ? GetSystemFramerate()
    136.                 : Smoothed ? 1 / Time.smoothDeltaTime : 1 / Time.deltaTime;
    137.  
    138.             _fps = 0;
    139.             bool loop = true;
    140.             int i = 0;
    141.             while (loop)
    142.             {
    143.                 if (i == SampleSize - 1) loop = false;
    144.                 _fps += FpsSamples[i];
    145.                 i++;
    146.             }
    147.             _fps /= FpsSamples.Length;
    148.             if (ForceIntResult) _fps = (int)_fps;
    149.         }
    150.  
    151.         protected virtual int GetSystemFramerate()
    152.         {
    153.             if (System.Environment.TickCount - _sysLastSysTick >= 1000)
    154.             {
    155.                 _sysLastFrameRate = _sysFrameRate;
    156.                 _sysFrameRate = 0;
    157.                 _sysLastSysTick = System.Environment.TickCount;
    158.             }
    159.             _sysFrameRate++;
    160.             return _sysLastFrameRate;
    161.         }
    162.     }
    163. }
    Have fun!
     
    Last edited: Dec 15, 2017
    wlwl2, Zooltan, Vedrit and 1 other person like this.
  2. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    This looks like a really cool script and want to try it out but it gives me 2 errors:

    Untitled.png
     
    LaneFox likes this.
  3. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    I'll definitely be wanting this.
    As for the errors, the first one is simple enough (If I understand what is being done, which I think I do)
    Code (csharp):
    1. for (int i = 0; i < FpsSamples.Length; i++) FpsSamples[i] = 0.001f;
    I'm assuming we're resetting the values in the array.
    I -think- it's something similar on line 93 (94 in the provided script)
    Code (csharp):
    1. _fps += FpsSamples[i];
    But I'm not sure about that one.
     
    LaneFox likes this.
  4. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    Ahhh I know what happened, I edited the post and screwed the code brackets up, so the {i} registered as a UBB italic box and disappeared.

    Anyway, I fixed the OP - thanks for pointing it out.
     
  5. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    Added a toggle to read the System Ticks instead of the Time class. Seems basically identical, but I was curious.
     
  6. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    Added a version with zero runtime alloc by using a string map containing numbers and pointing the fps to that as an index, thus bypassing the .ToString() method.

    I might have a problem...
     
    wlwl2 and TooManySugar like this.
  7. andiCR

    andiCR

    Joined:
    Sep 6, 2012
    Posts:
    27
    Some time after this post, but wanted to thank you. Specially the zero runtime alloc, genius haha
     
    LaneFox likes this.
  8. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    Here's a small update to this. Removed some unused things and made zero-alloc the default. Instead of looking to a pre-built string array it just creates the string array at startup based on how many values you want to allocate, then clamps the results inside those values.

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. namespace Cleverous.Shared
    5. {
    6.     /// <summary>
    7.     /// <para>Pushes the Framerate value to a Text component.</para>
    8.     /// </summary>
    9.     public class FpsToText : MonoBehaviour
    10.     {
    11.         [Header("// Sample Groups of Data ")]
    12.         [Tooltip("Instead of reporting a specific framerate, this will sample many frames and report the average.")]
    13.         public bool GroupSampling;
    14.  
    15.         [Tooltip("If Group Sampling is on, how many frames would you want to sample to report an average on?")]
    16.         [Range(0, 20)]
    17.         public int SampleSize;
    18.  
    19.         [Header("// Config ")]
    20.         [Tooltip("The Text Component you want the result pushed to. If you're using TMP then you need to change this in the code to a TMP_Text component instead.")]
    21.         public Text TargetText;
    22.  
    23.         [Tooltip("How often (in frames) do you want to update the Text component?")]
    24.         [Range(1, 20)]
    25.         public int UpdateTextEvery;
    26.  
    27.         [Tooltip("This will smooth out the results so they blend together between updates and are easier to read.")]
    28.         public bool Smoothed;
    29.  
    30.         [Tooltip("This sets how many numbers are buffered into memory as strings in order to obtain zero allocations at runtime.\n\nAlthough this is trivial in memory usage, realistically, there's no reason to be over 1000.")]
    31.         [Range(0, 1000)]
    32.         public int NumberBufferSize;
    33.  
    34.         [Header("// System FPS (updates once/sec)")]
    35.         [Tooltip("Would you like to read the System Tick instead of calculating it in this script?\n\nTests show that differences are negligible, but the option remains available to you.")]
    36.         public bool UseSystemTick;
    37.  
    38.         [Header("// Color Config ")]
    39.         [Tooltip("Optionally change the color of the TargetText based on FPS performance.")]
    40.         public bool UseColors;
    41.         [Tooltip("If the framerate is above 'OkayBelow' it will be the 'Good' color.")]
    42.         public Color Good;
    43.         [Tooltip("If the framerate is below 'OkayBelow' it will be the 'Okay' color.")]
    44.         public Color Okay;
    45.         [Tooltip("If the framerate is below 'BadBelow' it will be the 'Bad' color.")]
    46.         public Color Bad;
    47.         [Tooltip("Threshold for defining an 'okay' framerate. Below this value is considered okay, but not high enough to be good, and not low enough to be bad.")]
    48.         public int OkayBelow;
    49.         [Tooltip("Threshold for defining an 'bad' framerate. Below this value is considered bad.")]
    50.         public int BadBelow;
    51.  
    52.         public int Framerate { get; private set; }
    53.  
    54.         protected int[] FpsSamples;
    55.         protected int SampleIndex;
    56.         protected int TextUpdateIndex;
    57.  
    58.         private int m_sysLastSysTick;
    59.         private int m_sysLastFrameRate;
    60.         private int m_sysFrameRate;
    61.         private string m_localfps;
    62.  
    63.         private static string[] m_numbers;
    64.  
    65.         protected virtual void Reset()
    66.         {
    67.             GroupSampling = true;
    68.             SampleSize = 20;
    69.             UpdateTextEvery = 1;
    70.             Smoothed = true;
    71.             UseColors = true;
    72.  
    73.             Good = Color.green;
    74.             Okay = Color.yellow;
    75.             Bad = Color.red;
    76.  
    77.             OkayBelow = 60;
    78.             BadBelow = 30;
    79.  
    80.             UseSystemTick = false;
    81.             NumberBufferSize = 1000;
    82.         }
    83.  
    84.         protected virtual void Start()
    85.         {
    86.             m_numbers = new string[NumberBufferSize];
    87.             for (int i = 0; i < NumberBufferSize; i++) m_numbers[i] = i.ToString();
    88.  
    89.             FpsSamples = new int[SampleSize];
    90.             for (int i = 0; i < FpsSamples.Length; i++) FpsSamples[i] = 1;
    91.             if (!TargetText) enabled = false;
    92.         }
    93.  
    94.         protected virtual void Update()
    95.         {
    96.             if (GroupSampling) Group();
    97.             else SingleFrame();
    98.  
    99.             m_localfps = m_numbers[Framerate];
    100.  
    101.             SampleIndex = SampleIndex < SampleSize-1 ? SampleIndex + 1 : 0;
    102.             TextUpdateIndex = TextUpdateIndex > UpdateTextEvery ? 0 : TextUpdateIndex + 1;
    103.             if (TextUpdateIndex == UpdateTextEvery) TargetText.text = m_localfps;
    104.  
    105.             if (!UseColors) return;
    106.             if (Framerate < BadBelow)
    107.             {
    108.                 TargetText.color = Bad;
    109.                 return;
    110.             }
    111.             TargetText.color = Framerate < OkayBelow ? Okay : Good;
    112.         }
    113.  
    114.         protected virtual void SingleFrame()
    115.         {
    116.             Framerate = Mathf.Clamp(UseSystemTick
    117.                 ? GetSystemFramerate()
    118.                 : (int)(Smoothed
    119.                     ? 1 / Time.smoothDeltaTime
    120.                     : 1 / Time.deltaTime),
    121.                 0,
    122.                 m_numbers.Length-1);
    123.         }
    124.  
    125.         protected virtual void Group()
    126.         {
    127.             FpsSamples[SampleIndex] = Mathf.Clamp(UseSystemTick
    128.                 ? GetSystemFramerate()
    129.                 : (int)(Smoothed
    130.                     ? 1 / Time.smoothDeltaTime
    131.                     : 1 / Time.deltaTime),
    132.                 0,
    133.                 m_numbers.Length-1);
    134.  
    135.             Framerate = 0;
    136.             bool loop = true;
    137.             int i = 0;
    138.             while (loop)
    139.             {
    140.                 if (i == SampleSize - 1) loop = false;
    141.                 Framerate += FpsSamples[i];
    142.                 i++;
    143.             }
    144.             Framerate /= FpsSamples.Length;
    145.         }
    146.  
    147.         protected virtual int GetSystemFramerate()
    148.         {
    149.             if (System.Environment.TickCount - m_sysLastSysTick >= 1000)
    150.             {
    151.                 m_sysLastFrameRate = m_sysFrameRate;
    152.                 m_sysFrameRate = 0;
    153.                 m_sysLastSysTick = System.Environment.TickCount;
    154.             }
    155.             m_sysFrameRate++;
    156.             return m_sysLastFrameRate;
    157.         }
    158.     }
    159. }
     
    gameDevMode and Stardog like this.
  9. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519