YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerCompareViewer.cs

529 lines
19 KiB
C#
Raw Normal View History

2026-01-21 15:46:51 +08:00
#if UNITY_2019_4_OR_NEWER
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
/// <summary>
/// 帧对比查看器
/// </summary>
internal class DebuggerCompareViewer
{
/// <summary>
/// 差异类型
/// </summary>
internal enum DiffType
{
RefIncreased, // 引用计数增加(包括新增资源)
RefDecreased, // 引用计数减少(包括移除资源)
Unchanged // 无变化
}
/// <summary>
/// 差异信息
/// </summary>
internal class DiffProviderInfo
{
public DiffType Type;
public DebugProviderInfo ProviderInfo;
public DebugProviderInfo OldProviderInfo; // 用于对比修改前后的值
public int RefCountDelta; // 引用计数变化值
public int SecondaryRefCountDelta; // 二级引用计数变化值
public string GetDiffTypeString()
{
switch (Type)
{
case DiffType.RefIncreased: return "[↑] 引用增加";
case DiffType.RefDecreased: return "[↓] 引用减少";
case DiffType.Unchanged: return "[=] 无变化";
default: return "";
}
}
public Color GetDiffColor()
{
switch (Type)
{
case DiffType.RefIncreased: return new Color(0.3f, 0.8f, 0.3f);
case DiffType.RefDecreased: return new Color(0.9f, 0.3f, 0.3f);
case DiffType.Unchanged: return Color.gray;
default: return Color.white;
}
}
}
private VisualElement _root;
private ListView _diffListView;
private Label _frame1Label;
private Label _frame2Label;
private Label _summaryLabel;
private Toggle _showRefIncreasedToggle;
private Toggle _showRefDecreasedToggle;
private Toggle _showUnchangedToggle;
private DebugReport _report1;
private DebugReport _report2;
private List<DiffProviderInfo> _allDiffInfos = new List<DiffProviderInfo>();
private List<DiffProviderInfo> _filteredDiffInfos = new List<DiffProviderInfo>();
private string _searchKeyWord;
/// <summary>
/// 初始化页面
/// </summary>
public void InitViewer()
{
_root = new VisualElement();
_root.style.flexGrow = 1f;
// 顶部信息栏
var infoBar = new VisualElement();
infoBar.style.flexDirection = FlexDirection.Row;
infoBar.style.height = 25;
infoBar.style.backgroundColor = new Color(0.2f, 0.2f, 0.2f);
infoBar.style.paddingLeft = 5;
infoBar.style.paddingRight = 5;
_root.Add(infoBar);
_frame1Label = new Label("Frame A: 未选择");
_frame1Label.style.unityTextAlign = TextAnchor.MiddleLeft;
_frame1Label.style.width = 200;
infoBar.Add(_frame1Label);
_frame2Label = new Label("Frame B: 未选择");
_frame2Label.style.unityTextAlign = TextAnchor.MiddleLeft;
_frame2Label.style.width = 200;
infoBar.Add(_frame2Label);
_summaryLabel = new Label("");
_summaryLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
_summaryLabel.style.flexGrow = 1;
infoBar.Add(_summaryLabel);
// 过滤器栏
var filterBar = new VisualElement();
filterBar.style.flexDirection = FlexDirection.Row;
filterBar.style.height = 25;
filterBar.style.backgroundColor = new Color(0.25f, 0.25f, 0.25f);
filterBar.style.paddingLeft = 5;
filterBar.style.alignItems = Align.Center;
_root.Add(filterBar);
var filterLabel = new Label("显示过滤: ");
filterLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
filterLabel.style.marginRight = 10;
filterBar.Add(filterLabel);
_showRefIncreasedToggle = CreateFilterToggle("引用增加", true);
_showRefIncreasedToggle.RegisterValueChangedCallback(evt => RefreshFilteredList());
filterBar.Add(_showRefIncreasedToggle);
_showRefDecreasedToggle = CreateFilterToggle("引用减少", true);
_showRefDecreasedToggle.RegisterValueChangedCallback(evt => RefreshFilteredList());
filterBar.Add(_showRefDecreasedToggle);
_showUnchangedToggle = CreateFilterToggle("无变化", false);
_showUnchangedToggle.RegisterValueChangedCallback(evt => RefreshFilteredList());
filterBar.Add(_showUnchangedToggle);
// 列表头部
var headerBar = new VisualElement();
headerBar.style.flexDirection = FlexDirection.Row;
headerBar.style.height = 22;
headerBar.style.backgroundColor = new Color(0.3f, 0.3f, 0.3f);
_root.Add(headerBar);
AddHeaderLabel(headerBar, "差异类型", 100);
AddHeaderLabel(headerBar, "包裹名", 120);
AddHeaderLabel(headerBar, "资源路径", 0, true); // 自动填充剩余宽度
AddHeaderLabel(headerBar, "一级引用", 100);
AddHeaderLabel(headerBar, "一级变化", 80);
AddHeaderLabel(headerBar, "二级引用", 100);
AddHeaderLabel(headerBar, "二级变化", 80);
// 差异列表
_diffListView = new ListView();
_diffListView.style.flexGrow = 1f;
_diffListView.makeItem = MakeDiffListViewItem;
_diffListView.bindItem = BindDiffListViewItem;
_diffListView.itemsSource = _filteredDiffInfos;
#if UNITY_2021_2_OR_NEWER
_diffListView.fixedItemHeight = 22;
#else
_diffListView.itemHeight = 22;
#endif
_root.Add(_diffListView);
}
private void AddHeaderLabel(VisualElement parent, string text, float width, bool flexGrow = false)
{
var label = new Label(text);
label.style.unityTextAlign = TextAnchor.MiddleLeft;
if (flexGrow)
{
label.style.flexGrow = 1f;
label.style.minWidth = 200;
}
else
{
label.style.width = width;
}
label.style.unityFontStyleAndWeight = FontStyle.Bold;
label.style.paddingLeft = 3;
parent.Add(label);
}
private Toggle CreateFilterToggle(string label, bool defaultValue)
{
var toggle = new Toggle();
toggle.value = defaultValue;
toggle.style.marginRight = 5;
// 创建标签并设置样式
var labelElement = new Label(label);
labelElement.style.marginLeft = 2;
labelElement.style.marginRight = 10;
toggle.Add(labelElement);
return toggle;
}
/// <summary>
/// 设置对比的两帧数据
/// </summary>
public void SetCompareFrames(DebugReport report1, DebugReport report2)
{
_report1 = report1;
_report2 = report2;
if (report1 != null)
_frame1Label.text = $"Frame A: {report1.FrameCount}";
else
_frame1Label.text = "Frame A: 未选择";
if (report2 != null)
_frame2Label.text = $"Frame B: {report2.FrameCount}";
else
_frame2Label.text = "Frame B: 未选择";
ComputeDiff();
RefreshFilteredList();
}
/// <summary>
/// 设置搜索关键字
/// </summary>
public void SetSearchKeyWord(string searchKeyWord)
{
_searchKeyWord = searchKeyWord;
RefreshFilteredList();
}
/// <summary>
/// 计算差异
/// </summary>
private void ComputeDiff()
{
_allDiffInfos.Clear();
if (_report1 == null || _report2 == null)
{
UpdateSummary();
return;
}
// 收集两帧的所有Provider信息
var providers1 = new Dictionary<string, DebugProviderInfo>();
var providers2 = new Dictionary<string, DebugProviderInfo>();
foreach (var packageData in _report1.PackageDatas)
{
foreach (var providerInfo in packageData.ProviderInfos)
{
var key = $"{packageData.PackageName}|{providerInfo.AssetPath}";
providerInfo.PackageName = packageData.PackageName;
providers1[key] = providerInfo;
}
}
foreach (var packageData in _report2.PackageDatas)
{
foreach (var providerInfo in packageData.ProviderInfos)
{
var key = $"{packageData.PackageName}|{providerInfo.AssetPath}";
providerInfo.PackageName = packageData.PackageName;
providers2[key] = providerInfo;
}
}
// 找出新增的在report2中有在report1中没有- 归类为引用增加
foreach (var kvp in providers2)
{
if (!providers1.ContainsKey(kvp.Key))
{
_allDiffInfos.Add(new DiffProviderInfo
{
Type = DiffType.RefIncreased,
ProviderInfo = kvp.Value,
OldProviderInfo = null,
RefCountDelta = kvp.Value.RefCount,
SecondaryRefCountDelta = kvp.Value.SecondaryRefCount
});
}
}
// 找出移除的在report1中有在report2中没有- 归类为引用减少
foreach (var kvp in providers1)
{
if (!providers2.ContainsKey(kvp.Key))
{
_allDiffInfos.Add(new DiffProviderInfo
{
Type = DiffType.RefDecreased,
ProviderInfo = kvp.Value,
OldProviderInfo = null,
RefCountDelta = -kvp.Value.RefCount,
SecondaryRefCountDelta = -kvp.Value.SecondaryRefCount
});
}
}
// 找出修改的和未变化的
foreach (var kvp in providers1)
{
if (providers2.TryGetValue(kvp.Key, out var provider2))
{
var provider1 = kvp.Value;
int refCountDelta = provider2.RefCount - provider1.RefCount;
int secondaryRefCountDelta = provider2.SecondaryRefCount - provider1.SecondaryRefCount;
// 判断差异类型:一级或二级引用计数有任一增加则为增加,有任一减少则为减少
DiffType diffType;
if (refCountDelta > 0 || secondaryRefCountDelta > 0)
diffType = DiffType.RefIncreased;
else if (refCountDelta < 0 || secondaryRefCountDelta < 0)
diffType = DiffType.RefDecreased;
else
diffType = DiffType.Unchanged;
_allDiffInfos.Add(new DiffProviderInfo
{
Type = diffType,
ProviderInfo = provider2,
OldProviderInfo = provider1,
RefCountDelta = refCountDelta,
SecondaryRefCountDelta = secondaryRefCountDelta
});
}
}
// 排序:按差异类型排序,同类型按资源路径排序
_allDiffInfos.Sort((a, b) =>
{
int typeCompare = a.Type.CompareTo(b.Type);
if (typeCompare != 0) return typeCompare;
return string.CompareOrdinal(a.ProviderInfo.AssetPath, b.ProviderInfo.AssetPath);
});
UpdateSummary();
}
/// <summary>
/// 更新统计信息
/// </summary>
private void UpdateSummary()
{
int refIncreasedCount = _allDiffInfos.Count(d => d.Type == DiffType.RefIncreased);
int refDecreasedCount = _allDiffInfos.Count(d => d.Type == DiffType.RefDecreased);
int unchangedCount = _allDiffInfos.Count(d => d.Type == DiffType.Unchanged);
_summaryLabel.text = $"统计: 引用↑ {refIncreasedCount} | 引用↓ {refDecreasedCount} | 无变化 {unchangedCount}";
}
/// <summary>
/// 刷新过滤后的列表
/// </summary>
private void RefreshFilteredList()
{
_filteredDiffInfos.Clear();
foreach (var diffInfo in _allDiffInfos)
{
// 类型过滤
if (diffInfo.Type == DiffType.RefIncreased && !_showRefIncreasedToggle.value) continue;
if (diffInfo.Type == DiffType.RefDecreased && !_showRefDecreasedToggle.value) continue;
if (diffInfo.Type == DiffType.Unchanged && !_showUnchangedToggle.value) continue;
// 关键字过滤
if (!string.IsNullOrEmpty(_searchKeyWord))
{
if (!diffInfo.ProviderInfo.AssetPath.Contains(_searchKeyWord))
continue;
}
_filteredDiffInfos.Add(diffInfo);
}
_diffListView.itemsSource = _filteredDiffInfos;
_diffListView.Rebuild();
}
/// <summary>
/// 清空页面
/// </summary>
public void ClearView()
{
_report1 = null;
_report2 = null;
_allDiffInfos.Clear();
_filteredDiffInfos.Clear();
_frame1Label.text = "Frame A: 未选择";
_frame2Label.text = "Frame B: 未选择";
_summaryLabel.text = "";
_diffListView.Rebuild();
}
/// <summary>
/// 挂接到父类页面上
/// </summary>
public void AttachParent(VisualElement parent)
{
parent.Add(_root);
}
/// <summary>
/// 从父类页面脱离开
/// </summary>
public void DetachParent()
{
_root.RemoveFromHierarchy();
}
// 列表项相关
private VisualElement MakeDiffListViewItem()
{
VisualElement element = new VisualElement();
element.style.flexDirection = FlexDirection.Row;
// 差异类型
AddItemLabel(element, "Label0", 100);
// 包裹名
AddItemLabel(element, "Label1", 120);
// 资源路径 - 自动填充剩余宽度
AddItemLabel(element, "Label2", 0, true);
// 一级引用计数
AddItemLabel(element, "Label3", 100);
// 一级引用变化
AddItemLabel(element, "Label4", 80);
// 二级引用计数
AddItemLabel(element, "Label5", 100);
// 二级引用变化
AddItemLabel(element, "Label6", 80);
return element;
}
private void AddItemLabel(VisualElement parent, string name, float width, bool flexGrow = false)
{
var label = new Label();
label.name = name;
label.style.unityTextAlign = TextAnchor.MiddleLeft;
label.style.marginLeft = 3f;
if (flexGrow)
{
label.style.flexGrow = 1f;
label.style.minWidth = 200;
}
else
{
label.style.width = width;
}
parent.Add(label);
}
private void BindDiffListViewItem(VisualElement element, int index)
{
var diffInfo = _filteredDiffInfos[index];
var providerInfo = diffInfo.ProviderInfo;
// 差异类型
var label0 = element.Q<Label>("Label0");
label0.text = diffInfo.GetDiffTypeString();
label0.style.color = diffInfo.GetDiffColor();
// 包裹名
var label1 = element.Q<Label>("Label1");
label1.text = providerInfo.PackageName;
// 资源路径
var label2 = element.Q<Label>("Label2");
label2.text = providerInfo.AssetPath;
// 一级引用计数
var label3 = element.Q<Label>("Label3");
if (diffInfo.RefCountDelta != 0 && diffInfo.OldProviderInfo != null)
{
label3.text = $"{diffInfo.OldProviderInfo.RefCount} -> {providerInfo.RefCount}";
}
else
{
label3.text = providerInfo.RefCount.ToString();
}
// 一级引用变化
var label4 = element.Q<Label>("Label4");
if (diffInfo.RefCountDelta > 0)
{
label4.text = $"+{diffInfo.RefCountDelta}";
label4.style.color = new Color(0.3f, 0.8f, 0.3f);
}
else if (diffInfo.RefCountDelta < 0)
{
label4.text = diffInfo.RefCountDelta.ToString();
label4.style.color = new Color(0.9f, 0.3f, 0.3f);
}
else
{
label4.text = "0";
label4.style.color = Color.gray;
}
// 二级引用计数
var label5 = element.Q<Label>("Label5");
if (diffInfo.SecondaryRefCountDelta != 0 && diffInfo.OldProviderInfo != null)
{
label5.text = $"{diffInfo.OldProviderInfo.SecondaryRefCount} -> {providerInfo.SecondaryRefCount}";
}
else
{
label5.text = providerInfo.SecondaryRefCount.ToString();
}
// 二级引用变化
var label6 = element.Q<Label>("Label6");
if (diffInfo.SecondaryRefCountDelta > 0)
{
label6.text = $"+{diffInfo.SecondaryRefCountDelta}";
label6.style.color = new Color(0.3f, 0.8f, 0.3f);
}
else if (diffInfo.SecondaryRefCountDelta < 0)
{
label6.text = diffInfo.SecondaryRefCountDelta.ToString();
label6.style.color = new Color(0.9f, 0.3f, 0.3f);
}
else
{
label6.text = "0";
label6.style.color = Color.gray;
}
}
}
}
#endif