Skip to content

Commit

Permalink
Remove some allocations from resource tree.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ottermandias committed Sep 19, 2023
1 parent 89c7095 commit c29d0a5
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 33 deletions.
4 changes: 2 additions & 2 deletions Penumbra/Api/PenumbraApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ public IReadOnlyDictionary<ushort, IReadOnlyDictionary<string, string[]>> GetPla
params ushort[] gameObjects)
{
var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType<Character>();
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUIData : 0);
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0);
var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type);

return Array.ConvertAll(gameObjects, obj => resDictionaries.TryGetValue(obj, out var resDict) ? resDict : null);
Expand All @@ -1046,7 +1046,7 @@ public IReadOnlyDictionary<ushort, IReadOnlyDictionary<string, string[]>> GetPla
bool withUIData)
{
var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly
| (withUIData ? ResourceTreeFactory.Flags.WithUIData : 0));
| (withUIData ? ResourceTreeFactory.Flags.WithUiData : 0));
var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type);

return resDictionaries.AsReadOnly();
Expand Down
2 changes: 1 addition & 1 deletion Penumbra/Collections/ModCollection.Cache.Access.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void Remove(TemporaryMod tempMod)
public IEnumerable<Utf8GamePath> ReverseResolvePath(FullPath path)
=> _cache?.ReverseResolvePath(path) ?? Array.Empty<Utf8GamePath>();

public HashSet<Utf8GamePath>[] ReverseResolvePaths(string[] paths)
public HashSet<Utf8GamePath>[] ReverseResolvePaths(IReadOnlyCollection<string> paths)
=> _cache?.ReverseResolvePaths(paths) ?? paths.Select(_ => new HashSet<Utf8GamePath>()).ToArray();

public FullPath? ResolvePath(Utf8GamePath path)
Expand Down
2 changes: 1 addition & 1 deletion Penumbra/Interop/ResourceTree/ResolveContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ static ushort GetTextureIndex(Material* mtrl, ushort texFlags, HashSet<uint> alr
return node;
}

internal List<Utf8GamePath> FilterGamePaths(List<Utf8GamePath> gamePaths)
internal List<Utf8GamePath> FilterGamePaths(IReadOnlyCollection<Utf8GamePath> gamePaths)
{
var filtered = new List<Utf8GamePath>(gamePaths.Count);
foreach (var path in gamePaths)
Expand Down
62 changes: 34 additions & 28 deletions Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ private TreeBuildCache CreateTreeBuildCache(bool withCharacters)
return null;

var localPlayerRelated = cache.IsLocalPlayerRelated(character);
var (name, related) = GetCharacterName(character, cache);
var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId;
var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, networked, collectionResolveData.ModCollection.Name);
var (name, related) = GetCharacterName(character, cache);
var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId;
var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related,
networked, collectionResolveData.ModCollection.Name);
var globalContext = new GlobalResolveContext(_identifier.AwaitedService, cache,
((Character*)gameObjStruct)->CharacterData.ModelCharaId, (flags & Flags.WithUIData) != 0);
((Character*)gameObjStruct)->CharacterData.ModelCharaId, (flags & Flags.WithUiData) != 0);
tree.LoadResources(globalContext);
tree.FlatNodes.UnionWith(globalContext.Nodes.Values);
tree.ProcessPostfix((node, _) => tree.FlatNodes.Add(node));
Expand All @@ -104,42 +105,47 @@ private TreeBuildCache CreateTreeBuildCache(bool withCharacters)

private static void ResolveGamePaths(ResourceTree tree, ModCollection collection)
{
var forwardSet = new HashSet<Utf8GamePath>();
var reverseSet = new HashSet<string>();
var forwardDictionary = new Dictionary<Utf8GamePath, FullPath?>();
var reverseDictionary = new Dictionary<string, HashSet<Utf8GamePath>>();
foreach (var node in tree.FlatNodes)
{
if (node.PossibleGamePaths.Length == 0 && !node.FullPath.InternalName.IsEmpty)
reverseSet.Add(node.FullPath.ToPath());
reverseDictionary.TryAdd(node.FullPath.ToPath(), null!);
else if (node.FullPath.InternalName.IsEmpty && node.PossibleGamePaths.Length == 1)
forwardSet.Add(node.GamePath);
forwardDictionary.TryAdd(node.GamePath, null);
}

var forwardDictionary = forwardSet.ToDictionary(path => path, collection.ResolvePath);
var reverseArray = reverseSet.ToArray();
var reverseResolvedArray = collection.ReverseResolvePaths(reverseArray);
var reverseDictionary = reverseArray.Zip(reverseResolvedArray).ToDictionary(pair => pair.First, pair => pair.Second);
foreach (var key in forwardDictionary.Keys)
forwardDictionary[key] = collection.ResolvePath(key);

var reverseResolvedArray = collection.ReverseResolvePaths(reverseDictionary.Keys);
foreach (var (key, set) in reverseDictionary.Keys.Zip(reverseResolvedArray))
reverseDictionary[key] = set;

foreach (var node in tree.FlatNodes)
{
if (node.PossibleGamePaths.Length == 0 && !node.FullPath.InternalName.IsEmpty)
{
if (reverseDictionary.TryGetValue(node.FullPath.ToPath(), out var resolvedSet))
if (!reverseDictionary.TryGetValue(node.FullPath.ToPath(), out var resolvedSet))
continue;

IReadOnlyCollection<Utf8GamePath> resolvedList = resolvedSet;
if (resolvedList.Count > 1)
{
var resolvedList = resolvedSet.ToList();
if (resolvedList.Count > 1)
{
var filteredList = node.ResolveContext!.FilterGamePaths(resolvedList);
if (filteredList.Count > 0)
resolvedList = filteredList;
}
if (resolvedList.Count != 1)
{
Penumbra.Log.Information($"Found {resolvedList.Count} game paths while reverse-resolving {node.FullPath} in {collection.Name}:");
foreach (var gamePath in resolvedList)
Penumbra.Log.Information($"Game path: {gamePath}");
}
node.PossibleGamePaths = resolvedList.ToArray();
var filteredList = node.ResolveContext!.FilterGamePaths(resolvedList);
if (filteredList.Count > 0)
resolvedList = filteredList;
}

if (resolvedList.Count != 1)
{
Penumbra.Log.Debug(
$"Found {resolvedList.Count} game paths while reverse-resolving {node.FullPath} in {collection.Name}:");
foreach (var gamePath in resolvedList)
Penumbra.Log.Debug($"Game path: {gamePath}");
}

node.PossibleGamePaths = resolvedList.ToArray();
}
else if (node.FullPath.InternalName.IsEmpty && node.PossibleGamePaths.Length == 1)
{
Expand Down Expand Up @@ -237,7 +243,7 @@ private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.C
public enum Flags
{
RedactExternalPaths = 1,
WithUIData = 2,
WithUiData = 2,
LocalPlayerRelatedOnly = 4,
WithOwnership = 8,
}
Expand Down
2 changes: 1 addition & 1 deletion Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class ResourceTreeViewer
{
private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags =
ResourceTreeFactory.Flags.RedactExternalPaths |
ResourceTreeFactory.Flags.WithUIData |
ResourceTreeFactory.Flags.WithUiData |
ResourceTreeFactory.Flags.WithOwnership;

private readonly Configuration _config;
Expand Down

0 comments on commit c29d0a5

Please sign in to comment.