initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,386 @@
#if UNITY_2022_2_OR_NEWER
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
public class AssetsViewBuildReportItem : IAddressablesBuildReportItem
{
public string Name { get; set; }
public string BundleName { get; set; }
public string GroupName { get; set; }
public ulong FileSizePlusRefs { get; set; }
public ulong FileSizeUncompressed { get; set; }
public ulong BundleSize { get; set; }
public int RefsTo { get; set; }
public int RefsBy { get; set; }
public BuildLayout.ExplicitAsset ExplicitAsset { get; set; }
public BuildLayout.DataFromOtherAsset DataFromOtherAsset { get; set; }
public BuildLayout.Bundle Bundle { get; set; }
public List<BuildLayout.Bundle> Bundles { get; set; }
public ulong SizeWDependencies => 9999;
public virtual void CreateGUI(VisualElement rootVisualElement) { }
public virtual string GetCellContent(string colName)
{
if (colName == BuildReportUtility.AssetsContentViewColAssetName)
return Name;
else if (colName == BuildReportUtility.AssetsContentViewColSizePlusRefs)
{
if (FileSizePlusRefs == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizePlusRefs);
}
else if (colName == BuildReportUtility.AssetsContentViewColSizeUncompressed)
{
if (FileSizeUncompressed == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeUncompressed);
}
else if (colName == BuildReportUtility.AssetsContentViewColBundleSize)
{
if (BundleSize == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(BundleSize);
}
else if (colName == BuildReportUtility.AssetsContentViewColRefsTo)
{
if (RefsTo == -1)
return "--";
return RefsTo.ToString();
}
else if (colName == BuildReportUtility.AssetsContentViewColRefsBy)
{
if (RefsBy == -1)
return "--";
return RefsBy.ToString();
}
return "";
}
public string GetSortContent(string colName)
{
if (colName == BuildReportUtility.AssetsContentViewColSizePlusRefs)
return FileSizePlusRefs.ToString();
if (colName == BuildReportUtility.AssetsContentViewColSizeUncompressed)
return FileSizeUncompressed.ToString();
if (colName == BuildReportUtility.AssetsContentViewColBundleSize)
return BundleSize.ToString();
return GetCellContent(colName);
}
}
public class AssetsViewTypeHeader : AssetsViewBuildReportItem
{
public AssetsViewTypeHeader(string name)
{
Name = name;
}
public override string GetCellContent(string colName)
{
if (colName == BuildReportUtility.AssetsContentViewColAssetName)
return Name;
else
{
return "";
}
}
public override void CreateGUI(VisualElement rootVisualElement)
{
}
}
public class AssetsViewBuildReportBundle : AssetsViewBuildReportItem, IAddressablesBuildReportBundle
{
public AssetsViewBuildReportBundle(BuildLayout.Bundle bundle)
{
Name = bundle.Name;
Bundle = bundle;
BundleSize = bundle.FileSize;
GroupName = bundle.Group.Name;
BundleName = "";
FileSizePlusRefs = bundle.FileSize;
foreach (var b in Bundle.ExpandedDependencies)
FileSizePlusRefs += b.FileSize;
RefsTo = Bundle.ExpandedDependencies.Count;
RefsBy = Bundle.DependentBundles.Count;
}
}
public class AssetsViewBuildReportUnrelatedAssets : AssetsViewBuildReportItem
{
public AssetsViewBuildReportUnrelatedAssets(ulong assetSize, int assetCount)
{
Name = $"({assetCount} unrelated assets)";
FileSizeUncompressed = assetSize;
GroupName = "";
BundleName = "";
}
}
public class AssetsViewBuildReportAsset : AssetsViewBuildReportItem, IAddressablesBuildReportAsset
{
public List<BuildLayout.ExplicitAsset> InternallyReferencedAssets { get; }
public List<BuildLayout.ExplicitAsset> ExternallyReferencedAssets { get; }
public List<BuildLayout.DataFromOtherAsset> ImplicitDependencies { get; }
List<BuildLayout.ExplicitAsset> ReferencingAssets { get; }
public AssetsViewBuildReportAsset(BuildLayout.ExplicitAsset asset)
{
ExplicitAsset = asset;
Name = asset.AddressableName;
Bundle = asset.Bundle;
Bundles = new List<BuildLayout.Bundle>(){ Bundle };
BundleName = asset.Bundle?.Name;
ExternallyReferencedAssets = asset.ExternallyReferencedAssets;
InternallyReferencedAssets = asset.InternalReferencedExplicitAssets;
ImplicitDependencies = asset.InternalReferencedOtherAssets;
ReferencingAssets = asset.ReferencingAssets;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = FileSizeUncompressed;
foreach (var r in asset.ExternallyReferencedAssets)
if (r != null)
FileSizePlusRefs += r.SerializedSize + asset.StreamedSize;
foreach (var r in asset.InternalReferencedExplicitAssets)
if (r != null)
FileSizePlusRefs += r.SerializedSize + asset.StreamedSize;
foreach (var r in asset.InternalReferencedOtherAssets)
if (r != null)
FileSizePlusRefs += r.SerializedSize + asset.StreamedSize;
RefsTo = asset.ExternallyReferencedAssets.Count + asset.InternalReferencedExplicitAssets.Count + asset.InternalReferencedOtherAssets.Count;
RefsBy = asset.ReferencingAssets != null ? asset.ReferencingAssets.Count : -1;
}
public AssetsViewBuildReportAsset(BuildLayout.DataFromOtherAsset asset)
{
DataFromOtherAsset = asset;
Bundles = new List<BuildLayout.Bundle>() { asset.File.Bundle };
Name = asset.AssetPath;
RefsBy = asset.ReferencingAssets.Count;
RefsTo = -1;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = asset.SerializedSize + asset.StreamedSize;
}
}
class AssetsContentView : ContentView
{
public AssetsContentView(BuildReportHelperConsumer helperConsumer, DetailsView detailsView)
: base(helperConsumer, detailsView)
{
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
}
internal override ContentViewColumnData[] ColumnDataForView
{
get
{
return new ContentViewColumnData[]
{
new ContentViewColumnData(BuildReportUtility.AssetsContentViewColAssetName, this, true, "Asset Name"),
new ContentViewColumnData(BuildReportUtility.AssetsContentViewColSizePlusRefs, this, false, "Total Size (+ refs)"),
new ContentViewColumnData(BuildReportUtility.AssetsContentViewColSizeUncompressed, this, false, "Uncompressed Size"),
new ContentViewColumnData(BuildReportUtility.AssetsContentViewColBundleSize, this, false, "Bundle File Size"),
new ContentViewColumnData(BuildReportUtility.AssetsContentViewColRefsTo, this, false, "Refs To"),
new ContentViewColumnData(BuildReportUtility.AssetsContentViewColRefsBy, this, false, "Refs By")
};
}
}
// Data about assets from our currently selected build report.
public override IList<IAddressablesBuildReportItem> CreateTreeViewItems(BuildLayout report)
{
List<IAddressablesBuildReportItem> buildReportAssets = new List<IAddressablesBuildReportItem>();
if (report == null)
return buildReportAssets;
foreach (BuildLayout.ExplicitAsset asset in BuildLayoutHelpers.EnumerateAssets(report))
buildReportAssets.Add(new AssetsViewBuildReportAsset(asset));
foreach (BuildLayout.DataFromOtherAsset implicitAsset in BuildLayoutHelpers.EnumerateImplicitAssets(report))
buildReportAssets.Add(new AssetsViewBuildReportAsset(implicitAsset));
return buildReportAssets;
}
public override void Consume(BuildLayout buildReport)
{
if (buildReport == null)
return;
m_Report = buildReport;
m_TreeItems = CreateTreeViewItems(m_Report);
m_TreeView.SetRootItems(CreateTreeRootsNestedList(m_TreeItems));
m_TreeView.Rebuild();
m_TreeView.columnSortingChanged += ColumnSortingChanged;
}
private void ColumnSortingChanged()
{
var columnList = m_TreeView.sortedColumns;
IList<IAddressablesBuildReportItem> sortedRootList = new List<IAddressablesBuildReportItem>();
foreach (var col in columnList)
{
sortedRootList = SortByColumnDescription(col);
}
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
m_TreeView.SetRootItems(CreateTreeRootsNestedList(sortedRootList));
m_TreeView.Rebuild();
}
public override void CreateGUI(VisualElement rootVisualElement)
{
VisualElement view = rootVisualElement.Q<VisualElement>(BuildReportUtility.ContentView);
TreeBuilder tb = new TreeBuilder()
.With(ColumnDataForView)
.With((items) => ItemsSelected.Invoke(items));
m_TreeView = tb.Build();
view.Add(m_TreeView);
SetCallbacksForColumns(m_TreeView.columns, ColumnDataForView);
m_SearchField = rootVisualElement.Q<ToolbarSearchField>(BuildReportUtility.SearchField);
m_SearchField.RegisterValueChangedCallback(OnSearchValueChanged);
m_SearchValue = m_SearchField.value;
}
private void OnSearchValueChanged(ChangeEvent<string> evt)
{
if (m_TreeItems == null)
return;
m_SearchValue = evt.newValue.ToLower();
m_TreeView.SetRootItems(CreateTreeRootsNestedList(m_TreeItems));
m_TreeView.Rebuild();
}
public IList<TreeViewItemData<AssetsViewBuildReportItem>> CreateTreeRootsNestedList(IList<IAddressablesBuildReportItem> items)
{
int id = 0;
var roots = new List<TreeViewItemData<AssetsViewBuildReportItem>>(items.Count);
foreach (AssetsViewBuildReportItem item in items)
{
AssetsViewBuildReportAsset asset = item as AssetsViewBuildReportAsset;
if (asset == null)
continue;
bool includeAllDependencies = EntryAppearsInSearch(asset, m_SearchValue);
bool assetIsExplicitAsset = asset.ExplicitAsset != null;
if (assetIsExplicitAsset)
{
var children = CreateChildrenOfAsset(asset, ref id, includeAllDependencies);
if (children.Count > 0 || EntryAppearsInSearch(asset, m_SearchValue))
roots.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, item, children));
}
else if (includeAllDependencies)
{
roots.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, item));
}
}
return roots;
}
List<TreeViewItemData<AssetsViewBuildReportItem>> CreateChildrenOfAsset(AssetsViewBuildReportAsset asset, ref int id, bool includeAllDependencies)
{
var children = new List<TreeViewItemData<AssetsViewBuildReportItem>>();
PopulateInternallyReferencedEntries(children, asset, ref id, includeAllDependencies);
PopulateExternallyReferencedEntries(children, asset, ref id, includeAllDependencies);
PopulateImplicitEntries(children, asset, ref id, includeAllDependencies);
return children;
}
void PopulateInternallyReferencedEntries(List<TreeViewItemData<AssetsViewBuildReportItem>> children, AssetsViewBuildReportAsset asset, ref int id, bool includeAllDependencies)
{
foreach (var dep in asset.InternallyReferencedAssets)
{
var buildReportAsset = new AssetsViewBuildReportAsset(dep);
if (EntryAppearsInSearch(buildReportAsset, m_SearchValue) || includeAllDependencies)
children.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, buildReportAsset));
}
}
void PopulateExternallyReferencedEntries(List<TreeViewItemData<AssetsViewBuildReportItem>> children, AssetsViewBuildReportAsset asset, ref int id, bool includeAllDependencies)
{
Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>> bundleToAssetList = new Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>>();
foreach (var dep in asset.ExternallyReferencedAssets)
{
if (!bundleToAssetList.ContainsKey(dep.Bundle))
bundleToAssetList.Add(dep.Bundle, new List<BuildLayout.ExplicitAsset>());
bundleToAssetList[dep.Bundle].Add(dep);
}
foreach (var bundle in bundleToAssetList.Keys)
{
var assetTreeViewItems = new List<TreeViewItemData<AssetsViewBuildReportItem>>();
var assetList = bundleToAssetList[bundle];
ulong unrelatedAssetSize = bundle.FileSize;
foreach (var bundleAsset in assetList)
{
var bundleReportAsset = new AssetsViewBuildReportAsset(bundleAsset);
if (includeAllDependencies || EntryAppearsInSearch(bundleReportAsset, m_SearchValue))
assetTreeViewItems.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, bundleReportAsset));
unrelatedAssetSize -= (bundleAsset.SerializedSize + bundleAsset.StreamedSize);
}
int unrelatedAssetCount = bundle.AssetCount - assetList.Count;
if (unrelatedAssetCount > 0 && includeAllDependencies)
assetTreeViewItems.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, new AssetsViewBuildReportUnrelatedAssets(unrelatedAssetSize, unrelatedAssetCount)));
if (includeAllDependencies || assetTreeViewItems.Count > 0)
children.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, new AssetsViewBuildReportBundle(bundle), assetTreeViewItems));
}
}
void PopulateImplicitEntries(List<TreeViewItemData<AssetsViewBuildReportItem>> children, AssetsViewBuildReportAsset asset, ref int id, bool includeAllDependencies)
{
foreach (var dep in asset.ImplicitDependencies)
{
var reportImplicitAsset = new AssetsViewBuildReportAsset(dep);
if (EntryAppearsInSearch(reportImplicitAsset, m_SearchValue) || includeAllDependencies)
children.Add(new TreeViewItemData<AssetsViewBuildReportItem>(++id, reportImplicitAsset));
}
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d314bd85e421fcf4d819507bd4554b38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,481 @@
#if UNITY_2022_2_OR_NEWER
using System;
using System.Collections.Generic;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.Build.Reporting;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using BuildLayout = UnityEditor.AddressableAssets.Build.Layout.BuildLayout;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
// Nested class that represents a generic item in the bundle view (can be an asset, header, or bundle).
class BundlesViewBuildReportItem : IAddressablesBuildReportItem
{
public string Name { get; protected set; }
public ulong FileSize { get; protected set; }
public int RefsTo { get; protected set; }
public int RefsBy { get; protected set; }
public ulong FileSizeUncompressed { get; set; }
public ulong FileSizePlusRefs { get; set; }
public ulong FileSizeBundle { get; set; }
public int AssetsCount { get; set; }
public string GroupName { get; protected set; }
public virtual void CreateGUI(VisualElement rootVisualElement) { }
public virtual string GetCellContent(string colName)
{
if (colName == BuildReportUtility.BundlesContentViewColBundleName)
return Name;
else if (colName == BuildReportUtility.BundlesContentViewColSizePlusRefs)
{
if (FileSizePlusRefs == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizePlusRefs);
}
if (colName == BuildReportUtility.BundlesContentViewColSizeUncompressed)
{
if (FileSizeUncompressed == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeUncompressed);
}
else if (colName == BuildReportUtility.BundlesContentViewBundleSize)
{
if (FileSizeBundle == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeBundle);
}
else if (colName == BuildReportUtility.BundlesContentViewColRefsTo)
{
if (RefsTo == -1)
return "--";
return RefsTo.ToString();
}
else if (colName == BuildReportUtility.BundlesContentViewColRefsBy)
{
if (RefsBy == -1)
return "--";
return RefsBy.ToString();
}
return "";
}
public string GetSortContent(string colName)
{
if (colName == BuildReportUtility.BundlesContentViewColSizePlusRefs)
return FileSizePlusRefs.ToString();
if (colName == BuildReportUtility.BundlesContentViewColSizeUncompressed)
return FileSizeUncompressed.ToString();
if (colName == BuildReportUtility.BundlesContentViewBundleSize)
return FileSizeBundle.ToString();
return GetCellContent(colName);
}
}
// Nested class that represents an bundle.
class BundlesViewBuildReportBundle : BundlesViewBuildReportItem, IAddressablesBuildReportBundle
{
public BuildLayout.Bundle Bundle { get; set; }
public List<BuildLayout.Bundle> Dependencies { get; set; }
public List<BuildLayout.Bundle> DependentBundles { get; set; }
public BundlesViewBuildReportBundle(BuildLayout.Bundle bundle)
{
Bundle = bundle;
Name = bundle.Name;
foreach (var file in bundle.Files)
RefsTo += file.Assets.Count + file.OtherAssets.Count;
RefsBy = bundle.DependentBundles.Count;
GroupName = bundle.Group?.Name;
Dependencies = bundle.Dependencies;
DependentBundles = bundle.DependentBundles;
foreach (var f in bundle.Files)
{
foreach (var ex in f.Assets)
ExplicitAssetsFromLayout.Add(ex);
}
FileSizeBundle = bundle.FileSize;
FileSizeUncompressed = bundle.UncompressedFileSize;
FileSizePlusRefs = bundle.FileSize + bundle.ExpandedDependencyFileSize + bundle.DependencyFileSize;
}
public List<BuildLayout.ExplicitAsset> ExplicitAssetsFromLayout = new List<BuildLayout.ExplicitAsset>();
public List<BuildLayout.ExplicitAsset> ExplicitAssets { get; set; }
public List<BuildLayout.DataFromOtherAsset> ImplicitAssets { get; set; }
}
class BundlesViewBuildReportAsset : BundlesViewBuildReportItem, IAddressablesBuildReportAsset
{
public BuildLayout.ExplicitAsset ExplicitAsset { get; }
public BuildLayout.DataFromOtherAsset DataFromOtherAsset { get; }
public List<BuildLayout.Bundle> Bundles { get;}
public string AssetPath { get; private set; }
public ulong SizeWDependencies { get; set; }
public bool IsAddressable = false;
public BundlesViewBuildReportAsset(BuildLayout.ExplicitAsset asset)
{
ExplicitAsset = asset;
Bundles = new List<BuildLayout.Bundle>(){ asset.Bundle };
Name = asset.AddressableName;
RefsTo = asset.InternalReferencedOtherAssets.Count + asset.ExternallyReferencedAssets.Count + asset.InternalReferencedExplicitAssets.Count;
RefsBy = asset.ReferencingAssets != null ? asset.ReferencingAssets.Count : -1;
FileSize = asset.SerializedSize + asset.StreamedSize;
AssetPath = asset.AssetPath;
IsAddressable = true;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = FileSizeUncompressed;
foreach (var r in asset.ExternallyReferencedAssets)
if (r != null)
FileSizePlusRefs += r.SerializedSize + r.StreamedSize;
foreach (var r in asset.InternalReferencedExplicitAssets)
if (r != null)
FileSizePlusRefs += r.SerializedSize + r.StreamedSize;
SizeWDependencies = FileSizePlusRefs;
}
public BundlesViewBuildReportAsset(BuildLayout.DataFromOtherAsset asset)
{
DataFromOtherAsset = asset;
Bundles = new List<BuildLayout.Bundle>() { asset.File.Bundle };
Name = asset.AssetPath;
AssetPath = asset.AssetPath;
RefsBy = asset.ReferencingAssets.Count;
RefsTo = -1;
IsAddressable = false;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = asset.SerializedSize + asset.StreamedSize;
SizeWDependencies = FileSizePlusRefs;
FileSizeBundle = 0;
}
}
class BundlesViewBuildReportUnrelatedAssets : BundlesViewBuildReportItem
{
public BundlesViewBuildReportUnrelatedAssets(ulong assetSize, int assetCount)
{
Name = $"({assetCount} unrelated assets)";
FileSizeUncompressed = assetSize;
}
}
class BundlesViewBuildReportIndirectlyReferencedBundles : BundlesViewBuildReportItem
{
public BundlesViewBuildReportIndirectlyReferencedBundles(List<BuildLayout.Bundle> bundles)
{
Name = bundles.Count > 1 ? $"{bundles.Count} indirectly referenced bundles" : $"{bundles.Count} indirectly referenced bundle";
FileSizeBundle = 0;
FileSizeUncompressed = 0;
HashSet<BuildLayout.Bundle> countedBundles = new HashSet<BuildLayout.Bundle>();
foreach (var b in bundles)
{
FileSizeBundle += b.FileSize;
FileSizeUncompressed += b.UncompressedFileSize;
if (!countedBundles.Contains(b))
{
FileSizePlusRefs += b.FileSize;
countedBundles.Add(b);
}
foreach (var depB in b.ExpandedDependencies)
{
if (!countedBundles.Contains(depB))
{
FileSizePlusRefs += depB.FileSize;
countedBundles.Add(depB);
}
}
foreach (var depB in b.Dependencies)
{
if (!countedBundles.Contains(depB))
{
FileSizePlusRefs += depB.FileSize;
countedBundles.Add(depB);
}
}
}
}
}
class BundlesContentView : ContentView
{
IList<TreeViewItemData<BundlesViewBuildReportItem>> m_TreeRoots;
public BundlesContentView(BuildReportHelperConsumer helperConsumer, DetailsView detailsView)
: base(helperConsumer, detailsView)
{
}
internal override ContentViewColumnData[] ColumnDataForView
{
get
{
return new ContentViewColumnData[]
{
new ContentViewColumnData(BuildReportUtility.BundlesContentViewColBundleName, this, true, "Bundle Name"),
new ContentViewColumnData(BuildReportUtility.BundlesContentViewColSizePlusRefs, this, false, "Total Size (+ refs)"),
new ContentViewColumnData(BuildReportUtility.BundlesContentViewColSizeUncompressed, this, false, "Uncompressed Size"),
new ContentViewColumnData(BuildReportUtility.BundlesContentViewBundleSize, this, false, "Bundle File Size"),
new ContentViewColumnData(BuildReportUtility.BundlesContentViewColRefsTo, this, false, "Refs To"),
new ContentViewColumnData(BuildReportUtility.BundlesContentViewColRefsBy, this, false, "Refs By"),
};
}
}
// Data about bundles from our currently selected build report.
public override IList<IAddressablesBuildReportItem> CreateTreeViewItems(BuildLayout report)
{
List<IAddressablesBuildReportItem> buildReportBundles = new List<IAddressablesBuildReportItem>();
if (report == null)
return buildReportBundles;
foreach (BuildLayout.Bundle bundle in BuildLayoutHelpers.EnumerateBundles(report))
{
var buildReportBundle = new BundlesViewBuildReportBundle(bundle);
var explicitAssets = new List<BuildLayout.ExplicitAsset>();
var implicitAssets = new List<BuildLayout.DataFromOtherAsset>();
foreach (BuildLayout.File file in bundle.Files)
{
foreach (BuildLayout.ExplicitAsset asset in file.Assets)
{
explicitAssets.Add(asset);
}
foreach (BuildLayout.DataFromOtherAsset asset in file.OtherAssets)
{
implicitAssets.Add(asset);
}
}
buildReportBundle.AssetsCount = explicitAssets.Count + implicitAssets.Count;
buildReportBundle.ExplicitAssets = explicitAssets;
buildReportBundle.ImplicitAssets = implicitAssets;
buildReportBundles.Add(buildReportBundle);
}
return buildReportBundles;
}
public override void Consume(BuildLayout buildReport)
{
if (buildReport == null)
return;
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
m_Report = buildReport;
m_TreeItems = CreateTreeViewItems(m_Report);
IList<TreeViewItemData<BundlesViewBuildReportItem>> treeRoots = CreateTreeRootsNestedList(m_TreeItems, m_DataHashtoReportItem);
m_TreeView.SetRootItems(treeRoots);
m_TreeView.Rebuild();
m_TreeView.columnSortingChanged += ColumnSortingChanged;
}
private void ColumnSortingChanged()
{
var columnList = m_TreeView.sortedColumns;
IList<IAddressablesBuildReportItem> sortedRootList = new List<IAddressablesBuildReportItem>();
foreach (var col in columnList)
{
sortedRootList = SortByColumnDescription(col);
}
m_TreeView.SetRootItems(CreateTreeRootsNestedList(sortedRootList, m_DataHashtoReportItem));
m_TreeView.Rebuild();
}
public override void CreateGUI(VisualElement rootVisualElement)
{
VisualElement view = rootVisualElement.Q<VisualElement>(BuildReportUtility.ContentView);
TreeBuilder tb = new TreeBuilder()
.With(ColumnDataForView)
.With((items) => ItemsSelected.Invoke(items));
m_TreeView = tb.Build();
view.Add(m_TreeView);
SetCallbacksForColumns(m_TreeView.columns, ColumnDataForView);
m_SearchField = rootVisualElement.Q<ToolbarSearchField>(BuildReportUtility.SearchField);
m_SearchField.RegisterValueChangedCallback(OnSearchValueChanged);
m_SearchValue = m_SearchField.value;
}
private void OnSearchValueChanged(ChangeEvent<string> evt)
{
if (m_TreeItems == null)
return;
m_SearchValue = evt.newValue.ToLowerInvariant();
m_TreeRoots = CreateTreeRootsNestedList(m_TreeItems, m_DataHashtoReportItem);
m_TreeView.SetRootItems(m_TreeRoots);
m_TreeView.Rebuild();
}
// Expresses bundle data as a hierarchal list of BuildReportBundleViewItem objects.
public IList<TreeViewItemData<BundlesViewBuildReportItem>> CreateTreeRootsNestedList(IList<IAddressablesBuildReportItem> items, Dictionary<Hash128, TreeDataReportItem> dataHashToReportItem)
{
int id = 0;
var roots = new List<TreeViewItemData<BundlesViewBuildReportItem>>();
foreach (BundlesViewBuildReportItem item in items)
{
BundlesViewBuildReportBundle bundle = item as BundlesViewBuildReportBundle;
if (bundle == null)
continue;
bool includeAllDependencies = EntryAppearsInSearch(item, m_SearchValue);
var children = CreateChildrenOfBundle(bundle, ref id, includeAllDependencies);
if (children.Count > 0 || includeAllDependencies)
{
var rootItem = new TreeViewItemData<BundlesViewBuildReportItem>(++id, item, children);
dataHashToReportItem.TryAdd(BuildReportUtility.ComputeDataHash(item.Name), new TreeDataReportItem(id, rootItem.data));
roots.Add(rootItem);
}
}
return roots;
}
private List<TreeViewItemData<BundlesViewBuildReportItem>> CreateChildrenOfBundle(BundlesViewBuildReportBundle bundle, ref int id, bool includeAllDependencies)
{
var children = new List<TreeViewItemData<BundlesViewBuildReportItem>>();
var indirectlyReferencedBundleReportItems = new List<TreeViewItemData<BundlesViewBuildReportItem>>();
var indirectlyReferencedBundles = new List<BuildLayout.Bundle>();
CreateAssetEntries(children, out var bundlesReferencedByAssetEntries, bundle, ref id, includeAllDependencies);
foreach (var depBundle in bundle.Bundle.ExpandedDependencies)
{
if (!bundlesReferencedByAssetEntries.Contains(depBundle))
{
var reportBundle = new BundlesViewBuildReportBundle(depBundle);
if (includeAllDependencies || EntryAppearsInSearch(reportBundle, m_SearchValue))
{
indirectlyReferencedBundleReportItems.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportBundle));
indirectlyReferencedBundles.Add(depBundle);
}
}
}
if (indirectlyReferencedBundles.Count > 0)
{
children.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, new BundlesViewBuildReportIndirectlyReferencedBundles(indirectlyReferencedBundles), indirectlyReferencedBundleReportItems));
}
return children;
}
private void CreateAssetEntries(List<TreeViewItemData<BundlesViewBuildReportItem>> children, out HashSet<BuildLayout.Bundle> directlyReferencedBundles, BundlesViewBuildReportBundle bundle, ref int id, bool includeAllDependencies)
{
directlyReferencedBundles = new HashSet<BuildLayout.Bundle>();
foreach (var asset in bundle.ExplicitAssets)
{
if (asset == null)
continue;
var reportAsset = new BundlesViewBuildReportAsset(asset);
bool includeAsset = EntryAppearsInSearch(reportAsset, m_SearchValue);
var childrenOfAsset = GenerateChildrenOfAsset(asset, ref id, directlyReferencedBundles, includeAllDependencies || includeAsset);
if (includeAsset || includeAllDependencies || childrenOfAsset.Count > 0)
{
var dataItem = new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportAsset, childrenOfAsset);
m_DataHashtoReportItem.TryAdd(BuildReportUtility.ComputeDataHash(bundle.Name, asset.AddressableName), new TreeDataReportItem(id, dataItem.data));
children.Add(dataItem);
}
}
foreach (var asset in bundle.ImplicitAssets)
{
if (asset == null)
continue;
var reportAsset = new BundlesViewBuildReportAsset(asset);
if (EntryAppearsInSearch(reportAsset, m_SearchValue) || includeAllDependencies)
{
children.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportAsset));
}
}
}
private List<TreeViewItemData<BundlesViewBuildReportItem>> GenerateChildrenOfAsset(BuildLayout.ExplicitAsset asset, ref int id, HashSet<BuildLayout.Bundle> referencedBundles, bool includeAllDependencies)
{
var childrenOfAsset = new List<TreeViewItemData<BundlesViewBuildReportItem>>();
Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>> bundleToAssetList = new Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>>();
foreach (var dep in asset.InternalReferencedExplicitAssets)
{
var reportAsset = new BundlesViewBuildReportAsset(dep);
if (EntryAppearsInSearch(reportAsset, m_SearchValue) || includeAllDependencies)
{
childrenOfAsset.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportAsset));
}
}
foreach (var dep in asset.ExternallyReferencedAssets)
{
if (!bundleToAssetList.ContainsKey(dep.Bundle))
{
bundleToAssetList.Add(dep.Bundle, new List<BuildLayout.ExplicitAsset>());
referencedBundles.Add(dep.Bundle);
}
bundleToAssetList[dep.Bundle].Add(dep);
}
foreach (var bundle in bundleToAssetList.Keys)
{
var reportBundle = new BundlesViewBuildReportBundle(bundle);
bool bundleIncludedInSearch = EntryAppearsInSearch(reportBundle, m_SearchValue) || includeAllDependencies;
var assetTreeViewItems = new List<TreeViewItemData<BundlesViewBuildReportItem>>();
var assetList = bundleToAssetList[bundle];
ulong unrelatedAssetSize = bundle.FileSize;
foreach (var bundleAsset in assetList)
{
var reportBundleAsset = new BundlesViewBuildReportAsset(bundleAsset);
if (bundleIncludedInSearch || EntryAppearsInSearch(reportBundleAsset, m_SearchValue))
{
assetTreeViewItems.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportBundleAsset));
unrelatedAssetSize -= bundleAsset.SerializedSize + bundleAsset.StreamedSize;
}
}
int unrelatedAssetCount = bundle.AssetCount - assetList.Count;
if (unrelatedAssetCount > 0 && bundleIncludedInSearch)
assetTreeViewItems.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, new BundlesViewBuildReportUnrelatedAssets(unrelatedAssetSize, unrelatedAssetCount)));
if (bundleIncludedInSearch || assetTreeViewItems.Count > 0)
{
childrenOfAsset.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportBundle, assetTreeViewItems));
}
}
foreach (var dep in asset.InternalReferencedOtherAssets)
{
var reportAsset = new BundlesViewBuildReportAsset(dep);
if (EntryAppearsInSearch(reportAsset, m_SearchValue) || includeAllDependencies)
{
childrenOfAsset.Add(new TreeViewItemData<BundlesViewBuildReportItem>(++id, reportAsset));
}
}
return childrenOfAsset;
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97211b0f482ed5c4f94f6efe397a40ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,311 @@
#if UNITY_2022_2_OR_NEWER
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
internal abstract class ContentView : IAddressableView, IBuildReportConsumer
{
protected BuildLayout m_Report;
internal ToolbarSearchField m_SearchField;
internal string m_SearchValue;
protected MultiColumnTreeView m_TreeView = null;
public MultiColumnTreeView ContentTreeView
{
get { return m_TreeView; }
}
protected IList<IAddressablesBuildReportItem> m_TreeItems = null;
public IList<IAddressablesBuildReportItem> TreeItems
{
get { return m_TreeItems; }
}
public struct TreeDataReportItem
{
public int Id;
public IAddressablesBuildReportItem ReportItem;
public TreeDataReportItem(int id, IAddressablesBuildReportItem reportItem)
{
Id = id;
ReportItem = reportItem;
}
}
protected Dictionary<Hash128, TreeDataReportItem> m_DataHashtoReportItem = null;
public Dictionary<Hash128, TreeDataReportItem> DataHashtoReportItem
{
get { return m_DataHashtoReportItem; }
}
public Action<IEnumerable<object>> ItemsSelected;
internal abstract ContentViewColumnData[] ColumnDataForView { get; }
public abstract void Consume(BuildLayout buildReport);
public abstract void CreateGUI(VisualElement rootVisualElement);
public virtual void ClearGUI()
{
if (m_TreeView != null)
{
// Clear removes the column header
// m_TreeView.Clear();
m_TreeView.SetRootItems(default(IList<TreeViewItemData<IAddressablesBuildReportItem>>));
m_TreeView.Rebuild();
}
}
internal BuildReportHelperConsumer m_HelperConsumer;
DetailsView m_DetailsView;
VisualTreeAsset m_TreeViewItem;
VisualTreeAsset m_TreeViewNavigableItem;
internal ContentView(BuildReportHelperConsumer helperConsumer, DetailsView detailsView)
{
m_HelperConsumer = helperConsumer;
m_DetailsView = detailsView;
m_TreeViewItem = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(BuildReportUtility.TreeViewItemFilePath);
m_TreeViewNavigableItem = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(BuildReportUtility.TreeViewNavigableItemFilePath);
}
public abstract IList<IAddressablesBuildReportItem> CreateTreeViewItems(BuildLayout report);
// Expresses bundle data as a flat list of TreeViewItemData objects.
protected IList<TreeViewItemData<IAddressablesBuildReportItem>> CreateTreeRootsFlatList(IList<IAddressablesBuildReportItem> items, Dictionary<Hash128, TreeDataReportItem> dataHashToReportItem)
{
int id = 0;
var roots = new List<TreeViewItemData<IAddressablesBuildReportItem>>(items.Count);
foreach (IAddressablesBuildReportItem item in items)
{
dataHashToReportItem.Add(BuildReportUtility.ComputeDataHash(item.Name, ""), new TreeDataReportItem(id, item));
roots.Add(new TreeViewItemData<IAddressablesBuildReportItem>(id++, item));
}
return roots;
}
internal static void SetCallbacksForColumns(Columns columns, ContentViewColumnData[] columnNameToWidth)
{
foreach (ContentViewColumnData data in columnNameToWidth)
{
Column col = columns[data.Name];
col.makeCell = () => new Label();
col.bindCell = data.BindCellCallback;
col.makeHeader = () => new Label();
col.bindHeader = data.BindHeaderCallback;
}
}
private IOrderedEnumerable<IAddressablesBuildReportItem> OrderByType(string columnName, Type t)
{
if (t == typeof(int))
return m_TreeItems.OrderBy(item => int.Parse(item.GetSortContent(columnName)));
if (t == typeof(ulong))
return m_TreeItems.OrderBy(item => ulong.Parse(item.GetSortContent(columnName)));
return null;
}
readonly Dictionary<string, Type> m_NumericColumnNames = new Dictionary<string, Type>()
{
{BuildReportUtility.AssetsContentViewColSizePlusRefs, typeof(ulong)},
{BuildReportUtility.AssetsContentViewColSizeUncompressed, typeof(ulong)},
{BuildReportUtility.AssetsContentViewColBundleSize, typeof(ulong)},
{BuildReportUtility.AssetsContentViewColRefsBy, typeof(int)},
{BuildReportUtility.AssetsContentViewColRefsTo, typeof(int)},
{BuildReportUtility.BundlesContentViewColSizePlusRefs, typeof(ulong)},
{BuildReportUtility.BundlesContentViewColSizeUncompressed, typeof(ulong)},
{BuildReportUtility.BundlesContentViewBundleSize, typeof(ulong)},
{BuildReportUtility.BundlesContentViewColRefsBy, typeof(int)},
{BuildReportUtility.BundlesContentViewColRefsTo, typeof(int)},
{BuildReportUtility.GroupsContentViewColSizePlusRefs, typeof(ulong)},
{BuildReportUtility.GroupsContentViewColSizeUncompressed, typeof(ulong)},
{BuildReportUtility.GroupsContentViewColBundleSize, typeof(ulong)},
{BuildReportUtility.GroupsContentViewColRefsBy, typeof(int)},
{BuildReportUtility.GroupsContentViewColRefsTo, typeof(int)},
{BuildReportUtility.LabelsContentViewColSizePlusRefs, typeof(ulong)},
{BuildReportUtility.LabelsContentViewColSizeUncompressed, typeof(ulong)},
{BuildReportUtility.LabelsContentViewColSizeBundle, typeof(ulong)},
{BuildReportUtility.LabelsContentViewColRefsBy, typeof(int)},
{BuildReportUtility.LabelsContentViewColRefsTo, typeof(int)},
{BuildReportUtility.DuplicatedAssetsContentViewSpaceSaved, typeof(ulong)},
{BuildReportUtility.DuplicatedAssetsContentViewDuplicationCount, typeof(int)},
{BuildReportUtility.DuplicatedAssetsContentViewColSize, typeof(ulong)}
};
public void CreateTreeViewHeader(VisualElement element, string colName, bool isAssetColumn)
{
(element as Label).text = ContentTreeView.columns[colName].title;
if (isAssetColumn)
element.AddToClassList(BuildReportUtility.TreeViewAssetHeader);
else
element.AddToClassList(BuildReportUtility.TreeViewHeader);
}
public void CreateTreeViewCell(VisualElement element, int index, string colName, bool isNameColumn, Type type)
{
IAddressablesBuildReportItem itemData = null;
if (type == typeof(AssetsContentView))
itemData = ContentTreeView.GetItemDataForIndex<AssetsViewBuildReportItem>(index);
if (type == typeof(BundlesContentView))
itemData = ContentTreeView.GetItemDataForIndex<BundlesViewBuildReportItem>(index);
if (type == typeof(LabelsContentView))
itemData = ContentTreeView.GetItemDataForIndex<LabelsViewBuildReportItem>(index);
if (type == typeof(GroupsContentView))
itemData = ContentTreeView.GetItemDataForIndex<GroupsViewBuildReportItem>(index);
if (type == typeof(DuplicatedAssetsContentView))
itemData = ContentTreeView.GetItemDataForIndex<DuplicatedAssetsViewBuildReportItem>(index);
if (isNameColumn)
{
ShowEntryIcon(element, itemData, m_TreeViewItem, colName);
element.AddToClassList(BuildReportUtility.TreeViewIconElement);
}
else
{
(element as Label).text = itemData.GetCellContent(colName);
element.AddToClassList(BuildReportUtility.TreeViewElement);
}
}
protected bool EntryAppearsInSearch(IAddressablesBuildReportItem item, string searchValue)
{
if (string.IsNullOrEmpty(searchValue))
return true;
if (item.Name.ToLowerInvariant().Contains(searchValue))
return true;
return false;
}
public void ShowEntryIcon(VisualElement element, IAddressablesBuildReportItem itemData, VisualTreeAsset baseItem, string colName)
{
(element as Label).text = string.Empty;
element.Clear();
VisualElement treeItem = baseItem.Clone(element);
var icon = treeItem.Q<Image>(BuildReportUtility.TreeViewItemIcon);
var name = treeItem.Q<TextElement>(BuildReportUtility.TreeViewItemName);
name.text = itemData.GetCellContent(colName);
if (itemData is IAddressablesBuildReportAsset asset)
{
string path = asset.ExplicitAsset == null ? asset.DataFromOtherAsset.AssetPath : asset.ExplicitAsset.AssetPath;
Texture iconTexture = AssetDatabase.GetCachedIcon(path);
if (iconTexture == null)
icon.AddToClassList(BuildReportUtility.TreeViewItemNoIcon);
else
icon.image = iconTexture;
if (asset.DataFromOtherAsset != null)
name.AddToClassList(BuildReportUtility.TreeViewImplicitAsset);
if (asset is DuplicatedAssetsViewBuildReportDuplicatedAsset)
name.AddToClassList(BuildReportUtility.TreeViewDuplicatedAsset);
}
else if (itemData is LabelsViewBuildReportLabel label)
icon.image = EditorGUIUtility.IconContent("FilterByLabel").image as Texture2D;
else if (itemData is GroupsViewBuildReportGroup group ||
itemData is BundlesViewBuildReportIndirectlyReferencedBundles ||
itemData is GroupsViewBuildReportIndirectlyReferencedBundles)
icon.image = EditorGUIUtility.IconContent("d_FolderOpened Icon").image as Texture2D;
else if (itemData is IAddressablesBuildReportBundle)
icon.image = EditorGUIUtility.IconContent("Package Manager").image as Texture2D;
else
icon.AddToClassList(BuildReportUtility.TreeViewItemNoIcon);
name.AddManipulator(new ContextualMenuManipulator((ContextualMenuPopulateEvent evt) =>
{
evt.menu.AppendAction("Search in this window", (e) =>
{
string newSearchValue = name.text;
m_SearchField.Q<TextField>().value = newSearchValue;
});
}));
}
public ContentView UseCachedView(VisualElement rootVisualElement)
{
VisualElement view = rootVisualElement.Q<VisualElement>(BuildReportUtility.ContentView);
view.Add(m_TreeView);
return this;
}
internal List<IAddressablesBuildReportItem> SortByColumnDescription(SortColumnDescription col)
{
IOrderedEnumerable<IAddressablesBuildReportItem> sortedTreeRootEnumerable;
if (m_NumericColumnNames.ContainsKey(col.columnName))
{
Type t = m_NumericColumnNames[col.columnName];
sortedTreeRootEnumerable = OrderByType(col.columnName, t);
}
else
{
sortedTreeRootEnumerable = m_TreeItems.OrderBy(item => item.GetSortContent(col.columnName));
}
List<IAddressablesBuildReportItem> finalTreeRoots = new List<IAddressablesBuildReportItem>(m_TreeItems.Count);
foreach (var item in sortedTreeRootEnumerable)
finalTreeRoots.Add(item);
if (col.direction == SortDirection.Ascending)
finalTreeRoots.Reverse();
return finalTreeRoots;
}
}
internal struct ContentViewColumnData
{
public string Name;
public string Title;
public Action<VisualElement, int> BindCellCallback;
public Action<VisualElement> BindHeaderCallback;
public ContentViewColumnData(string name, ContentView view, bool isNameColumn, string title = "N/a")
{
Name = name;
Title = title;
BindCellCallback = ((element, index) =>
{
view.CreateTreeViewCell(element, index, name, isNameColumn, view.GetType());
});
BindHeaderCallback = ((element) =>
{
view.CreateTreeViewHeader(element, name, isNameColumn);
});
}
}
// Nested interface that can be either a bundle or asset.
public interface IAddressablesBuildReportItem
{
public string Name { get; }
void CreateGUI(VisualElement rootVisualElement);
string GetCellContent(string colName);
string GetSortContent(string colName);
}
public interface IAddressablesBuildReportBundle
{
public BuildLayout.Bundle Bundle { get; }
}
public interface IAddressablesBuildReportAsset
{
public BuildLayout.ExplicitAsset ExplicitAsset { get; }
public BuildLayout.DataFromOtherAsset DataFromOtherAsset { get; }
public List<BuildLayout.Bundle> Bundles { get; }
public ulong SizeWDependencies { get; }
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c7dcb1750e8ec04cb1c92c4cae35c39
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,95 @@
#if UNITY_2022_2_OR_NEWER
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.AddressableAssets.GUIElements;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using static UnityEditor.AddressableAssets.BuildReportVisualizer.BuildReportWindow;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
internal enum DetailsViewTab
{
ReferencesTo,
ReferencedBy
}
class DetailsView : IAddressableView
{
DetailsViewTab m_ActiveContentsTab;
VisualElement m_Root;
BuildReportWindow m_Window;
DetailsContentView m_Contents;
DetailsSummaryView m_Summary;
object m_DetailsRootObject;
object m_DetailsActiveObject;
internal DetailsView(BuildReportWindow window)
{
m_Window = window;
m_ActiveContentsTab = DetailsViewTab.ReferencesTo;
}
public void CreateGUI(VisualElement rootVisualElement)
{
m_Root = rootVisualElement;
m_Summary = new DetailsSummaryView(rootVisualElement, m_Window);
m_Contents = new DetailsContentView(rootVisualElement, m_Window);
rootVisualElement.Q<RibbonButton>("ReferencesToTab").clicked += () =>
{
m_ActiveContentsTab = DetailsViewTab.ReferencesTo;
DetailsStack.Clear();
DisplayContents(m_DetailsActiveObject);
};
rootVisualElement.Q<RibbonButton>("ReferencedByTab").clicked += () =>
{
m_ActiveContentsTab = DetailsViewTab.ReferencedBy;
DetailsStack.Clear();
DisplayContents(m_DetailsActiveObject);
};
}
public void OnSelected(IEnumerable<object> items)
{
ClearGUI();
DetailsStack.Clear();
foreach (object item in items)
{
DisplayItemSummary(item);
DisplayContents(item);
m_DetailsRootObject = m_DetailsActiveObject = item;
}
}
public void DisplayItemSummary(object item)
{
m_Summary.UpdateSummary(item);
}
public void DisplayContents(object contents)
{
m_Contents.DisplayContents(contents, m_ActiveContentsTab);
}
public void ClearGUI()
{
m_Summary.ClearSummary();
m_Contents.ClearContents();
DisplayContents(null);
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f4712e2e66a54047bd4181d8a3d827f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,304 @@
#if UNITY_2022_2_OR_NEWER
using System.Collections.Generic;
using System.Linq;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.IMGUI.Controls;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
internal class DuplicatedAssetsViewBuildReportItem : IAddressablesBuildReportItem
{
public string Name { get; set; }
public BuildLayout.ExplicitAsset ExplicitAsset { get; set; }
public BuildLayout.DataFromOtherAsset DataFromOtherAsset { get; set; }
public List<BuildLayout.Bundle> Bundles { get; set; }
public List<BuildLayout.ExplicitAsset> AssetDepsOf { get; set; }
public ulong SizeWDependencies { get; protected set; }
public ulong Size { get; protected set; }
public int DepsCount { get; set; }
public int DepsOfCount { get; set; }
public ulong SpaceSavedIfDeduplicated { get; set; }
public int DuplicationCount { get; set; }
public void CreateGUI(VisualElement rootVisualElement)
{
}
public string GetCellContent(string colName)
{
if (colName == BuildReportUtility.DuplicatedAssetsContentViewColAssetName)
return Name;
if (colName == BuildReportUtility.DuplicatedAssetsContentViewColSize)
return BuildReportUtility.GetDenominatedBytesString(Size);
if (colName == BuildReportUtility.DuplicatedAssetsContentViewSpaceSaved)
{
if (this is DuplicatedAssetsViewBuildReportAsset asset)
return asset.UsedByString;
if (DuplicationCount == -1)
return "";
return BuildReportUtility.GetDenominatedBytesString(SpaceSavedIfDeduplicated);
}
if (colName == BuildReportUtility.DuplicatedAssetsContentViewDuplicationCount)
{
if (DuplicationCount == -1)
return "";
return DuplicationCount.ToString();
}
return "";
}
public string GetSortContent(string colName)
{
if (colName == BuildReportUtility.DuplicatedAssetsContentViewColSize)
return Size.ToString();
if (colName == BuildReportUtility.DuplicatedAssetsContentViewSpaceSaved)
return SpaceSavedIfDeduplicated.ToString();
if (colName == BuildReportUtility.DuplicatedAssetsContentViewSpaceSaved)
return DuplicationCount.ToString();
return GetCellContent(colName);
}
}
internal class DuplicatedAssetsViewBuildReportDuplicatedAsset : DuplicatedAssetsViewBuildReportItem, IAddressablesBuildReportAsset
{
public new string GetCellContent(string colName)
{
if (colName == BuildReportUtility.DuplicatedAssetsContentViewColAssetName)
return Name;
if (colName == BuildReportUtility.DuplicatedAssetsContentViewColSize)
return BuildReportUtility.GetDenominatedBytesString(Size);
if (colName == BuildReportUtility.DuplicatedAssetsContentViewSpaceSaved)
return BuildReportUtility.GetDenominatedBytesString(SpaceSavedIfDeduplicated);
if (colName == BuildReportUtility.DuplicatedAssetsContentViewDuplicationCount)
return DuplicationCount.ToString();
return "";
}
public DuplicatedAssetsViewBuildReportDuplicatedAsset(BuildReportHelperDuplicateImplicitAsset helperAsset)
{
DataFromOtherAsset = helperAsset.Asset;
Name = DataFromOtherAsset.AssetPath;
SizeWDependencies = DataFromOtherAsset.SerializedSize + DataFromOtherAsset.StreamedSize;
Size = DataFromOtherAsset.SerializedSize + DataFromOtherAsset.StreamedSize;
DepsCount = 0;
Bundles = helperAsset.Bundles;
AssetDepsOf = helperAsset.GUIDToReferencingAssets.Values.ToList();
DepsOfCount = AssetDepsOf.Count;
DuplicationCount = Bundles.Count;
if (DuplicationCount > 1)
{
SpaceSavedIfDeduplicated = (ulong) (DuplicationCount - 1) * Size;
}
}
public DuplicatedAssetsViewBuildReportDuplicatedAsset(BuildLayout.DataFromOtherAsset implicitAsset)
{
DataFromOtherAsset = implicitAsset;
Bundles = new List<BuildLayout.Bundle>() { implicitAsset.File.Bundle };
Name = DataFromOtherAsset.AssetPath;
SizeWDependencies = DataFromOtherAsset.SerializedSize + DataFromOtherAsset.StreamedSize;
Size = DataFromOtherAsset.SerializedSize + DataFromOtherAsset.StreamedSize;
DuplicationCount = -1;
}
}
internal class DuplicatedAssetsViewBuildReportAsset : DuplicatedAssetsViewBuildReportItem, IAddressablesBuildReportAsset
{
public string UsedByString { get; set; }
public DuplicatedAssetsViewBuildReportAsset(BuildLayout.ExplicitAsset asset, BuildLayout.DataFromOtherAsset implicitAsset)
{
ExplicitAsset = asset;
Name = asset.AddressableName;
Size = asset.SerializedSize + asset.StreamedSize;
UsedByString = $"Uses {implicitAsset.AssetPath}";
DuplicationCount = -1;
SpaceSavedIfDeduplicated = 0;
}
}
internal class DuplicatedAssetsViewBuildReportUnrelatedAssets : DuplicatedAssetsViewBuildReportItem
{
public DuplicatedAssetsViewBuildReportUnrelatedAssets(ulong assetSize, int assetCount)
{
Name = $"({assetCount} unrelated assets)";
Size = assetSize;
DuplicationCount = -1;
SpaceSavedIfDeduplicated = 0;
}
}
internal class DuplicatedAssetsViewBuildReportBundle : DuplicatedAssetsViewBuildReportItem, IAddressablesBuildReportBundle
{
public DuplicatedAssetsViewBuildReportBundle(BuildLayout.Bundle bundle)
{
Bundle = bundle;
Name = bundle.Name;
Size = bundle.FileSize;
DuplicationCount = -1;
SpaceSavedIfDeduplicated = 0;
}
public BuildLayout.Bundle Bundle { get; set; }
}
class DuplicatedAssetsContentView : ContentView
{
public DuplicatedAssetsContentView(BuildReportHelperConsumer helperConsumer, DetailsView detailsView)
: base(helperConsumer, detailsView) { }
internal override ContentViewColumnData[] ColumnDataForView
{
get
{
return new ContentViewColumnData[]
{
new ContentViewColumnData(BuildReportUtility.DuplicatedAssetsContentViewColAssetName,this, true, "Asset Name"),
new ContentViewColumnData(BuildReportUtility.DuplicatedAssetsContentViewColSize, this, false, "Size"),
new ContentViewColumnData(BuildReportUtility.DuplicatedAssetsContentViewSpaceSaved, this, false, "Space saved if De-duplicated"),
new ContentViewColumnData(BuildReportUtility.DuplicatedAssetsContentViewDuplicationCount, this, false, "Number of times duplicated")
};
}
}
public override IList<IAddressablesBuildReportItem> CreateTreeViewItems(BuildLayout report)
{
List<IAddressablesBuildReportItem> buildReportAssets = new List<IAddressablesBuildReportItem>();
if (report == null)
return buildReportAssets;
foreach (BuildReportHelperDuplicateImplicitAsset helperAsset in m_HelperConsumer.GUIDToDuplicateAssets.Values)
{
buildReportAssets.Add(new DuplicatedAssetsViewBuildReportDuplicatedAsset(helperAsset));
}
return buildReportAssets;
}
internal IList<TreeViewItemData<DuplicatedAssetsViewBuildReportItem>> CreateTreeRootsNestedList(IList<IAddressablesBuildReportItem> items)
{
int id = 0;
var roots = new List<TreeViewItemData<DuplicatedAssetsViewBuildReportItem>>();
foreach (DuplicatedAssetsViewBuildReportItem item in items)
{
DuplicatedAssetsViewBuildReportDuplicatedAsset duplicatedAsset = item as DuplicatedAssetsViewBuildReportDuplicatedAsset;
if (duplicatedAsset == null)
continue;
bool includeAllDependencies = EntryAppearsInSearch(duplicatedAsset, m_SearchValue);
var bundles = CreateBundleEntries(duplicatedAsset, ref id, includeAllDependencies);
if (includeAllDependencies || bundles != null)
roots.Add(new TreeViewItemData<DuplicatedAssetsViewBuildReportItem>(id++, item, bundles));
}
return roots;
}
private List<TreeViewItemData<DuplicatedAssetsViewBuildReportItem>> CreateBundleEntries(DuplicatedAssetsViewBuildReportDuplicatedAsset duplicatedAsset, ref int id, bool includeAllDependencies)
{
var bundles = new List<TreeViewItemData<DuplicatedAssetsViewBuildReportItem>>();
var bundleToAssetList = new Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>>();
foreach (var dependentAsset in duplicatedAsset.AssetDepsOf)
{
if (!bundleToAssetList.ContainsKey(dependentAsset.Bundle))
bundleToAssetList.Add(dependentAsset.Bundle, new List<BuildLayout.ExplicitAsset>());
bundleToAssetList[dependentAsset.Bundle].Add(dependentAsset);
}
bool includeDuplicationInSearch = includeAllDependencies;
foreach (var bundle in bundleToAssetList.Keys)
{
var bundleReportItem = new DuplicatedAssetsViewBuildReportBundle(bundle);
var depAssets = new List<TreeViewItemData<DuplicatedAssetsViewBuildReportItem>>();
var unrelatedAssetsSize = bundle.FileSize;
depAssets.Add(new TreeViewItemData<DuplicatedAssetsViewBuildReportItem>(id++, new DuplicatedAssetsViewBuildReportDuplicatedAsset(duplicatedAsset.DataFromOtherAsset)));
foreach (var dependentAsset in bundleToAssetList[bundle])
{
var assetReportItem = new DuplicatedAssetsViewBuildReportAsset(dependentAsset, duplicatedAsset.DataFromOtherAsset);
includeDuplicationInSearch = includeDuplicationInSearch || EntryAppearsInSearch(assetReportItem, m_SearchValue);
unrelatedAssetsSize -= dependentAsset.SerializedSize + dependentAsset.StreamedSize;
depAssets.Add(new TreeViewItemData<DuplicatedAssetsViewBuildReportItem>(id++, assetReportItem));
}
int unrelatedAssetCount = bundle.AssetCount - bundleToAssetList[bundle].Count;
if (unrelatedAssetCount > 0)
{
depAssets.Add(new TreeViewItemData<DuplicatedAssetsViewBuildReportItem>(id++, new DuplicatedAssetsViewBuildReportUnrelatedAssets(unrelatedAssetsSize, unrelatedAssetCount)));
}
bundles.Add(new TreeViewItemData<DuplicatedAssetsViewBuildReportItem>(id++, bundleReportItem, depAssets));
}
if (includeDuplicationInSearch)
return bundles;
return null;
}
public override void Consume(BuildLayout buildReport)
{
if (buildReport == null)
return;
m_Report = buildReport;
m_TreeItems = CreateTreeViewItems(m_Report);
IList<TreeViewItemData<DuplicatedAssetsViewBuildReportItem>> treeRoots = CreateTreeRootsNestedList(m_TreeItems);
m_TreeView.SetRootItems(treeRoots);
m_TreeView.Rebuild();
m_TreeView.columnSortingChanged += ColumnSortingChanged;
}
private void ColumnSortingChanged()
{
var columnList = m_TreeView.sortedColumns;
IList<IAddressablesBuildReportItem> sortedRootList = new List<IAddressablesBuildReportItem>();
foreach (var col in columnList)
{
sortedRootList = SortByColumnDescription(col);
}
m_TreeView.SetRootItems(CreateTreeRootsNestedList(sortedRootList));
m_TreeView.Rebuild();
}
public override void CreateGUI(VisualElement rootVisualElement)
{
VisualElement view = rootVisualElement.Q<VisualElement>(BuildReportUtility.ContentView);
TreeBuilder tb = new TreeBuilder()
.With(ColumnDataForView)
.With((items) => ItemsSelected.Invoke(items));
m_TreeView = tb.Build();
view.Add(m_TreeView);
SetCallbacksForColumns(m_TreeView.columns, ColumnDataForView);
m_SearchField = rootVisualElement.Q<ToolbarSearchField>(BuildReportUtility.SearchField);
m_SearchField.RegisterValueChangedCallback(OnSearchValueChanged);
m_SearchValue = m_SearchField.value;
}
private void OnSearchValueChanged(ChangeEvent<string> evt)
{
if (m_TreeItems == null)
return;
m_SearchValue = evt.newValue.ToLower();
m_TreeView.SetRootItems(CreateTreeRootsNestedList(m_TreeItems));
m_TreeView.Rebuild();
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5c3475f04d410a47ade712e307b9d75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,479 @@
#if UNITY_2022_2_OR_NEWER
using System.Collections.Generic;
using System.Linq;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
internal class GroupsViewBuildReportItem : IAddressablesBuildReportItem
{
public BuildLayout.Group Group { get; set; }
public string Name { get; protected set; }
public ulong FileSizePlusRefs { get; set; }
public ulong FileSizeUncompressed { get; set; }
public ulong FileSizeBundle { get; set; }
public int RefsTo { get; set; }
public int RefsBy { get; set; }
public virtual void CreateGUI(VisualElement rootVisualElement)
{
}
public virtual string GetCellContent(string colName)
{
if (colName == BuildReportUtility.GroupsContentViewColGroupName)
return Name;
else if (colName == BuildReportUtility.GroupsContentViewColSizePlusRefs)
{
if (FileSizePlusRefs == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizePlusRefs);
}
else if (colName == BuildReportUtility.GroupsContentViewColSizeUncompressed)
{
if (FileSizeUncompressed == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeUncompressed);
}
else if (colName == BuildReportUtility.GroupsContentViewColBundleSize)
{
if (FileSizeBundle == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeBundle);
}
else if (colName == BuildReportUtility.GroupsContentViewColRefsTo)
{
if (RefsTo == -1)
return "--";
return RefsTo.ToString();
}
else if (colName == BuildReportUtility.GroupsContentViewColRefsBy)
{
if (RefsBy == -1)
return "--";
return RefsBy.ToString();
}
return "";
}
public string GetSortContent(string colName)
{
if (colName == BuildReportUtility.GroupsContentViewColSizePlusRefs)
return FileSizePlusRefs.ToString();
if (colName == BuildReportUtility.GroupsContentViewColSizeUncompressed)
return FileSizeUncompressed.ToString();
if (colName == BuildReportUtility.GroupsContentViewColBundleSize)
return FileSizeBundle.ToString();
if (colName == BuildReportUtility.GroupsContentViewColRefsBy)
return RefsBy.ToString();
if (colName == BuildReportUtility.GroupsContentViewColRefsTo)
return RefsTo.ToString();
return GetCellContent(colName);
}
}
internal class GroupsViewBuildReportGroup : GroupsViewBuildReportItem
{
public List<BuildLayout.ExplicitAsset> BuildReportAssets;
public GroupsViewBuildReportGroup(BuildLayout.Group group, List<BuildLayout.ExplicitAsset> reportAssets)
{
Name = group.Name;
Group = group;
BuildReportAssets = reportAssets;
FileSizePlusRefs = 0;
FileSizeBundle = 0;
FileSizeUncompressed = 0;
RefsBy = -1;
RefsTo = group.Bundles.Count;
}
}
internal class GroupsViewBuildReportAsset : GroupsViewBuildReportItem, IAddressablesBuildReportAsset
{
public List<BuildLayout.ExplicitAsset> InternallyReferencedAssets { get; }
public List<BuildLayout.ExplicitAsset> ExternallyReferencedAssets { get; }
public List<BuildLayout.DataFromOtherAsset> ImplicitDependencies { get; }
public BuildLayout.ExplicitAsset ExplicitAsset { get; }
public BuildLayout.DataFromOtherAsset DataFromOtherAsset { get; }
public List<BuildLayout.Bundle> Bundles { get; }
public ulong SizeWDependencies { get; }
public List<BuildLayout.ExplicitAsset> ReferencingAssets { get; }
public GroupsViewBuildReportAsset(BuildLayout.ExplicitAsset asset)
{
ExplicitAsset = asset;
Name = asset.AddressableName;
ExternallyReferencedAssets = asset.ExternallyReferencedAssets;
InternallyReferencedAssets = asset.InternalReferencedExplicitAssets;
ImplicitDependencies = asset.InternalReferencedOtherAssets;
ReferencingAssets = asset.ReferencingAssets;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = FileSizeUncompressed;
foreach (var r in asset.ExternallyReferencedAssets)
FileSizePlusRefs += r.SerializedSize + r.StreamedSize;
foreach (var r in asset.InternalReferencedExplicitAssets)
FileSizePlusRefs += r.SerializedSize + r.StreamedSize;
RefsTo = asset.ExternallyReferencedAssets.Count + asset.InternalReferencedExplicitAssets.Count;
RefsBy = asset.ReferencingAssets != null ? asset.ReferencingAssets.Count : -1;
}
public GroupsViewBuildReportAsset(BuildLayout.DataFromOtherAsset asset)
{
DataFromOtherAsset = asset;
Bundles = new List<BuildLayout.Bundle>() { asset.File.Bundle };
Name = asset.AssetPath;
RefsBy = asset.ReferencingAssets.Count;
RefsTo = -1;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = asset.SerializedSize + asset.StreamedSize;
}
}
internal class GroupsViewBuildReportBundle : GroupsViewBuildReportItem, IAddressablesBuildReportBundle
{
public GroupsViewBuildReportBundle(BuildLayout.Bundle bundle)
{
Name = bundle.Name;
Bundle = bundle;
FileSizeBundle = bundle.FileSize;
FileSizeUncompressed = bundle.UncompressedFileSize;
FileSizePlusRefs = bundle.FileSize + bundle.ExpandedDependencyFileSize + bundle.DependencyFileSize;
foreach (var file in bundle.Files)
RefsTo += file.Assets.Count + file.OtherAssets.Count;
RefsBy = Bundle.DependentBundles.Count;
}
public BuildLayout.Bundle Bundle { get; }
}
internal class GroupsViewBuildReportUnrelatedAssets : GroupsViewBuildReportItem
{
public GroupsViewBuildReportUnrelatedAssets(ulong assetSize, int assetCount)
{
Name = $"({assetCount} unrelated assets)";
FileSizeUncompressed = assetSize;
}
}
internal class GroupsViewBuildReportIndirectlyReferencedBundles : GroupsViewBuildReportItem
{
public GroupsViewBuildReportIndirectlyReferencedBundles(List<BuildLayout.Bundle> bundles)
{
Name = bundles.Count > 1 ? $"{bundles.Count} indirectly referenced bundles" : $"{bundles.Count} indirectly referenced bundle";
FileSizeBundle = 0;
FileSizeUncompressed = 0;
HashSet<BuildLayout.Bundle> countedBundles = new HashSet<BuildLayout.Bundle>();
foreach (var b in bundles)
{
FileSizeBundle += b.FileSize;
FileSizeUncompressed += b.UncompressedFileSize;
if (!countedBundles.Contains(b))
{
FileSizePlusRefs += b.FileSize;
countedBundles.Add(b);
}
foreach (var depB in b.ExpandedDependencies)
{
if (!countedBundles.Contains(depB))
{
FileSizePlusRefs += depB.FileSize;
countedBundles.Add(depB);
}
}
foreach (var depB in b.Dependencies)
{
if (!countedBundles.Contains(depB))
{
FileSizePlusRefs += depB.FileSize;
countedBundles.Add(depB);
}
}
}
}
}
class GroupsContentView : ContentView
{
public GroupsContentView(BuildReportHelperConsumer helperConsumer, DetailsView detailsView)
: base(helperConsumer, detailsView)
{
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
}
internal override ContentViewColumnData[] ColumnDataForView
{
get
{
return new ContentViewColumnData[]
{
new ContentViewColumnData(BuildReportUtility.GroupsContentViewColGroupName, this, true, "Group Name"),
new ContentViewColumnData(BuildReportUtility.GroupsContentViewColSizePlusRefs, this, false, "Total Size (+ refs)"),
new ContentViewColumnData(BuildReportUtility.GroupsContentViewColSizeUncompressed, this, false, "Uncompressed Size"),
new ContentViewColumnData(BuildReportUtility.GroupsContentViewColBundleSize, this, false, "Bundle File Size"),
new ContentViewColumnData(BuildReportUtility.GroupsContentViewColRefsTo, this, false, "Refs To"),
new ContentViewColumnData(BuildReportUtility.GroupsContentViewColRefsBy, this, false, "Refs By")
};
}
}
public override IList<IAddressablesBuildReportItem> CreateTreeViewItems(BuildLayout report)
{
List<IAddressablesBuildReportItem> buildReportGroups = new List<IAddressablesBuildReportItem>();
if (report == null)
return buildReportGroups;
var groupToAssets = new Dictionary<BuildLayout.Group, List<BuildLayout.ExplicitAsset>>();
foreach (var asset in BuildLayoutHelpers.EnumerateAssets(report))
{
if (groupToAssets.ContainsKey(asset.Bundle.Group))
groupToAssets[asset.Bundle.Group].Add(asset);
else
groupToAssets.Add(asset.Bundle.Group, new List<BuildLayout.ExplicitAsset> { asset });
}
foreach (var pair in groupToAssets)
{
buildReportGroups.Add(new GroupsViewBuildReportGroup(pair.Key, pair.Value));
}
return buildReportGroups;
}
internal IList<TreeViewItemData<GroupsViewBuildReportItem>> CreateTreeRootsNestedList(IList<IAddressablesBuildReportItem> items)
{
int id = 0;
var roots = new List<TreeViewItemData<GroupsViewBuildReportItem>>();
foreach (var item in items)
{
var group = item as GroupsViewBuildReportGroup;
if (group == null)
continue;
bool includeAllDependencies = EntryAppearsInSearch(group, m_SearchValue);
List<TreeViewItemData<GroupsViewBuildReportItem>> bundlesUnderGroup = CreateGroupBundles(group, ref id, includeAllDependencies);
if (bundlesUnderGroup.Count > 0 || includeAllDependencies)
{
var groupItem = new TreeViewItemData<GroupsViewBuildReportItem>(++id, group, bundlesUnderGroup);
m_DataHashtoReportItem.TryAdd(BuildReportUtility.ComputeDataHash(group.Name), new TreeDataReportItem(id, groupItem.data));
roots.Add(groupItem);
}
}
return roots;
}
private List<TreeViewItemData<GroupsViewBuildReportItem>> CreateGroupBundles(GroupsViewBuildReportGroup group, ref int id, bool includeAllDependencies)
{
var bundlesUnderGroup = new List<TreeViewItemData<GroupsViewBuildReportItem>>();
foreach (var bundle in group.Group.Bundles)
{
var bundleReportItem = new GroupsViewBuildReportBundle(bundle);
var children = new List<TreeViewItemData<GroupsViewBuildReportItem>>();
var directlyReferencedBundles = new HashSet<BuildLayout.Bundle>();
PopulateAssets(children, directlyReferencedBundles, bundle, ref id, includeAllDependencies);
PopulateIndirectlyReferencedBundles(children, directlyReferencedBundles, bundle, ref id, includeAllDependencies);
if (children.Count > 0 || EntryAppearsInSearch(bundleReportItem, m_SearchValue))
{
var bundleItem = new TreeViewItemData<GroupsViewBuildReportItem>(++id, bundleReportItem, children);
m_DataHashtoReportItem.TryAdd(BuildReportUtility.ComputeDataHash(group.Name, bundle.Name), new TreeDataReportItem(id, bundleItem.data));
bundlesUnderGroup.Add(bundleItem);
}
}
return bundlesUnderGroup;
}
private void PopulateAssets(List<TreeViewItemData<GroupsViewBuildReportItem>> children,
HashSet<BuildLayout.Bundle> directlyReferencedBundles, BuildLayout.Bundle bundle, ref int id, bool includeAllDependencies)
{
foreach (var asset in BuildLayoutHelpers.EnumerateAssets(bundle))
{
var reportAssetItem = new GroupsViewBuildReportAsset(asset);
bool assetAppearsInSearch = EntryAppearsInSearch(reportAssetItem, m_SearchValue) || includeAllDependencies;
var childrenOfAsset = GenerateChildrenOfAsset(asset, ref id, directlyReferencedBundles, assetAppearsInSearch);
if (assetAppearsInSearch || childrenOfAsset.Count > 0)
{
var assetItem = new TreeViewItemData<GroupsViewBuildReportItem>(++id, reportAssetItem, childrenOfAsset);
m_DataHashtoReportItem.TryAdd(BuildReportUtility.ComputeDataHash(asset.AddressableName), new TreeDataReportItem(id, assetItem.data));
children.Add(assetItem);
}
}
foreach (var asset in BuildLayoutHelpers.EnumerateImplicitAssets(bundle))
{
var reportImplicitAssetItem = new GroupsViewBuildReportAsset(asset);
if (includeAllDependencies || EntryAppearsInSearch(reportImplicitAssetItem, m_SearchValue))
children.Add(new TreeViewItemData<GroupsViewBuildReportItem>(++id, reportImplicitAssetItem));
}
}
private void PopulateIndirectlyReferencedBundles(List<TreeViewItemData<GroupsViewBuildReportItem>> children,
HashSet<BuildLayout.Bundle> directlyReferencedBundles, BuildLayout.Bundle bundle, ref int id, bool includeAllDependencies)
{
var indirectlyReferencedBundleReportItems = new List<TreeViewItemData<GroupsViewBuildReportItem>>();
var indirectlyReferencedBundles = new List<BuildLayout.Bundle>();
foreach (var depBundle in bundle.ExpandedDependencies)
{
if (!directlyReferencedBundles.Contains(depBundle))
{
var reportBundle = new GroupsViewBuildReportBundle(depBundle);
if (includeAllDependencies || EntryAppearsInSearch(reportBundle, m_SearchValue))
{
indirectlyReferencedBundleReportItems.Add(new TreeViewItemData<GroupsViewBuildReportItem>(++id, reportBundle));
indirectlyReferencedBundles.Add(depBundle);
}
}
}
if (indirectlyReferencedBundles.Count > 0)
{
children.Add(new TreeViewItemData<GroupsViewBuildReportItem>(++id, new GroupsViewBuildReportIndirectlyReferencedBundles(indirectlyReferencedBundles), indirectlyReferencedBundleReportItems));
}
}
private List<TreeViewItemData<GroupsViewBuildReportItem>> GenerateChildrenOfAsset(BuildLayout.ExplicitAsset asset, ref int id, HashSet<BuildLayout.Bundle> referencedBundles, bool includeAllDependencies)
{
var childrenOfAsset = new List<TreeViewItemData<GroupsViewBuildReportItem>>();
foreach (var dep in asset.InternalReferencedExplicitAssets)
{
var reportDepAsset = new GroupsViewBuildReportAsset(dep);
if (includeAllDependencies || EntryAppearsInSearch(reportDepAsset, m_SearchValue))
{
var tvid = new TreeViewItemData<GroupsViewBuildReportItem>(++id, reportDepAsset);
childrenOfAsset.Add(tvid);
}
}
Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>> bundleToAssetList = new Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>>();
foreach (var dep in asset.ExternallyReferencedAssets)
{
if (!bundleToAssetList.ContainsKey(dep.Bundle))
{
bundleToAssetList.Add(dep.Bundle, new List<BuildLayout.ExplicitAsset>());
referencedBundles.Add(dep.Bundle);
}
bundleToAssetList[dep.Bundle].Add(dep);
}
foreach (var bundle in bundleToAssetList.Keys)
{
var bundleReportAsset = new GroupsViewBuildReportBundle(bundle);
bool includeAllDependenciesUnderBundle = EntryAppearsInSearch(bundleReportAsset, m_SearchValue) || includeAllDependencies;
var assetTreeViewItems = new List<TreeViewItemData<GroupsViewBuildReportItem>>();
var assetList = bundleToAssetList[bundle];
ulong unrelatedAssetSize = bundle.FileSize;
foreach (var bundleAsset in assetList)
{
var reportAsset = new GroupsViewBuildReportAsset(bundleAsset);
if (includeAllDependenciesUnderBundle || EntryAppearsInSearch(reportAsset, m_SearchValue))
{
var tvid = new TreeViewItemData<GroupsViewBuildReportItem>(++id, reportAsset);
assetTreeViewItems.Add(tvid);
unrelatedAssetSize -= bundleAsset.SerializedSize + bundleAsset.StreamedSize;
}
}
int unrelatedAssetCount = bundle.AssetCount - assetList.Count;
if (unrelatedAssetCount > 0 && includeAllDependenciesUnderBundle)
{
var tvid = new TreeViewItemData<GroupsViewBuildReportItem>(++id, new GroupsViewBuildReportUnrelatedAssets(unrelatedAssetSize, unrelatedAssetCount));
assetTreeViewItems.Add(tvid);
}
if (includeAllDependenciesUnderBundle || assetTreeViewItems.Count > 0)
{
var rootItem = new TreeViewItemData<GroupsViewBuildReportItem>(++id, bundleReportAsset, assetTreeViewItems);
childrenOfAsset.Add(rootItem);
}
}
foreach (var dep in asset.InternalReferencedOtherAssets)
{
var reportDepAsset = new GroupsViewBuildReportAsset(dep);
if (includeAllDependencies || EntryAppearsInSearch(reportDepAsset, m_SearchValue))
{
var tvid = new TreeViewItemData<GroupsViewBuildReportItem>(++id, reportDepAsset);
childrenOfAsset.Add(tvid);
}
}
return childrenOfAsset;
}
public override void Consume(BuildLayout buildReport)
{
if (buildReport == null)
return;
m_Report = buildReport;
m_TreeItems = CreateTreeViewItems(m_Report);
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
IList<TreeViewItemData<GroupsViewBuildReportItem>> treeRoots = CreateTreeRootsNestedList(m_TreeItems);
m_TreeView.SetRootItems(treeRoots);
m_TreeView.Rebuild();
m_TreeView.columnSortingChanged += ColumnSortingChanged;
}
private void ColumnSortingChanged()
{
var columnList = m_TreeView.sortedColumns;
IList<IAddressablesBuildReportItem> sortedRootList = new List<IAddressablesBuildReportItem>();
foreach (var col in columnList)
{
sortedRootList = SortByColumnDescription(col);
}
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
m_TreeView.SetRootItems(CreateTreeRootsNestedList(sortedRootList));
m_TreeView.Rebuild();
}
public override void CreateGUI(VisualElement rootVisualElement)
{
VisualElement view = rootVisualElement.Q<VisualElement>(BuildReportUtility.ContentView);
TreeBuilder tb = new TreeBuilder()
.With(ColumnDataForView)
.With((items) => ItemsSelected.Invoke(items));
m_TreeView = tb.Build();
view.Add(m_TreeView);
SetCallbacksForColumns(m_TreeView.columns, ColumnDataForView);
m_SearchField = rootVisualElement.Q<ToolbarSearchField>(BuildReportUtility.SearchField);
m_SearchField.RegisterValueChangedCallback(OnSearchValueChanged);
m_SearchValue = m_SearchField.value;
}
private void OnSearchValueChanged(ChangeEvent<string> evt)
{
if (m_TreeItems == null)
return;
m_SearchValue = evt.newValue.ToLower();
m_TreeView.SetRootItems(CreateTreeRootsNestedList(m_TreeItems));
m_TreeView.Rebuild();
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 238dc259a6e2a034a9a94e1a26ebb37b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,398 @@
#if UNITY_2022_2_OR_NEWER
using System;
using System.Collections.Generic;
using UnityEditor.AddressableAssets.Build.Layout;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using static UnityEditor.AddressableAssets.BuildReportVisualizer.BuildReportWindow;
namespace UnityEditor.AddressableAssets.BuildReportVisualizer
{
internal class LabelsViewBuildReportItem : IAddressablesBuildReportItem
{
public string Name { get; protected set; }
public ulong FileSizePlusRefs { get; set; }
public ulong FileSizeUncompressed { get; set; }
public ulong FileSizeBundle { get; set; }
public int RefsBy { get; set; }
public int RefsTo { get; set; }
public virtual void CreateGUI(VisualElement rootVisualElement) { }
public virtual string GetCellContent(string colName)
{
if (colName == BuildReportUtility.LabelsContentViewColLabelName)
return Name;
else if (colName == BuildReportUtility.LabelsContentViewColSizePlusRefs)
{
if (FileSizePlusRefs == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizePlusRefs);
}
else if (colName == BuildReportUtility.LabelsContentViewColSizeUncompressed)
{
if (FileSizeUncompressed == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeUncompressed);
}
else if (colName == BuildReportUtility.LabelsContentViewColSizeBundle)
{
if (FileSizeBundle == 0)
return "--";
return BuildReportUtility.GetDenominatedBytesString(FileSizeBundle);
}
else if (colName == BuildReportUtility.LabelsContentViewColRefsTo)
{
if (RefsTo == -1)
return "--";
return RefsTo.ToString();
}
else if (colName == BuildReportUtility.LabelsContentViewColRefsBy)
{
if (RefsBy == -1)
return "--";
return RefsBy.ToString();
}
return "";
}
public virtual string GetSortContent(string colName)
{
if (colName == BuildReportUtility.LabelsContentViewColSizePlusRefs)
return FileSizePlusRefs.ToString();
if (colName == BuildReportUtility.LabelsContentViewColSizeUncompressed)
return FileSizeUncompressed.ToString();
if (colName == BuildReportUtility.LabelsContentViewColSizeBundle)
return FileSizeBundle.ToString();
if (colName == BuildReportUtility.LabelsContentViewColRefsBy)
return RefsBy.ToString();
if (colName == BuildReportUtility.LabelsContentViewColRefsTo)
return RefsTo.ToString();
return GetCellContent(colName);
}
}
internal class LabelsViewBuildReportLabel : LabelsViewBuildReportItem
{
public ulong SizeWDependencies { get; set; }
public List<BuildLayout.ExplicitAsset> BuildReportAssets { get; set; }
public LabelsViewBuildReportLabel(string name, List<BuildLayout.ExplicitAsset> reportAssets)
{
Name = name;
BuildReportAssets = reportAssets;
FileSizePlusRefs = 0;
FileSizeBundle = 0;
RefsBy = -1;
RefsTo = reportAssets.Count;
var seenBundles = new HashSet<BuildLayout.Bundle>();
foreach (var reportItem in reportAssets)
{
FileSizePlusRefs += reportItem.SerializedSize + reportItem.StreamedSize;
if (!seenBundles.Contains(reportItem.Bundle))
{
seenBundles.Add(reportItem.Bundle);
FileSizeBundle += reportItem.Bundle.FileSize;
}
foreach (var dep in reportItem.ExternallyReferencedAssets)
{
FileSizePlusRefs += dep.SerializedSize + dep.StreamedSize;
if (!seenBundles.Contains(dep.Bundle))
{
seenBundles.Add(dep.Bundle);
FileSizeBundle += dep.Bundle.FileSize;
}
}
}
FileSizeUncompressed = FileSizePlusRefs;
}
}
internal class LabelsViewBuildReportAsset : LabelsViewBuildReportItem, IAddressablesBuildReportAsset
{
public BuildLayout.ExplicitAsset ExplicitAsset { get; }
public BuildLayout.DataFromOtherAsset DataFromOtherAsset { get; }
public List<BuildLayout.ExplicitAsset> InternallyReferencedAssets { get; }
public List<BuildLayout.ExplicitAsset> ExternallyReferencedAssets { get; }
List<BuildLayout.ExplicitAsset> ReferencingAssets { get; }
public List<BuildLayout.DataFromOtherAsset> ImplicitDependencies { get; }
public List<BuildLayout.Bundle> Bundles { get; }
public ulong SizeWDependencies { get; }
public bool IsAddressable => ExplicitAsset != null;
public LabelsViewBuildReportAsset(BuildLayout.ExplicitAsset asset)
{
ExplicitAsset = asset;
Name = asset.AddressableName;
ExternallyReferencedAssets = asset.ExternallyReferencedAssets;
InternallyReferencedAssets = asset.InternalReferencedExplicitAssets;
ImplicitDependencies = asset.InternalReferencedOtherAssets;
ReferencingAssets = asset.ReferencingAssets;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = FileSizeUncompressed;
foreach (var r in asset.ExternallyReferencedAssets)
if (r!= null)
FileSizePlusRefs += r.SerializedSize + r.StreamedSize;
foreach (var r in asset.InternalReferencedExplicitAssets)
if (r!= null)
FileSizePlusRefs += r.SerializedSize + r.StreamedSize;
RefsTo = asset.ExternallyReferencedAssets.Count + asset.InternalReferencedExplicitAssets.Count;
RefsBy = asset.ReferencingAssets != null ? asset.ReferencingAssets.Count : -1;
}
public LabelsViewBuildReportAsset(BuildLayout.DataFromOtherAsset asset)
{
DataFromOtherAsset = asset;
Bundles = new List<BuildLayout.Bundle>() { asset.File.Bundle };
Name = asset.AssetPath;
RefsBy = asset.ReferencingAssets.Count;
RefsTo = -1;
FileSizeUncompressed = asset.SerializedSize + asset.StreamedSize;
FileSizePlusRefs = asset.SerializedSize + asset.StreamedSize;
}
}
internal class LabelsViewBuildReportBundle : LabelsViewBuildReportItem, IAddressablesBuildReportBundle
{
public LabelsViewBuildReportBundle(BuildLayout.Bundle bundle)
{
Name = bundle.Name;
Bundle = bundle;
FileSizePlusRefs = bundle.FileSize + bundle.ExpandedDependencyFileSize + bundle.DependencyFileSize;
FileSizeBundle = bundle.FileSize;
FileSizeUncompressed = bundle.UncompressedFileSize;
RefsTo = Bundle.ExpandedDependencies.Count;
RefsBy = Bundle.DependentBundles.Count;
}
public BuildLayout.Bundle Bundle { get; }
}
internal class LabelsViewBuildReportUnrelatedAssets : LabelsViewBuildReportItem
{
public LabelsViewBuildReportUnrelatedAssets(ulong assetSize, int assetCount)
{
Name = $"({assetCount} unrelated assets)";
FileSizeUncompressed = assetSize;
}
}
class LabelsContentView : ContentView
{
public LabelsContentView(BuildReportHelperConsumer helperConsumer, DetailsView detailsView)
: base(helperConsumer, detailsView) { }
internal override ContentViewColumnData[] ColumnDataForView
{
get
{
return new ContentViewColumnData[]
{
new ContentViewColumnData(BuildReportUtility.LabelsContentViewColLabelName, this, true, "Label Name"),
new ContentViewColumnData(BuildReportUtility.LabelsContentViewColSizePlusRefs, this, false, "Total Size (+ refs)"),
new ContentViewColumnData(BuildReportUtility.LabelsContentViewColSizeUncompressed, this, false, "Uncompressed Size"),
new ContentViewColumnData(BuildReportUtility.LabelsContentViewColSizeBundle, this, false, "Bundle File Size"),
new ContentViewColumnData(BuildReportUtility.LabelsContentViewColRefsTo, this, false, "Refs To"),
new ContentViewColumnData(BuildReportUtility.LabelsContentViewColRefsBy, this, false, "Refs By")
};
}
}
public override IList<IAddressablesBuildReportItem> CreateTreeViewItems(BuildLayout report)
{
List<IAddressablesBuildReportItem> buildReportLabels = new List<IAddressablesBuildReportItem>();
if (report == null)
return buildReportLabels;
var labelToAssets = new Dictionary<string, List<BuildLayout.ExplicitAsset>>();
foreach (var asset in BuildLayoutHelpers.EnumerateAssets(report))
{
foreach (string label in asset.Labels)
{
if (labelToAssets.ContainsKey(label))
labelToAssets[label].Add(asset);
else
labelToAssets.Add(label, new List<BuildLayout.ExplicitAsset> { asset });
}
}
foreach (var pair in labelToAssets)
{
buildReportLabels.Add(new LabelsViewBuildReportLabel(pair.Key, pair.Value));
}
return buildReportLabels;
}
IList<TreeViewItemData<LabelsViewBuildReportItem>> CreateTreeRootsNestedList(IList<IAddressablesBuildReportItem> items)
{
int id = 0;
var roots = new List<TreeViewItemData<LabelsViewBuildReportItem>>();
foreach (var item in items)
{
var label = item as LabelsViewBuildReportLabel;
if (label == null)
continue;
bool includeAllDependencies = EntryAppearsInSearch(label, m_SearchValue);
var assetsUnderLabel = new List<TreeViewItemData<LabelsViewBuildReportItem>>();
foreach (var asset in label.BuildReportAssets)
{
var assetReportItem = new LabelsViewBuildReportAsset(asset);
bool assetAppearsInSearch = EntryAppearsInSearch(assetReportItem, m_SearchValue) || includeAllDependencies;
var childrenOfAsset = GenerateChildrenOfAsset(asset, ref id, assetAppearsInSearch);
if (assetAppearsInSearch || childrenOfAsset.Count > 0)
{
var tvid = new TreeViewItemData<LabelsViewBuildReportItem>(++id, assetReportItem, childrenOfAsset);
m_DataHashtoReportItem.TryAdd(BuildReportUtility.ComputeDataHash(label.Name, asset.AddressableName), new TreeDataReportItem(id, tvid.data));
assetsUnderLabel.Add(tvid);
}
}
if (includeAllDependencies || assetsUnderLabel.Count > 0)
{
var rootItem = new TreeViewItemData<LabelsViewBuildReportItem>(++id, label, assetsUnderLabel);
m_DataHashtoReportItem.TryAdd(BuildReportUtility.ComputeDataHash(label.Name), new TreeDataReportItem(id, rootItem.data));
roots.Add(rootItem);
}
}
return roots;
}
private List<TreeViewItemData<LabelsViewBuildReportItem>> GenerateChildrenOfAsset(BuildLayout.ExplicitAsset asset, ref int id, bool includeAllDependencies)
{
var childrenOfAsset = new List<TreeViewItemData<LabelsViewBuildReportItem>>();
foreach (var dep in asset.InternalReferencedExplicitAssets)
{
var reportDepAsset = new LabelsViewBuildReportAsset(dep);
if (includeAllDependencies || EntryAppearsInSearch(reportDepAsset, m_SearchValue))
{
var tvid = new TreeViewItemData<LabelsViewBuildReportItem>(++id, reportDepAsset);
childrenOfAsset.Add(tvid);
}
}
Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>> bundleToAssetList = new Dictionary<BuildLayout.Bundle, List<BuildLayout.ExplicitAsset>>();
foreach (var dep in asset.ExternallyReferencedAssets)
{
if (!bundleToAssetList.ContainsKey(dep.Bundle))
bundleToAssetList.Add(dep.Bundle, new List<BuildLayout.ExplicitAsset>());
bundleToAssetList[dep.Bundle].Add(dep);
}
foreach (var bundle in bundleToAssetList.Keys)
{
var bundleReportAsset = new LabelsViewBuildReportBundle(bundle);
bool includeAllDependenciesUnderBundle = EntryAppearsInSearch(bundleReportAsset, m_SearchValue) || includeAllDependencies;
var assetTreeViewItems = new List<TreeViewItemData<LabelsViewBuildReportItem>>();
var assetList = bundleToAssetList[bundle];
ulong unrelatedAssetSize = bundle.FileSize;
foreach (var bundleAsset in assetList)
{
var reportAsset = new LabelsViewBuildReportAsset(bundleAsset);
if (includeAllDependenciesUnderBundle || EntryAppearsInSearch(reportAsset, m_SearchValue))
{
var tvid = new TreeViewItemData<LabelsViewBuildReportItem>(++id, reportAsset);
assetTreeViewItems.Add(tvid);
unrelatedAssetSize -= bundleAsset.SerializedSize + bundleAsset.StreamedSize;
}
}
int unrelatedAssetCount = bundle.AssetCount - assetList.Count;
if (unrelatedAssetCount > 0 && includeAllDependenciesUnderBundle)
{
var tvid = new TreeViewItemData<LabelsViewBuildReportItem>(++id, new LabelsViewBuildReportUnrelatedAssets(unrelatedAssetSize, unrelatedAssetCount));
assetTreeViewItems.Add(tvid);
}
if (includeAllDependenciesUnderBundle || assetTreeViewItems.Count > 0)
{
var rootItem = new TreeViewItemData<LabelsViewBuildReportItem>(++id, bundleReportAsset, assetTreeViewItems);
childrenOfAsset.Add(rootItem);
}
}
foreach (var dep in asset.InternalReferencedOtherAssets)
{
var reportDepAsset = new LabelsViewBuildReportAsset(dep);
if (includeAllDependencies || EntryAppearsInSearch(reportDepAsset, m_SearchValue))
{
var tvid = new TreeViewItemData<LabelsViewBuildReportItem>(++id, reportDepAsset);
childrenOfAsset.Add(tvid);
}
}
return childrenOfAsset;
}
public override void Consume(BuildLayout buildReport)
{
if (buildReport == null)
return;
m_Report = buildReport;
m_TreeItems = CreateTreeViewItems(m_Report);
m_DataHashtoReportItem = new Dictionary<Hash128, TreeDataReportItem>();
IList<TreeViewItemData<LabelsViewBuildReportItem>> treeRoots = CreateTreeRootsNestedList(m_TreeItems);
m_TreeView.SetRootItems(treeRoots);
m_TreeView.Rebuild();
m_TreeView.columnSortingChanged += ColumnSortingChanged;
}
private void ColumnSortingChanged()
{
var columnList = m_TreeView.sortedColumns;
IList<IAddressablesBuildReportItem> sortedRootList = new List<IAddressablesBuildReportItem>();
foreach (var col in columnList)
{
sortedRootList = SortByColumnDescription(col);
}
m_TreeView.SetRootItems(CreateTreeRootsNestedList(sortedRootList));
m_TreeView.Rebuild();
}
public override void CreateGUI(VisualElement rootVisualElement)
{
VisualElement view = rootVisualElement.Q<VisualElement>(BuildReportUtility.ContentView);
TreeBuilder tb = new TreeBuilder()
.With(ColumnDataForView)
.With((items) => ItemsSelected.Invoke(items));
m_TreeView = tb.Build();
view.Add(m_TreeView);
SetCallbacksForColumns(m_TreeView.columns, ColumnDataForView);
m_SearchField = rootVisualElement.Q<ToolbarSearchField>(BuildReportUtility.SearchField);
m_SearchField.RegisterValueChangedCallback(OnSearchValueChanged);
m_SearchValue = m_SearchField.value;
}
private void OnSearchValueChanged(ChangeEvent<string> evt)
{
if (m_TreeItems == null)
return;
m_SearchValue = evt.newValue.ToLower();
m_TreeView.SetRootItems(CreateTreeRootsNestedList(m_TreeItems));
m_TreeView.Rebuild();
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96cc958f02550384ab2d0e1964ebb8ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: