static void ApplySpikeOverlay(SpikeWindowInfo info, Color spikeColor)
        {
            var spikeOverlayParent = info.spikeOverlayParent;

            if (info.isInspectorElement)
            {
                if (info.spikeOverlay == null)
                {
                    info.spikeOverlay      = CreateSpikeOverlay();
                    info.spikeOverlay.name = info.trackerName;
                    spikeOverlayParent.Add(info.spikeOverlay);
                }

                var color = spikeColor;
                color.a = 0.05f;
                info.spikeOverlay.style.backgroundColor = color;
            }
            else
            {
                if (spikeOverlayParent == null)
                {
                    return;
                }

                if (info.spikeOverlay == null)
                {
                    info.spikeOverlay = CreateSpikeOverlay();
                    spikeOverlayParent.Add(info.spikeOverlay);
                }
                else
                {
                    // Check if it is still the last element:
                    var childCount = spikeOverlayParent.childCount;
                    if (childCount > 0 && spikeOverlayParent.ElementAt(childCount - 1) == info.spikeOverlay)
                    {
                        // All is good, perf Overlay is the last item. Nothing to do.
                    }
                    else
                    {
                        // Ensure it is last:
                        if (spikeOverlayParent.Contains(info.spikeOverlay))
                        {
                            spikeOverlayParent.Remove(info.spikeOverlay);
                        }
                        spikeOverlayParent.Add(info.spikeOverlay);
                    }
                }
                WindowBorderState.ApplyToStyle(info.spikeOverlay, spikeColor);
            }

            info.paintTriggeredByStateChanged = true;
            if (PerformanceTrackerSettings.spikeHighlightStrategy == SpikeHighlightStrategy.AvgTime)
            {
                EditorPerformanceTracker.Reset(info.trackerName);
            }
        }
        static void PerformSpikeWindowHighlight(double now, string[] trackerNames)
        {
            if (!PerformanceTrackerSettings.spikeHighlightEnabled)
            {
                return;
            }

            var allEditorWindows = Resources.FindObjectsOfTypeAll <EditorWindow>();

            foreach (var window in allEditorWindows)
            {
                var windowMarker = GetWindowPaintMarker(window);
                if (EditorPerformanceTracker.Exists(windowMarker))
                {
                    if (!s_SpikeWindowInfos.TryGetValue(windowMarker, out var spikeInfo))
                    {
                        spikeInfo = new SpikeWindowInfo(window, windowMarker);
                        s_SpikeWindowInfos.Add(windowMarker, spikeInfo);
                    }
                    UpdateSpikeWindow(now, spikeInfo);

                    if (spikeInfo.isInspectorWindow && PerformanceTrackerSettings.inspectorSpikeHighlightEnabled)
                    {
                        using (new PerformanceTracker("Tracker.UpdateInspectors"))
                        {
                            UpdateInspectors(now, spikeInfo);
                        }
                    }
                }
                else if (s_SpikeWindowInfos.TryGetValue(windowMarker, out var spikeInfo))
                {
                    RemoveSpikeOverlay(spikeInfo);
                }
            }

            // Window is hidden or closed: clean up all spikeOverlay
            var infos = s_SpikeWindowInfos.Values.ToArray();

            foreach (var info in infos)
            {
                if (!info.window)
                {
                    RemoveSpikeOverlay(info);
                }
            }
        }
        static void UpdateInspectors(double now, SpikeWindowInfo inspectorInfo)
        {
            var editorsList = inspectorInfo.window.rootVisualElement.Q(null, "unity-inspector-editors-list");

            if (editorsList != null)
            {
                foreach (var editorElement in editorsList.Children())
                {
                    var name       = editorElement.name;
                    var markerName = $"Editor.{name}.OnInspectorGUI";
                    s_InspectorWindowInfos.TryGetValue(markerName, out var componentInfo);
                    if (!EditorPerformanceTracker.Exists(markerName))
                    {
                        if (componentInfo != null)
                        {
                            componentInfo.inUse = false;
                        }
                        continue;
                    }

                    if (componentInfo == null)
                    {
                        componentInfo = new SpikeWindowInfo(inspectorInfo.window, markerName, true, editorElement);
                        s_InspectorWindowInfos.Add(markerName, componentInfo);
                    }
                    else if (editorElement != componentInfo.spikeOverlayParent)
                    {
                        InvalidateParent(componentInfo, editorElement);
                    }

                    componentInfo.inUse = true;
                    UpdateSpikeWindow(now, componentInfo);
                }
            }

            var inspectorInfos = s_InspectorWindowInfos.Values.ToArray();

            foreach (var info in inspectorInfos)
            {
                if (!info.inUse)
                {
                    RemoveSpikeOverlay(info);
                }
            }
        }
 static void RemoveSpikeOverlay(SpikeWindowInfo info)
 {
     ResetSpikeOverlay(info);
     if (info.isInspectorElement)
     {
         s_InspectorWindowInfos.Remove(info.trackerName);
     }
     else
     {
         s_SpikeWindowInfos.Remove(info.trackerName);
         if (info.isInspectorWindow)
         {
             var inspectorInfos = s_InspectorWindowInfos.Values.ToArray();
             foreach (var inspectorInfo in inspectorInfos)
             {
                 RemoveSpikeOverlay(inspectorInfo);
             }
         }
     }
 }
        static void ResetSpikeOverlay(SpikeWindowInfo info, bool ignoreNextEvent = true)
        {
            info.spikeOverlay?.RemoveFromHierarchy();
            info.spikeOverlay  = null;
            info.lastSpikeTime = 0;
            info.lastTime      = 0;
            if (ignoreNextEvent)
            {
                info.paintTriggeredByStateChanged = true;
            }

            if (info.window)
            {
                info.window.Repaint();
            }

            if (PerformanceTrackerSettings.spikeHighlightStrategy == SpikeHighlightStrategy.AvgTime)
            {
                EditorPerformanceTracker.Reset(info.trackerName);
            }
        }
 static void InvalidateParent(SpikeWindowInfo info, VisualElement parent = null)
 {
     info.spikeOverlayExplicitParent = parent;
     // We will be processing the event so do not prevent it.
     ResetSpikeOverlay(info, false);
 }
        static void UpdateSpikeWindow(double now, SpikeWindowInfo info)
        {
            var avgTime         = EditorPerformanceTracker.GetAverageTime(info.trackerName);
            var trackerLastTime = EditorPerformanceTracker.GetLastTime(info.trackerName);
            var sampleCount     = EditorPerformanceTracker.GetSampleCount(info.trackerName);
            var peakTime        = EditorPerformanceTracker.GetPeakTime(info.trackerName);
            var spikeDuration   = info.lastSpikeTime == 0 ? 0 : now - info.lastSpikeTime;

            var currentTime  = PerformanceTrackerSettings.spikeHighlightStrategy == SpikeHighlightStrategy.LastTime ? trackerLastTime : avgTime;
            var infoLastTime = PerformanceTrackerSettings.spikeHighlightStrategy == SpikeHighlightStrategy.LastTime ? info.lastTime : info.lastAvgTime;

            if (info.isSpiking && info.spikeOverlay.parent != info.spikeOverlayParent)
            {
                // Spike Overlay is not attached to the right window: probably a window that got docked or undocked.
                InvalidateParent(info);
            }

            if (!info.isSpiking && sampleCount == info.lastSampleCount)
            {
                // Nothing to do
            }
            else if (info.isSpiking && sampleCount == info.lastSampleCount)
            {
                // Check if needs to fade out
                if (spikeDuration > PerformanceTrackerSettings.spikeDuration)
                {
                    ResetSpikeOverlay(info);
                }
                // Do not fade if we are already long to redraw
                else if (currentTime < PerformanceTrackerSettings.spikeCriticalThreshold && info.supportsFading)
                {
                    // Try to fade out gently:
                    var alphaFading = (float)((PerformanceTrackerSettings.spikeDuration - spikeDuration) / PerformanceTrackerSettings.spikeDuration);
                    var color       = info.spikeOverlay.style.borderBottomColor.value;
                    color.a = alphaFading;
                    ApplySpikeOverlay(info, color);
                }
            }
            else
            {
                // We just had an update of sample count:
                if (info.paintTriggeredByStateChanged)
                {
                    // Discard this Paint event since it was caused by one of our state change
                    info.paintTriggeredByStateChanged = false;
                }
                else if (currentTime > PerformanceTrackerSettings.spikeCriticalThreshold)
                {
                    if (!info.isSpiking)
                    {
                        ApplySpikeOverlay(info, ComputeSpikeColor((float)currentTime, PerformanceTrackerSettings.spikeCriticalThreshold, 10.0f, PtStyles.criticalColor, Color.black));
                    }
                    info.lastSpikeTime = now;
                }
                else if (currentTime > PerformanceTrackerSettings.spikeWarningThreshold)
                {
                    if (!info.isSpiking)
                    {
                        ApplySpikeOverlay(info, ComputeSpikeColor((float)currentTime, PerformanceTrackerSettings.spikeWarningThreshold, PerformanceTrackerSettings.spikeCriticalThreshold, PtStyles.warningColor, PtStyles.criticalColor));
                    }
                    info.lastSpikeTime = now;
                }
                else if (infoLastTime > PerformanceTrackerSettings.spikeWarningThreshold)
                {
                    ResetSpikeOverlay(info);
                }
            }

            info.lastAvgTime     = avgTime;
            info.lastTime        = trackerLastTime;
            info.lastSampleCount = sampleCount;
            info.lastPeakTime    = peakTime;
        }