From 60551c87393e4a2b988852688040b7a4ba36f441 Mon Sep 17 00:00:00 2001 From: Exter-N Date: Tue, 14 Nov 2023 20:38:21 +0100 Subject: [PATCH] ResourceTree: Are we fast yet? --- Penumbra/Collections/Cache/MetaCache.cs | 5 --- .../ResolveContext.PathResolution.cs | 31 +++++++++++-------- .../Interop/ResourceTree/ResolveContext.cs | 8 ++--- Penumbra/Interop/ResourceTree/ResourceTree.cs | 4 +-- Penumbra/Meta/Files/ImcFile.cs | 10 +++++- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Penumbra/Collections/Cache/MetaCache.cs b/Penumbra/Collections/Cache/MetaCache.cs index 0da11022..d5acf249 100644 --- a/Penumbra/Collections/Cache/MetaCache.cs +++ b/Penumbra/Collections/Cache/MetaCache.cs @@ -187,11 +187,6 @@ public class MetaCache : IDisposable, IEnumerable _imcCache.GetImcFile(path, out file); - public ImcEntry GetImcEntry(Utf8GamePath path, EquipSlot slot, Variant variantIdx, out bool exists) - => GetImcFile(path, out var file) - ? file.GetEntry(Meta.Files.ImcFile.PartIndex(slot), variantIdx, out exists) - : Meta.Files.ImcFile.GetDefault(_manager, path, slot, variantIdx, out exists); - internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, SetId setId) { var eqdpFile = _eqdpCache.EqdpFile(race, accessory); diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index d7d80c21..f4081de1 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -1,8 +1,10 @@ using Dalamud.Game.ClientState.Objects.Enums; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; +using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; using Penumbra.String; using Penumbra.String.Classes; @@ -74,22 +76,22 @@ internal partial record ResolveContext return Utf8GamePath.FromByteString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty; } - private unsafe Utf8GamePath ResolveMaterialPath(Utf8GamePath modelPath, Utf8GamePath imcPath, byte* mtrlFileName) + private unsafe Utf8GamePath ResolveMaterialPath(Utf8GamePath modelPath, ResourceHandle* imc, byte* mtrlFileName) { // Safety: // Resolving a material path through the game's code can dereference null pointers for equipment materials. return ModelType switch { - ModelType.Human when SlotIndex < 10 && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName), - ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName), - ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imcPath, mtrlFileName), + ModelType.Human when SlotIndex < 10 && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName), + ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName), + ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imc, mtrlFileName), _ => ResolveMaterialPathNative(mtrlFileName), }; } - private unsafe Utf8GamePath ResolveEquipmentMaterialPath(Utf8GamePath modelPath, Utf8GamePath imcPath, byte* mtrlFileName) + private unsafe Utf8GamePath ResolveEquipmentMaterialPath(Utf8GamePath modelPath, ResourceHandle* imc, byte* mtrlFileName) { - var variant = ResolveMaterialVariant(imcPath); + var variant = ResolveMaterialVariant(imc); var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName); Span pathBuffer = stackalloc byte[260]; @@ -98,7 +100,7 @@ internal partial record ResolveContext return Utf8GamePath.FromSpan(pathBuffer, out var path) ? path.Clone() : Utf8GamePath.Empty; } - private unsafe Utf8GamePath ResolveWeaponMaterialPath(Utf8GamePath modelPath, Utf8GamePath imcPath, byte* mtrlFileName) + private unsafe Utf8GamePath ResolveWeaponMaterialPath(Utf8GamePath modelPath, ResourceHandle* imc, byte* mtrlFileName) { var setIdHigh = Equipment.Set.Id / 100; // All MCH (20??) weapons' materials C are one and the same @@ -111,7 +113,7 @@ internal partial record ResolveContext var setIdLow = Equipment.Set.Id % 100; if (setIdLow > 50) { - var variant = ResolveMaterialVariant(imcPath); + var variant = ResolveMaterialVariant(imc); var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName); var mirroredSetId = (ushort)(Equipment.Set.Id - 50); @@ -132,16 +134,19 @@ internal partial record ResolveContext } } - return ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName); + return ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName); } - private byte ResolveMaterialVariant(Utf8GamePath imcPath) + private unsafe byte ResolveMaterialVariant(ResourceHandle* imc) { - var metaCache = Global.Collection.MetaCache; - if (metaCache == null) + var imcFileData = imc->GetDataSpan(); + if (imcFileData.IsEmpty) + { + Penumbra.Log.Warning($"IMC resource handle with path {GetResourceHandlePath(imc, false)} doesn't have a valid data span"); return Equipment.Variant.Id; + } - var entry = metaCache.GetImcEntry(imcPath, Slot, Equipment.Variant, out var exists); + var entry = ImcFile.GetEntry(imcFileData, Slot, Equipment.Variant, out var exists); if (!exists) return Equipment.Variant.Id; diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index f34a6ae2..73abcb4d 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -148,7 +148,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, PointerTexture, &tex->ResourceHandle, path); } - public unsafe ResourceNode? CreateNodeFromModel(Model* mdl, Utf8GamePath imcPath) + public unsafe ResourceNode? CreateNodeFromModel(Model* mdl, ResourceHandle* imc) { if (mdl == null || mdl->ModelResourceHandle == null) return null; @@ -169,7 +169,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer= 0 && i < array.Length ? array[i] : null; } - internal static unsafe ByteString GetResourceHandlePath(ResourceHandle* handle) + internal static unsafe ByteString GetResourceHandlePath(ResourceHandle* handle, bool stripPrefix = true) { if (handle == null) return ByteString.Empty; @@ -367,7 +367,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, PointerModels[i]; - var mdlNode = slotContext.CreateNodeFromModel(mdl, imcNode?.GamePath ?? Utf8GamePath.Empty); + var mdlNode = slotContext.CreateNodeFromModel(mdl, imc); if (mdlNode != null) { if (globalContext.WithUiData) @@ -135,7 +135,7 @@ public class ResourceTree } var mdl = subObject->Models[i]; - var mdlNode = slotContext.CreateNodeFromModel(mdl, imcNode?.GamePath ?? Utf8GamePath.Empty); + var mdlNode = slotContext.CreateNodeFromModel(mdl, imc); if (mdlNode != null) { if (globalContext.WithUiData) diff --git a/Penumbra/Meta/Files/ImcFile.cs b/Penumbra/Meta/Files/ImcFile.cs index e3c31a42..68d3f5b3 100644 --- a/Penumbra/Meta/Files/ImcFile.cs +++ b/Penumbra/Meta/Files/ImcFile.cs @@ -168,11 +168,19 @@ public unsafe class ImcFile : MetaBaseFile if (file == null) throw new Exception(); - fixed (byte* ptr = file.Data) + return GetEntry(file.Data, slot, variantIdx, out exists); + } + + public static ImcEntry GetEntry(ReadOnlySpan imcFileData, EquipSlot slot, Variant variantIdx, out bool exists) + { + fixed (byte* ptr = imcFileData) { var entry = VariantPtr(ptr, PartIndex(slot), variantIdx); if (entry == null) + { + exists = false; return new ImcEntry(); + } exists = true; return *entry;