Some formatting, use ConcurrentSet explicitly for clarity.

This commit is contained in:
Ottermandias 2023-09-05 14:48:06 +02:00
parent 32608ea45b
commit 0e0733dab0
4 changed files with 61 additions and 94 deletions

@ -1 +1 @@
Subproject commit 8c7a309d039fdf008c85cf51923b4eac51b32428 Subproject commit 86ec4d72c9c9ed57aa7be4a7d0c81069c5b94ad7

View file

@ -11,7 +11,7 @@ using Penumbra.GameData.Enums;
using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Services; using Penumbra.Interop.Services;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.String; using Penumbra.String;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using Penumbra.Util; using Penumbra.Util;
@ -27,7 +27,7 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
{ {
private readonly PerformanceTracker _performance; private readonly PerformanceTracker _performance;
private readonly ResourceLoader _loader; private readonly ResourceLoader _loader;
private readonly GameEventManager _events; private readonly GameEventManager _events;
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly ThreadLocal<ResolveData> _mtrlData = new(() => ResolveData.Invalid); private readonly ThreadLocal<ResolveData> _mtrlData = new(() => ResolveData.Invalid);
@ -41,7 +41,7 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
_performance = performance; _performance = performance;
_loader = loader; _loader = loader;
_events = events; _events = events;
_communicator = communicator; _communicator = communicator;
_loadMtrlShpkHook.Enable(); _loadMtrlShpkHook.Enable();
@ -121,7 +121,7 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
switch (handle->FileType) switch (handle->FileType)
{ {
case ResourceType.Mtrl: case ResourceType.Mtrl:
case ResourceType.Avfx: case ResourceType.Avfx:
if (handle->FileSize == 0) if (handle->FileSize == 0)
_subFileCollection[(nint)handle] = resolveData; _subFileCollection[(nint)handle] = resolveData;
@ -153,11 +153,11 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
private byte LoadMtrlShpkDetour(nint mtrlResourceHandle) private byte LoadMtrlShpkDetour(nint mtrlResourceHandle)
{ {
using var performance = _performance.Measure(PerformanceType.LoadShaders); using var performance = _performance.Measure(PerformanceType.LoadShaders);
var last = _mtrlData.Value; var last = _mtrlData.Value;
var mtrlData = LoadFileHelper(mtrlResourceHandle); var mtrlData = LoadFileHelper(mtrlResourceHandle);
_mtrlData.Value = mtrlData; _mtrlData.Value = mtrlData;
var ret = _loadMtrlShpkHook.Original(mtrlResourceHandle); var ret = _loadMtrlShpkHook.Original(mtrlResourceHandle);
_mtrlData.Value = last; _mtrlData.Value = last;
_communicator.MtrlShpkLoaded.Invoke(mtrlResourceHandle, mtrlData.AssociatedGameObject); _communicator.MtrlShpkLoaded.Invoke(mtrlResourceHandle, mtrlData.AssociatedGameObject);
return ret; return ret;
} }

View file

@ -1,21 +1,19 @@
using System; using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using OtterGui.Classes;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.Util;
namespace Penumbra.Interop.Services; namespace Penumbra.Interop.Services;
public sealed unsafe class SkinFixer : IDisposable public sealed unsafe class SkinFixer : IDisposable
{ {
public static ReadOnlySpan<byte> SkinShpkName public static ReadOnlySpan<byte> SkinShpkName
=> "skin.shpk"u8; => "skin.shpk"u8;
[Signature(Sigs.HumanVTable, ScanType = ScanType.StaticAddress)] [Signature(Sigs.HumanVTable, ScanType = ScanType.StaticAddress)]
@ -37,90 +35,85 @@ public sealed unsafe class SkinFixer : IDisposable
private readonly GameEventManager _gameEvents; private readonly GameEventManager _gameEvents;
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly ResourceLoader _resources;
private readonly CharacterUtility _utility; private readonly CharacterUtility _utility;
// MaterialResourceHandle set // MaterialResourceHandle set
private readonly ConcurrentDictionary<nint, Unit> _moddedSkinShpkMaterials = new(); private readonly ConcurrentSet<nint> _moddedSkinShpkMaterials = new();
private readonly object _lock = new(); private readonly object _lock = new();
// ConcurrentDictionary.Count uses a lock in its current implementation. // ConcurrentDictionary.Count uses a lock in its current implementation.
private int _moddedSkinShpkCount = 0; private int _moddedSkinShpkCount;
private ulong _slowPathCallDelta = 0; private ulong _slowPathCallDelta;
public bool Enabled { get; internal set; } = true; public bool Enabled { get; internal set; } = true;
public int ModdedSkinShpkCount public int ModdedSkinShpkCount
=> _moddedSkinShpkCount; => _moddedSkinShpkCount;
public SkinFixer(GameEventManager gameEvents, ResourceLoader resources, CharacterUtility utility, CommunicatorService communicator) public SkinFixer(GameEventManager gameEvents, CharacterUtility utility, CommunicatorService communicator)
{ {
SignatureHelper.Initialise(this); SignatureHelper.Initialise(this);
_gameEvents = gameEvents; _gameEvents = gameEvents;
_resources = resources;
_utility = utility; _utility = utility;
_communicator = communicator; _communicator = communicator;
_onRenderMaterialHook = Hook<OnRenderMaterialDelegate>.FromAddress(_humanVTable[62], OnRenderHumanMaterial); _onRenderMaterialHook = Hook<OnRenderMaterialDelegate>.FromAddress(_humanVTable[62], OnRenderHumanMaterial);
_communicator.MtrlShpkLoaded.Subscribe(OnMtrlShpkLoaded, MtrlShpkLoaded.Priority.SkinFixer); _communicator.MtrlShpkLoaded.Subscribe(OnMtrlShpkLoaded, MtrlShpkLoaded.Priority.SkinFixer);
_gameEvents.ResourceHandleDestructor += OnResourceHandleDestructor; _gameEvents.ResourceHandleDestructor += OnResourceHandleDestructor;
_onRenderMaterialHook.Enable(); _onRenderMaterialHook.Enable();
} }
public void Dispose() public void Dispose()
{ {
_onRenderMaterialHook.Dispose(); _onRenderMaterialHook.Dispose();
_communicator.MtrlShpkLoaded.Unsubscribe(OnMtrlShpkLoaded); _communicator.MtrlShpkLoaded.Unsubscribe(OnMtrlShpkLoaded);
_gameEvents.ResourceHandleDestructor -= OnResourceHandleDestructor; _gameEvents.ResourceHandleDestructor -= OnResourceHandleDestructor;
_moddedSkinShpkMaterials.Clear(); _moddedSkinShpkMaterials.Clear();
_moddedSkinShpkCount = 0; _moddedSkinShpkCount = 0;
} }
public ulong GetAndResetSlowPathCallDelta() public ulong GetAndResetSlowPathCallDelta()
=> Interlocked.Exchange(ref _slowPathCallDelta, 0); => Interlocked.Exchange(ref _slowPathCallDelta, 0);
private static bool IsSkinMaterial(Structs.MtrlResource* mtrlResource) private static bool IsSkinMaterial(Structs.MtrlResource* mtrlResource)
{ {
if (mtrlResource == null) if (mtrlResource == null)
return false; return false;
var shpkName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlResource->ShpkString); var shpkName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlResource->ShpkString);
return SkinShpkName.SequenceEqual(shpkName); return SkinShpkName.SequenceEqual(shpkName);
} }
private void OnMtrlShpkLoaded(nint mtrlResourceHandle, nint gameObject) private void OnMtrlShpkLoaded(nint mtrlResourceHandle, nint gameObject)
{ {
var mtrl = (Structs.MtrlResource*)mtrlResourceHandle; var mtrl = (Structs.MtrlResource*)mtrlResourceHandle;
var shpk = mtrl->ShpkResourceHandle; var shpk = mtrl->ShpkResourceHandle;
if (shpk == null) if (shpk == null)
return; return;
if (!IsSkinMaterial(mtrl)) if (!IsSkinMaterial(mtrl) || (nint)shpk == _utility.DefaultSkinShpkResource)
return; return;
if ((nint)shpk != _utility.DefaultSkinShpkResource) if (_moddedSkinShpkMaterials.TryAdd(mtrlResourceHandle))
{ Interlocked.Increment(ref _moddedSkinShpkCount);
if (_moddedSkinShpkMaterials.TryAdd(mtrlResourceHandle, Unit.Instance)) }
Interlocked.Increment(ref _moddedSkinShpkCount);
} private void OnResourceHandleDestructor(Structs.ResourceHandle* handle)
} {
if (_moddedSkinShpkMaterials.TryRemove((nint)handle))
private void OnResourceHandleDestructor(Structs.ResourceHandle* handle) Interlocked.Decrement(ref _moddedSkinShpkCount);
{
if (_moddedSkinShpkMaterials.TryRemove((nint)handle, out _))
Interlocked.Decrement(ref _moddedSkinShpkCount);
} }
private nint OnRenderHumanMaterial(nint human, OnRenderMaterialParams* param) private nint OnRenderHumanMaterial(nint human, OnRenderMaterialParams* param)
{ {
// If we don't have any on-screen instances of modded skin.shpk, we don't need the slow path at all. // If we don't have any on-screen instances of modded skin.shpk, we don't need the slow path at all.
if (!Enabled || _moddedSkinShpkCount == 0) if (!Enabled || _moddedSkinShpkCount == 0)
return _onRenderMaterialHook!.Original(human, param); return _onRenderMaterialHook.Original(human, param);
var material = param->Model->Materials[param->MaterialIndex]; var material = param->Model->Materials[param->MaterialIndex];
var mtrlResource = (Structs.MtrlResource*)material->MaterialResourceHandle; var mtrlResource = (Structs.MtrlResource*)material->MaterialResourceHandle;
if (!IsSkinMaterial(mtrlResource)) if (!IsSkinMaterial(mtrlResource))
return _onRenderMaterialHook!.Original(human, param); return _onRenderMaterialHook.Original(human, param);
Interlocked.Increment(ref _slowPathCallDelta); Interlocked.Increment(ref _slowPathCallDelta);
@ -134,7 +127,7 @@ public sealed unsafe class SkinFixer : IDisposable
try try
{ {
_utility.Address->SkinShpkResource = (Structs.ResourceHandle*)mtrlResource->ShpkResourceHandle; _utility.Address->SkinShpkResource = (Structs.ResourceHandle*)mtrlResource->ShpkResourceHandle;
return _onRenderMaterialHook!.Original(human, param); return _onRenderMaterialHook.Original(human, param);
} }
finally finally
{ {

View file

@ -1,26 +0,0 @@
using System;
namespace Penumbra.Util;
/// <summary>
/// An empty structure. Can be used as value of a concurrent dictionary, to use it as a set.
/// </summary>
public readonly struct Unit : IEquatable<Unit>
{
public static readonly Unit Instance = new();
public bool Equals(Unit other)
=> true;
public override bool Equals(object? obj)
=> obj is Unit;
public override int GetHashCode()
=> 0;
public static bool operator ==(Unit left, Unit right)
=> true;
public static bool operator !=(Unit left, Unit right)
=> false;
}