Version: 2023.2
Creating a custom module details panel
Profiling tools

低级原生插件 Profiler API

Unity 性能分析器是一种可用于分析和改进应用程序性能的工具。性能分析器对应用程序的原生 Unity 代码和托管代码中的函数调用执行帧时间分析。可以使用 ProfilerMarker API 将计时标记插入自己的代码中,以便更好地了解应用程序特定区域的性能。

可以使用低级原生插件性能分析器 API 扩展性能分析器并分析原生插件代码的性能,或是准备性能分析数据以发送到第三方性能分析工具,例如 Razor (PS4)、PIX(Xbox、Windows)、Chrome Tracing、ETW、ITT、Vtune 或 Telemetry。低级原生插件性能分析器 API 提供以下接口来用于 Unity 性能分析器与外部工具之间的通信:

  • IUnityProfiler:使用此接口可从 C/C++ 原生插件代码向 Unity 性能分析器添加检测事件。
  • IUnityProfilerCallbacks:使用此接口可拦截 Unity 性能分析器事件并将它们存储或重定向到其他工具。

IUnityProfiler API

使用 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;
}

IUnityProfilerCallbacks API 回调

原生性能分析器插件 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

UnitySystracePlugin 示例

您可以每帧动态注册和取消注册标记回调。以下示例根据第三方性能分析状态通过启用和禁用回调来最大限度降低性能分析开销。

| 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
  • GC.Alloc

Profiler.DefaultMarker

Profiler.DefaultMarker 是 Unity 为 Profiler.BeginSampleProfiler.EndSample 事件保留的标记。

在上面的示例中,kUnityProfilerMarkerEventTypeBegin eventType 对应于 Profiler.BeginSample 事件,具有以下数据:

  • Int32:UnityEngine.Object 实例 ID。如果未指定对象,则这为 0。
  • UInt16 数组:传递给 Profiler.BeginSample 的 UTF16 字符串。此大小以字节为单位。
  • UInt32:类别索引。

GC.Alloc

GC.Alloc 是与垃圾收集分配相对应的标记。该标记提供以下数据:

  • Int64:分配大小。
Creating a custom module details panel
Profiling tools