完成可编译
This commit is contained in:
802
README.md
Normal file
802
README.md
Normal file
@@ -0,0 +1,802 @@
|
||||
# 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)
|
||||
|
||||
Reference in New Issue
Block a user