| | 1 | | // Copyright (c) 2020-2024 dotBunny Inc. |
| | 2 | | // dotBunny licenses this file to you under the BSL-1.0 license. |
| | 3 | | // See the LICENSE file in the project root for more information. |
| | 4 | |
|
| | 5 | | #if !UNITY_DOTSRUNTIME |
| | 6 | |
|
| | 7 | | using System; |
| | 8 | | using System.Collections.Generic; |
| | 9 | | using System.Text; |
| | 10 | | using GDX.Developer.Reports.Resource; |
| | 11 | | using GDX.Developer.Reports.Resource.Objects; |
| | 12 | | using GDX.Developer.Reports.Resource.Sections; |
| | 13 | |
|
| | 14 | | namespace GDX.Developer.Reports |
| | 15 | | { |
| | 16 | | /// <exception cref="UnsupportedRuntimeException">Not supported on DOTS Runtime.</exception> |
| | 17 | | public class ResourcesDiffReport : ResourceReport |
| | 18 | | { |
| 1 | 19 | | public readonly Dictionary<Type, Dictionary<TransientReference, ObjectInfo>> AddedObjects = |
| | 20 | | new Dictionary<Type, Dictionary<TransientReference, ObjectInfo>>(); |
| | 21 | |
|
| 1 | 22 | | public readonly Dictionary<Type, Dictionary<TransientReference, ObjectInfo>> CommonObjects = |
| | 23 | | new Dictionary<Type, Dictionary<TransientReference, ObjectInfo>>(); |
| | 24 | |
|
| 1 | 25 | | public readonly Dictionary<Type, LongDiff> KnownUsage = |
| | 26 | | new Dictionary<Type, LongDiff>(); |
| | 27 | |
|
| | 28 | | public readonly MemoryDiffSection MemoryContext; |
| | 29 | |
|
| | 30 | | public readonly IntegerDiff ObjectCount; |
| | 31 | |
|
| 1 | 32 | | public readonly Dictionary<Type, Dictionary<TransientReference, ObjectInfo>> RemovedObjects = |
| | 33 | | new Dictionary<Type, Dictionary<TransientReference, ObjectInfo>>(); |
| | 34 | |
|
| 1 | 35 | | public ResourcesDiffReport(ResourcesAuditReport lhs, ResourcesAuditReport rhs) |
| 1 | 36 | | { |
| 1 | 37 | | ObjectCount = new IntegerDiff(lhs.ObjectCount, rhs.ObjectCount); |
| 1 | 38 | | MemoryContext = MemoryDiffSection.Get(lhs.MemoryContext, rhs.MemoryContext); |
| | 39 | |
|
| | 40 | | // Known Usage Changes |
| 25 | 41 | | foreach (KeyValuePair<Type, long> kvp in lhs.KnownUsage) |
| 11 | 42 | | { |
| 11 | 43 | | KnownUsage.Add(kvp.Key, |
| | 44 | | rhs.KnownUsage.ContainsKey(kvp.Key) |
| | 45 | | ? new LongDiff(kvp.Value, rhs.KnownUsage[kvp.Key]) |
| | 46 | | : new LongDiff(kvp.Value, 0)); |
| 11 | 47 | | } |
| | 48 | |
|
| 5 | 49 | | foreach (KeyValuePair<Type, long> kvp in rhs.KnownUsage) |
| 1 | 50 | | { |
| 1 | 51 | | if (!lhs.KnownUsage.ContainsKey(kvp.Key)) |
| 1 | 52 | | { |
| 1 | 53 | | KnownUsage.Add(kvp.Key, new LongDiff(0, kvp.Value)); |
| 1 | 54 | | } |
| 1 | 55 | | } |
| | 56 | |
|
| | 57 | | // Known Objects - Start on right hand side |
| 25 | 58 | | foreach (KeyValuePair<Type, Dictionary<TransientReference, ObjectInfo>> kvpType in lhs.KnownObjects) |
| 11 | 59 | | { |
| | 60 | | // Lets just add something ahead to be safe |
| 11 | 61 | | if (!AddedObjects.ContainsKey(kvpType.Key)) |
| 11 | 62 | | { |
| 11 | 63 | | AddedObjects.Add(kvpType.Key, new Dictionary<TransientReference, ObjectInfo>()); |
| 11 | 64 | | } |
| | 65 | |
|
| 11 | 66 | | if (!RemovedObjects.ContainsKey(kvpType.Key)) |
| 11 | 67 | | { |
| 11 | 68 | | RemovedObjects.Add(kvpType.Key, new Dictionary<TransientReference, ObjectInfo>()); |
| 11 | 69 | | } |
| | 70 | |
|
| 11 | 71 | | if (!CommonObjects.ContainsKey(kvpType.Key)) |
| 11 | 72 | | { |
| 11 | 73 | | CommonObjects.Add(kvpType.Key, new Dictionary<TransientReference, ObjectInfo>()); |
| 11 | 74 | | } |
| | 75 | |
|
| | 76 | | // RHS Dictionary? |
| 11 | 77 | | Dictionary<TransientReference, ObjectInfo> rhsKnownObjects = null; |
| 11 | 78 | | if (rhs.KnownObjects.ContainsKey(kvpType.Key)) |
| 0 | 79 | | { |
| 0 | 80 | | rhsKnownObjects = rhs.KnownObjects[kvpType.Key]; |
| 0 | 81 | | } |
| | 82 | |
|
| | 83 | | // Iterate over everything in the left hand side |
| 1831 | 84 | | foreach (KeyValuePair<TransientReference, ObjectInfo> knownObject in kvpType.Value) |
| 899 | 85 | | { |
| | 86 | | // None of this type in the right, means removed |
| 899 | 87 | | if (rhsKnownObjects == null) |
| 899 | 88 | | { |
| 899 | 89 | | RemovedObjects[kvpType.Key].Add(knownObject.Key, knownObject.Value); |
| 899 | 90 | | } |
| | 91 | | else |
| 0 | 92 | | { |
| 0 | 93 | | if (rhsKnownObjects.ContainsKey(knownObject.Key)) |
| 0 | 94 | | { |
| 0 | 95 | | ObjectInfo clone = rhsKnownObjects[knownObject.Key].Clone(); |
| 0 | 96 | | clone.CopyCount -= knownObject.Value.CopyCount; |
| | 97 | |
|
| 0 | 98 | | CommonObjects[kvpType.Key].Add(knownObject.Key, clone); |
| 0 | 99 | | } |
| | 100 | | else |
| 0 | 101 | | { |
| 0 | 102 | | RemovedObjects[kvpType.Key].Add(knownObject.Key, knownObject.Value); |
| 0 | 103 | | } |
| 0 | 104 | | } |
| 899 | 105 | | } |
| 11 | 106 | | } |
| | 107 | |
|
| | 108 | | // Iterate over rhs for added |
| 5 | 109 | | foreach (KeyValuePair<Type, Dictionary<TransientReference, ObjectInfo>> kvpType in rhs.KnownObjects) |
| 1 | 110 | | { |
| 1 | 111 | | if (!lhs.KnownObjects.ContainsKey(kvpType.Key)) |
| 1 | 112 | | { |
| 1 | 113 | | AddedObjects.Add(kvpType.Key, new Dictionary<TransientReference, ObjectInfo>()); |
| | 114 | | // Iterate over everything in the left hand side |
| 14505 | 115 | | foreach (KeyValuePair<TransientReference, ObjectInfo> knownObject in kvpType.Value) |
| 7251 | 116 | | { |
| 7251 | 117 | | AddedObjects[kvpType.Key].Add(knownObject.Key, knownObject.Value); |
| 7251 | 118 | | } |
| 1 | 119 | | } |
| 1 | 120 | | } |
| | 121 | |
|
| 1 | 122 | | List<Type> removeType = new List<Type>(); |
| 27 | 123 | | foreach (KeyValuePair<Type, Dictionary<TransientReference, ObjectInfo>> kvpType in AddedObjects) |
| 12 | 124 | | { |
| 12 | 125 | | if (kvpType.Value.Count == 0) |
| 11 | 126 | | { |
| 11 | 127 | | removeType.Add(kvpType.Key); |
| 11 | 128 | | } |
| 12 | 129 | | } |
| | 130 | |
|
| 25 | 131 | | foreach (Type type in removeType) |
| 11 | 132 | | { |
| 11 | 133 | | AddedObjects.Remove(type); |
| 11 | 134 | | } |
| | 135 | |
|
| 1 | 136 | | removeType.Clear(); |
| 25 | 137 | | foreach (KeyValuePair<Type, Dictionary<TransientReference, ObjectInfo>> kvpType in RemovedObjects) |
| 11 | 138 | | { |
| 11 | 139 | | if (kvpType.Value.Count == 0) |
| 1 | 140 | | { |
| 1 | 141 | | removeType.Add(kvpType.Key); |
| 1 | 142 | | } |
| 11 | 143 | | } |
| | 144 | |
|
| 5 | 145 | | foreach (Type type in removeType) |
| 1 | 146 | | { |
| 1 | 147 | | RemovedObjects.Remove(type); |
| 1 | 148 | | } |
| | 149 | |
|
| 1 | 150 | | removeType.Clear(); |
| 25 | 151 | | foreach (KeyValuePair<Type, Dictionary<TransientReference, ObjectInfo>> kvpType in CommonObjects) |
| 11 | 152 | | { |
| 11 | 153 | | if (kvpType.Value.Count == 0) |
| 11 | 154 | | { |
| 11 | 155 | | removeType.Add(kvpType.Key); |
| 11 | 156 | | } |
| 11 | 157 | | } |
| | 158 | |
|
| 25 | 159 | | foreach (Type type in removeType) |
| 11 | 160 | | { |
| 11 | 161 | | CommonObjects.Remove(type); |
| 11 | 162 | | } |
| 1 | 163 | | } |
| | 164 | |
|
| | 165 | | /// <inheritdoc /> |
| | 166 | | public override bool Output(StringBuilder builder, ResourceReportContext context = null) |
| 1 | 167 | | { |
| | 168 | | // We need to make the context if its not provided |
| 1 | 169 | | context ??= new ResourceReportContext(); |
| | 170 | |
|
| | 171 | | // Create header |
| 1 | 172 | | builder.AppendLine(CreateHeader(context, "START: Resources Diff Report")); |
| | 173 | |
|
| | 174 | | // Custom header information |
| 1 | 175 | | builder.AppendLine(CreateKeyValuePair(context, "Total Objects", ObjectCount.GetOutput(context))); |
| 1 | 176 | | builder.AppendLine(); |
| 27 | 177 | | foreach (KeyValuePair<Type, LongDiff> usage in KnownUsage) |
| 12 | 178 | | { |
| 12 | 179 | | builder.AppendLine( |
| | 180 | | CreateKeyValuePair(context, usage.Key.ToString(), usage.Value.GetSizeOutput(context))); |
| 12 | 181 | | } |
| | 182 | |
|
| 1 | 183 | | builder.AppendLine(); |
| | 184 | |
|
| | 185 | | // Add memory information |
| 1 | 186 | | MemoryContext.Output(context, builder); |
| 1 | 187 | | builder.AppendLine(); |
| | 188 | |
|
| 1 | 189 | | builder.AppendLine(CreateHeader(context, "Added Objects")); |
| 1 | 190 | | OutputObjectInfos(builder, context, AddedObjects); |
| | 191 | |
|
| 1 | 192 | | builder.AppendLine(CreateHeader(context, "Common Objects")); |
| 1 | 193 | | OutputObjectInfos(builder, context, CommonObjects); |
| | 194 | |
|
| 1 | 195 | | builder.AppendLine(CreateHeader(context, "Removed Objects")); |
| 1 | 196 | | OutputObjectInfos(builder, context, RemovedObjects); |
| | 197 | |
|
| | 198 | | // Footer |
| 1 | 199 | | builder.AppendLine(CreateHeader(context, "END: Resources Diff Report")); |
| | 200 | |
|
| 1 | 201 | | return true; |
| 1 | 202 | | } |
| | 203 | |
|
| | 204 | | static void OutputObjectInfos(StringBuilder builder, ResourceReportContext context, |
| | 205 | | Dictionary<Type, Dictionary<TransientReference, ObjectInfo>> targetObjects) |
| 3 | 206 | | { |
| 31 | 207 | | foreach (KeyValuePair<Type, Dictionary<TransientReference, ObjectInfo>> target in targetObjects) |
| 11 | 208 | | { |
| 11 | 209 | | int count = target.Value.Count; |
| | 210 | |
|
| 11 | 211 | | builder.AppendLine(CreateHeader(context, $"{target.Key} [{count.ToString()}] ", '-')); |
| | 212 | |
|
| | 213 | | // Sort the known objects based on size as that's the most useful context to have them listed |
| 11 | 214 | | List<ObjectInfo> newList = new List<ObjectInfo>(count); |
| 16333 | 215 | | foreach (KeyValuePair<TransientReference, ObjectInfo> objectInfo in target.Value) |
| 8150 | 216 | | { |
| 8150 | 217 | | newList.Add(objectInfo.Value); |
| 8150 | 218 | | } |
| | 219 | |
|
| 11 | 220 | | newList.Sort(); |
| | 221 | |
|
| | 222 | | // Output each item |
| 16322 | 223 | | for (int i = 0; i < count; i++) |
| 8150 | 224 | | { |
| 8150 | 225 | | newList[i].Output(context, builder); |
| 8150 | 226 | | } |
| | 227 | |
|
| 11 | 228 | | builder.AppendLine(); |
| 11 | 229 | | } |
| 3 | 230 | | } |
| | 231 | | } |
| | 232 | | } |
| | 233 | | #endif // !UNITY_DOTSRUNTIME |