| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- //#define LOGGING
- #if UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1
- #define LEGACY_UI
- #endif
- namespace SRDebugger.UI.Controls
- {
- using System.Collections.Generic;
- using Services;
- using SRF;
- using SRF.Service;
- using UnityEngine;
- using UnityEngine.UI;
- [ExecuteInEditMode]
- [RequireComponent(typeof (RectTransform))]
- [RequireComponent(typeof (CanvasRenderer))]
- public class ProfilerGraphControl : Graphic
- {
- public enum VerticalAlignments
- {
- Top,
- Bottom
- }
- public VerticalAlignments VerticalAlignment = VerticalAlignments.Bottom;
- private static readonly float[] ScaleSteps =
- {
- 1f/200f,
- 1f/160f,
- 1f/120f,
- 1f/100f,
- 1f/60f,
- 1f/30f,
- 1f/20f,
- 1f/12f,
- 1f/6f
- };
- /// <summary>
- /// Resize the y-axis to fit the nearest useful fps value
- /// </summary>
- public bool FloatingScale;
- /// <summary>
- /// If not using FloatingScale, use the target FPS set by Application.targetFrameRate for TargetFps
- /// </summary>
- public bool TargetFpsUseApplication;
- /// <summary>
- /// Toggle drawing of the various axes
- /// </summary>
- public bool DrawAxes = true;
- /// <summary>
- /// If FloatingScale is disabled, use this value to determine y-axis
- /// </summary>
- public int TargetFps = 60;
- public bool Clip = true;
- public const float DataPointMargin = 2f;
- public const float DataPointVerticalMargin = 2f;
- public const float DataPointWidth = 4f;
- public int VerticalPadding = 10;
- public const int LineCount = 3;
- public Color[] LineColours = new Color[0];
- private IProfilerService _profilerService;
- private ProfilerGraphAxisLabel[] _axisLabels;
- private Rect _clipBounds;
- #if LEGACY_UI
- private List<UIVertex> _vbo;
- #else
- private readonly List<Vector3> _meshVertices = new List<Vector3>();
- private readonly List<Color32> _meshVertexColors = new List<Color32>();
- private readonly List<int> _meshTriangles = new List<int>();
- #endif
- protected override void Awake()
- {
- base.Awake();
- _profilerService = SRServiceManager.GetService<IProfilerService>();
- }
- protected override void Start()
- {
- base.Start();
- }
- protected void Update()
- {
- SetVerticesDirty();
- }
- #if LEGACY_UI
- protected override void OnFillVBO(List<UIVertex> vbo)
- #else
- [System.ObsoleteAttribute]
- protected override void OnPopulateMesh(Mesh m)
- #endif
- {
- #if LEGACY_UI
- _vbo = vbo;
- #else
- _meshVertices.Clear();
- _meshVertexColors.Clear();
- _meshTriangles.Clear();
- #endif
- #if LOGGING
- if(!Application.isPlaying)
- Debug.Log("Draw");
- #endif
- var graphWidth = rectTransform.rect.width;
- var graphHeight = rectTransform.rect.height;
- _clipBounds = new Rect(0, 0, graphWidth, graphHeight);
- var targetFps = TargetFps;
- if (Application.isPlaying && TargetFpsUseApplication && Application.targetFrameRate > 0)
- {
- targetFps = Application.targetFrameRate;
- }
- var maxValue = 1f/targetFps;
- // Holds the index of the nearest 'useful' FPS step
- var fpsStep = -1;
- var maxFrameTime = FloatingScale ? CalculateMaxFrameTime() : 1f/targetFps;
- if (FloatingScale)
- {
- for (var i = 0; i < ScaleSteps.Length; i++)
- {
- var step = ScaleSteps[i];
- if (maxFrameTime < step*1.1f)
- {
- maxValue = step;
- fpsStep = i;
- break;
- }
- }
- // Fall back on the largest one
- if (fpsStep < 0)
- {
- fpsStep = ScaleSteps.Length - 1;
- maxValue = ScaleSteps[fpsStep];
- }
- }
- else
- {
- // Search for the next scale step after the user-provided step
- for (var i = 0; i < ScaleSteps.Length; i++)
- {
- var step = ScaleSteps[i];
- if (maxFrameTime > step)
- {
- fpsStep = i;
- }
- }
- }
- var verticalScale = (graphHeight - (VerticalPadding*2))/maxValue;
- // Number of data points that can fit into the graph space
- var availableDataPoints = CalculateVisibleDataPointCount();
- // Reallocate vertex array if insufficient length (or not yet created)
- var sampleCount = GetFrameBufferCurrentSize();
- for (var i = 0; i < sampleCount; i++)
- {
- // Break loop if all visible data points have been drawn
- if (i >= availableDataPoints)
- {
- break;
- }
- // When using right-alignment, read from the end of the profiler buffer
- var frame = GetFrame(sampleCount - i - 1);
- // Left-hand x coord
- var lx = graphWidth - DataPointWidth*i - DataPointWidth - graphWidth/2f;
- DrawDataPoint(lx, verticalScale, frame);
- }
- if (DrawAxes)
- {
- if (!FloatingScale)
- {
- DrawAxis(maxValue, maxValue*verticalScale, GetAxisLabel(0));
- }
- var axisCount = 2;
- var j = 0;
- if (!FloatingScale)
- {
- j++;
- }
- for (var i = fpsStep; i >= 0; --i)
- {
- if (j >= axisCount)
- {
- break;
- }
- DrawAxis(ScaleSteps[i], ScaleSteps[i]*verticalScale, GetAxisLabel(j));
- ++j;
- }
- }
- #if !LEGACY_UI
- m.Clear();
- m.SetVertices(_meshVertices);
- m.SetColors(_meshVertexColors);
- m.SetTriangles(_meshTriangles, 0);
- #endif
- }
- protected void DrawDataPoint(float xPosition, float verticalScale, ProfilerFrame frame)
- {
- // Right-hand x-coord
- var rx = Mathf.Min(_clipBounds.width/2f, xPosition + DataPointWidth - DataPointMargin);
- var currentLineHeight = 0f;
- for (var j = 0; j < LineCount; j++)
- {
- var lineIndex = j;
- var value = 0f;
- if (j == 0)
- {
- value = (float) frame.UpdateTime;
- }
- else if (j == 1)
- {
- value = (float) frame.RenderTime;
- }
- else if (j == 2)
- {
- value = (float) frame.OtherTime;
- }
- value *= verticalScale;
- if (value.ApproxZero() || value - DataPointVerticalMargin*2f < 0f)
- {
- continue;
- }
- // Lower y-coord
- var ly = currentLineHeight + DataPointVerticalMargin - rectTransform.rect.height/2f;
- if (VerticalAlignment == VerticalAlignments.Top)
- {
- ly = rectTransform.rect.height/2f - currentLineHeight - DataPointVerticalMargin;
- }
- // Upper y-coord
- var uy = ly + value - DataPointVerticalMargin;
- if (VerticalAlignment == VerticalAlignments.Top)
- {
- uy = ly - value + DataPointVerticalMargin;
- }
- var c = LineColours[lineIndex];
- AddRect(new Vector3(Mathf.Max(-_clipBounds.width/2f, xPosition), ly),
- new Vector3(Mathf.Max(-_clipBounds.width/2f, xPosition), uy), new Vector3(rx, uy),
- new Vector3(rx, ly), c);
- currentLineHeight += value;
- }
- }
- protected void DrawAxis(float frameTime, float yPosition, ProfilerGraphAxisLabel label)
- {
- #if LOGGING
- if(!Application.isPlaying)
- Debug.Log("Draw Axis: {0}".Fmt(yPosition));
- #endif
- var lx = -rectTransform.rect.width*0.5f;
- var rx = -lx;
- var uy = yPosition - rectTransform.rect.height*0.5f + 0.5f;
- var ly = yPosition - rectTransform.rect.height*0.5f - 0.5f;
- var c = new Color(1f, 1f, 1f, 0.4f);
- AddRect(new Vector3(lx, ly), new Vector3(lx, uy), new Vector3(rx, uy), new Vector3(rx, ly), c);
- if (label != null)
- {
- label.SetValue(frameTime, yPosition);
- }
- }
- protected void AddRect(Vector3 tl, Vector3 tr, Vector3 bl, Vector3 br, Color c)
- {
- #if LEGACY_UI
- var v = UIVertex.simpleVert;
- v.color = c;
- v.position = tl;
- _vbo.Add(v);
- v.position = tr;
- _vbo.Add(v);
- v.position = bl;
- _vbo.Add(v);
- v.position = br;
- _vbo.Add(v);
- #else
- // New UI system uses triangles
- _meshVertices.Add(tl);
- _meshVertices.Add(tr);
- _meshVertices.Add(bl);
- _meshVertices.Add(br);
- _meshTriangles.Add(_meshVertices.Count - 4); // tl
- _meshTriangles.Add(_meshVertices.Count - 3); // tr
- _meshTriangles.Add(_meshVertices.Count - 1); // br
- _meshTriangles.Add(_meshVertices.Count - 2); // bl
- _meshTriangles.Add(_meshVertices.Count - 1); // br
- _meshTriangles.Add(_meshVertices.Count - 3); // tr
- _meshVertexColors.Add(c);
- _meshVertexColors.Add(c);
- _meshVertexColors.Add(c);
- _meshVertexColors.Add(c);
- #endif
- }
- protected ProfilerFrame GetFrame(int i)
- {
- #if UNITY_EDITOR
- if (!Application.isPlaying)
- {
- return TestData[i];
- }
- #endif
- return _profilerService.FrameBuffer[i];
- }
- protected int CalculateVisibleDataPointCount()
- {
- return Mathf.RoundToInt(rectTransform.rect.width/DataPointWidth);
- }
- protected int GetFrameBufferCurrentSize()
- {
- #if UNITY_EDITOR
- if (!Application.isPlaying)
- {
- return TestData.Length;
- }
- #endif
- return _profilerService.FrameBuffer.Count;
- }
- protected int GetFrameBufferMaxSize()
- {
- #if UNITY_EDITOR
- if (!Application.isPlaying)
- {
- return TestData.Length;
- }
- #endif
- return _profilerService.FrameBuffer.Capacity;
- }
- protected float CalculateMaxFrameTime()
- {
- var frameCount = GetFrameBufferCurrentSize();
- var c = Mathf.Min(CalculateVisibleDataPointCount(), frameCount);
- var max = 0d;
- for (var i = 0; i < c; i++)
- {
- var frameNumber = frameCount - i - 1;
- var t = GetFrame(frameNumber);
- if (t.FrameTime > max)
- {
- max = t.FrameTime;
- }
- }
- return (float) max;
- }
- private ProfilerGraphAxisLabel GetAxisLabel(int index)
- {
- if (_axisLabels == null || !Application.isPlaying)
- {
- _axisLabels = GetComponentsInChildren<ProfilerGraphAxisLabel>();
- }
- if (_axisLabels.Length > index)
- {
- return _axisLabels[index];
- }
- Debug.LogWarning("[SRDebugger.Profiler] Not enough axis labels in pool");
- return null;
- }
- #region Editor Only test data
- #if UNITY_EDITOR
- private ProfilerFrame[] TestData
- {
- get
- {
- if (_testData == null)
- {
- _testData = GenerateSampleData();
- }
- return _testData;
- }
- }
- private ProfilerFrame[] _testData;
- protected static ProfilerFrame[] GenerateSampleData()
- {
- var sampleCount = 200;
- var data = new ProfilerFrame[sampleCount];
- for (var i = 0; i < sampleCount; i++)
- {
- var frame = new ProfilerFrame();
- for (var j = 0; j < 3; j++)
- {
- var v = 0d;
- if (j == 0)
- {
- v = Mathf.PerlinNoise(i/200f, 0);
- }
- else if (j == 1)
- {
- v = Mathf.PerlinNoise(0, i/200f);
- }
- else
- {
- v = Random.Range(0, 1f);
- }
- v *= (1f/60f)*0.333f;
- // Simulate spikes
- if (Random.value > 0.8f)
- {
- v *= Random.Range(1.2f, 1.8f);
- }
- if (j == 2)
- {
- v *= 0.1f;
- }
- if (j == 0)
- {
- frame.UpdateTime = v;
- }
- else if (j == 1)
- {
- frame.RenderTime = v;
- }
- else if (j == 2)
- {
- frame.FrameTime = frame.RenderTime + frame.UpdateTime + v;
- }
- }
- data[i] = frame;
- }
- data[0] = new ProfilerFrame
- {
- FrameTime = 0.005,
- RenderTime = 0.005,
- UpdateTime = 0.005
- };
- data[sampleCount - 1] = new ProfilerFrame
- {
- FrameTime = 1d/60d,
- RenderTime = 0.007,
- UpdateTime = 0.007
- };
- return data;
- }
- #endif
- #endregion
- }
- }
|