Fix some issues in IL2CPP, improve type cache efficiency, reduce alloc

This commit is contained in:
Sinai
2021-05-06 04:02:42 +10:00
parent e4ff86259b
commit 22435176bf
21 changed files with 300 additions and 183 deletions

View File

@ -8,17 +8,13 @@ using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Widgets.AutoComplete
{
public class TypeCompleter : ISuggestionProvider
{
public class CachedType
{
public Type Type;
public string FullNameValue;
public string DisplayName;
}
internal static readonly Dictionary<string, string> sharedTypeToLabel = new Dictionary<string, string>(4096);
public event Action<Suggestion> SuggestionClicked;
@ -31,10 +27,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
private readonly List<Suggestion> suggestions = new List<Suggestion>();
private float timeOfLastCheck;
public Dictionary<string, CachedType> AllTypes = new Dictionary<string, CachedType>();
// cached type trees from all autocompleters
private static readonly Dictionary<string, Dictionary<string, CachedType>> typeCache = new Dictionary<string, Dictionary<string, CachedType>>();
private HashSet<Type> allowedTypes;
public TypeCompleter(Type baseType, InputField inputField)
{
@ -47,6 +40,11 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
CacheTypes();
}
public void CacheTypes()
{
allowedTypes = ReflectionUtility.GetImplementationsOf(BaseType, true, false);
}
public void OnSuggestionClicked(Suggestion suggestion)
{
timeOfLastCheck = Time.realtimeSinceStartup;
@ -84,61 +82,29 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
{
suggestions.Clear();
var added = new HashSet<string>();
// Check for exact match first
if (AllTypes.TryGetValue(value, out CachedType cache))
AddSuggestion(cache);
foreach (var entry in AllTypes.Values)
AddSuggestion(entry);
void AddSuggestion(CachedType entry)
if (BaseType == null)
{
if (entry.FullNameValue == null)
entry.FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(entry.Type).FullName;
if (added.Contains(entry.FullNameValue))
return;
added.Add(entry.FullNameValue);
if (entry.DisplayName == null)
entry.DisplayName = Utility.SignatureHighlighter.ParseFullSyntax(entry.Type, true);
suggestions.Add(new Suggestion(entry.DisplayName, entry.FullNameValue));
}
}
public void CacheTypes()
{
var key = BaseType.AssemblyQualifiedName;
if (typeCache.ContainsKey(key))
{
AllTypes = typeCache[key];
ExplorerCore.LogWarning("Autocompleter Base type is null!");
return;
}
AllTypes = new Dictionary<string, CachedType>();
// Check for exact match first
if (ReflectionUtility.AllTypes.TryGetValue(value, out Type t) && allowedTypes.Contains(t))
AddSuggestion(t);
var list = ReflectionUtility.GetImplementationsOf(BaseType, true, false)
.Select(it => new CachedType()
{
Type = it,
FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(it).FullName
})
.ToList();
list.Sort((CachedType a, CachedType b) => a.FullNameValue.CompareTo(b.FullNameValue));
foreach (var cache in list)
foreach (var entry in allowedTypes)
{
if (AllTypes.ContainsKey(cache.FullNameValue))
continue;
AllTypes.Add(cache.FullNameValue, cache);
if (entry.FullName.ContainsIgnoreCase(value))
AddSuggestion(entry);
}
}
typeCache.Add(key, AllTypes);
void AddSuggestion(Type type)
{
if (!sharedTypeToLabel.ContainsKey(type.FullName))
sharedTypeToLabel.Add(type.FullName, SignatureHighlighter.Parse(type, true));
suggestions.Add(new Suggestion(sharedTypeToLabel[type.FullName], type.FullName));
}
}
}

View File

@ -40,6 +40,19 @@ namespace UnityExplorer.UI.Widgets
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeHeight);
private float? m_defaultHeight;
/// <summary>
/// Lookup table for "which data index first appears at this position"<br/>
/// Index: DefaultHeight * index from top of data<br/>
/// Value: the first data index at this position<br/>
/// </summary>
private readonly List<int> rangeCache = new List<int>();
/// <summary>Same as GetRangeIndexOfPosition, except this rounds up to the next division if there was remainder from the previous cell.</summary>
private int GetRangeCeilingOfPosition(float position) => (int)Math.Ceiling((decimal)position / (decimal)DefaultHeight);
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
/// <summary>Get the data index at the specified position of the total height cache.</summary>
public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _);
@ -81,19 +94,6 @@ namespace UnityExplorer.UI.Widgets
return dataIndex;
}
/// <summary>
/// Lookup table for "which data index first appears at this position"<br/>
/// Index: DefaultHeight * index from top of data<br/>
/// Value: the first data index at this position<br/>
/// </summary>
private readonly List<int> rangeCache = new List<int>();
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
/// <summary>Same as GetRangeIndexOfPosition, except this rounds up to the next division if there was remainder from the previous cell.</summary>
private int GetRangeCeilingOfPosition(float position) => (int)Math.Ceiling((decimal)position / (decimal)DefaultHeight);
/// <summary>
/// Get the spread of the height, starting from the start position.<br/><br/>
/// The "spread" begins at the start of the next interval of the DefaultHeight, then increases for
@ -116,6 +116,8 @@ namespace UnityExplorer.UI.Widgets
/// <summary>Append a data index to the cache with the provided height value.</summary>
public void Add(float value)
{
value = (float)Math.Floor(value);
int spread = GetRangeSpread(totalHeight, value);
heightCache.Add(new DataViewInfo()
@ -150,6 +152,8 @@ namespace UnityExplorer.UI.Widgets
/// <summary>Set a given data index with the specified value.</summary>
public void SetIndex(int dataIndex, float height)
{
height = (float)Math.Floor(height);
// If the index being set is beyond the DataSource item count, prune and return.
if (dataIndex >= ScrollPool.DataSource.ItemCount)
{
@ -189,20 +193,17 @@ namespace UnityExplorer.UI.Widgets
int spread = GetRangeSpread(cache.startPosition, height);
// If the previous item in the range cache is not the previous data index, there is a gap.
if (dataIndex > 0 && rangeCache.Count > rangeIndex && rangeCache[rangeIndex - 1] != (dataIndex - 1))
if (dataIndex > 0 && rangeCache[rangeIndex - 1] != (dataIndex - 1))
{
// Recalculate start positions up to this index. The gap could be anywhere.
RecalculateStartPositions(dataIndex);
// Recalculate start positions up to this index. The gap could be anywhere before here.
RecalculateStartPositions(dataIndex + 1);
// Get the range index and spread again after rebuilding
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
spread = GetRangeSpread(cache.startPosition, height);
}
// Should never happen
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
throw new Exception($"Trying to set range index but cache is corrupt after rebuild!\r\n" +
$"dataIndex: {dataIndex}, rangeIndex: {rangeIndex}, rangeCache.Count: {rangeCache.Count}, " +
$"startPos: {cache.startPosition}/{TotalHeight}");
throw new Exception("ScrollPool data height cache is corrupt or invalid, rebuild failed!");
if (spread != cache.normalizedSpread)
{
@ -217,13 +218,21 @@ namespace UnityExplorer.UI.Widgets
{
if (spreadDiff > 0)
{
for (int i = 0; i < spreadDiff; i++)
while (rangeCache[rangeIndex] == dataIndex && spreadDiff > 0)
{
rangeCache.Insert(rangeIndex, dataIndex);
spreadDiff--;
}
}
else
{
for (int i = 0; i < -spreadDiff; i++)
while (rangeCache[rangeIndex] == dataIndex && spreadDiff < 0)
{
rangeCache.RemoveAt(rangeIndex);
spreadDiff++;
}
//for (int i = 0; i < -spreadDiff; i++)
// rangeCache.RemoveAt(rangeIndex);
}
}
@ -233,20 +242,37 @@ namespace UnityExplorer.UI.Widgets
return;
DataViewInfo cache;
DataViewInfo prev = heightCache[0];
for (int i = 1; i <= toIndex && i < heightCache.Count; i++)
DataViewInfo prev = null;
for (int i = 0; i <= toIndex && i < heightCache.Count; i++)
{
cache = heightCache[i];
cache.startPosition = prev.startPosition + prev.height;
if (prev != null)
cache.startPosition = prev.startPosition + prev.height;
else
cache.startPosition = 0;
var prevSpread = cache.normalizedSpread;
var origSpread = cache.normalizedSpread;
cache.normalizedSpread = GetRangeSpread(cache.startPosition, cache.height);
if (cache.normalizedSpread != prevSpread)
SetSpread(i, GetRangeCeilingOfPosition(cache.startPosition), cache.normalizedSpread - prevSpread);
if (cache.normalizedSpread != origSpread)
SetSpread(i, GetRangeCeilingOfPosition(cache.startPosition), cache.normalizedSpread - origSpread);
prev = cache;
}
}
//private void HardRebuildRanges()
//{
// var tempList = new List<float>();
// for (int i = 0; i < heightCache.Count; i++)
// tempList.Add(heightCache[i]);
//
// heightCache.Clear();
// rangeCache.Clear();
// totalHeight = 0;
//
// for (int i = 0; i < tempList.Count; i++)
// SetIndex(i, tempList[i]);
//}
}
}