From a241b933ca75424d8d99943b8ccd663d70f15f0d Mon Sep 17 00:00:00 2001 From: Exter-N Date: Mon, 18 Sep 2023 12:35:41 +0200 Subject: [PATCH] ResourceTree: Avoid enumerating the whole object table in some cases --- Penumbra/Api/PenumbraApi.cs | 8 ++-- .../ResourceTree/ResourceTreeFactory.cs | 40 +++++++++++-------- .../Interop/ResourceTree/TreeBuildCache.cs | 22 ++++++---- .../UI/AdvancedWindow/ResourceTreeViewer.cs | 7 +++- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 0572d868..4a31199c 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -1019,7 +1019,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary?[] GetGameObjectResourcePaths(ushort[] gameObjects) { var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType(); - var resourceTrees = _resourceTreeFactory.FromCharacters(characters, false, false); + var resourceTrees = _resourceTreeFactory.FromCharacters(characters, 0); var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees); return Array.ConvertAll(gameObjects, obj => pathDictionaries.TryGetValue(obj, out var pathDict) ? pathDict : null); @@ -1027,7 +1027,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary> GetPlayerResourcePaths() { - var resourceTrees = _resourceTreeFactory.FromObjectTable(true, false, false); + var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly); var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees); return pathDictionaries.AsReadOnly(); @@ -1036,7 +1036,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary?[] GetGameObjectResourcesOfType(ushort[] gameObjects, ResourceType type, bool withUIData) { var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType(); - var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData, false); + 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); @@ -1044,7 +1044,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary> GetPlayerResourcesOfType(ResourceType type, bool withUIData) { - var resourceTrees = _resourceTreeFactory.FromObjectTable(true, withUIData, false); + var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly | (withUIData ? ResourceTreeFactory.Flags.WithUIData : 0)); var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type); return resDictionaries.AsReadOnly(); diff --git a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs index e3418e5b..1d91948d 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs @@ -27,49 +27,46 @@ public class ResourceTreeFactory _actors = actors; } - private TreeBuildCache CreateTreeBuildCache() - => new(_objects, _gameData, _actors); + private TreeBuildCache CreateTreeBuildCache(bool withCharacters) + => new(_objects, _gameData, _actors, withCharacters); public IEnumerable GetLocalPlayerRelatedCharacters() { - var cache = CreateTreeBuildCache(); + var cache = CreateTreeBuildCache(true); return cache.Characters.Where(cache.IsLocalPlayerRelated); } public IEnumerable<(Dalamud.Game.ClientState.Objects.Types.Character Character, ResourceTree ResourceTree)> FromObjectTable( - bool localPlayerRelatedOnly = false, bool withUIData = true, bool redactExternalPaths = true) + Flags flags) { - var cache = CreateTreeBuildCache(); - var characters = localPlayerRelatedOnly ? cache.Characters.Where(cache.IsLocalPlayerRelated) : cache.Characters; + var cache = CreateTreeBuildCache(true); + var characters = (flags & Flags.LocalPlayerRelatedOnly) != 0 ? cache.Characters.Where(cache.IsLocalPlayerRelated) : cache.Characters; foreach (var character in characters) { - var tree = FromCharacter(character, cache, withUIData, redactExternalPaths); + var tree = FromCharacter(character, cache, flags); if (tree != null) yield return (character, tree); } } public IEnumerable<(Dalamud.Game.ClientState.Objects.Types.Character Character, ResourceTree ResourceTree)> FromCharacters( - IEnumerable characters, - bool withUIData = true, bool redactExternalPaths = true) + IEnumerable characters, Flags flags) { - var cache = CreateTreeBuildCache(); + var cache = CreateTreeBuildCache((flags & Flags.WithOwnership) != 0); foreach (var character in characters) { - var tree = FromCharacter(character, cache, withUIData, redactExternalPaths); + var tree = FromCharacter(character, cache, flags); if (tree != null) yield return (character, tree); } } - public ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, bool withUIData = true, - bool redactExternalPaths = true) - => FromCharacter(character, CreateTreeBuildCache(), withUIData, redactExternalPaths); + public ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, Flags flags) + => FromCharacter(character, CreateTreeBuildCache((flags & Flags.WithOwnership) != 0), flags); - private unsafe ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache, - bool withUIData = true, bool redactExternalPaths = true) + private unsafe ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache, Flags flags) { if (!character.IsValid()) return null; @@ -88,7 +85,7 @@ public class ResourceTreeFactory 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(_config, _identifier.AwaitedService, cache, collectionResolveData.ModCollection, - ((Character*)gameObjStruct)->CharacterData.ModelCharaId, withUIData, redactExternalPaths); + ((Character*)gameObjStruct)->CharacterData.ModelCharaId, (flags & Flags.WithUIData) != 0, (flags & Flags.RedactExternalPaths) != 0); tree.LoadResources(globalContext); tree.FlatNodes.UnionWith(globalContext.Nodes.Values); return tree; @@ -119,4 +116,13 @@ public class ResourceTreeFactory return (name, playerRelated); } + + [Flags] + public enum Flags + { + RedactExternalPaths = 1, + WithUIData = 2, + LocalPlayerRelatedOnly = 4, + WithOwnership = 8, + } } diff --git a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs index 60714fbb..d889cf5d 100644 --- a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs +++ b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs @@ -16,16 +16,24 @@ internal class TreeBuildCache public readonly List Characters; public readonly Dictionary CharactersById; - public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors) + public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors, bool withCharacters) { _dataManager = dataManager; _actors = actors; - Characters = objects.OfType().Where(ch => ch.IsValid()).ToList(); - CharactersById = Characters - .Where(c => c.ObjectId != GameObject.InvalidGameObjectId) - .GroupBy(c => c.ObjectId) - .ToDictionary(c => c.Key, c => c.First()); - _localPlayerId = Characters.Count > 0 && Characters[0].ObjectIndex == 0 ? Characters[0].ObjectId : GameObject.InvalidGameObjectId; + _localPlayerId = objects[0]?.ObjectId ?? GameObject.InvalidGameObjectId; + if (withCharacters) + { + Characters = objects.OfType().Where(ch => ch.IsValid()).ToList(); + CharactersById = Characters + .Where(c => c.ObjectId != GameObject.InvalidGameObjectId) + .GroupBy(c => c.ObjectId) + .ToDictionary(c => c.Key, c => c.First()); + } + else + { + Characters = new(); + CharactersById = new(); + } } public unsafe bool IsLocalPlayerRelated(Character character) diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index 2d474a83..3901b431 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -9,6 +9,11 @@ namespace Penumbra.UI.AdvancedWindow; public class ResourceTreeViewer { + private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags = + ResourceTreeFactory.Flags.RedactExternalPaths | + ResourceTreeFactory.Flags.WithUIData | + ResourceTreeFactory.Flags.WithOwnership; + private readonly Configuration _config; private readonly ResourceTreeFactory _treeFactory; private readonly ChangedItemDrawer _changedItemDrawer; @@ -101,7 +106,7 @@ public class ResourceTreeViewer { try { - return _treeFactory.FromObjectTable() + return _treeFactory.FromObjectTable(ResourceTreeFactoryFlags) .Select(entry => entry.ResourceTree) .ToArray(); }