mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
Improve eqdp.
This commit is contained in:
parent
600fd2ecd3
commit
91d9e465ed
7 changed files with 31 additions and 62 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit cf1ff07e900e2f93ab628a1fa535fc2b103794a5
|
||||
Subproject commit 3fbc704515b7b5fa9be02fb2a44719fc333747c1
|
||||
|
|
@ -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 _)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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}.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue