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 _groups = new Dictionary(); private List _items = new List(); private List _cachedMeshes = new List(); 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(); _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 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 (); } 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 visible = new List(); 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; } } }