803 lines
19 KiB
Markdown
803 lines
19 KiB
Markdown
# 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)
|
||
|