From c01aa000fb94afcbfc13fcfe87c4ab6bd5b5f86d Mon Sep 17 00:00:00 2001 From: Exter-N Date: Sat, 3 Aug 2024 17:46:29 +0200 Subject: [PATCH] Optimize I/O of ShPk for ResourceTree generation --- Penumbra/Interop/ResourceTree/ResolveContext.cs | 11 ++++++++--- Penumbra/Interop/ResourceTree/TreeBuildCache.cs | 14 +++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index 3fc1ae3c..41485d75 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -230,8 +230,8 @@ internal unsafe partial record ResolveContext( node.Children.Add(shpkNode); } - var shpkFile = Global.WithUiData && shpkNode != null ? Global.TreeBuildCache.ReadShaderPackage(shpkNode.FullPath) : null; - var shpk = Global.WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null; + var shpkNames = Global.WithUiData && shpkNode != null ? Global.TreeBuildCache.ReadShaderPackageNames(shpkNode.FullPath) : null; + var shpk = Global.WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null; var alreadyProcessedSamplerIds = new HashSet(); for (var i = 0; i < resource->TextureCount; i++) @@ -255,7 +255,12 @@ internal unsafe partial record ResolveContext( alreadyProcessedSamplerIds.Add(samplerId.Value); var samplerCrc = GetSamplerCrcById(shpk, samplerId.Value); if (samplerCrc.HasValue) - name = shpkFile?.GetSamplerById(samplerCrc.Value)?.Name ?? $"Texture 0x{samplerCrc.Value:X8}"; + { + if (shpkNames != null && shpkNames.TryGetValue(samplerCrc.Value, out var samplerName)) + name = samplerName.Value; + else + name = $"Texture 0x{samplerCrc.Value:X8}"; + } } } diff --git a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs index ca5ff736..49e00547 100644 --- a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs +++ b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs @@ -1,8 +1,11 @@ +using System.IO.MemoryMappedFiles; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Files; +using Penumbra.GameData.Files.ShaderStructs; +using Penumbra.GameData.Files.Utility; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; using Penumbra.String.Classes; @@ -11,7 +14,7 @@ namespace Penumbra.Interop.ResourceTree; internal readonly struct TreeBuildCache(ObjectManager objects, IDataManager dataManager, ActorManager actors) { - private readonly Dictionary _shaderPackages = []; + private readonly Dictionary?> _shaderPackageNames = []; public unsafe bool IsLocalPlayerRelated(ICharacter character) { @@ -68,10 +71,10 @@ internal readonly struct TreeBuildCache(ObjectManager objects, IDataManager data } /// Try to read a shpk file from the given path and cache it on success. - public ShpkFile? ReadShaderPackage(FullPath path) - => ReadFile(dataManager, path, _shaderPackages, bytes => new ShpkFile(bytes)); + public IReadOnlyDictionary? ReadShaderPackageNames(FullPath path) + => ReadFile(dataManager, path, _shaderPackageNames, bytes => ShpkFile.FastExtractNames(bytes.Span)); - private static T? ReadFile(IDataManager dataManager, FullPath path, Dictionary cache, Func parseFile) + private static T? ReadFile(IDataManager dataManager, FullPath path, Dictionary cache, Func, T> parseFile) where T : class { if (path.FullName.Length == 0) @@ -86,7 +89,8 @@ internal readonly struct TreeBuildCache(ObjectManager objects, IDataManager data { if (path.IsRooted) { - parsed = parseFile(File.ReadAllBytes(pathStr)); + using var mmFile = MmioMemoryManager.CreateFromFile(pathStr, access: MemoryMappedFileAccess.Read); + parsed = parseFile(mmFile.Memory); } else {