Improve eqdp.

This commit is contained in:
Ottermandias 2024-06-16 12:30:47 +02:00
parent 600fd2ecd3
commit 91d9e465ed
7 changed files with 31 additions and 62 deletions

@ -1 +1 @@
Subproject commit cf1ff07e900e2f93ab628a1fa535fc2b103794a5
Subproject commit 3fbc704515b7b5fa9be02fb2a44719fc333747c1

View file

@ -1,20 +1,22 @@
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public sealed class EqdpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<EqdpIdentifier, EqdpEntry>(manager, collection)
{
private readonly Dictionary<(PrimaryId Id, GenderRace GenderRace, bool Accessory), EqdpEntry> _fullEntries = [];
private readonly Dictionary<(PrimaryId Id, GenderRace GenderRace, bool Accessory), (EqdpEntry Entry, EqdpEntry InverseMask)> _fullEntries =
[];
public override void SetFiles()
{ }
public bool TryGetFullEntry(PrimaryId id, GenderRace genderRace, bool accessory, out EqdpEntry entry)
=> _fullEntries.TryGetValue((id, genderRace, accessory), out entry);
public EqdpEntry ApplyFullEntry(PrimaryId id, GenderRace genderRace, bool accessory, EqdpEntry originalEntry)
=> _fullEntries.TryGetValue((id, genderRace, accessory), out var pair)
? (originalEntry & pair.InverseMask) | pair.Entry
: originalEntry;
protected override void IncorporateChangesInternal()
{ }
@ -27,36 +29,27 @@ public sealed class EqdpCache(MetaFileManager manager, ModCollection collection)
protected override void ApplyModInternal(EqdpIdentifier identifier, EqdpEntry entry)
{
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
var mask = Eqdp.Mask(identifier.Slot);
if (!_fullEntries.TryGetValue(tuple, out var currentEntry))
currentEntry = ExpandedEqdpFile.GetDefault(Manager, identifier);
_fullEntries[tuple] = (currentEntry & ~mask) | (entry & mask);
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
var mask = Eqdp.Mask(identifier.Slot);
var inverseMask = ~mask;
if (_fullEntries.TryGetValue(tuple, out var pair))
pair = ((pair.Entry & inverseMask) | (entry & mask), pair.InverseMask & inverseMask);
else
pair = (entry & mask, inverseMask);
_fullEntries[tuple] = pair;
}
protected override void RevertModInternal(EqdpIdentifier identifier)
{
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
var mask = Eqdp.Mask(identifier.Slot);
if (_fullEntries.TryGetValue(tuple, out var currentEntry))
{
var def = ExpandedEqdpFile.GetDefault(Manager, identifier);
var newEntry = (currentEntry & ~mask) | (def & mask);
if (currentEntry != newEntry)
{
_fullEntries[tuple] = newEntry;
}
else
{
var slots = tuple.Item3 ? EquipSlotExtensions.AccessorySlots : EquipSlotExtensions.EquipmentSlots;
if (slots.All(s => !ContainsKey(identifier with { Slot = s })))
_fullEntries.Remove(tuple);
else
_fullEntries[tuple] = newEntry;
}
}
if (!_fullEntries.Remove(tuple, out var pair))
return;
var mask = Eqdp.Mask(identifier.Slot);
var newMask = pair.InverseMask | mask;
if (newMask is not EqdpEntry.FullMask)
_fullEntries[tuple] = (pair.Entry & ~mask, newMask);
}
protected override void Dispose(bool _)

View file

@ -122,9 +122,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
=> Imc.GetFile(path, out file);
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
=> Eqdp.TryGetFullEntry(primaryId, race, accessory, out var entry)
? entry
: Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId);
=> Eqdp.ApplyFullEntry(primaryId, race, accessory, Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId));
internal EstEntry GetEstEntry(EstType type, GenderRace genderRace, PrimaryId primaryId)
=> Est.GetEstEntry(new EstIdentifier(primaryId, type, genderRace));

View file

@ -3,7 +3,6 @@ using OtterGui.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.PathResolving;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Interop.Hooks.Meta;
@ -21,12 +20,10 @@ public unsafe class EqdpAccessoryHook : FastHook<EqdpAccessoryHook.Delegate>
private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode)
{
Task.Result.Original(utility, entry, setId, raceCode);
if (_metaState.EqdpCollection.TryPeek(out var collection)
&& collection is { Valid: true, ModCollection.MetaCache: { } cache }
&& cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, true, out var newEntry))
*entry = newEntry;
else
Task.Result.Original(utility, entry, setId, raceCode);
&& collection is { Valid: true, ModCollection.MetaCache: { } cache })
*entry = cache.Eqdp.ApplyFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, true, *entry);
Penumbra.Log.Information(
$"[GetEqdpAccessoryEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}.");
}

View file

@ -20,12 +20,10 @@ public unsafe class EqdpEquipHook : FastHook<EqdpEquipHook.Delegate>
private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode)
{
Task.Result.Original(utility, entry, setId, raceCode);
if (_metaState.EqdpCollection.TryPeek(out var collection)
&& collection is { Valid: true, ModCollection.MetaCache: { } cache }
&& cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, false, out var newEntry))
*entry = newEntry;
else
Task.Result.Original(utility, entry, setId, raceCode);
&& collection is { Valid: true, ModCollection.MetaCache: { } cache })
*entry = cache.Eqdp.ApplyFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, false, *entry);
Penumbra.Log.Information(
$"[GetEqdpEquipEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}.");
}

View file

@ -2,13 +2,11 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes;
using Penumbra.Collections;
using Penumbra.Api.Enums;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Services;
using Penumbra.Services;
using Penumbra.String.Classes;
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
using Penumbra.Interop.Hooks.Objects;
@ -87,21 +85,6 @@ public sealed unsafe class MetaState : IDisposable
public DecalReverter ResolveDecal(ResolveData resolve, bool which)
=> new(_config, _characterUtility, _resources, resolve, which);
public static GenderRace GetHumanGenderRace(nint human)
=> (GenderRace)((Human*)human)->RaceSexId;
public static GenderRace GetDrawObjectGenderRace(nint drawObject)
{
var draw = (DrawObject*)drawObject;
if (draw->Object.GetObjectType() != ObjectType.CharacterBase)
return GenderRace.Unknown;
var c = (CharacterBase*)drawObject;
return c->GetModelType() == CharacterBase.ModelType.Human
? GetHumanGenderRace(drawObject)
: GenderRace.Unknown;
}
public void Dispose()
{
_createCharacterBase.Unsubscribe(OnCreatingCharacterBase);

View file

@ -139,9 +139,9 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
// Performance considerations:
// - This function is called from several threads simultaneously, hence the need for synchronization in the swapping path ;
// - Function is called each frame for each material on screen, after culling, i. e. up to thousands of times a frame in crowded areas ;
// - Function is called each frame for each material on screen, after culling, i.e. up to thousands of times a frame in crowded areas ;
// - Swapping path is taken up to hundreds of times a frame.
// At the time of writing, the lock doesn't seem to have a noticeable impact in either framerate or CPU usage, but the swapping path shall still be avoided as much as possible.
// At the time of writing, the lock doesn't seem to have a noticeable impact in either frame rate or CPU usage, but the swapping path shall still be avoided as much as possible.
lock (_skinLock)
{
try