ResourceTree: Are we fast yet?

This commit is contained in:
Exter-N 2023-11-14 20:38:21 +01:00
parent b2bf6eb0f7
commit 60551c8739
5 changed files with 33 additions and 25 deletions

View file

@ -187,11 +187,6 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
=> _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);

View file

@ -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<byte> 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;

View file

@ -148,7 +148,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
return GetOrCreateNode(ResourceType.Tex, (nint)tex->Texture, &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<Char
continue;
var mtrlFileName = GetMaterialFileNameBySlot(mdlResource, (uint)i);
var mtrlNode = CreateNodeFromMaterial(mtrl, ResolveMaterialPath(path, imcPath, mtrlFileName));
var mtrlNode = CreateNodeFromMaterial(mtrl, ResolveMaterialPath(path, imc, mtrlFileName));
if (mtrlNode != null)
{
if (Global.WithUiData)
@ -358,7 +358,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
return i >= 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, Pointer<Char
if (name.IsEmpty)
return ByteString.Empty;
if (name[0] == (byte)'|')
if (stripPrefix && name[0] == (byte)'|')
{
var pos = name.IndexOf((byte)'|', 1);
if (pos < 0)

View file

@ -83,7 +83,7 @@ public class ResourceTree
}
var mdl = model->Models[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)

View file

@ -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<byte> 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;