Unity 性能分析器是一种可用于分析和改进应用程序性能的工具。性能分析器对应用程序的原生 Unity 代码和托管代码中的函数调用执行帧时间分析。可以使用 ProfilerMarker API 将计时标记插入自己的代码中,以便更好地了解应用程序特定区域的性能。
可以使用低级原生插件性能分析器 API 扩展性能分析器并分析原生插件代码的性能,或是准备性能分析数据以发送到第三方性能分析工具,例如 Razor (PS4)、PIX(Xbox、Windows)、Chrome Tracing、ETW、ITT、Vtune 或 Telemetry。低级原生插件性能分析器 API 提供以下接口来用于 Unity 性能分析器与外部工具之间的通信:
使用 IUnityProfiler 插件 API 可将检测添加到原生插件的 C/C++ 代码。
该插件 API 由 IUnityProfiler 接口表示,该接口在 IUnityProfiler.h
头文件中进行声明。Unity 将该头文件存储在 Unity 安装的 <UnityInstallPath>\Editor\Data\PluginAPI
文件夹中。(在 macOS 上,右键单击 Unity 应用程序,然后选择 Show Package Contents。该头文件处于 Contents\PluginAPI
中)。
方法 | 描述 |
---|---|
CreateMarker |
创建性能分析器标记,它表示命名检测范围,随后可以用于生成检测样本。 |
SetMarkerMetadataName |
指定可以与检测样本一起传递以用于性能分析器标记的自定义参数名称。 |
BeginSample |
开始按性能分析器标记命名的代码的检测部分。 |
EndSample |
结束检测部分。 |
EmitEvent |
使用元数据发出通用事件。 |
IsEnabled |
如果性能分析器在捕获数据,则返回 1。 |
IsAvailable |
对于可使用性能分析器的编辑器或开发播放器返回 1,对于发布播放器返回 0。 |
RegisterThread |
以指定名称注册当前线程。 |
UnregisterThread |
从性能分析器取消注册当前线程。 |
此示例生成性能分析器事件,这些事件随后会显示在 Profiler 窗口中。
# include <IUnityInterface.h>
# include <IUnityProfiler.h>
static IUnityProfiler* s_UnityProfiler = NULL;
static const UnityProfilerMarkerDesc* s_MyPluginMarker = NULL;
static bool s_IsDevelopmentBuild = false;
static void MyPluginWorkMethod()
{
if (s_IsDevelopmentBuild)
s_UnityProfiler->BeginSample(s_MyPluginMarker);
// 希望在 Unity 性能分析器中显示为"MyPluginMethod"的代码。
// ...
if (s_IsDevelopmentBuild)
s_UnityProfiler->EndSample(s_MyPluginMarker);
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
s_UnityProfiler = unityInterfaces->Get<IUnityProfiler>();
if (s_UnityProfiler == NULL)
return;
s_IsDevelopmentBuild = s_UnityProfiler->IsAvailable() != 0;
s_UnityProfiler->CreateMarker(&s_MyPluginMarker, "MyPluginMethod", kUnityProfilerCategoryOther, kUnityProfilerMarkerFlagDefault, 0);
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload()
{
s_UnityProfiler = NULL;
}
原生性能分析器插件 API 在 Unity 子系统与第三方性能分析 API 之间提供了接口,以便可以使用外部性能分析工具对 Unity 应用程序进行性能分析。IUnityProfilerCallbacks 头文件公开了该 API,Unity 将它存储在 Unity 安装的 <UnityInstallPath>\Editor\Data\PluginAPI
文件夹中。(在 macOS 上,右键单击 Unity 应用程序,然后选择 Show Package Contents。该头文件处于 Contents\PluginAPI
中)。
以下 Unity 性能分析器功能有助于捕获检测数据,以便可以分析应用程序的性能。
性能分析器功能 | 描述 |
---|---|
类别 | Unity 将性能分析数据分组到不同类别,例如渲染 (Rendering)、脚本 (Scripting) 和动画 (Animation),并为每个类别指定颜色。进行颜色编码的类别有助于在 Profiler 窗口中直观区分数据类型。性能分析器原生插件 API 可以获取这些颜色,以便可以在外部性能分析工具中使用它们。 |
用途标志 | 用途标志充当过滤器,可减少 Unity 发送到外部性能分析工具的数据量。在 Unity 将性能分析数据发送到外部工具之前,可以使用用途标志从这些数据中移除不必要的信息。性能分析器将以下用途标志应用于事件标记,以便可以过滤数据: 可用性标志,标记在 Unity 编辑器、开发播放器或发布播放器中是否可使用标记。 详细级别,与在编辑器中执行的任务类型,以及任务所需的信息级别(例如内部、调试或用户级别)相关。 |
帧事件 | 可以使用性能分析器原生插件 API 在外部性能分析工具中执行帧时间分析。 |
线程性能分析 | Unity 在线程(例如,主线程、渲染线程和作业系统工作线程)上执行大量工作。可以使用性能分析器原生插件 API 在任何线程上启用性能分析。 |
要在外部分析器中使用 Unity 性能分析器生成的检测数据,可以在集成第三方性能分析器的 C/C++ 插件代码中使用最少量的回调:
回调 | 功能 |
---|---|
RegisterCreateCategoryCallback |
注册 IUnityProfilerCreateCategoryCallback 回调,以便每当 Unity 创建类别时都获取性能分析器类别名称和颜色。 |
RegisterCreateMarkerCallback |
注册 IUnityProfilerCreateMarkerCallback 回调,每当 Unity 创建标记时都会调用该回调。使用它可获取标记的名称、性能分析器类别和用途标志。回调函数的 const UnityProfilerMarkerDesc* markerDesc 参数表示指向标记描述的持久性指针,可以用于在 RegisterMarkerEventCallback 中过滤标记。 |
RegisterMarkerEventCallback |
注册 IUnityProfilerMarkerEventCallback 回调,Unity 在发生单次事件、限定范围的事件、内存分配事件或垃圾收集事件时会调用该回调。随后可以使用此回调在外部性能分析工具中调用相关函数。注意:Unity 使用 GC.Alloc 标记表示内存分配事件,使用 GC.Collect 标记表示垃圾收集事件。 |
RegisterFrameCallback |
将样本封装到逻辑帧中,以便不使用帧的外部性能分析工具可以使用这些样本。还注册一个回调,Unity 性能分析器在 Unity 启动下一个逻辑 CPU 帧时会运行该回调。 |
RegisterCreateThreadCallback |
注册一个回调,以便每当 Unity 注册用于性能分析的线程时都可获取内部线程名称。 |
此示例演示如何将 Unity 性能分析器事件传递给另一个具有压入/弹出语义的性能分析器。它提供两种函数:
void MyProfilerPushMarker(const char* name)
- 压入命名标记。void MyProfilerPopMarker()
- 弹出检测标记。以下示例提供将开始和结束检测事件从 Unity 性能分析器传递到外部性能分析器所需的最小实现:
# include <IUnityInterface.h>
# include <IUnityProfilerCallbacks.h>
static IUnityProfilerCallbacks* s_UnityProfilerCallbacks = NULL;
static void UNITY_INTERFACE_API MyProfilerEventCallback(const UnityProfilerMarkerDesc* markerDesc, UnityProfilerMarkerEventType eventType, unsigned short eventDataCount, const UnityProfilerMarkerData* eventData, void* userData)
{
switch (eventType)
{
case kUnityProfilerMarkerEventTypeBegin:
{
MyProfilerPushMarker(markerDesc->name);
break;
}
case kUnityProfilerMarkerEventTypeEnd:
{
MyProfilerPopMarker();
break;
}
}
}
static void UNITY_INTERFACE_API MyProfilerCreateMarkerCallback(const UnityProfilerMarkerDesc* markerDesc, void* userData)
{
s_UnityProfilerCallbacks->RegisterMarkerEventCallback(markerDesc, MyProfilerEventCallback, NULL);
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
s_UnityProfilerCallbacks = unityInterfaces->Get<IUnityProfilerCallbacks>();
s_UnityProfilerCallbacks->RegisterCreateMarkerCallback(&MyProfilerCreateMarkerCallback, NULL);
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload()
{
s_UnityProfilerCallbacks->UnregisterCreateMarkerCallback(&MyProfilerCreateMarkerCallback, NULL);
s_UnityProfilerCallbacks->UnregisterMarkerEventCallback(NULL, &MyProfilerEventCallback, NULL);
}
注意:要从所有标记中取消注册给定的回调,请将第一个参数设置为 null
后运行 UnregisterEventCallback
。
您可以每帧动态注册和取消注册标记回调。以下示例根据第三方性能分析状态通过启用和禁用回调来最大限度降低性能分析开销。
| static void UNITY_INTERFACE_API SystraceFrameCallback(void* userData)
{
bool isCapturing = ATrace_isEnabled();
if (isCapturing != s_isCapturing)
{
s_isCapturing = isCapturing;
if (isCapturing)
{
s_UnityProfilerCallbacks->
RegisterCreateMarkerCallback(SystraceCreateEventCallback, NULL);
}
else
{
s_UnityProfilerCallbacks->
UnregisterCreateMarkerCallback(SystraceCreateEventCallback, NULL);
s_UnityProfilerCallbacks->
UnregisterMarkerEventCallback(NULL, SystraceEventCallback, NULL);
}
}
}
注意:要从所有标记中取消注册给定的回调,请将第一个参数设置为 null
后运行 UnregisterEventCallback
。
Unity 具有以下包含有用元数据的特殊标记:
Profiler.DefaultMarker
是 Unity 为 Profiler.BeginSample 和 Profiler.EndSample 事件保留的标记。
在上面的示例中,kUnityProfilerMarkerEventTypeBegin eventType
对应于 Profiler.BeginSample
事件,具有以下数据:
UnityEngine.Object
实例 ID。如果未指定对象,则这为 0。Profiler.BeginSample
的 UTF16 字符串。此大小以字节为单位。GC.Alloc
是与垃圾收集分配相对应的标记。该标记提供以下数据: