using System;
|
|
using NUnit.Framework.Constraints;
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using Treemap;
|
|
using UnityEditor;
|
|
using Assets.Editor.Treemap;
|
|
using System.Linq;
|
|
using UnityEngine.UI;
|
|
|
|
namespace MemoryProfilerWindow
|
|
{
|
|
public class TreeMapView
|
|
{
|
|
CrawledMemorySnapshot _unpackedCrawl;
|
|
private ZoomArea _ZoomArea;
|
|
private Dictionary<string, Group> _groups = new Dictionary<string, Group>();
|
|
private List<Item> _items = new List<Item>();
|
|
private List<Mesh> _cachedMeshes = new List<Mesh>();
|
|
private Item _selectedItem;
|
|
private Group _selectedGroup;
|
|
private Item _mouseDownItem;
|
|
|
|
MemoryProfilerWindow _hostWindow;
|
|
|
|
private Vector2 mouseTreemapPosition { get { return _ZoomArea.ViewToDrawingTransformPoint(Event.current.mousePosition); } }
|
|
|
|
public void Setup(MemoryProfilerWindow hostWindow, CrawledMemorySnapshot _unpackedCrawl)
|
|
{
|
|
this._unpackedCrawl = _unpackedCrawl;
|
|
this._hostWindow = hostWindow;
|
|
|
|
_ZoomArea = new ZoomArea(true)
|
|
{
|
|
vRangeMin = -110f,
|
|
vRangeMax = 110f,
|
|
hRangeMin = -110f,
|
|
hRangeMax = 110f,
|
|
hBaseRangeMin = -110f,
|
|
vBaseRangeMin = -110f,
|
|
hBaseRangeMax = 110f,
|
|
vBaseRangeMax = 110f,
|
|
shownArea = new Rect(-110f, -110f, 220f, 220f)
|
|
};
|
|
RefreshCaches();
|
|
RefreshMesh();
|
|
}
|
|
|
|
public void Draw()
|
|
{
|
|
if (_hostWindow == null)
|
|
return;
|
|
|
|
Rect r = new Rect(0f, 25f, _hostWindow.position.width - _hostWindow._inspector.width, _hostWindow.position.height - 25f);
|
|
|
|
_ZoomArea.rect = r;
|
|
_ZoomArea.BeginViewGUI();
|
|
|
|
GUI.BeginGroup(r);
|
|
Handles.matrix = _ZoomArea.drawingToViewMatrix;
|
|
HandleMouseClick();
|
|
RenderTreemap();
|
|
GUI.EndGroup();
|
|
|
|
_ZoomArea.EndViewGUI();
|
|
}
|
|
|
|
private void OnHoveredGroupChanged()
|
|
{
|
|
RefreshCachedRects(false);
|
|
}
|
|
|
|
private void HandleMouseClick()
|
|
{
|
|
if ((Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseUp) && Event.current.button == 0)
|
|
{
|
|
if (_ZoomArea.drawRect.Contains(Event.current.mousePosition))
|
|
{
|
|
Group group = _groups.Values.FirstOrDefault(i => i._position.Contains(mouseTreemapPosition));
|
|
Item item = _items.FirstOrDefault(i => i._position.Contains(mouseTreemapPosition));
|
|
|
|
if (item != null && _selectedGroup == item._group)
|
|
{
|
|
switch (Event.current.type)
|
|
{
|
|
case EventType.MouseDown:
|
|
_mouseDownItem = item;
|
|
break;
|
|
|
|
case EventType.MouseUp:
|
|
if (_mouseDownItem == item)
|
|
{
|
|
_hostWindow.SelectThing(item._thingInMemory);
|
|
Event.current.Use();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (group != null)
|
|
{
|
|
switch (Event.current.type)
|
|
{
|
|
case EventType.MouseUp:
|
|
_hostWindow.SelectGroup(group);
|
|
Event.current.Use();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SelectThing(ThingInMemory thing)
|
|
{
|
|
_selectedItem = _items.First(i => i._thingInMemory == thing);
|
|
_selectedGroup = _selectedItem._group;
|
|
RefreshCachedRects(false);
|
|
}
|
|
|
|
public void SelectGroup(Group group)
|
|
{
|
|
_selectedItem = null;
|
|
_selectedGroup = group;
|
|
RefreshCachedRects(false);
|
|
}
|
|
|
|
void RefreshCaches()
|
|
{
|
|
_items.Clear();
|
|
_groups.Clear();
|
|
|
|
foreach (ThingInMemory thingInMemory in _unpackedCrawl.allObjects)
|
|
{
|
|
string groupName = GetGroupName(thingInMemory);
|
|
if (groupName.Length == 0)
|
|
continue;
|
|
|
|
if (!_groups.ContainsKey(groupName))
|
|
{
|
|
Group newGroup = new Group();
|
|
newGroup._name = groupName;
|
|
newGroup._items = new List<Item>();
|
|
_groups.Add(groupName, newGroup);
|
|
}
|
|
|
|
Item item = new Item(thingInMemory, _groups[groupName]);
|
|
_items.Add(item);
|
|
_groups[groupName]._items.Add(item);
|
|
}
|
|
|
|
foreach (Group group in _groups.Values)
|
|
{
|
|
group._items.Sort();
|
|
}
|
|
|
|
_items.Sort();
|
|
RefreshCachedRects(true);
|
|
}
|
|
|
|
private void RefreshCachedRects(bool fullRefresh)
|
|
{
|
|
Rect space = new Rect(-100f, -100f, 200f, 200f);
|
|
|
|
if (fullRefresh)
|
|
{
|
|
List<Group> groups = _groups.Values.ToList();
|
|
groups.Sort();
|
|
float[] groupTotalValues = new float[groups.Count];
|
|
for (int i = 0; i < groups.Count; i++)
|
|
{
|
|
groupTotalValues[i] = groups.ElementAt(i).totalMemorySize;
|
|
}
|
|
|
|
Rect[] groupRects = Utility.GetTreemapRects(groupTotalValues, space);
|
|
for (int groupIndex = 0; groupIndex < groupRects.Length; groupIndex++)
|
|
{
|
|
Group group = groups[groupIndex];
|
|
group._position = groupRects[groupIndex];
|
|
}
|
|
}
|
|
|
|
if (_selectedGroup != null)
|
|
{
|
|
Rect[] rects = Utility.GetTreemapRects(_selectedGroup.memorySizes, _selectedGroup._position);
|
|
|
|
for (int i = 0; i < rects.Length; i++)
|
|
{
|
|
_selectedGroup._items[i]._position = rects[i];
|
|
}
|
|
}
|
|
|
|
RefreshMesh();
|
|
}
|
|
|
|
public void CleanupMeshes ()
|
|
{
|
|
if (_cachedMeshes == null) {
|
|
_cachedMeshes = new List<Mesh> ();
|
|
}
|
|
else {
|
|
for (int i = 0; i < _cachedMeshes.Count; i++) {
|
|
UnityEngine.Object.DestroyImmediate (_cachedMeshes [i]);
|
|
}
|
|
|
|
_cachedMeshes.Clear ();
|
|
}
|
|
}
|
|
|
|
private void RefreshMesh()
|
|
{
|
|
CleanupMeshes ();
|
|
|
|
const int maxVerts = 32000;
|
|
Vector3[] vertices = new Vector3[maxVerts];
|
|
Color[] colors = new Color[maxVerts];
|
|
int[] triangles = new int[maxVerts * 6 / 4];
|
|
|
|
int meshItemIndex = 0;
|
|
int totalItemIndex = 0;
|
|
|
|
List<ITreemapRenderable> visible = new List<ITreemapRenderable>();
|
|
foreach (Group group in _groups.Values)
|
|
{
|
|
if (group != _selectedGroup)
|
|
{
|
|
visible.Add(group);
|
|
}
|
|
else
|
|
{
|
|
foreach (Item item in group._items)
|
|
{
|
|
visible.Add(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (ITreemapRenderable item in visible)
|
|
{
|
|
int index = meshItemIndex * 4;
|
|
vertices[index++] = new Vector3(item.GetPosition().xMin, item.GetPosition().yMin, 0f);
|
|
vertices[index++] = new Vector3(item.GetPosition().xMax, item.GetPosition().yMin, 0f);
|
|
vertices[index++] = new Vector3(item.GetPosition().xMax, item.GetPosition().yMax, 0f);
|
|
vertices[index++] = new Vector3(item.GetPosition().xMin, item.GetPosition().yMax, 0f);
|
|
|
|
index = meshItemIndex * 4;
|
|
var color = item.GetColor();
|
|
if (item == _selectedItem)
|
|
color *= 1.5f;
|
|
|
|
colors[index++] = color;
|
|
colors[index++] = color * 0.75f;
|
|
colors[index++] = color * 0.5f;
|
|
colors[index++] = color * 0.75f;
|
|
|
|
index = meshItemIndex * 6;
|
|
triangles[index++] = meshItemIndex * 4 + 0;
|
|
triangles[index++] = meshItemIndex * 4 + 1;
|
|
triangles[index++] = meshItemIndex * 4 + 3;
|
|
triangles[index++] = meshItemIndex * 4 + 1;
|
|
triangles[index++] = meshItemIndex * 4 + 2;
|
|
triangles[index++] = meshItemIndex * 4 + 3;
|
|
|
|
meshItemIndex++;
|
|
totalItemIndex++;
|
|
|
|
if (meshItemIndex >= maxVerts / 4 || totalItemIndex == visible.Count)
|
|
{
|
|
Mesh mesh = new Mesh();
|
|
mesh.hideFlags = HideFlags.DontSaveInEditor | HideFlags.HideInHierarchy | HideFlags.NotEditable;
|
|
mesh.vertices = vertices;
|
|
mesh.triangles = triangles;
|
|
mesh.colors = colors;
|
|
_cachedMeshes.Add(mesh);
|
|
|
|
vertices = new Vector3[maxVerts];
|
|
colors = new Color[maxVerts];
|
|
triangles = new int[maxVerts * 6 / 4];
|
|
meshItemIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RenderTreemap()
|
|
{
|
|
if (_cachedMeshes == null)
|
|
return;
|
|
|
|
Material mat = (Material)EditorGUIUtility.LoadRequired("SceneView/2DHandleLines.mat");
|
|
mat.SetPass(0);
|
|
|
|
for (int i = 0; i < _cachedMeshes.Count; i++)
|
|
{
|
|
Graphics.DrawMeshNow(_cachedMeshes[i], Handles.matrix);
|
|
}
|
|
RenderLabels();
|
|
}
|
|
|
|
private void RenderLabels()
|
|
{
|
|
if (_groups == null)
|
|
return;
|
|
|
|
GUI.color = Color.black;
|
|
Matrix4x4 mat = _ZoomArea.drawingToViewMatrix;
|
|
|
|
foreach (Group group in _groups.Values)
|
|
{
|
|
if (Utility.IsInside(group._position, _ZoomArea.shownArea))
|
|
{
|
|
if (_selectedItem != null && _selectedItem._group == group)
|
|
{
|
|
RenderGroupItems(group);
|
|
}
|
|
else
|
|
{
|
|
RenderGroupLabel(group);
|
|
}
|
|
}
|
|
}
|
|
|
|
GUI.color = Color.white;
|
|
}
|
|
|
|
private void RenderGroupLabel(Group group)
|
|
{
|
|
Matrix4x4 mat = _ZoomArea.drawingToViewMatrix;
|
|
|
|
Vector3 p1 = mat.MultiplyPoint(new Vector3(group._position.xMin, group._position.yMin));
|
|
Vector3 p2 = mat.MultiplyPoint(new Vector3(group._position.xMax, group._position.yMax));
|
|
|
|
if (p2.x - p1.x > 30f)
|
|
{
|
|
Rect rect = new Rect(p1.x, p2.y, p2.x - p1.x, p1.y - p2.y);
|
|
GUI.Label(rect, group.GetLabel());
|
|
}
|
|
}
|
|
|
|
private void RenderGroupItems(Group group)
|
|
{
|
|
Matrix4x4 mat = _ZoomArea.drawingToViewMatrix;
|
|
|
|
foreach (Item item in group._items)
|
|
{
|
|
if (Utility.IsInside(item._position, _ZoomArea.shownArea))
|
|
{
|
|
Vector3 p1 = mat.MultiplyPoint(new Vector3(item._position.xMin, item._position.yMin));
|
|
Vector3 p2 = mat.MultiplyPoint(new Vector3(item._position.xMax, item._position.yMax));
|
|
|
|
if (p2.x - p1.x > 30f)
|
|
{
|
|
Rect rect = new Rect(p1.x, p2.y, p2.x - p1.x, p1.y - p2.y);
|
|
string row1 = item._group._name;
|
|
string row2 = EditorUtility.FormatBytes(item.memorySize);
|
|
GUI.Label(rect, row1 + "\n" + row2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public string GetGroupName(ThingInMemory thing)
|
|
{
|
|
if (thing is NativeUnityEngineObject)
|
|
return (thing as NativeUnityEngineObject).className ?? "MissingName";
|
|
if (thing is ManagedObject)
|
|
return (thing as ManagedObject).typeDescription.name;
|
|
return thing.GetType().Name;
|
|
}
|
|
}
|
|
}
|