// Counts the number of frames that hit each frame time bucket, and works out // the percentages that hit 90,75,60, and worse fps. public void AnalyseData(ProfileDataset.SampledFunction root) { m_FrametimeHistogram = new int[m_NumBuckets]; float[] limits = Enumerable.Range(0, m_NumBuckets).Select(x => 10f + x * m_BucketSize).ToArray(); limits[m_NumBuckets - 1] = float.MaxValue; foreach (var frame in root.frameData.Values) { for (int i = 0; i < m_NumBuckets; ++i) { if (frame.totalMilliseconds <= limits[i]) { m_FrametimeHistogram[i]++; break; } } } m_HistogramMax = m_FrametimeHistogram.Max(); float percentScale = 100.0f / root.frameData.Count; m_90fps = m_FrametimeHistogram.Take(6).Sum() * percentScale; m_75fps = m_FrametimeHistogram.Take(12).Sum() * percentScale; m_60fps = m_FrametimeHistogram.Take(21).Sum() * percentScale; m_under60fps = m_FrametimeHistogram.Skip(21).Sum() * percentScale; }
/// Sorts the children of a function, depending on the sorting settings. private void EnsureSorted(ProfileDataset.SampledFunction fn) { // If this function is already sorted correctly, early out. if (fn.sortIndex == m_sortColumn && fn.sortAscending == m_sortAscending) { return; } switch (m_sortColumn) { case 0: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.id).ToList(); break; case 1: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.name).ToList(); break; case 2: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.frameData.Count).ToList(); break; case 3: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.mean).ToList(); break; case 4: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.median).ToList(); break; case 5: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.min).ToList(); break; case 6: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.max).ToList(); break; case 7: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.medianStdDev).ToList(); break; case 8: fn.sortedChildren = fn.sortedChildren.OrderBy(x => x.medianStdDevPc).ToList(); break; } if (!m_sortAscending) { fn.sortedChildren.Reverse(); } }
/// Renders the data in a table. /// Sorting is triggered just-in time; each visible list of samples is asked to make sure /// they're sorted whenever they are displayed. private void RenderData() { // Draw the header. int[] intWidths = { 50, 400, 100, 100, 100, 100, 100, 120, 120 }; string[] names = { "id", "Name", "Frame Count", "Average ms", "Median ms", "Min ms", "Max ms", "Median StdDev", "Median StdDev %", }; GUILayoutOption[] widths = intWidths.Select(x => GUILayout.Width(x)).ToArray(); if (m_Data == null || m_Data.Root == null || m_Data.Root.children == null) { return; } GUILayout.BeginVertical(); Vector2 headerScroll = m_Scroll; headerScroll.y = 0; GUILayout.BeginScrollView(headerScroll, GUIStyle.none, GUIStyle.none); GUILayout.BeginHorizontal(); for (int i = 0; i < names.Length; ++i) { // Creates a header titles, with the a marker on the sorting index. string marker = (i == m_sortColumn) ? (m_sortAscending ? " ^" : " v") : ""; string headerTitle = names[i] + marker; // Change sorting parameters. if (GUILayout.Button(headerTitle, widths[i])) { if (i == m_sortColumn) { m_sortAscending = !m_sortAscending; } else { m_sortColumn = i; } } } GUILayout.EndHorizontal(); GUILayout.EndScrollView(); // Draw the rest of the data. m_Scroll = GUILayout.BeginScrollView(m_Scroll); // Styles for the name section - clipping has to be turned off. GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout); foldoutStyle.clipping = TextClipping.Clip; GUIStyle labelStyle = new GUIStyle(foldoutStyle); labelStyle.imagePosition = ImagePosition.TextOnly; labelStyle.normal.background = EditorStyles.label.normal.background; // Alternate background styles are used for each line to make the table easier to read. GUIStyle[] lineStyles = new GUIStyle[2]; lineStyles[0] = new GUIStyle(TreeView.DefaultStyles.backgroundEven); lineStyles[1] = new GUIStyle(TreeView.DefaultStyles.backgroundOdd); lineStyles[0].border = new RectOffset(3, 3, 3, 3); lineStyles[1].border = new RectOffset(3, 3, 3, 3); lineStyles[0].margin = new RectOffset(3, 3, 3, 3); lineStyles[1].margin = new RectOffset(3, 3, 3, 3); // Make sure the children of the root are sorted. EnsureSorted(m_Data.Root); // Iterate through all children, and their open children - uses a stack instead of recursion. var stack = new Stack <IEnumerator <ProfileDataset.SampledFunction> >(); stack.Push(m_Data.Root.sortedChildren.GetEnumerator()); // used to work out which color line background to use. int lineIndex = 0; while (true) { // Get the next child item, or grab the next enumerator off the stack. ProfileDataset.SampledFunction child = null; while (true) { if (stack.Peek().MoveNext()) { child = stack.Peek().Current; break; } stack.Pop(); if (!stack.Any()) { break; } } if (child == null) { break; } // Make sure the children are sorted, just-in-time EnsureSorted(child); GUILayout.BeginHorizontal(lineStyles[lineIndex++ % 2]); int columnIndex = 0; GUILayout.Label(child.id.ToString(), widths[columnIndex++]); int indent = (child.depth - 1) * 10; foldoutStyle.fixedWidth = intWidths[columnIndex] - indent; labelStyle.fixedWidth = foldoutStyle.fixedWidth; GUILayout.Space(indent); if (child.sortedChildren.Any()) { // A hashset is used to store whether an item with children is expanded. bool wasOpen = m_Expanded.Contains(child.id); bool toggle = GUILayout.Button(child.name, foldoutStyle); if (toggle) { if (wasOpen) { m_Expanded.Remove(child.id); } else { m_Expanded.Add(child.id); } } if (wasOpen) { stack.Push(child.sortedChildren.GetEnumerator()); } } else { GUILayout.Label(child.name, labelStyle); } columnIndex++; GUILayout.Label(child.frameData.Count.ToString(), widths[columnIndex++]); GUILayout.Label(child.mean.ToString("F3"), widths[columnIndex++]); GUILayout.Label(child.median.ToString("F3"), widths[columnIndex++]); GUILayout.Label(child.min.ToString("F3"), widths[columnIndex++]); GUILayout.Label(child.max.ToString("F3"), widths[columnIndex++]); GUILayout.Label(child.medianStdDev.ToString("F3"), widths[columnIndex++]); GUILayout.Label(child.medianStdDevPc.ToString("F0"), widths[columnIndex++]); GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); }