版本:Unity 6 (6000.0)
语言英语
  • C#

IProfilerFrameTimeViewSampleSelectionController.SetSelection

建议变更

成功!

感谢您帮助我们改进 Unity 文档的质量。尽管我们无法接受所有提交,但我们确实会阅读用户建议的每个变更,并在适用时进行更新。

关闭

提交失败

由于某种原因,无法提交您建议的变更。请在几分钟后<a>重试</a>。感谢您花时间帮助我们改进 Unity 文档的质量。

关闭

取消

声明

public bool SetSelection(Profiling.ProfilerTimeSampleSelection selection);

参数

selection 通过 ProfilerTimeSampleSelection 构造函数创建或先前通过 ProfilerWindow.selection 检索的描述完整的选择。

返回值

bool 如果成功设置选择项,则返回 true;如果由于找不到合适的样本而拒绝选择,则返回 false。

描述

在基于帧时间样本的 Profiler 模块(如 CPU 使用率模块GPU 使用率 Profiler 模块)中设置当前选择。

如果您不知道要选择的样本的 rawSampleIndex,您可以使用 RawFrameDataView 或扩展方法 ProfilerEditorUtility.SetSelection 来查找它。此扩展方法按名称或 Profiler 标记 ID、帧和发生线程来搜索样本,并可以选择应找到样本的样本堆栈。

当 Profiler 窗口中没有可用的帧数据时,Unity 会引发异常。您可以检查 ProfilerWindow.firstAvailableFrameIndex 是否大于或等于 0 来验证帧数据是否可用。

当选择对象的帧索引超出 ProfilerWindow.firstAvailableFrameIndexProfilerWindow.lastAvailableFrameIndex 描述的范围时,Unity 会引发 ArgumentOutOfRangeException。

当找不到选择对象引用的线程时,Unity 会引发 ArgumentException。

其他资源:ProfilerEditorUtility.SetSelectionIProfilerFrameTimeViewSampleSelectionController.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 的所有主要变化及其差异。