using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using Unity.Profiling;
using System.Diagnostics;
using Debug = UnityEngine.Debug;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace AibisDream.Utility
{
///
/// 性能分析工具,用于监控游戏性能
///
public class PerformanceProfiler : MonoBehaviour
{
[Header("性能监控设置")]
public bool enableProfiling = true;
public bool showFPS = true;
public bool showMemory = true;
public bool showDrawCalls = true;
public bool showBatches = true;
public bool showTris = true;
public bool showVerts = true;
[Header("数据记录设置")]
public bool enableCsvLogging = false;
public float logInterval = 0.5f; // 秒
public string csvFileName = "performance_log";
[Header("性能警告阈值")]
public float fpsWarningThreshold = 30f;
public float memoryWarningThreshold = 100f; // MB
public int drawCallWarningThreshold = 100;
public int batchWarningThreshold = 50;
[Header("显示设置")]
public Color normalColor = Color.white;
public Color warningColor = Color.yellow;
public Color errorColor = Color.red;
public int fontSize = 14;
[Header("CPU/GPU 时间分析")]
public bool showCPUTime = true;
public bool showGPUTime = true;
public float cpuTimeWarningThreshold = 16.67f; // 60fps对应的帧时间
public float gpuTimeWarningThreshold = 16.67f;
[Header("GC监控")]
public bool showGCStats = true;
public int gcWarningThreshold = 5; // 每秒GC次数警告阈值
[Header("资源监控")]
public bool showResourceStats = true;
public long textureMemoryWarningThreshold = 1024; // MB
public long meshMemoryWarningThreshold = 512; // MB
[Header("系统性能分析")]
public bool enableSystemProfiling = true;
public float systemTimeWarningThreshold = 8f; // ms
private float deltaTime = 0.0f;
private float cpuFrameTime;
private float gpuFrameTime;
private long lastGCCount;
private float gcTimeAccumulator;
private int gcCountInLastSecond;
private int loadedTextureCount;
private int loadedMeshCount;
private long totalTextureMemory;
private long totalMeshMemory;
private Dictionary systemProfilers = new Dictionary();
public class CustomProfiler
{
public string name;
public float totalTime;
public float maxTime;
public int sampleCount;
public Stopwatch stopwatch;
public float AverageTime => sampleCount > 0 ? totalTime / sampleCount : 0;
public void Reset()
{
totalTime = 0;
maxTime = 0;
sampleCount = 0;
stopwatch = new Stopwatch();
}
}
private float fps = 0.0f;
private float memoryUsage = 0.0f;
private int drawCalls = 0;
private int batches = 0;
private int triangles = 0;
private int vertices = 0;
private GUIStyle style;
private List performanceWarnings = new List();
// CSV 日志
private StreamWriter csvWriter;
private float nextLogTime = 0f;
private string logFilePath;
private void Start()
{
if (!enableProfiling) return;
// 创建GUI样式
style = new GUIStyle();
style.fontSize = fontSize;
style.normal.textColor = normalColor;
// 启动性能监控协程
StartCoroutine(PerformanceMonitor());
// 初始化CSV日志
if (enableCsvLogging)
{
InitCsvLogging();
}
}
private void Update()
{
if (!enableProfiling) return;
// 计算FPS
deltaTime += (Time.unscaledDeltaTime - deltaTime) * 0.1f;
fps = 1.0f / deltaTime;
// 获取渲染统计信息
#if UNITY_EDITOR
drawCalls = UnityEditor.UnityStats.drawCalls;
batches = UnityEditor.UnityStats.batches;
triangles = UnityEditor.UnityStats.triangles;
vertices = UnityEditor.UnityStats.vertices;
#else
drawCalls = 0;
batches = 0;
triangles = 0;
vertices = 0;
#endif
// 获取内存使用情况
memoryUsage = Profiler.GetTotalAllocatedMemoryLong() / (1024f * 1024f); // 转换为MB
// CPU和GPU时间
cpuFrameTime = Time.unscaledDeltaTime * 1000f; // 转换为毫秒
if (FrameTimingManager.IsFeatureEnabled())
{
var frameData = new FrameTiming[1];
FrameTimingManager.GetLatestTimings(1, frameData);
if (frameData.Length > 0)
{
gpuFrameTime = (float)frameData[0].gpuFrameTime;
}
}
// GC监控
if (System.GC.CollectionCount(0) > lastGCCount)
{
gcCountInLastSecond++;
lastGCCount = System.GC.CollectionCount(0);
}
// 资源监控
if (showResourceStats)
{
var textures = Resources.FindObjectsOfTypeAll();
var meshes = Resources.FindObjectsOfTypeAll();
loadedTextureCount = textures.Length;
loadedMeshCount = meshes.Length;
totalTextureMemory = 0;
foreach (var tex in textures)
{
if (tex != null)
totalTextureMemory += Profiler.GetRuntimeMemorySizeLong(tex);
}
totalMeshMemory = 0;
foreach (var mesh in meshes)
{
if (mesh != null)
totalMeshMemory += Profiler.GetRuntimeMemorySizeLong(mesh);
}
}
// 按间隔写入CSV
if (enableCsvLogging && csvWriter != null && Time.unscaledTime >= nextLogTime)
{
WriteCsvRow();
nextLogTime = Time.unscaledTime + Mathf.Max(0.05f, logInterval);
gcCountInLastSecond = 0; // 重置GC计数器
}
}
private System.Collections.IEnumerator PerformanceMonitor()
{
while (enableProfiling)
{
performanceWarnings.Clear();
// 检查FPS
if (fps < fpsWarningThreshold)
{
performanceWarnings.Add($"FPS过低: {fps:F1}");
}
// 检查内存使用
if (memoryUsage > memoryWarningThreshold)
{
performanceWarnings.Add($"内存使用过高: {memoryUsage:F1}MB");
}
// 检查绘制调用
if (drawCalls > drawCallWarningThreshold)
{
performanceWarnings.Add($"绘制调用过多: {drawCalls}");
}
// 检查批处理
if (batches > batchWarningThreshold)
{
performanceWarnings.Add($"批处理过多: {batches}");
}
yield return new WaitForSeconds(1f);
}
}
private void OnGUI()
{
if (!enableProfiling) return;
float yPos = 10f;
float lineHeight = fontSize + 5f;
// 显示FPS
if (showFPS)
{
Color fpsColor = fps < fpsWarningThreshold ? errorColor : normalColor;
style.normal.textColor = fpsColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"FPS: {fps:F1}", style);
yPos += lineHeight;
}
// 显示内存使用
if (showMemory)
{
Color memColor = memoryUsage > memoryWarningThreshold ? warningColor : normalColor;
style.normal.textColor = memColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"内存: {memoryUsage:F1}MB", style);
yPos += lineHeight;
}
// 显示绘制调用
if (showDrawCalls)
{
Color drawColor = drawCalls > drawCallWarningThreshold ? warningColor : normalColor;
style.normal.textColor = drawColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"绘制调用: {drawCalls}", style);
yPos += lineHeight;
}
// 显示批处理
if (showBatches)
{
Color batchColor = batches > batchWarningThreshold ? warningColor : normalColor;
style.normal.textColor = batchColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"批处理: {batches}", style);
yPos += lineHeight;
}
// 显示三角形数量
if (showTris)
{
style.normal.textColor = normalColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"三角形: {triangles:N0}", style);
yPos += lineHeight;
}
// 显示顶点数量
if (showVerts)
{
style.normal.textColor = normalColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"顶点: {vertices:N0}", style);
yPos += lineHeight;
}
// 显示CPU时间
if (showCPUTime)
{
Color timeColor = cpuFrameTime > cpuTimeWarningThreshold ? warningColor : normalColor;
style.normal.textColor = timeColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"CPU时间: {cpuFrameTime:F2}ms", style);
yPos += lineHeight;
}
// 显示GPU时间
if (showGPUTime && FrameTimingManager.IsFeatureEnabled())
{
Color timeColor = gpuFrameTime > gpuTimeWarningThreshold ? warningColor : normalColor;
style.normal.textColor = timeColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"GPU时间: {gpuFrameTime:F2}ms", style);
yPos += lineHeight;
}
// 显示GC信息
if (showGCStats)
{
Color gcColor = gcCountInLastSecond > gcWarningThreshold ? warningColor : normalColor;
style.normal.textColor = gcColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), $"GC次数/秒: {gcCountInLastSecond}", style);
yPos += lineHeight;
}
// 显示资源信息
if (showResourceStats)
{
yPos += 10f;
style.normal.textColor = normalColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), "资源统计:", style);
yPos += lineHeight;
Color texMemColor = (totalTextureMemory / (1024f * 1024f)) > textureMemoryWarningThreshold ? warningColor : normalColor;
style.normal.textColor = texMemColor;
GUI.Label(new Rect(20, yPos, 300, lineHeight),
$"贴图: {loadedTextureCount}个 ({totalTextureMemory / (1024f * 1024f):F1}MB)", style);
yPos += lineHeight;
Color meshMemColor = (totalMeshMemory / (1024f * 1024f)) > meshMemoryWarningThreshold ? warningColor : normalColor;
style.normal.textColor = meshMemColor;
GUI.Label(new Rect(20, yPos, 300, lineHeight),
$"网格: {loadedMeshCount}个 ({totalMeshMemory / (1024f * 1024f):F1}MB)", style);
yPos += lineHeight;
}
// 显示系统性能分析
if (enableSystemProfiling && systemProfilers.Count > 0)
{
yPos += 10f;
style.normal.textColor = normalColor;
GUI.Label(new Rect(10, yPos, 200, lineHeight), "系统性能:", style);
yPos += lineHeight;
foreach (var profiler in systemProfilers.Values)
{
if (profiler.sampleCount > 0)
{
Color timeColor = profiler.AverageTime > systemTimeWarningThreshold ? warningColor : normalColor;
style.normal.textColor = timeColor;
GUI.Label(new Rect(20, yPos, 400, lineHeight),
$"{profiler.name}: {profiler.AverageTime:F2}ms (最大: {profiler.maxTime:F2}ms)", style);
yPos += lineHeight;
}
}
}
// 显示性能警告
if (performanceWarnings.Count > 0)
{
yPos += 10f;
style.normal.textColor = errorColor;
GUI.Label(new Rect(10, yPos, 400, lineHeight), "性能警告:", style);
yPos += lineHeight;
foreach (string warning in performanceWarnings)
{
GUI.Label(new Rect(20, yPos, 400, lineHeight), $"• {warning}", style);
yPos += lineHeight;
}
}
}
///
/// 获取当前FPS
///
public float GetFPS()
{
return fps;
}
///
/// 获取内存使用量(MB)
///
public float GetMemoryUsage()
{
return memoryUsage;
}
///
/// 获取绘制调用数量
///
public int GetDrawCalls()
{
return drawCalls;
}
///
/// 获取批处理数量
///
public int GetBatches()
{
return batches;
}
///
/// 记录性能数据到日志
///
public void LogPerformanceData()
{
Debug.Log($"[性能数据] FPS: {fps:F1}, 内存: {memoryUsage:F1}MB, 绘制调用: {drawCalls}, 批处理: {batches}");
}
private void OnDisable()
{
CloseCsv();
}
private void OnDestroy()
{
CloseCsv();
}
private void InitCsvLogging()
{
try
{
var directory = Application.persistentDataPath;
logFilePath = Path.Combine(directory, $"perf_{System.DateTime.Now:yyyyMMdd_HHmmss}.csv");
csvWriter = new StreamWriter(new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite));
csvWriter.WriteLine("time,fps,memoryMB,drawCalls,batches,triangles,vertices,cpuTime,gpuTime,gcCount," +
"textureCount,meshCount,textureMemoryMB,meshMemoryMB");
csvWriter.Flush();
nextLogTime = Time.unscaledTime + Mathf.Max(0.05f, logInterval);
Debug.Log($"性能CSV日志: {logFilePath}");
}
catch (System.Exception e)
{
Debug.LogWarning($"初始化CSV日志失败: {e.Message}");
enableCsvLogging = false;
}
}
private void WriteCsvRow()
{
try
{
if (csvWriter == null) return;
csvWriter.WriteLine($"{Time.unscaledTime:F3},{fps:F2},{memoryUsage:F1},{drawCalls},{batches},{triangles},{vertices}," +
$"{cpuFrameTime:F2},{gpuFrameTime:F2},{gcCountInLastSecond}," +
$"{loadedTextureCount},{loadedMeshCount},{totalTextureMemory/1024/1024:F1},{totalMeshMemory/1024/1024:F1}");
csvWriter.Flush();
}
catch (System.Exception e)
{
Debug.LogWarning($"写入CSV失败: {e.Message}");
}
}
private void CloseCsv()
{
try
{
if (csvWriter != null)
{
csvWriter.Flush();
csvWriter.Close();
csvWriter.Dispose();
csvWriter = null;
}
}
catch {}
}
public string GetLogFilePath()
{
return logFilePath;
}
///
/// 开始系统性能采样
///
public void BeginSample(string systemName)
{
if (!enableSystemProfiling) return;
if (!systemProfilers.ContainsKey(systemName))
{
systemProfilers[systemName] = new CustomProfiler
{
name = systemName,
stopwatch = new Stopwatch()
};
}
systemProfilers[systemName].stopwatch.Start();
}
///
/// 结束系统性能采样
///
public void EndSample(string systemName)
{
if (!enableSystemProfiling) return;
if (systemProfilers.TryGetValue(systemName, out var profiler))
{
profiler.stopwatch.Stop();
float time = profiler.stopwatch.ElapsedTicks / (float)System.TimeSpan.TicksPerMillisecond;
profiler.totalTime += time;
profiler.maxTime = Mathf.Max(profiler.maxTime, time);
profiler.sampleCount++;
profiler.stopwatch.Reset();
}
}
///
/// 重置所有系统性能分析器
///
public void ResetAllProfilers()
{
foreach (var profiler in systemProfilers.Values)
{
profiler.Reset();
}
}
///
/// 获取系统性能数据
///
public Dictionary GetSystemProfilingData()
{
var result = new Dictionary();
foreach (var kvp in systemProfilers)
{
var profiler = kvp.Value;
result[kvp.Key] = (profiler.AverageTime, profiler.maxTime, profiler.sampleCount);
}
return result;
}
}
}