# Tracy 集成到 Unity 的完整指南

## 概述

Tracy 是一个强大的 C++ 性能分析工具，要在 Unity 中使用它，需要通过 **Native Plugin** 的方式集成。本指南将介绍两种主要的集成方法。

---

## 方法一：通过 Native Plugin 集成（推荐）

这是最常用和最灵活的方法，适用于所有Unity项目。

### 1. 创建 Tracy Native Plugin

#### 步骤 1：创建 C++ 动态库项目

创建一个新的 C++ DLL/Shared Library 项目：

**目录结构：**
```
UnityTracyPlugin/
├── src/
│   ├── TracyUnityPlugin.cpp
│   └── TracyUnityPlugin.h
├── include/
│   └── tracy/              # Tracy 源代码
├── CMakeLists.txt
└── build/
```

#### 步骤 2：编写 Plugin 代码

**TracyUnityPlugin.h:**
```cpp
#ifndef TRACY_UNITY_PLUGIN_H
#define TRACY_UNITY_PLUGIN_H

// Unity Native Plugin 需要使用 C 导出
#ifdef __cplusplus
extern "C" {
#endif

// 平台定义
#if defined(_WIN32) || defined(_WIN64)
    #define UNITY_PLUGIN_EXPORT __declspec(dllexport)
#elif defined(__APPLE__) || defined(__linux__)
    #define UNITY_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
    #define UNITY_PLUGIN_EXPORT
#endif

// Tracy API
UNITY_PLUGIN_EXPORT void TracyInit();
UNITY_PLUGIN_EXPORT void TracyShutdown();
UNITY_PLUGIN_EXPORT void TracyFrameMark();
UNITY_PLUGIN_EXPORT void TracyBeginZone(const char* name, const char* function, const char* file, int line);
UNITY_PLUGIN_EXPORT void TracyEndZone();
UNITY_PLUGIN_EXPORT void TracyPlotValue(const char* name, double value);
UNITY_PLUGIN_EXPORT void TracyMessage(const char* message);
UNITY_PLUGIN_EXPORT void TracySetThreadName(const char* name);

#ifdef __cplusplus
}
#endif

#endif // TRACY_UNITY_PLUGIN_H
```

**TracyUnityPlugin.cpp:**
```cpp
#include "TracyUnityPlugin.h"
#include "tracy/Tracy.hpp"
#include <string>
#include <unordered_map>
#include <mutex>

// 用于管理 Zone 的结构
struct ZoneContext {
    tracy::ScopedZone* zone;
};

static std::unordered_map<int, ZoneContext> g_zones;
static std::mutex g_zonesMutex;
static int g_nextZoneId = 1;

extern "C" {

UNITY_PLUGIN_EXPORT void TracyInit() {
    // Tracy 会自动初始化，这里可以做一些额外的设置
}

UNITY_PLUGIN_EXPORT void TracyShutdown() {
    std::lock_guard<std::mutex> lock(g_zonesMutex);
    g_zones.clear();
}

UNITY_PLUGIN_EXPORT void TracyFrameMark() {
    FrameMark;
}

UNITY_PLUGIN_EXPORT int TracyBeginZone(const char* name, const char* function, const char* file, int line) {
    std::lock_guard<std::mutex> lock(g_zonesMutex);
    
    int zoneId = g_nextZoneId++;
    
    // 创建 Zone（注意：这是简化版本，实际使用中需要更复杂的处理）
    tracy::SourceLocationData loc;
    loc.name = name;
    loc.function = function;
    loc.file = file;
    loc.line = (uint32_t)line;
    
    // 实际使用中，你可能需要使用 Tracy 的宏来正确创建 Zone
    // 这里仅作示例
    
    return zoneId;
}

UNITY_PLUGIN_EXPORT void TracyEndZone(int zoneId) {
    std::lock_guard<std::mutex> lock(g_zonesMutex);
    
    auto it = g_zones.find(zoneId);
    if (it != g_zones.end()) {
        delete it->second.zone;
        g_zones.erase(it);
    }
}

UNITY_PLUGIN_EXPORT void TracyPlotValue(const char* name, double value) {
    TracyPlot(name, value);
}

UNITY_PLUGIN_EXPORT void TracyMessage(const char* message) {
    TracyMessage(message, strlen(message));
}

UNITY_PLUGIN_EXPORT void TracySetThreadName(const char* name) {
    tracy::SetThreadName(name);
}

} // extern "C"
```

#### 步骤 3：CMakeLists.txt 配置

```cmake
cmake_minimum_required(VERSION 3.10)
project(UnityTracyPlugin)

set(CMAKE_CXX_STANDARD 17)

# Tracy 配置
option(TRACY_ENABLE "Enable Tracy profiling" ON)
option(TRACY_ON_DEMAND "Tracy on-demand profiling" ON)

add_definitions(-DTRACY_ENABLE)
add_definitions(-DTRACY_ON_DEMAND)

# Tracy 源文件
set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/tracy")
set(TRACY_SOURCES
    ${TRACY_DIR}/public/TracyClient.cpp
)

# Plugin 源文件
set(PLUGIN_SOURCES
    src/TracyUnityPlugin.cpp
    ${TRACY_SOURCES}
)

# 包含目录
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/src
    ${TRACY_DIR}/public
)

# 创建动态库
add_library(UnityTracyPlugin SHARED ${PLUGIN_SOURCES})

# Windows 平台设置
if(WIN32)
    target_link_libraries(UnityTracyPlugin ws2_32 dbghelp)
endif()

# macOS/iOS 平台设置
if(APPLE)
    set_target_properties(UnityTracyPlugin PROPERTIES
        FRAMEWORK FALSE
        MACOSX_RPATH TRUE
    )
endif()

# Linux/Android 平台设置
if(UNIX AND NOT APPLE)
    target_link_libraries(UnityTracyPlugin pthread dl)
endif()
```

#### 步骤 4：编译 Plugin

```bash
# Windows
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -A x64
cmake --build . --config Release

# macOS
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make

# Linux
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
```

### 2. Unity 端集成

#### 步骤 1：将编译好的 DLL 放入 Unity 项目

```
UnityProject/
└── Assets/
    └── Plugins/
        ├── x86_64/
        │   └── UnityTracyPlugin.dll        # Windows 64位
        ├── x86/
        │   └── UnityTracyPlugin.dll        # Windows 32位
        ├── Android/
        │   ├── arm64-v8a/
        │   │   └── libUnityTracyPlugin.so  # Android ARM64
        │   └── armeabi-v7a/
        │       └── libUnityTracyPlugin.so  # Android ARM32
        ├── iOS/
        │   └── libUnityTracyPlugin.a       # iOS 静态库
        └── macOS/
            └── libUnityTracyPlugin.dylib   # macOS
```

#### 步骤 2：创建 C# Wrapper

**Tracy.cs:**
```csharp
using System;
using System.Runtime.InteropServices;
using UnityEngine;

namespace TracyProfiler
{
    /// <summary>
    /// Tracy 性能分析器的 Unity 封装
    /// </summary>
    public static class Tracy
    {
        private const string DLL_NAME = "UnityTracyPlugin";

        #region Native Methods

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracyInit();

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracyShutdown();

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracyFrameMark();

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern int TracyBeginZone(string name, string function, string file, int line);

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracyEndZone(int zoneId);

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracyPlotValue(string name, double value);

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracyMessage(string message);

        [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
        private static extern void TracySetThreadName(string name);

        #endregion

        private static bool s_initialized = false;

        /// <summary>
        /// 初始化 Tracy
        /// </summary>
        public static void Initialize()
        {
            if (s_initialized) return;

            try
            {
                TracyInit();
                s_initialized = true;
                Debug.Log("Tracy Profiler 已初始化");
            }
            catch (Exception e)
            {
                Debug.LogError($"Tracy 初始化失败: {e.Message}");
            }
        }

        /// <summary>
        /// 关闭 Tracy
        /// </summary>
        public static void Shutdown()
        {
            if (!s_initialized) return;

            try
            {
                TracyShutdown();
                s_initialized = false;
            }
            catch (Exception e)
            {
                Debug.LogError($"Tracy 关闭失败: {e.Message}");
            }
        }

        /// <summary>
        /// 标记帧边界（通常在每帧末尾调用）
        /// </summary>
        public static void MarkFrame()
        {
            if (!s_initialized) return;
            TracyFrameMark();
        }

        /// <summary>
        /// 绘制数值（用于实时监控变量）
        /// </summary>
        public static void Plot(string name, double value)
        {
            if (!s_initialized) return;
            TracyPlotValue(name, value);
        }

        /// <summary>
        /// 发送消息到 Tracy
        /// </summary>
        public static void Message(string message)
        {
            if (!s_initialized) return;
            TracyMessage(message);
        }

        /// <summary>
        /// 设置当前线程名称
        /// </summary>
        public static void SetThreadName(string name)
        {
            if (!s_initialized) return;
            TracySetThreadName(name);
        }

        /// <summary>
        /// Tracy Zone 的作用域包装器（使用 using 语句自动管理生命周期）
        /// </summary>
        public struct ZoneScope : IDisposable
        {
            private int zoneId;
            private bool isValid;

            public ZoneScope(string name, 
                           [System.Runtime.CompilerServices.CallerMemberName] string function = "",
                           [System.Runtime.CompilerServices.CallerFilePath] string file = "",
                           [System.Runtime.CompilerServices.CallerLineNumber] int line = 0)
            {
                if (s_initialized)
                {
                    zoneId = TracyBeginZone(name, function, file, line);
                    isValid = true;
                }
                else
                {
                    zoneId = -1;
                    isValid = false;
                }
            }

            public void Dispose()
            {
                if (isValid && s_initialized)
                {
                    TracyEndZone(zoneId);
                }
            }
        }

        /// <summary>
        /// 创建一个 Tracy Zone（性能追踪区域）
        /// 使用 using 语句确保自动结束
        /// </summary>
        public static ZoneScope BeginZone(string name)
        {
            return new ZoneScope(name);
        }
    }
}
```

#### 步骤 3：创建 Tracy Manager

**TracyManager.cs:**
```csharp
using UnityEngine;

namespace TracyProfiler
{
    /// <summary>
    /// Tracy 管理器 - 负责初始化和每帧更新
    /// </summary>
    public class TracyManager : MonoBehaviour
    {
        [Header("Tracy Settings")]
        [SerializeField] private bool enableOnStart = true;
        [SerializeField] private bool markFrames = true;

        private void Awake()
        {
            // 确保只有一个实例
            if (FindObjectsOfType<TracyManager>().Length > 1)
            {
                Destroy(gameObject);
                return;
            }

            DontDestroyOnLoad(gameObject);

            if (enableOnStart)
            {
                Tracy.Initialize();
            }
        }

        private void LateUpdate()
        {
            // 在每帧末尾标记帧边界
            if (markFrames)
            {
                Tracy.MarkFrame();
            }

            // 示例：绘制一些有用的性能数据
            Tracy.Plot("FPS", 1.0f / Time.deltaTime);
            Tracy.Plot("Frame Time (ms)", Time.deltaTime * 1000.0f);
            Tracy.Plot("Total Allocated Memory (MB)", GC.GetTotalMemory(false) / (1024.0 * 1024.0));
        }

        private void OnDestroy()
        {
            Tracy.Shutdown();
        }

        private void OnApplicationQuit()
        {
            Tracy.Shutdown();
        }
    }
}
```

#### 步骤 4：使用示例

**GameManager.cs:**
```csharp
using UnityEngine;
using TracyProfiler;

public class GameManager : MonoBehaviour
{
    private void Update()
    {
        // 追踪整个 Update 方法
        using (Tracy.BeginZone("GameManager.Update"))
        {
            ProcessInput();
            UpdateGameLogic();
            UpdateAI();
        }
    }

    private void ProcessInput()
    {
        using (Tracy.BeginZone("ProcessInput"))
        {
            // 输入处理代码
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Tracy.Message("玩家按下空格键");
            }
        }
    }

    private void UpdateGameLogic()
    {
        using (Tracy.BeginZone("UpdateGameLogic"))
        {
            // 游戏逻辑代码
            for (int i = 0; i < 1000; i++)
            {
                // 一些计算
            }
        }
    }

    private void UpdateAI()
    {
        using (Tracy.BeginZone("UpdateAI"))
        {
            // AI 更新代码
            Tracy.Plot("Enemy Count", GameObject.FindGameObjectsWithTag("Enemy").Length);
        }
    }
}
```

**PhysicsController.cs:**
```csharp
using UnityEngine;
using TracyProfiler;

public class PhysicsController : MonoBehaviour
{
    private void FixedUpdate()
    {
        using (Tracy.BeginZone("PhysicsController.FixedUpdate"))
        {
            PerformPhysicsCalculations();
        }
    }

    private void PerformPhysicsCalculations()
    {
        using (Tracy.BeginZone("Physics Calculations"))
        {
            // 物理计算
            Rigidbody[] rigidbodies = FindObjectsOfType<Rigidbody>();
            Tracy.Plot("Active Rigidbodies", rigidbodies.Length);
            
            foreach (var rb in rigidbodies)
            {
                // 处理刚体
            }
        }
    }
}
```

---

## 方法二：IL2CPP 后端集成

当 Unity 使用 IL2CPP 作为脚本后端时，可以直接在生成的 C++ 代码中集成 Tracy。

### 1. 配置 IL2CPP

在 Unity 项目中创建 `il2cpp_custom.cpp`：

```cpp
// 放置在: Assets/Plugins/IL2CPP/il2cpp_custom.cpp

#include "il2cpp-config.h"

// 包含 Tracy
#define TRACY_ENABLE
#include "tracy/Tracy.hpp"

// IL2CPP 钩子函数
extern "C" void UnityPluginLoad(void* unityInterfaces)
{
    // Tracy 会自动初始化
}

extern "C" void UnityPluginUnload()
{
    // Tracy 清理
}
```

### 2. 修改 IL2CPP 构建设置

在 Unity 的 Player Settings 中：
1. 切换到 IL2CPP 脚本后端
2. 添加额外的编译参数：`-DTRACY_ENABLE -DTRACY_ON_DEMAND`
3. 链接 Tracy 库

---

## 方法三：Unity Profiler 集成（可选）

可以创建一个桥接层，将 Unity Profiler 的数据转发到 Tracy。

**UnityToTracyBridge.cs:**
```csharp
using UnityEngine;
using UnityEngine.Profiling;
using TracyProfiler;

public class UnityToTracyBridge : MonoBehaviour
{
    private CustomSampler[] samplers;

    private void Start()
    {
        // 创建自定义采样器
        samplers = new CustomSampler[]
        {
            CustomSampler.Create("Render"),
            CustomSampler.Create("Physics"),
            CustomSampler.Create("Scripts"),
            CustomSampler.Create("GarbageCollector"),
        };
    }

    private void LateUpdate()
    {
        // 将 Unity Profiler 数据转发到 Tracy
        foreach (var sampler in samplers)
        {
            Tracy.Plot(sampler.name + " (ms)", sampler.GetRecorder().elapsedNanoseconds / 1000000.0);
        }

        // 内存统计
        Tracy.Plot("Used Heap Size (MB)", Profiler.usedHeapSizeLong / (1024.0 * 1024.0));
        Tracy.Plot("Total Allocated Memory (MB)", Profiler.GetTotalAllocatedMemoryLong() / (1024.0 * 1024.0));
        Tracy.Plot("Total Reserved Memory (MB)", Profiler.GetTotalReservedMemoryLong() / (1024.0 * 1024.0));
    }
}
```

---

## 平台特定配置

### Windows
- 使用 Visual Studio 编译 DLL
- 确保链接 `ws2_32.lib` 和 `dbghelp.lib`

### macOS / iOS
- 使用 Xcode 编译动态库或框架
- 注意代码签名要求

### Android
- 为不同的 ABI 编译：armeabi-v7a, arm64-v8a, x86, x86_64
- 在 `gradle.properties` 中配置 NDK 路径

### Linux
- 链接 `pthread` 和 `dl`
- 确保 GLIBC 版本兼容

---

## 使用 Tracy Profiler 查看数据

### 1. 启动 Tracy Profiler

```bash
# 从 Tracy 仓库构建或下载预编译版本
# 运行 Tracy.exe (Windows) 或 Tracy (macOS/Linux)
```

### 2. 连接到 Unity 应用

- Tracy Profiler 会自动发现本地网络中的 Tracy 客户端
- 点击连接即可开始分析

### 3. 查看性能数据

- **时间线视图**: 查看各个 Zone 的执行时间
- **统计视图**: 查看函数调用次数和平均耗时
- **内存视图**: 查看内存分配情况
- **图表视图**: 查看 Plot 数据的实时曲线

---

## 最佳实践

### 1. 性能开销
- Tracy 的开销很小，但仍建议在 Release 构建中禁用细粒度的 Zone
- 使用条件编译：`#if TRACY_ENABLE`

### 2. Zone 命名
- 使用清晰、描述性的名称
- 包含类名和方法名，如 `"PlayerController.Move"`

### 3. 合理使用 Plot
- 不要在每帧绘制过多数据点
- 专注于关键性能指标

### 4. 多线程支持
- Unity 的 Job System 需要特殊处理
- 在每个 Job 中调用 `Tracy.SetThreadName()`

### 5. 移动平台
- 注意无线网络连接的延迟
- 可以使用 Tracy 的文件保存功能，之后再分析

---

## 故障排除

### 问题 1: DLL 加载失败
**解决方案:**
- 检查 Plugin 导入设置中的平台配置
- 确保 DLL 与 Unity 架构匹配（x64/x86）
- 检查依赖库是否缺失

### 问题 2: Tracy Profiler 无法连接
**解决方案:**
- 确保防火墙允许 TCP 端口 8086
- 检查 `TRACY_ON_DEMAND` 是否正确定义
- 确认 Tracy.Initialize() 已被调用

### 问题 3: 性能数据不准确
**解决方案:**
- 使用 Release 构建测试
- 禁用 Unity Editor 的 Deep Profiling
- 确保 V-Sync 和帧率限制的设置符合预期

### 问题 4: Android 构建失败
**解决方案:**
- 检查 NDK 版本兼容性
- 在 `build.gradle` 中添加必要的链接器标志
- 确保所有 ABI 的库都已编译

---

## 进阶功能

### 1. 自定义内存分配追踪

```cpp
// 在 Plugin 中添加
UNITY_PLUGIN_EXPORT void TracyAllocNamed(void* ptr, size_t size, const char* name) {
    TracyAllocN(ptr, size, name);
}

UNITY_PLUGIN_EXPORT void TracyFreeNamed(void* ptr, const char* name) {
    TracyFreeN(ptr, name);
}
```

```csharp
// 在 C# 中使用
public class TrackedMemoryPool
{
    public void Allocate(int size)
    {
        IntPtr ptr = Marshal.AllocHGlobal(size);
        Tracy.AllocNamed(ptr, size, "Memory Pool");
    }

    public void Free(IntPtr ptr)
    {
        Tracy.FreeNamed(ptr, "Memory Pool");
        Marshal.FreeHGlobal(ptr);
    }
}
```

### 2. GPU 性能追踪

Tracy 支持 OpenGL、Vulkan、DirectX 的 GPU 追踪，但需要在渲染管线中集成。

### 3. 锁竞争分析

```cpp
// 在 Plugin 中
#define LockableName(type, varname) TracyLockableN(type, varname, #varname)
```

---

## 总结

将 Tracy 集成到 Unity 中可以提供比 Unity Profiler 更详细的性能分析数据，特别是对于：

1. **Native Code 性能** - 如果你使用了大量 Native Plugin
2. **多线程分析** - Tracy 的线程视图非常直观
3. **跨平台分析** - 统一的工具链
4. **帧级别分析** - 精确到微秒的时间线

根据你的项目需求，选择合适的集成方法。对于大多数项目，**方法一（Native Plugin）** 是最推荐的方式。

---

## 参考资源

- [Tracy 官方仓库](https://github.com/wolfpld/tracy)
- [Tracy 手册](https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf)
- [Unity Native Plugin 文档](https://docs.unity3d.com/Manual/NativePlugins.html)
- [IL2CPP 脚本后端](https://docs.unity3d.com/Manual/IL2CPP.html)

