Maze / Library /PackageCache /com.unity.performance.profile-analyzer@1.2.2 /Editor /ProfileAnalysis.cs
| using System; | |
| using System.Collections.Generic; | |
| using UnityEngine; | |
| namespace UnityEditor.Performance.ProfileAnalyzer | |
| { | |
| [] | |
| internal class ProfileAnalysis | |
| { | |
| FrameSummary m_FrameSummary = new FrameSummary(); | |
| List<MarkerData> m_Markers = new List<MarkerData>(); | |
| List<ThreadData> m_Threads = new List<ThreadData>(); | |
| public ProfileAnalysis() | |
| { | |
| m_FrameSummary.first = 0; | |
| m_FrameSummary.last = 0; | |
| m_FrameSummary.count = 0; | |
| m_FrameSummary.msTotal = 0.0; | |
| m_FrameSummary.msMin = float.MaxValue; | |
| m_FrameSummary.msMax = 0.0f; | |
| m_FrameSummary.minFrameIndex = 0; | |
| m_FrameSummary.maxFrameIndex = 0; | |
| m_FrameSummary.maxMarkerDepth = 0; | |
| m_FrameSummary.totalMarkers = 0; | |
| m_FrameSummary.markerCountMax = 0; | |
| m_FrameSummary.markerCountMaxMean = 0.0f; | |
| for (int b = 0; b < m_FrameSummary.buckets.Length; b++) | |
| m_FrameSummary.buckets[b] = 0; | |
| m_Markers.Clear(); | |
| m_Threads.Clear(); | |
| } | |
| public void SetRange(int firstFrameIndex, int lastFrameIndex) | |
| { | |
| m_FrameSummary.first = firstFrameIndex; | |
| m_FrameSummary.last = lastFrameIndex; | |
| // Ensure these are initialized to frame indices within the range | |
| m_FrameSummary.minFrameIndex = firstFrameIndex; | |
| // if this wasn't initialized, and all frames had 0 length, it wouldn't be set in the UpdateSummary step of the analysis and point out of range | |
| m_FrameSummary.maxFrameIndex = firstFrameIndex; | |
| } | |
| public void AddMarker(MarkerData marker) | |
| { | |
| m_Markers.Add(marker); | |
| } | |
| public void AddThread(ThreadData thread) | |
| { | |
| m_Threads.Add(thread); | |
| } | |
| public void UpdateSummary(int frameIndex, float msFrame) | |
| { | |
| m_FrameSummary.msTotal += msFrame; | |
| m_FrameSummary.count += 1; | |
| if (msFrame < m_FrameSummary.msMin) | |
| { | |
| m_FrameSummary.msMin = msFrame; | |
| m_FrameSummary.minFrameIndex = frameIndex; | |
| } | |
| if (msFrame > m_FrameSummary.msMax) | |
| { | |
| m_FrameSummary.msMax = msFrame; | |
| m_FrameSummary.maxFrameIndex = frameIndex; | |
| } | |
| m_FrameSummary.frames.Add(new FrameTime(frameIndex, msFrame, 1)); | |
| } | |
| FrameTime GetPercentageOffset(List<FrameTime> frames, float percent, out int outputFrameIndex) | |
| { | |
| int index = (int)((frames.Count - 1) * percent / 100); | |
| outputFrameIndex = frames[index].frameIndex; | |
| // True median is half of the sum of the middle 2 frames for an even count. However this would be a value never recorded so we avoid that. | |
| return frames[index]; | |
| } | |
| float GetThreadPercentageOffset(List<ThreadFrameTime> frames, float percent, out int outputFrameIndex) | |
| { | |
| int index = (int)((frames.Count - 1) * percent / 100); | |
| outputFrameIndex = frames[index].frameIndex; | |
| // True median is half of the sum of the middle 2 frames for an even count. However this would be a value never recorded so we avoid that. | |
| return frames[index].ms; | |
| } | |
| void CalculateStandardDeviations(MarkerData marker) | |
| { | |
| if (marker.frames.Count <= 1) | |
| { | |
| marker.msStandardDeviation = 0; | |
| marker.countStandardDeviation = 0; | |
| return; | |
| } | |
| int frameCount = marker.frames.Count; | |
| float msMean = marker.msMean; | |
| float countMean = marker.countMean; | |
| double msSum = 0.0; | |
| double countSum = 0.0; | |
| for (int i = 0; i < frameCount; ++i) | |
| { | |
| float delta = (marker.frames[i].ms - msMean); | |
| msSum += (delta * delta); | |
| delta = (marker.frames[i].count - countMean); | |
| countSum += (delta * delta); | |
| } | |
| double variance = msSum / (frameCount - 1); | |
| marker.msStandardDeviation = (float)Math.Sqrt(variance); | |
| variance = countSum / (frameCount - 1); | |
| marker.countStandardDeviation = (float)Math.Sqrt(variance); | |
| } | |
| public void SetupMarkers() | |
| { | |
| int countMax = 0; | |
| float countMaxMean = 0.0f; | |
| foreach (MarkerData marker in m_Markers) | |
| { | |
| marker.msAtMedian = 0.0f; | |
| marker.msMin = float.MaxValue; | |
| marker.msMax = float.MinValue; | |
| marker.minFrameIndex = 0; | |
| marker.maxFrameIndex = 0; | |
| marker.countMin = int.MaxValue; | |
| marker.countMax = int.MinValue; | |
| foreach (FrameTime frameTime in marker.frames) | |
| { | |
| var ms = frameTime.ms; | |
| int frameIndex = frameTime.frameIndex; | |
| // Total time for marker over frame | |
| if (ms < marker.msMin) | |
| { | |
| marker.msMin = ms; | |
| marker.minFrameIndex = frameIndex; | |
| } | |
| if (ms > marker.msMax) | |
| { | |
| marker.msMax = ms; | |
| marker.maxFrameIndex = frameIndex; | |
| } | |
| if (frameIndex == m_FrameSummary.medianFrameIndex) | |
| marker.msAtMedian = ms; | |
| var count = frameTime.count; | |
| // count for marker over frame | |
| if (count < marker.countMin) | |
| { | |
| marker.countMin = count; | |
| } | |
| if (count > marker.countMax) | |
| { | |
| marker.countMax = count; | |
| } | |
| } | |
| int unusedIndex; | |
| marker.msMean = marker.presentOnFrameCount > 0 ? (float)(marker.msTotal / marker.presentOnFrameCount) : 0f; | |
| marker.frames.Sort(FrameTime.CompareCount); | |
| marker.countMedian = GetPercentageOffset(marker.frames, 50, out marker.medianFrameIndex).count; | |
| marker.countLowerQuartile = GetPercentageOffset(marker.frames, 25, out unusedIndex).count; | |
| marker.countUpperQuartile = GetPercentageOffset(marker.frames, 75, out unusedIndex).count; | |
| marker.countMean = marker.presentOnFrameCount > 0 ? (float)marker.count / marker.presentOnFrameCount : 0f; | |
| marker.frames.Sort(FrameTime.CompareMs); | |
| marker.msMedian = GetPercentageOffset(marker.frames, 50, out marker.medianFrameIndex).ms; | |
| marker.msLowerQuartile = GetPercentageOffset(marker.frames, 25, out unusedIndex).ms; | |
| marker.msUpperQuartile = GetPercentageOffset(marker.frames, 75, out unusedIndex).ms; | |
| CalculateStandardDeviations(marker); | |
| if (marker.countMax > countMax) | |
| countMax = marker.countMax; | |
| if (marker.countMean > countMaxMean) | |
| countMaxMean = marker.countMean; | |
| } | |
| m_FrameSummary.markerCountMax = countMax; | |
| m_FrameSummary.markerCountMaxMean = countMaxMean; | |
| } | |
| public void SetupMarkerBuckets() | |
| { | |
| // using a for loop instead of foreach is surprisingly faster on Mono | |
| for (int i = 0, n = m_Markers.Count; i < n; i++) | |
| { | |
| var marker = m_Markers[i]; | |
| marker.ComputeBuckets(marker.msMin, marker.msMax); | |
| marker.ComputeCountBuckets(marker.countMin, marker.countMax); | |
| } | |
| } | |
| public void SetupFrameBuckets(float timeScaleMax) | |
| { | |
| float first = 0; | |
| float last = timeScaleMax; | |
| float range = last - first; | |
| int maxBucketIndex = m_FrameSummary.buckets.Length - 1; | |
| for (int bucketIndex = 0; bucketIndex < m_FrameSummary.buckets.Length; bucketIndex++) | |
| { | |
| m_FrameSummary.buckets[bucketIndex] = 0; | |
| } | |
| float scale = range > 0 ? m_FrameSummary.buckets.Length / range : 0; | |
| // using a for loop instead of foreach is surprisingly faster on Mono | |
| for (int i = 0, n = m_FrameSummary.frames.Count; i < n; i++) | |
| { | |
| var frameData = m_FrameSummary.frames[i]; | |
| var msFrame = frameData.ms; | |
| //var frameIndex = frameData.frameIndex; | |
| int bucketIndex = (int)((msFrame - first) * scale); | |
| if (bucketIndex < 0 || bucketIndex > maxBucketIndex) | |
| { | |
| // It can occur for the highest entry in the range (max-min/range) = 1 | |
| // if (ms > max) // Check for the spilling case | |
| // Debug.Log(string.Format("Frame {0}ms exceeds range {1}-{2} on frame {3}", msFrame, first, last, frameIndex)); | |
| if (bucketIndex > maxBucketIndex) | |
| bucketIndex = maxBucketIndex; | |
| else | |
| bucketIndex = 0; | |
| } | |
| m_FrameSummary.buckets[bucketIndex] += 1; | |
| } | |
| if (range == 0) | |
| { | |
| // All buckets will be the same | |
| for (int bucketIndex = 1; bucketIndex < m_FrameSummary.buckets.Length; bucketIndex++) | |
| { | |
| m_FrameSummary.buckets[bucketIndex] = m_FrameSummary.buckets[0]; | |
| } | |
| } | |
| } | |
| void CalculateThreadMedians() | |
| { | |
| foreach (var thread in m_Threads) | |
| { | |
| if (thread.frames.Count > 0) | |
| { | |
| thread.frames.Sort(); | |
| int unusedIndex; | |
| thread.msMin = GetThreadPercentageOffset(thread.frames, 0, out thread.minFrameIndex); | |
| thread.msLowerQuartile = GetThreadPercentageOffset(thread.frames, 25, out unusedIndex); | |
| thread.msMedian = GetThreadPercentageOffset(thread.frames, 50, out thread.medianFrameIndex); | |
| thread.msUpperQuartile = GetThreadPercentageOffset(thread.frames, 75, out unusedIndex); | |
| thread.msMax = GetThreadPercentageOffset(thread.frames, 100, out thread.maxFrameIndex); | |
| // Put back in order of frames | |
| thread.frames.Sort((a, b) => a.frameIndex.CompareTo(b.frameIndex)); | |
| } | |
| else | |
| { | |
| thread.msMin = 0f; | |
| thread.msLowerQuartile = 0f; | |
| thread.msMedian = 0f; | |
| thread.msUpperQuartile = 0f; | |
| thread.msMax = 0f; | |
| } | |
| } | |
| } | |
| public void Finalise(float timeScaleMax, int maxMarkerDepth) | |
| { | |
| if (m_FrameSummary.frames.Count > 0) | |
| { | |
| m_FrameSummary.frames.Sort(); | |
| m_FrameSummary.msMean = (float)(m_FrameSummary.msTotal / m_FrameSummary.count); | |
| m_FrameSummary.msMedian = GetPercentageOffset(m_FrameSummary.frames, 50, out m_FrameSummary.medianFrameIndex).ms; | |
| int unusedIndex; | |
| m_FrameSummary.msLowerQuartile = GetPercentageOffset(m_FrameSummary.frames, 25, out unusedIndex).ms; | |
| m_FrameSummary.msUpperQuartile = GetPercentageOffset(m_FrameSummary.frames, 75, out unusedIndex).ms; | |
| } | |
| else | |
| { | |
| m_FrameSummary.msMean = 0f; | |
| m_FrameSummary.msMedian = 0f; | |
| m_FrameSummary.msLowerQuartile = 0f; | |
| m_FrameSummary.msUpperQuartile = 0f; | |
| // This started as float.MaxValue and won't have been updated | |
| m_FrameSummary.msMin = 0f; | |
| } | |
| // No longer need the frame time list ? | |
| //m_frameSummary.msFrame.Clear(); | |
| m_FrameSummary.maxMarkerDepth = maxMarkerDepth; | |
| if (timeScaleMax <= 0.0f) | |
| { | |
| // If max frame time range not specified then use the max frame value found. | |
| timeScaleMax = m_FrameSummary.msMax; | |
| } | |
| else if (timeScaleMax < m_FrameSummary.msMax) | |
| { | |
| Debug.Log(string.Format("Expanding timeScaleMax {0} to match max value found {1}", timeScaleMax, m_FrameSummary.msMax)); | |
| // If max frame time range too small we must expand it. | |
| timeScaleMax = m_FrameSummary.msMax; | |
| } | |
| SetupMarkers(); | |
| SetupMarkerBuckets(); | |
| SetupFrameBuckets(timeScaleMax); | |
| // Sort in median order (highest first) | |
| m_Markers.Sort(SortByAtMedian); | |
| CalculateThreadMedians(); | |
| } | |
| int SortByAtMedian(MarkerData a, MarkerData b) | |
| { | |
| if (a.msAtMedian == b.msAtMedian) | |
| return -a.medianFrameIndex.CompareTo(b.medianFrameIndex); | |
| return -a.msAtMedian.CompareTo(b.msAtMedian); | |
| } | |
| public List<MarkerData> GetMarkers() | |
| { | |
| return m_Markers; | |
| } | |
| public List<ThreadData> GetThreads() | |
| { | |
| return m_Threads; | |
| } | |
| public ThreadData GetThreadByName(string threadNameWithIndex) | |
| { | |
| foreach (var thread in m_Threads) | |
| { | |
| if (thread.threadNameWithIndex == threadNameWithIndex) | |
| return thread; | |
| } | |
| return null; | |
| } | |
| public FrameSummary GetFrameSummary() | |
| { | |
| return m_FrameSummary; | |
| } | |
| public MarkerData GetMarker(int index) | |
| { | |
| if (index < 0 || index >= m_Markers.Count) | |
| return null; | |
| return m_Markers[index]; | |
| } | |
| public int GetMarkerIndexByName(string markerName) | |
| { | |
| if (markerName == null) | |
| return -1; | |
| for (int index = 0; index < m_Markers.Count; index++) | |
| { | |
| var marker = m_Markers[index]; | |
| if (marker.name == markerName) | |
| { | |
| return index; | |
| } | |
| } | |
| return -1; | |
| } | |
| public MarkerData GetMarkerByName(string markerName) | |
| { | |
| if (markerName == null) | |
| return null; | |
| for (int index = 0; index < m_Markers.Count; index++) | |
| { | |
| var marker = m_Markers[index]; | |
| if (marker.name == markerName) | |
| { | |
| return marker; | |
| } | |
| } | |
| return null; | |
| } | |
| } | |
| } | |