using UnityEditorInternal; using System.Reflection; using System; using System.Collections; using UnityEngine; using System.Collections.Generic; using System.IO; using UnityEngine.Profiling; using UnityEditor.Profiling; #if UNITY_2021_2_OR_NEWER using Unity.Profiling.Editor; // stub so that ProfilerWindow can be moved to this namespace in trunk without a need to change PA namespace Unity.Profiling.Editor {} #endif namespace UnityEditor.Performance.ProfileAnalyzer { internal class ProfilerWindowInterface { bool m_ProfilerWindowInitialized = false; const float k_NsToMs = 1000000; ProgressBarDisplay m_progressBar; [NonSerialized] bool m_SendingSelectionEventToProfilerWindowInProgress = false; [NonSerialized] int m_LastSelectedFrameInProfilerWindow = 0; #if UNITY_2021_1_OR_NEWER [NonSerialized] ProfilerWindow m_ProfilerWindow; [NonSerialized] IProfilerFrameTimeViewSampleSelectionController m_CpuProfilerModule; #else Type m_ProfilerWindowType; EditorWindow m_ProfilerWindow; FieldInfo m_CurrentFrameFieldInfo; FieldInfo m_TimeLineGUIFieldInfo; FieldInfo m_SelectedEntryFieldInfo; FieldInfo m_SelectedNameFieldInfo; FieldInfo m_SelectedTimeFieldInfo; FieldInfo m_SelectedDurationFieldInfo; FieldInfo m_SelectedInstanceIdFieldInfo; FieldInfo m_SelectedFrameIdFieldInfo; FieldInfo m_SelectedThreadIndexFieldInfo; FieldInfo m_SelectedNativeIndexFieldInfo; FieldInfo m_SelectedInstanceCountFieldInfo; FieldInfo m_SelectedInstanceCountForThreadFieldInfo; FieldInfo m_SelectedInstanceCountForFrameFieldInfo; FieldInfo m_SelectedMetaDataFieldInfo; FieldInfo m_SelectedThreadCountFieldInfo; FieldInfo m_SelectedCallstackInfoFieldInfo; MethodInfo m_GetProfilerModuleInfo; Type m_CPUProfilerModuleType; #endif public ProfilerWindowInterface(ProgressBarDisplay progressBar) { m_progressBar = progressBar; #if !UNITY_2021_1_OR_NEWER Assembly assem = typeof(Editor).Assembly; m_ProfilerWindowType = assem.GetType("UnityEditor.ProfilerWindow"); m_CurrentFrameFieldInfo = m_ProfilerWindowType.GetField("m_CurrentFrame", BindingFlags.NonPublic | BindingFlags.Instance); m_TimeLineGUIFieldInfo = m_ProfilerWindowType.GetField("m_CPUTimelineGUI", BindingFlags.NonPublic | BindingFlags.Instance); if (m_TimeLineGUIFieldInfo == null) { // m_CPUTimelineGUI isn't present in 2019.3.0a8 onward m_GetProfilerModuleInfo = m_ProfilerWindowType.GetMethod("GetProfilerModule", BindingFlags.NonPublic | BindingFlags.Instance); if (m_GetProfilerModuleInfo == null) { Debug.Log("Unable to initialise link to Profiler Timeline, no GetProfilerModule found"); } m_CPUProfilerModuleType = assem.GetType("UnityEditorInternal.Profiling.CPUProfilerModule"); m_TimeLineGUIFieldInfo = m_CPUProfilerModuleType.GetField("m_TimelineGUI", BindingFlags.NonPublic | BindingFlags.Instance); if (m_TimeLineGUIFieldInfo == null) { Debug.Log("Unable to initialise link to Profiler Timeline"); } } if (m_TimeLineGUIFieldInfo != null) m_SelectedEntryFieldInfo = m_TimeLineGUIFieldInfo.FieldType.GetField("m_SelectedEntry", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (m_SelectedEntryFieldInfo != null) { m_SelectedNameFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("name", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedTimeFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("time", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedDurationFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("duration", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedInstanceIdFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("instanceId", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedFrameIdFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("frameId", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // confusingly this is called threadId but is the thread _index_ m_SelectedThreadIndexFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("threadId", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedNativeIndexFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("nativeIndex", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedInstanceCountFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("instanceCount", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedInstanceCountForThreadFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("instanceCountForThread", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedInstanceCountForFrameFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("instanceCountForFrame", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedThreadCountFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("threadCount", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedMetaDataFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("metaData", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); m_SelectedCallstackInfoFieldInfo = m_SelectedEntryFieldInfo.FieldType.GetField("callstackInfo", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); } #endif } public bool IsReady() { return m_ProfilerWindow != null && m_ProfilerWindowInitialized; } public void GetProfilerWindowHandle() { Profiler.BeginSample("GetProfilerWindowHandle"); #if UNITY_2021_1_OR_NEWER if (m_CpuProfilerModule != null) { m_CpuProfilerModule.selectionChanged -= OnSelectionChangedInCpuProfilerModule; m_CpuProfilerModule = null; } var windows = Resources.FindObjectsOfTypeAll(); if (windows != null && windows.Length > 0) m_ProfilerWindow = windows[0]; if (m_ProfilerWindow != null) { #if UNITY_2021_2_OR_NEWER var cpuModuleIdentifier = ProfilerWindow.cpuModuleIdentifier; #else var cpuModuleIdentifier = ProfilerWindow.cpuModuleName; #endif m_CpuProfilerModule = m_ProfilerWindow.GetFrameTimeViewSampleSelectionController(cpuModuleIdentifier); m_CpuProfilerModule.selectionChanged -= OnSelectionChangedInCpuProfilerModule; m_CpuProfilerModule.selectionChanged += OnSelectionChangedInCpuProfilerModule; m_ProfilerWindow.Repaint(); m_ProfilerWindowInitialized = false; // wait a frame for the Profiler to get Repainted EditorApplication.delayCall += () => m_ProfilerWindowInitialized = true; } #else UnityEngine.Object[] windows = Resources.FindObjectsOfTypeAll(m_ProfilerWindowType); if (windows != null && windows.Length > 0) m_ProfilerWindow = (EditorWindow)windows[0]; m_ProfilerWindowInitialized = true; #endif Profiler.EndSample(); } public void OpenProfilerOrUseExisting() { // Note we use existing if possible to fix a bug after domain reload // Where calling EditorWindow.GetWindow directly causes a second window to open if (m_ProfilerWindow == null) { #if UNITY_2021_1_OR_NEWER m_ProfilerWindow = EditorWindow.GetWindow(); #if UNITY_2021_2_OR_NEWER var cpuModuleIdentifier = ProfilerWindow.cpuModuleIdentifier; #else var cpuModuleIdentifier = ProfilerWindow.cpuModuleName; #endif m_CpuProfilerModule = m_ProfilerWindow.GetFrameTimeViewSampleSelectionController(cpuModuleIdentifier); m_CpuProfilerModule.selectionChanged -= OnSelectionChangedInCpuProfilerModule; m_CpuProfilerModule.selectionChanged += OnSelectionChangedInCpuProfilerModule; #else // Create new m_ProfilerWindow = EditorWindow.GetWindow(m_ProfilerWindowType); #endif } } public bool GetFrameRangeFromProfiler(out int first, out int last) { if (m_ProfilerWindow != null) { first = 1 + ProfilerDriver.firstFrameIndex; last = 1 + ProfilerDriver.lastFrameIndex; return true; } first = 1; last = 1; return false; } public void CloseProfiler() { if (m_ProfilerWindow != null) m_ProfilerWindow.Close(); } #if !UNITY_2021_1_OR_NEWER object GetTimeLineGUI() { object timeLineGUI = null; if (m_CPUProfilerModuleType != null) { object[] parametersArray = new object[] { ProfilerArea.CPU }; var getCPUProfilerModuleInfo = m_GetProfilerModuleInfo.MakeGenericMethod(m_CPUProfilerModuleType); var cpuModule = getCPUProfilerModuleInfo.Invoke(m_ProfilerWindow, parametersArray); timeLineGUI = m_TimeLineGUIFieldInfo.GetValue(cpuModule); } else if (m_TimeLineGUIFieldInfo != null) { timeLineGUI = m_TimeLineGUIFieldInfo.GetValue(m_ProfilerWindow); } return timeLineGUI; } #endif #if UNITY_2021_1_OR_NEWER private void OnSelectionChangedInCpuProfilerModule(IProfilerFrameTimeViewSampleSelectionController controller, ProfilerTimeSampleSelection selection) { if (controller == m_CpuProfilerModule && !m_SendingSelectionEventToProfilerWindowInProgress) { if (selection != null && selection.markerNamePath != null && selection.markerNamePath.Count > 0) { selectedMarkerChanged(selection.markerNamePath[selection.markerNamePath.Count - 1], selection.threadGroupName, selection.threadName); } } } #endif public event Action selectedMarkerChanged = delegate {}; public void PollProfilerWindowMarkerName() { #if !UNITY_2021_1_OR_NEWER if (m_ProfilerWindow != null) { var timeLineGUI = GetTimeLineGUI(); if (timeLineGUI != null && m_SelectedEntryFieldInfo != null) { var selectedEntry = m_SelectedEntryFieldInfo.GetValue(timeLineGUI); if (selectedEntry != null && m_SelectedNameFieldInfo != null) { string threadGroupName = null; string threadName = null; if (m_SelectedFrameIdFieldInfo != null && m_SelectedThreadIndexFieldInfo != null) { using (RawFrameDataView frameData = ProfilerDriver.GetRawFrameDataView((int)m_SelectedFrameIdFieldInfo.GetValue(selectedEntry), (int)m_SelectedThreadIndexFieldInfo.GetValue(selectedEntry))) { if (frameData != null && frameData.valid) { threadGroupName = frameData.threadGroupName; threadName = frameData.threadName; } } } selectedMarkerChanged(m_SelectedNameFieldInfo.GetValue(selectedEntry).ToString(), threadGroupName, threadName); } } } #endif } public ProfileData PullFromProfiler(int firstFrameDisplayIndex, int lastFrameDisplayIndex) { Profiler.BeginSample("ProfilerWindowInterface.PullFromProfiler"); bool recording = IsRecording(); if (recording) StopRecording(); int firstFrameIndex = Mathf.Max(firstFrameDisplayIndex - 1, 0); int lastFrameIndex = lastFrameDisplayIndex - 1; ProfileData profileData = GetData(firstFrameIndex, lastFrameIndex); if (recording) StartRecording(); Profiler.EndSample(); return profileData; } public int GetThreadCountForFrame(int frameIndex) { if (!IsReady()) return 0; ProfilerFrameDataIterator frameData = new ProfilerFrameDataIterator(); frameData.SetRoot(frameIndex, 0); return frameData.GetThreadCount(frameIndex); } public ProfileFrame GetProfileFrameForThread(int frameIndex, int threadIndex) { if (!IsReady()) return null; var frame = new ProfileFrame(); using (RawFrameDataView frameData = ProfilerDriver.GetRawFrameDataView(frameIndex, threadIndex)) { frame.msStartTime = frameData.frameStartTimeMs; frame.msFrame = frameData.frameTimeMs; } return frame; } ProfileData GetDataRaw(ProfileData data, int firstFrameIndex, int lastFrameIndex) { bool firstError = true; data.SetFrameIndexOffset(firstFrameIndex); var depthStack = new Stack(); var threadNameCount = new Dictionary(); var markerIdToNameIndex = new Dictionary(); for (int frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; ++frameIndex) { m_progressBar.AdvanceProgressBar(); int threadIndex = 0; threadNameCount.Clear(); ProfileFrame frame = null; while (true) { using (RawFrameDataView frameData = ProfilerDriver.GetRawFrameDataView(frameIndex, threadIndex)) { if (threadIndex == 0) { if ((frameIndex == firstFrameIndex || frameIndex == lastFrameIndex) && firstFrameIndex != lastFrameIndex && (!frameData.valid || frameData.frameTimeNs == 0)) { // skip incomplete frames when they are at the beginning or end of the capture if (++frameIndex <= lastFrameIndex) { data.FirstFrameIncomplete = true; data.SetFrameIndexOffset(frameIndex); continue; } else { // break out entirely if this is the last frame data.LastFrameIncomplete = true; break; } } frame = new ProfileFrame(); if (frameData.valid) { frame.msStartTime = frameData.frameStartTimeMs; frame.msFrame = frameData.frameTimeMs; } data.Add(frame); } if (!frameData.valid) break; string threadNameWithIndex = null; string threadName = frameData.threadName; if (threadName.Trim() == "") { Debug.Log(string.Format("Warning: Unnamed thread found on frame {0}. Corrupted data suspected, ignoring frame", frameIndex)); threadIndex++; continue; } var groupName = frameData.threadGroupName; threadName = ProfileData.GetThreadNameWithGroup(threadName, groupName); int nameCount = 0; threadNameCount.TryGetValue(threadName, out nameCount); threadNameCount[threadName] = nameCount + 1; threadNameWithIndex = ProfileData.ThreadNameWithIndex(threadNameCount[threadName], threadName); var thread = new ProfileThread(); data.AddThreadName(threadNameWithIndex, thread); frame.Add(thread); // The markers are in depth first order depthStack.Clear(); // first sample is the thread name for (int i = 1; i < frameData.sampleCount; i++) { float durationMS = frameData.GetSampleTimeMs(i); int markerId = frameData.GetSampleMarkerId(i); if (durationMS < 0) { if (firstError) { int displayIndex = data.OffsetToDisplayFrame(frameIndex); string name = frameData.GetSampleName(i); Debug.LogFormat("Ignoring Invalid marker time found for {0} on frame {1} on thread {2} ({3} < 0)", name, displayIndex, threadName, durationMS); firstError = false; } } else { int depth = 1 + depthStack.Count; var markerData = ProfileMarker.Create(durationMS, depth); // Use name index directly if we have already stored this named marker before int nameIndex; if (markerIdToNameIndex.TryGetValue(markerId, out nameIndex)) { markerData.nameIndex = nameIndex; } else { string name = frameData.GetSampleName(i); data.AddMarkerName(name, markerData); markerIdToNameIndex[markerId] = markerData.nameIndex; } thread.AddMarker(markerData); } int childrenCount = frameData.GetSampleChildrenCount(i); if (childrenCount > 0) { depthStack.Push(childrenCount); } else { while (depthStack.Count > 0) { int remainingChildren = depthStack.Pop(); if (remainingChildren > 1) { depthStack.Push(remainingChildren - 1); break; } } } } } threadIndex++; } } data.Finalise(); return data; } ProfileData GetDataOriginal(ProfileData data, int firstFrameIndex, int lastFrameIndex) { ProfilerFrameDataIterator frameData = new ProfilerFrameDataIterator(); bool firstError = true; data.SetFrameIndexOffset(firstFrameIndex); Dictionary threadNameCount = new Dictionary(); for (int frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; ++frameIndex) { m_progressBar.AdvanceProgressBar(); int threadCount = frameData.GetThreadCount(frameIndex); frameData.SetRoot(frameIndex, 0); var msFrame = frameData.frameTimeMS; if ((frameIndex == firstFrameIndex || frameIndex == lastFrameIndex) && firstFrameIndex != lastFrameIndex && msFrame == 0) { var nextFrame = frameIndex + 1; // skip incomplete frames when they are at the beginning or end of the capture if (nextFrame <= lastFrameIndex) { data.FirstFrameIncomplete = true; data.SetFrameIndexOffset(nextFrame); continue; } else { // break out entirely if this is the last frame data.LastFrameIncomplete = true; break; } } /* if (frameIndex == lastFrameIndex) { // Check if last frame appears to be invalid data float median; float mean; float standardDeviation; CalculateFrameTimeStats(data, out median, out mean, out standardDeviation); float execessiveDeviation = (3f * standardDeviation); if (msFrame > (median + execessiveDeviation)) { Debug.LogFormat("Dropping last frame as it is significantly larger than the median of the rest of the data set {0} > {1} (median {2} + 3 * standard deviation {3})", msFrame, median + execessiveDeviation, median, standardDeviation); break; } if (msFrame < (median - execessiveDeviation)) { Debug.LogFormat("Dropping last frame as it is significantly smaller than the median of the rest of the data set {0} < {1} (median {2} - 3 * standard deviation {3})", msFrame, median - execessiveDeviation, median, standardDeviation); break; } } */ ProfileFrame frame = new ProfileFrame(); frame.msStartTime = 1000.0 * frameData.GetFrameStartS(frameIndex); frame.msFrame = msFrame; data.Add(frame); threadNameCount.Clear(); for (int threadIndex = 0; threadIndex < threadCount; ++threadIndex) { frameData.SetRoot(frameIndex, threadIndex); var threadName = frameData.GetThreadName(); if (threadName.Trim() == "") { Debug.Log(string.Format("Warning: Unnamed thread found on frame {0}. Corrupted data suspected, ignoring frame", frameIndex)); continue; } var groupName = frameData.GetGroupName(); threadName = ProfileData.GetThreadNameWithGroup(threadName, groupName); ProfileThread thread = new ProfileThread(); frame.Add(thread); int nameCount = 0; threadNameCount.TryGetValue(threadName, out nameCount); threadNameCount[threadName] = nameCount + 1; data.AddThreadName(ProfileData.ThreadNameWithIndex(threadNameCount[threadName], threadName), thread); const bool enterChildren = true; // The markers are in depth first order and the depth is known // So we can infer a parent child relationship while (frameData.Next(enterChildren)) { if (frameData.durationMS < 0) { if (firstError) { int displayIndex = data.OffsetToDisplayFrame(frameIndex); Debug.LogFormat("Ignoring Invalid marker time found for {0} on frame {1} on thread {2} ({3} < 0) : Instance id : {4}", frameData.name, displayIndex, threadName, frameData.durationMS, frameData.instanceId); firstError = false; } continue; } var markerData = ProfileMarker.Create(frameData); data.AddMarkerName(frameData.name, markerData); thread.AddMarker(markerData); } } } data.Finalise(); frameData.Dispose(); return data; } ProfileData GetData(int firstFrameIndex, int lastFrameIndex) { ProfileData data = new ProfileData(ProfileAnalyzerWindow.TmpPath); GetDataRaw(data, firstFrameIndex, lastFrameIndex); data.Write(); return data; } public float GetFrameTimeRaw(int frameIndex) { using (RawFrameDataView frameData = ProfilerDriver.GetRawFrameDataView(frameIndex, 0)) { if (!frameData.valid) return 0f; return frameData.frameTimeMs; } } public float GetFrameTime(int frameIndex) { return GetFrameTimeRaw(frameIndex); } struct ThreadIndexIterator { public ProfilerFrameDataIterator frameData; public int threadIndex; } IEnumerator GetNextThreadIndexFittingThreadFilters(int frameIndex, List threadFilters) { ProfilerFrameDataIterator frameData = new ProfilerFrameDataIterator(); int threadCount = frameData.GetThreadCount(frameIndex); Dictionary threadNameCount = new Dictionary(); for (int threadIndex = 0; threadIndex < threadCount; ++threadIndex) { frameData.SetRoot(frameIndex, threadIndex); var threadName = frameData.GetThreadName(); // Name here could be "Worker Thread 1" var groupName = frameData.GetGroupName(); threadName = ProfileData.GetThreadNameWithGroup(threadName, groupName); int nameCount = 0; threadNameCount.TryGetValue(threadName, out nameCount); threadNameCount[threadName] = nameCount + 1; var threadNameWithIndex = ProfileData.ThreadNameWithIndex(threadNameCount[threadName], threadName); // To compare on the filter we need to remove the postfix on the thread name // "3:Worker Thread 0" -> "1:Worker Thread" // The index of the thread (0) is used +1 as a prefix // The preceding number (3) is the count of number of threads with this name // Unfortunately multiple threads can have the same name threadNameWithIndex = ProfileData.CorrectThreadName(threadNameWithIndex); if (threadFilters.Contains(threadNameWithIndex)) { yield return new ThreadIndexIterator {frameData = frameData, threadIndex = threadIndex}; } } frameData.Dispose(); } bool GetMarkerInfo(string markerName, int frameIndex, List threadFilters, out int outThreadIndex, out int outNativeIndex, out float time, out float duration, out int instanceId) { outThreadIndex = 0; outNativeIndex = 0; time = 0.0f; duration = 0.0f; instanceId = 0; bool found = false; var iterator = GetNextThreadIndexFittingThreadFilters(frameIndex, threadFilters); while (iterator.MoveNext()) { const bool enterChildren = true; while (iterator.Current.frameData.Next(enterChildren)) { if (iterator.Current.frameData.name == markerName) { time = iterator.Current.frameData.startTimeMS; duration = iterator.Current.frameData.durationMS; instanceId = iterator.Current.frameData.instanceId; outNativeIndex = iterator.Current.frameData.sampleId; outThreadIndex = iterator.Current.threadIndex; found = true; break; } } if (found) break; } return found; } public bool SetProfilerWindowMarkerName(string markerName, List threadFilters) { m_SendingSelectionEventToProfilerWindowInProgress = true; if (m_ProfilerWindow == null) return false; #if UNITY_2021_1_OR_NEWER #if UNITY_2021_2_OR_NEWER var cpuModuleIdentifier = ProfilerWindow.cpuModuleIdentifier; var selectedModuleIdentifier = m_ProfilerWindow.selectedModuleIdentifier; #else var cpuModuleIdentifier = ProfilerWindow.cpuModuleName; var selectedModuleIdentifier = m_ProfilerWindow.selectedModuleName; #endif m_CpuProfilerModule = m_ProfilerWindow.GetFrameTimeViewSampleSelectionController(cpuModuleIdentifier); if (m_CpuProfilerModule != null && selectedModuleIdentifier == cpuModuleIdentifier && m_ProfilerWindow.firstAvailableFrameIndex >= 0) { // Read profiler data direct from profile to find time/duration int currentFrameIndex = (int)m_ProfilerWindow.selectedFrameIndex; var iterator = GetNextThreadIndexFittingThreadFilters(currentFrameIndex, threadFilters); while (iterator.MoveNext()) { using (var rawFrameDataView = ProfilerDriver.GetRawFrameDataView(currentFrameIndex, iterator.Current.threadIndex)) { if (m_CpuProfilerModule.SetSelection(currentFrameIndex, rawFrameDataView.threadGroupName, rawFrameDataView.threadName, markerName, threadId: rawFrameDataView.threadId)) { m_ProfilerWindow.Repaint(); m_SendingSelectionEventToProfilerWindowInProgress = false; return true; // setting the selection was successful, nothing more to do here. } } } // selection couldn't be found, so clear the current one to avoid confusion m_CpuProfilerModule.ClearSelection(); m_ProfilerWindow.Repaint(); } #else var timeLineGUI = GetTimeLineGUI(); if (timeLineGUI == null) { m_SendingSelectionEventToProfilerWindowInProgress = false; return false; } if (m_SelectedEntryFieldInfo != null) { var selectedEntry = m_SelectedEntryFieldInfo.GetValue(timeLineGUI); if (selectedEntry != null) { // Read profiler data direct from profile to find time/duration int currentFrameIndex = (int)m_CurrentFrameFieldInfo.GetValue(m_ProfilerWindow); float time; float duration; int instanceId; int nativeIndex; int threadIndex; if (GetMarkerInfo(markerName, currentFrameIndex, threadFilters, out threadIndex, out nativeIndex, out time, out duration, out instanceId)) { /* Debug.Log(string.Format("Setting profiler to {0} on {1} at frame {2} at {3}ms for {4}ms ({5})", markerName, currentFrameIndex, threadFilter, time, duration, instanceId)); */ if (m_SelectedNameFieldInfo != null) m_SelectedNameFieldInfo.SetValue(selectedEntry, markerName); if (m_SelectedTimeFieldInfo != null) m_SelectedTimeFieldInfo.SetValue(selectedEntry, time); if (m_SelectedDurationFieldInfo != null) m_SelectedDurationFieldInfo.SetValue(selectedEntry, duration); if (m_SelectedInstanceIdFieldInfo != null) m_SelectedInstanceIdFieldInfo.SetValue(selectedEntry, instanceId); if (m_SelectedFrameIdFieldInfo != null) m_SelectedFrameIdFieldInfo.SetValue(selectedEntry, currentFrameIndex); if (m_SelectedNativeIndexFieldInfo != null) m_SelectedNativeIndexFieldInfo.SetValue(selectedEntry, nativeIndex); if (m_SelectedThreadIndexFieldInfo != null) m_SelectedThreadIndexFieldInfo.SetValue(selectedEntry, threadIndex); // TODO : Update to fill in the total and number of instances. // For now we force Instance count to 1 to avoid the incorrect info showing. if (m_SelectedInstanceCountFieldInfo != null) m_SelectedInstanceCountFieldInfo.SetValue(selectedEntry, 1); if (m_SelectedInstanceCountForThreadFieldInfo != null) m_SelectedInstanceCountForThreadFieldInfo.SetValue(selectedEntry, 1); if (m_SelectedInstanceCountForFrameFieldInfo != null) m_SelectedInstanceCountForFrameFieldInfo.SetValue(selectedEntry, 1); if (m_SelectedThreadCountFieldInfo != null) m_SelectedThreadCountFieldInfo.SetValue(selectedEntry, 1); if (m_SelectedMetaDataFieldInfo != null) m_SelectedMetaDataFieldInfo.SetValue(selectedEntry, ""); if (m_SelectedCallstackInfoFieldInfo != null) m_SelectedCallstackInfoFieldInfo.SetValue(selectedEntry, ""); m_ProfilerWindow.Repaint(); m_SendingSelectionEventToProfilerWindowInProgress = false; return true; } } } #endif m_SendingSelectionEventToProfilerWindowInProgress = false; return false; } public bool JumpToFrame(int index) { if (m_ProfilerWindow == null) return false; if (index - 1 < ProfilerDriver.firstFrameIndex) return false; if (index - 1 > ProfilerDriver.lastFrameIndex) return false; #if UNITY_2021_1_OR_NEWER m_ProfilerWindow.selectedFrameIndex = index - 1; #else m_CurrentFrameFieldInfo.SetValue(m_ProfilerWindow, index - 1); #endif m_ProfilerWindow.Repaint(); return true; } public int selectedFrame { get { if (m_ProfilerWindow == null) return 0; #if UNITY_2021_1_OR_NEWER return (int)m_ProfilerWindow.selectedFrameIndex + 1; #else return (int)m_CurrentFrameFieldInfo.GetValue(m_ProfilerWindow) + 1; #endif } } public event Action selectedFrameChanged = delegate {}; public void PollSelectedFrameChanges() { var currentlySelectedFrame = selectedFrame; if (m_LastSelectedFrameInProfilerWindow != currentlySelectedFrame && !m_SendingSelectionEventToProfilerWindowInProgress) { m_LastSelectedFrameInProfilerWindow = currentlySelectedFrame; selectedFrameChanged(currentlySelectedFrame); } } public bool IsRecording() { return ProfilerDriver.enabled; } public void StopRecording() { // Stop recording first ProfilerDriver.enabled = false; } public void StartRecording() { // Stop recording first ProfilerDriver.enabled = true; } public void OnDisable() { if (m_ProfilerWindow != null) { m_ProfilerWindow = null; } #if UNITY_2021_1_OR_NEWER if (m_CpuProfilerModule != null) { m_CpuProfilerModule.selectionChanged -= OnSelectionChangedInCpuProfilerModule; m_CpuProfilerModule = null; } #endif } } }