ProfilerEditorUtility.SetSelection
Declaration
public static bool SetSelection(Profiling.IProfilerFrameTimeViewSampleSelectionController controller, string markerNameOrMarkerNamePath, long frameIndex, string threadGroupName, string threadName, ulong threadId);Параметры
| Параметр | Описание |
|---|---|
| контроллер | Объект контроллера модуля Profiler, выбор которого вы хотите установить. Когда значение нулевое, Unity возвращает NullArgumentException. |
| markerNameOrMarkerNamePath | Имя образца, который должен быть выбран, или имена всех образцов в стеке образцов. Разделите каждое имя символом /, заканчивающимся на образец, который должен быть выбран. Не добавляйте в конце /. Если Unity не может найти образец, который соответствует этому имени или стеку образцов, он не устанавливает выбор и этот метод возвращает false. Когда это значение является нулевым или пустой строкой, Unity выводит ArgumentException. |
| frameIndex | Индекс кадра, основанный на 0. По умолчанию это значение равняется -1, что означает, что выбор сделан на текущем отображаемом кадре. Обратите внимание, что окно Profiler UI показывает индекс кадра как n+1. Когда это значение находится вне диапазона, описанного ProfilerWindow.firstAvailableFrameIndex и ProfilerWindow.lastAvailableFrameIndex, или не -1, Unity выводит ArgumentOutOfRangeException. |
| threadGroupName | Имя группы потоков. По умолчанию этот параметр представляет собой пустую строку. Пустая или нулевая строка означает, что поток не является частью группы потоков. "Job", "Loading" и "Scripting Threads" являются примерами таких имен групп потоков. |
| threadName | Имя потока, e.g. "Main Thread", "Render Thread" или "Worker 0". По умолчанию этот параметр равен "Main Thread". Когда это значение нулево или пустое, Unity отправляет ArgumentException. |
| threadId | ID потока. Когда передаётся значение по умолчанию FrameDataView.invalidThreadId, Unity ищет образец в первом потоке, соответствующем указанным threadGroupName и threadName. Укажите этот threadId, если существует несколько потоков с одинаковым именем. Используйте RawFrameDataView.threadId или HierarchyFrameDataView.threadId для получения ID для конкретного потока, если вам нужно, чтобы он был конкретным. |
Возвращаемое значение
логическое Возвращает true, если выборка была успешно установлена, false, если она была отклонена, поскольку не удалось найти соответствующую выборку.
Описание
Установить текущее выделение в модуле Profiler, основанном на выборке времени кадра, например, в модуле использования CPU и модуле использования GPU Profiler.
Используйте эти методы расширения IProfilerFrameTimeViewSampleSelectionController для поиска образца по его имени или Profiler Идентификатору Маркера, кадру и потоку, в котором он возник, и, по желанию, стеку образцов, в котором он должен быть найден. Если образец, соответствующий этим критериям, найден, этот метод возвратит true, и вы можете получить подробности о обнаруженном и выбранном выборе с помощью IProfilerFrameTimeViewSampleSelectionController.selection.
Когда в окне Profiler данных о кадрах нет, Unity вызывает Exception. Вы можете проверить, что ProfilerWindow.firstAvailableFrameIndex больше или равно 0, чтобы проверить наличие данных о кадрах.
Когда указанный поток не может быть найден, Unity вызывает ArgumentException.
Если вы знаете rawSampleIndex образца, который вы хотите выбрать, вы можете использовать IProfilerFrameTimeViewSampleSelectionController.SetSelection непосредственно для установки выбора. Дополнительные ресурсы: IProfilerFrameTimeViewSampleSelectionController.SetSelection и IProfilerFrameTimeViewSampleSelectionController.ClearSelection.
Declaration
public static bool SetSelection(Profiling.IProfilerFrameTimeViewSampleSelectionController controller, long frameIndex, string threadGroupName, string threadName, string sampleName, string markerNamePath, ulong threadId);public static bool SetSelection(Profiling.IProfilerFrameTimeViewSampleSelectionController controller, long frameIndex, string threadGroupName, string threadName, int sampleMarkerId, List<int> markerIdPath, ulong threadId);Параметры
| Параметр | Описание |
|---|---|
| контроллер | Объект контроллера модуля Profiler, выбор которого вы хотите установить. Когда значение нулевое, Unity возвращает NullArgumentException. |
| frameIndex | Индекс кадра, основанный на 0. Обратите внимание, что окно Profiler UI показывает индекс кадра как n+1. Когда это значение находится вне диапазона, описанного ProfilerWindow.firstAvailableFrameIndex и ProfilerWindow.lastAvailableFrameIndex, или меньше 0, Unity выводит ArgumentOutOfRangeException. |
| threadGroupName | Имя группы потоков. Нулевая или пустая строка означает, что поток не является частью группы потоков. "Job", "Loading" и "Scripting Threads" являются примерами таких имен групп потоков. |
| threadName | Имя потока, e.g. "Main Thread", "Render Thread" или "Worker 0". Когда это значение является нулевым или пустой строкой, Unity вызывает ArgumentException. |
| sampleName | Имя образца для выбора. Если Unity не может найти образец, соответствующий этому имени, он не устанавливает выбор и этот метод возвращает false. Когда это значение является нулевым или пустой строкой, Unity возвращает ArgumentNullException или ArgumentException соответственно. |
| markerNamePath | Имена всех образцов в стеке образцов, разделенные символом /, которые определяют базовый путь для поиска. Подобно структуре папки файла, этот базовый путь определяет, где Unity ищет образец, который соответствует sampleName. Поискиваемый sampleName может быть последним элементом в этом пути маркера или любым дочерним образцом. Не добавляйте в конце /. Если не найдено образца, соответствующего этому пути стека образцов и sampleName, не устанавливается выбор и этот метод возвращает false. По умолчанию это значение равно null, что означает, что на стеке образцов образца не устанавливается никаких требований и выбирается первый образец, соответствующий sampleName. |
| threadId | ID потока. Когда передаётся значение по умолчанию FrameDataView.invalidThreadId, Unity ищет образец в первом потоке, соответствующем указанным threadGroupName и threadName. Укажите этот threadId, если существует несколько потоков с одинаковым именем. Используйте RawFrameDataView.threadId или HierarchyFrameDataView.threadId для получения ID для конкретного потока, если вам нужно, чтобы он был конкретным. |
| sampleMarkerId | Используйте HierarchyFrameDataView или RawFrameDataView для получения Идентификационных Данных Маркера. Когда не найдено образца, соответствующего этому пути стек-образца и sampleMarkerId, выбор не устанавливается и этот метод возвращает false. |
| markerIdPath | Список Profiler маркеров IDs для всех образцов в стеке образцов, которые определяют базовый путь для поиска. Подобно структуре папки файлов, этот базовый путь определяет, где Unity ищет образец, который соответствует sampleMarkerId. Ищемые sampleMarkerId могут быть последним элементом в этом пути маркеров или любым дочерним образцом. Если не может быть найден образец, соответствующий этому пути стека образцов и sampleMarkerId, выбор не устанавливается, и этот метод возвращает false. По умолчанию используется null, что означает, что на стеке образцов образца не устанавливается никаких требований, и выбирается первый образец, соответствующий sampleMarkerId. |
Возвращаемое значение
логическое Возвращает true, если выборка была успешно установлена, false, если она была отклонена, поскольку не удалось найти соответствующую выборку.
Описание
Установить текущее выделение в модуле Profiler, основанном на выборке времени кадра, например, в модуле использования CPU и модуле использования GPU Profiler.
Используйте эти методы расширения для IProfilerFrameTimeViewSampleSelectionController для поиска образца по его имени или Идентификатору Маркера Profiler, кадру и потоку, в котором он возник, и, по желанию, по стеку образцов, в котором он должен быть найден. Если образец, соответствующий этим критериям, найден, этот метод возвратит true и вы можете получить подробности о обнаруженном и выбранном выборе через IProfilerFrameTimeViewSampleSelectionController.selection.
Когда данные кадра не доступны в окне Profiler, Unity вызывает Исключение. Вы можете проверить, что ProfilerWindow.firstAvailableFrameIndex больше или равно 0, чтобы проверить наличие данных кадра.
Когда данные кадра не доступны в окне Profiler, Unity вызывает Исключение. Вы можете проверить, что ProfilerWindow.firstAvailableFrameIndex больше или равно 0, чтобы проверить наличие данных кадра.
Когда указанный поток не может быть найден, Unity вызывает ArgumentException.
Если вы знаете rawSampleIndex образца, который вы хотите выбрать, вы можете использовать IProfilerFrameTimeViewSampleSelectionController.SetSelection непосредственно для установки выбора. Дополнительные ресурсы: IProfilerFrameTimeViewSampleSelectionController.SetSelection и IProfilerFrameTimeViewSampleSelectionController.ClearSelection.
using System; using System.Collections.Generic; using UnityEditor; using UnityEditor.Profiling; using UnityEditorInternal; using UnityEngine;
// This example assumes the profiled scene contained a component of MyScript.cs: /* using UnityEngine;
public class MyScript : MonoBehaviour { void Update() { MethodWithABoxingAllocation(); }
object MethodWithABoxingAllocation() { return 1; } }*/
public class Example : EditorWindow { const string k_MainThreadGroupName = ""; const string k_MainThreadName = "Main Thread"; const string k_GCAllocSampleName = "GC.Alloc"; // Profiler samples that were instrumented by Unity's Message Invoking mechanism, // e.g. Update(), Start(), FixedUpdate() ... get an " [Invoke]" postfix const string k_InvokePostFix = " [Invoke]"; public enum UpdateNameMatchType { Short, Full, }
ProfilerWindow m_Profiler = null; UpdateNameMatchType m_UpdateNameMatchType = UpdateNameMatchType.Short; bool m_UseMarkerNames = true;
string GetUpdateSampleName(UpdateNameMatchType updateNameMatchType, bool deepProfiling) { switch (updateNameMatchType) { case UpdateNameMatchType.Short: if (deepProfiling) return k_UpdateSampleNameShort; return k_UpdateSampleNameShort + k_InvokePostFix; case UpdateNameMatchType.Full: if (deepProfiling) return k_UpdateSampleNameFull;
return k_UpdateSampleNameFull + k_InvokePostFix; default: throw new NotImplementedException(); } }
const string k_UpdateSampleNameFull = "Assembly-CSharp.dll!::MyScript.Update()"; // Invoked method samples or samples instrumented via Deep Profiling will by default be shown // without their fully qualifying type name as above // Instead the Profiler UI will strip out everything before the '!::' part of their name. // SetSelection will still find these samples, if the UI is set to not Show Full Scripting Method Names // Note that RawFrameDataView and HierarchyFrameDataView will not be able to identify the Marker IDs // for such samples from this shorter name. const string k_UpdateSampleNameShort = "MyScript.Update()";
static readonly List<string> k_SampleNames = new List<string> { "PlayerLoop", "Update.ScriptRunBehaviourUpdate", "BehaviourUpdate", };
[MenuItem("Window/Analysis/Profiler Extension")] public static void ShowExampleWindow() { var window = GetWindow<Example>(); window.m_Profiler = EditorWindow.GetWindow<ProfilerWindow>(); }
void OnGUI() { // First make sure there is an open Profiler Window if (m_Profiler == null) m_Profiler = EditorWindow.GetWindow<ProfilerWindow>();
// For demonstration purposes, let the user choose if the names or if Marker IDs should be used. m_UseMarkerNames = GUILayout.Toggle(m_UseMarkerNames, "Use Marker names instead of IDs"); if (!m_UseMarkerNames) m_UpdateNameMatchType = UpdateNameMatchType.Full;
// Marker IDs need to be gotten from the fully qualified type name, so the shorter name is not an option when using IDs using (new EditorGUI.DisabledScope(!m_UseMarkerNames)) { // For demonstration purposes, let the user choose if the short or the long name should be used. m_UpdateNameMatchType = (UpdateNameMatchType)EditorGUILayout.EnumPopup(m_UpdateNameMatchType); }
// If the currently selected Module is not the CPU Usage module, setting the selection will not be visible to the user immediately if (m_Profiler.selectedModuleIdentifier == ProfilerWindow.cpuModuleIdentifier) { // Get the CPU Usage Profiler module's selection controller interface to interact with the selection var cpuSampleSelectionController = m_Profiler.GetFrameTimeViewSampleSelectionController(ProfilerWindow.cpuModuleIdentifier); // If the current selection object is null, there is no selection to print out. using (new EditorGUI.DisabledScope(m_Profiler.lastAvailableFrameIndex < 0)) { if (GUILayout.Button("Check my Script for GC.Alloc")) { string samplePath = ""; for (int i = 0; i < k_SampleNames.Count; i++) { samplePath += $"{k_SampleNames[i]}/"; }
var samplePathDeepProfiling = samplePath + $"{GetUpdateSampleName(m_UpdateNameMatchType, true)}/"; samplePath = samplePath + $"{GetUpdateSampleName(m_UpdateNameMatchType, false)}/"; // the sample we are looking for, without a trailing '/' samplePath += k_GCAllocSampleName; samplePathDeepProfiling += k_GCAllocSampleName;
// This check will fail in Deep Profiling because "MethodWithABoxingAllocation()" will be instrumented // and sitting in the sample stack between "Update()" and the "GC.Alloc". if (cpuSampleSelectionController.SetSelection(samplePath) || cpuSampleSelectionController.SetSelection(samplePathDeepProfiling)) { Debug.LogWarning("MyScript allocates in its Update loop"); } else { if (m_UseMarkerNames) { samplePath = ""; for (int i = 0; i < k_SampleNames.Count; i++) { samplePath += $"{k_SampleNames[i]}{ (i < k_SampleNames.Count - 1 ?"/": "")}"; } // SetSelection calls that take sample names as strings will find shortened scripting sample names var mySctiprSamplePathDeepProfiling = $"{samplePath}/{GetUpdateSampleName(m_UpdateNameMatchType, true)}"; var myScriptSamplePath = $"{samplePath}/{GetUpdateSampleName(m_UpdateNameMatchType, false)}";
if (cpuSampleSelectionController.SetSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, k_GCAllocSampleName, myScriptSamplePath) || cpuSampleSelectionController.SetSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, k_GCAllocSampleName, mySctiprSamplePathDeepProfiling)) { Debug.LogWarning("MyScript allocates in its Update loop"); } // MyScript did not have a GC.Alloc sample underneath it, but maybe a different Update sample allocated // Search through all Update() samples else if (cpuSampleSelectionController.SetSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, k_GCAllocSampleName, samplePath)) { Debug.LogWarning($"MyScript does not allocate but {cpuSampleSelectionController.selection.markerNamePath[k_SampleNames.Count]} allocates in its Update loop"); } else { Debug.Log("No Script is allocating in its Update Loop"); FindAnyGCAllocSample(cpuSampleSelectionController); } } else { List<int> markerIdPath = new List<int>(k_SampleNames.Count + 1); List<int> deepProfilingmarkerIdPath = new List<int>(k_SampleNames.Count + 1); int gcAllocMarkerId = FrameDataView.invalidMarkerId; using (var frameData = ProfilerDriver.GetRawFrameDataView((int)m_Profiler.selectedFrameIndex, 0)) { for (int i = 0; i < k_SampleNames.Count; i++) { markerIdPath.Add(frameData.GetMarkerId(k_SampleNames[i])); } deepProfilingmarkerIdPath.AddRange(markerIdPath); // GetMarkerId needs the full length marker name to be able to identify this sample. markerIdPath.Add(frameData.GetMarkerId(GetUpdateSampleName(UpdateNameMatchType.Full, false))); deepProfilingmarkerIdPath.Add(frameData.GetMarkerId(GetUpdateSampleName(UpdateNameMatchType.Full, true)));
gcAllocMarkerId = frameData.GetMarkerId(k_GCAllocSampleName); }
if (cpuSampleSelectionController.SetSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, gcAllocMarkerId, markerIdPath) || cpuSampleSelectionController.SetSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, gcAllocMarkerId, deepProfilingmarkerIdPath)) { Debug.LogWarning("MyScript allocates in its Update loop"); } else { // MyScript did not have a GC.Alloc sample underneath it, but maybe a different Update sample allocated // Remove the MyScript sample id from the path and search through all Update() samples markerIdPath.Remove(markerIdPath.Count - 1); if (cpuSampleSelectionController.SetSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, gcAllocMarkerId, markerIdPath)) { Debug.LogWarning($"MyScript does not allocate but {cpuSampleSelectionController.selection.markerNamePath[k_SampleNames.Count]} allocates in its Update loop"); return; } Debug.Log("No Script is allocating in its Update Loop"); FindAnyGCAllocSample(cpuSampleSelectionController); } } } } } } }
void FindAnyGCAllocSample(IProfilerFrameTimeViewSampleSelectionController cpuSampleSelectionController) { using (var frameData = ProfilerDriver.GetRawFrameDataView((int)m_Profiler.selectedFrameIndex, 0)) { var gcAllocMarkerId = frameData.GetMarkerId(k_GCAllocSampleName); for (int i = 0; i < frameData.sampleCount; i++) { if (frameData.GetSampleMarkerId(i) == gcAllocMarkerId) { var selection = new ProfilerTimeSampleSelection(m_Profiler.selectedFrameIndex, k_MainThreadGroupName, k_MainThreadName, frameData.threadId, i); if (cpuSampleSelectionController.SetSelection(selection)) { // do not use the selection object here. The CPU Profiler Module created a new one. // Instead Get the selection object from the Profiler Debug.LogWarning($"MyScript does not allocate but {cpuSampleSelectionController.selection.markerNamePath[k_SampleNames.Count]} allocates in its Update loop"); return; } } } Debug.Log("No Script is allocating anything"); } } }
Этот пример показывает все основные варианты SetSelection и показывает их различия.