一.插件
1.uwa
一个账号一个月有一次免费使用的机会,还没用过。
----------------------
2.SRDebugger
一个视频教程看到的,
https://assetstore.unity.com/packages/tools/gui/srdebugger-console-tools-on-device-27688
淘宝上有
3.Advanced FPS Counter
https://assetstore.unity.com/packages/tools/utilities/advanced-fps-counter-14656
淘宝有
4.Mesh Profiler
https://assetstore.unity.com/packages/tools/modeling/mesh-profiler-158255
淘宝没找到,不是Runtime
2.2020.2的新特性
Capturing Profiler Stats at Runtime
官方教程:https://resources.unity.com/unitenow/onlinesessions/capturing-profiler-stats-at-runtime
在官方的基础上封装了一下,添加新的性能参数会方便一点
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using UnityEngine;
using Unity.Profiling;
using Unity.Profiling.LowLevel.Unsafe;
using UnityEngine.Profiling;
public class ProfilerController : MonoBehaviour
{
/************************************************************************************************************
* Source: https://docs.unity3d.com/2020.2/Documentation/ScriptReference/Unity.Profiling.ProfilerRecorder.html
*************************************************************************************************************/
//public static ProfilerMarker UpdatePlayerProfilerMarker = new ProfilerMarker("Player.Update");
string statsText;
//ProfilerRecorder mainThreadTimeRecorder;
//ProfilerRecorder drawCallsCountRecorder;
static double GetRecorderFrameAverage(ProfilerRecorder recorder)
{
var samplesCount = recorder.Capacity;
if (samplesCount == 0)
return 0;
double r = 0;
var samples = new List<ProfilerRecorderSample>(samplesCount);
recorder.CopyTo(samples);
for (var i = 0; i < samples.Count; ++i)
r += samples[i].Value;
r /= samplesCount;
return r;
}
private Dictionary<string,StatInfo> statDict;
private Dictionary<StatInfo,ProfilerRecorder> Recorders=new Dictionary<StatInfo,ProfilerRecorder>();
private void AddProfiler(string statName){
if(statDict==null){
statDict=GetAvailableProfilerStats.EnumerateProfilerStats();
}
if(statDict.ContainsKey(statName))
{
StatInfo info=statDict[statName];
//Debug.Log("AddProfiler:"+statName)
ProfilerRecorder recorder = ProfilerRecorder.StartNew(info.Cat, statName);
Recorders.Add(info,recorder);
}
else{
Debug.LogError("No StatName:"+statName);
}
}
private void AddMemoryProfiler(string statName){
AddProfiler(statName,ProfilerCategory.Memory,ProfilerMarkerDataUnit.Bytes);
}
private void AddProfiler(string statName,ProfilerCategory pc,ProfilerMarkerDataUnit unit){
if(statDict==null){
statDict=GetAvailableProfilerStats.EnumerateProfilerStats();
}
if(statDict.ContainsKey(statName))
{
StatInfo info=statDict[statName];
//Debug.Log("AddProfiler:"+statName)
ProfilerRecorder recorder = ProfilerRecorder.StartNew(info.Cat, statName);
Recorders.Add(info,recorder);
}
else{
// StatInfo info=new StatInfo
// {
// Name="*"+statName,
// Cat=pc,
// Unit=unit
// };
// ProfilerRecorder recorder = ProfilerRecorder.StartNew(info.Cat, statName);
// Recorders.Add(info,recorder);
Debug.LogError("No StatName:"+statName);
}
}
void OnEnable()
{
//systemMemoryRecorder = ProfilerRecorder.StartNew(new ProfilerCategory("Memory"), "System Used Memory");
// gcReservedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Reserved Memory");
// gcUsedMemoryRecorder= ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Used Memory");
// totalReservedMemoryRecorder=ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Reserved Memory");
// totalUsedMemoryRecorder=ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Used Memory");
//mainThreadTimeRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Internal, "Main Thread", 15);
//Recorders.Add(info,mainThreadTimeRecorder);
//drawCallsCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "Draw Calls Count");
InitProfilers();
}
private void InitProfilers()
{
statDict = GetAvailableProfilerStats.EnumerateProfilerStats();
AddMemoryProfiler("System Used Memory");
AddMemoryProfiler("Total Reserved Memory");
AddMemoryProfiler("Total Used Memory");
AddMemoryProfiler("GC Reserved Memory");
AddMemoryProfiler("GC Used Memory");
AddMemoryProfiler("Gfx Used Memory");
AddProfiler("Texture Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddMemoryProfiler("Texture Memory");
AddProfiler("Material Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddMemoryProfiler("Material Memory");
AddProfiler("Mesh Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddMemoryProfiler("Mesh Memory");
AddMemoryProfiler("Profiler Used Memory");
AddProfiler("GameObject Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddProfiler("Object Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddProfiler("Asset Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddProfiler("Scene Object Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
AddProfiler("Draw Calls Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
AddProfiler("Batches Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
AddProfiler("SetPass Calls Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
AddProfiler("Triangles Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
AddProfiler("Vertices Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
AddProfiler("Used Buffers Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
AddProfiler("Used Buffers Bytes", ProfilerCategory.Render, ProfilerMarkerDataUnit.Bytes);
AddProfiler("Main Thread", ProfilerCategory.Render, ProfilerMarkerDataUnit.TimeNanoseconds);
AddProfiler("Camera.Render", ProfilerCategory.Render, ProfilerMarkerDataUnit.TimeNanoseconds);
AddProfiler("RenderLoop.Draw", ProfilerCategory.Render, ProfilerMarkerDataUnit.TimeNanoseconds);
}
void OnDisable()
{
//mainThreadTimeRecorder.Dispose();
//drawCallsCountRecorder.Dispose();
foreach(StatInfo info in Recorders.Keys)
{
ProfilerRecorder recorder=Recorders[info];
recorder.Dispose();
}
Recorders.Clear();
}
void Update()
{
var sb = new StringBuilder(500);
foreach(StatInfo info in Recorders.Keys)
{
ProfilerRecorder recorder=Recorders[info];
if(info.Unit==ProfilerMarkerDataUnit.Bytes)
sb.AppendLine($"{info.Name}: {recorder.LastValue / (1024f * 1024f):F2} MB");
else if (info.Unit == ProfilerMarkerDataUnit.TimeNanoseconds)
sb.AppendLine($"{info.Name}: {GetRecorderFrameAverage(recorder) * (1e-6f):F2} ms");
else
sb.AppendLine($"{info.Name}: {recorder.LastValue} ");
}
//sb.AppendLine($"-----");
//sb.AppendLine($"Frame Time: {GetRecorderFrameAverage(mainThreadTimeRecorder) * (1e-6f):F1} ms");
// sb.AppendLine($"GC Reserved Memory: {gcReservedMemoryRecorder.LastValue / (1024 * 1024)} MB");
// sb.AppendLine($"GC Used Memory: {gcUsedMemoryRecorder.LastValue / (1024 * 1024)} MB");
// sb.AppendLine($"System Used Memory: {systemMemoryRecorder.LastValue / (1024 * 1024)} MB");
// sb.AppendLine($"Total Reserved Memory: {totalReservedMemoryRecorder.LastValue / (1024 * 1024)} MB");
// sb.AppendLine($"Total Used Memory: {totalUsedMemoryRecorder.LastValue / (1024 * 1024)} MB");
//sb.AppendLine($"Draw Calls: {drawCallsCountRecorder.LastValue}");
statsText = sb.ToString();
}
void OnGUI()
{
GUI.TextArea(new Rect(310, 10, 240, 500), statsText);
}
}
打包后:
或者如下,*的都是打包后不支持的性能参数。为什么不支持?GameObject Count这种的都不支持?
3.Profiler.GetRuntimeMemorySizeLong(Unity.Object obj)
官方:https://docs.unity3d.com/ScriptReference/Profiling.Profiler.GetRuntimeMemorySizeLong.html
测试:
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Profiling;
public class MemoryController : MonoBehaviour
{
public List<UnityEngine.Object> TestObjects = new List<UnityEngine.Object>();
public List<Mesh> meshes = new List<Mesh>();
string runtimeMemoryText;
private string GetObjectsRuntimeMemory()
{
var sb = new StringBuilder(500);
foreach (var obj in TestObjects)
{
if (obj != null)
{
long size = Profiler.GetRuntimeMemorySizeLong(obj);
sb.AppendLine($"{obj.GetType()}_{obj.name}: {GetSizeString(size)}");
}
}
foreach (var mesh in meshes)
{
long size = Profiler.GetRuntimeMemorySizeLong(mesh);
sb.AppendLine($"{mesh.GetType()}_{mesh.name}: {GetSizeString(size)}");
}
runtimeMemoryText = sb.ToString();
return runtimeMemoryText;
}
// Start is called before the first frame update
void Start()
{
GetObjectsRuntimeMemory();
}
// Update is called once per frame
void OnGUI()
{
GUI.TextArea(new Rect(10, 10, 300, 500), runtimeMemoryText);
}
public static string GetSizeString(long size)
{
if (size < 1024)
{
return $"{size} B";
}
else if (size < 1048576)//1024*1024
{
return $"{size / (1024f):F2} KB";
}
else
{
return $"{size / (1048576f):F2} MB";
}
}
}
发现空的GameObject是288B,非空的GameObject是320B,GameObject中的Render中的Mesh要另外计算,它不会自动把一个GameObject相关的内存空间都计算上。
基本上Unity中的大部分类都是Unity.Object的子类。