mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-22 00:19:19 +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.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
using Penumbra.Meta.Files;
|
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
namespace Penumbra.Collections.Cache;
|
namespace Penumbra.Collections.Cache;
|
||||||
|
|
||||||
public sealed class EqdpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<EqdpIdentifier, EqdpEntry>(manager, collection)
|
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 override void SetFiles()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public bool TryGetFullEntry(PrimaryId id, GenderRace genderRace, bool accessory, out EqdpEntry entry)
|
public EqdpEntry ApplyFullEntry(PrimaryId id, GenderRace genderRace, bool accessory, EqdpEntry originalEntry)
|
||||||
=> _fullEntries.TryGetValue((id, genderRace, accessory), out entry);
|
=> _fullEntries.TryGetValue((id, genderRace, accessory), out var pair)
|
||||||
|
? (originalEntry & pair.InverseMask) | pair.Entry
|
||||||
|
: originalEntry;
|
||||||
|
|
||||||
protected override void IncorporateChangesInternal()
|
protected override void IncorporateChangesInternal()
|
||||||
{ }
|
{ }
|
||||||
|
|
@ -27,36 +29,27 @@ public sealed class EqdpCache(MetaFileManager manager, ModCollection collection)
|
||||||
|
|
||||||
protected override void ApplyModInternal(EqdpIdentifier identifier, EqdpEntry entry)
|
protected override void ApplyModInternal(EqdpIdentifier identifier, EqdpEntry entry)
|
||||||
{
|
{
|
||||||
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
|
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
|
||||||
var mask = Eqdp.Mask(identifier.Slot);
|
var mask = Eqdp.Mask(identifier.Slot);
|
||||||
if (!_fullEntries.TryGetValue(tuple, out var currentEntry))
|
var inverseMask = ~mask;
|
||||||
currentEntry = ExpandedEqdpFile.GetDefault(Manager, identifier);
|
if (_fullEntries.TryGetValue(tuple, out var pair))
|
||||||
|
pair = ((pair.Entry & inverseMask) | (entry & mask), pair.InverseMask & inverseMask);
|
||||||
_fullEntries[tuple] = (currentEntry & ~mask) | (entry & mask);
|
else
|
||||||
|
pair = (entry & mask, inverseMask);
|
||||||
|
_fullEntries[tuple] = pair;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void RevertModInternal(EqdpIdentifier identifier)
|
protected override void RevertModInternal(EqdpIdentifier identifier)
|
||||||
{
|
{
|
||||||
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
|
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
|
||||||
var mask = Eqdp.Mask(identifier.Slot);
|
|
||||||
|
|
||||||
if (_fullEntries.TryGetValue(tuple, out var currentEntry))
|
if (!_fullEntries.Remove(tuple, out var pair))
|
||||||
{
|
return;
|
||||||
var def = ExpandedEqdpFile.GetDefault(Manager, identifier);
|
|
||||||
var newEntry = (currentEntry & ~mask) | (def & mask);
|
var mask = Eqdp.Mask(identifier.Slot);
|
||||||
if (currentEntry != newEntry)
|
var newMask = pair.InverseMask | mask;
|
||||||
{
|
if (newMask is not EqdpEntry.FullMask)
|
||||||
_fullEntries[tuple] = newEntry;
|
_fullEntries[tuple] = (pair.Entry & ~mask, newMask);
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool _)
|
protected override void Dispose(bool _)
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
|
||||||
=> Imc.GetFile(path, out file);
|
=> Imc.GetFile(path, out file);
|
||||||
|
|
||||||
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
|
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
|
||||||
=> Eqdp.TryGetFullEntry(primaryId, race, accessory, out var entry)
|
=> Eqdp.ApplyFullEntry(primaryId, race, accessory, Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId));
|
||||||
? entry
|
|
||||||
: Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId);
|
|
||||||
|
|
||||||
internal EstEntry GetEstEntry(EstType type, GenderRace genderRace, PrimaryId primaryId)
|
internal EstEntry GetEstEntry(EstType type, GenderRace genderRace, PrimaryId primaryId)
|
||||||
=> Est.GetEstEntry(new EstIdentifier(primaryId, type, genderRace));
|
=> Est.GetEstEntry(new EstIdentifier(primaryId, type, genderRace));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using OtterGui.Services;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
using Penumbra.Meta.Manipulations;
|
|
||||||
|
|
||||||
namespace Penumbra.Interop.Hooks.Meta;
|
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)
|
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)
|
if (_metaState.EqdpCollection.TryPeek(out var collection)
|
||||||
&& collection is { Valid: true, ModCollection.MetaCache: { } cache }
|
&& collection is { Valid: true, ModCollection.MetaCache: { } cache })
|
||||||
&& cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, true, out var newEntry))
|
*entry = cache.Eqdp.ApplyFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, true, *entry);
|
||||||
*entry = newEntry;
|
|
||||||
else
|
|
||||||
Task.Result.Original(utility, entry, setId, raceCode);
|
|
||||||
Penumbra.Log.Information(
|
Penumbra.Log.Information(
|
||||||
$"[GetEqdpAccessoryEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}.");
|
$"[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)
|
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)
|
if (_metaState.EqdpCollection.TryPeek(out var collection)
|
||||||
&& collection is { Valid: true, ModCollection.MetaCache: { } cache }
|
&& collection is { Valid: true, ModCollection.MetaCache: { } cache })
|
||||||
&& cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, false, out var newEntry))
|
*entry = cache.Eqdp.ApplyFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, false, *entry);
|
||||||
*entry = newEntry;
|
|
||||||
else
|
|
||||||
Task.Result.Original(utility, entry, setId, raceCode);
|
|
||||||
Penumbra.Log.Information(
|
Penumbra.Log.Information(
|
||||||
$"[GetEqdpEquipEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}.");
|
$"[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 OtterGui.Classes;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.Api.Enums;
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.ResourceLoading;
|
using Penumbra.Interop.ResourceLoading;
|
||||||
using Penumbra.Interop.Services;
|
using Penumbra.Interop.Services;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
|
|
||||||
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
|
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
|
||||||
using Penumbra.Interop.Hooks.Objects;
|
using Penumbra.Interop.Hooks.Objects;
|
||||||
|
|
||||||
|
|
@ -87,21 +85,6 @@ public sealed unsafe class MetaState : IDisposable
|
||||||
public DecalReverter ResolveDecal(ResolveData resolve, bool which)
|
public DecalReverter ResolveDecal(ResolveData resolve, bool which)
|
||||||
=> new(_config, _characterUtility, _resources, resolve, 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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_createCharacterBase.Unsubscribe(OnCreatingCharacterBase);
|
_createCharacterBase.Unsubscribe(OnCreatingCharacterBase);
|
||||||
|
|
|
||||||
|
|
@ -139,9 +139,9 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
||||||
|
|
||||||
// Performance considerations:
|
// Performance considerations:
|
||||||
// - This function is called from several threads simultaneously, hence the need for synchronization in the swapping path ;
|
// - 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.
|
// - 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)
|
lock (_skinLock)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue