diff --git a/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs b/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs index 5783c099..870229d6 100644 --- a/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs +++ b/Penumbra/Interop/Hooks/PostProcessing/HumanSetupScalingHook.cs @@ -11,10 +11,8 @@ public unsafe class HumanSetupScalingHook : FastHook("Human.SetupScaling", vTables.HumanVTable[58], Detour, + => Task = hooks.CreateHook("Human.SetupScaling", vTables.HumanVTable[58], Detour, !HookOverrides.Instance.PostProcessing.HumanSetupScaling); - } private void Detour(CharacterBase* drawObject, uint slotIndex) { @@ -32,6 +30,7 @@ public unsafe class HumanSetupScalingHook : FastHook replacements, ref int numReplacements, ref IDisposable? pbdDisposable, ref object? shpkLock); - + public readonly record struct Replacement(nint AddressToReplace, nint ValueToSet, nint ValueToRestore); } diff --git a/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs b/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs index 9273a2cb..30e643c7 100644 --- a/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs +++ b/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs @@ -19,7 +19,7 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi Utf8GamePath.FromSpan("chara/xls/boneDeformer/human.pbd"u8, MetaDataComputation.All, out var p) ? p : Utf8GamePath.Empty; // Approximate name guess. - private delegate void* CharacterBaseCreateDeformerDelegate(CharacterBase* drawObject, uint slotIndex); + private delegate void* CharacterBaseCreateDeformerDelegate(CharacterBase* drawObject, uint slotIndex); private readonly Hook _humanCreateDeformerHook; @@ -32,12 +32,12 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi public PreBoneDeformerReplacer(CharacterUtility utility, CollectionResolver collectionResolver, ResourceLoader resourceLoader, HookManager hooks, IFramework framework, CharacterBaseVTables vTables, HumanSetupScalingHook humanSetupScalingHook) { - _utility = utility; - _collectionResolver = collectionResolver; - _resourceLoader = resourceLoader; - _framework = framework; - _humanSetupScalingHook = humanSetupScalingHook; - _humanSetupScalingHook.SetupReplacements += SetupHSSReplacements; + _utility = utility; + _collectionResolver = collectionResolver; + _resourceLoader = resourceLoader; + _framework = framework; + _humanSetupScalingHook = humanSetupScalingHook; + _humanSetupScalingHook.SetupReplacements += SetupHssReplacements; _humanCreateDeformerHook = hooks.CreateHook("HumanCreateDeformer", vTables.HumanVTable[101], CreateDeformer, !HookOverrides.Instance.PostProcessing.HumanCreateDeformer).Result; } @@ -45,7 +45,7 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi public void Dispose() { _humanCreateDeformerHook.Dispose(); - _humanSetupScalingHook.SetupReplacements -= SetupHSSReplacements; + _humanSetupScalingHook.SetupReplacements -= SetupHssReplacements; } private SafeResourceHandle GetPreBoneDeformerForCharacter(CharacterBase* drawObject) @@ -57,18 +57,19 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi return cache.CustomResources.Get(ResourceCategory.Chara, ResourceType.Pbd, PreBoneDeformerPath, resolveData); } - private void SetupHSSReplacements(CharacterBase* drawObject, uint slotIndex, Span replacements, + private void SetupHssReplacements(CharacterBase* drawObject, uint slotIndex, Span replacements, ref int numReplacements, ref IDisposable? pbdDisposable, ref object? shpkLock) { if (!_framework.IsInFrameworkUpdateThread) Penumbra.Log.Warning( - $"{nameof(PreBoneDeformerReplacer)}.{nameof(SetupHSSReplacements)}(0x{(nint)drawObject:X}, {slotIndex}) called out of framework thread"); + $"{nameof(PreBoneDeformerReplacer)}.{nameof(SetupHssReplacements)}(0x{(nint)drawObject:X}, {slotIndex}) called out of framework thread"); var preBoneDeformer = GetPreBoneDeformerForCharacter(drawObject); try { pbdDisposable = preBoneDeformer; - replacements[numReplacements++] = new((nint)(&_utility.Address->HumanPbdResource), (nint)preBoneDeformer.ResourceHandle, + replacements[numReplacements++] = new HumanSetupScalingHook.Replacement((nint)(&_utility.Address->HumanPbdResource), + (nint)preBoneDeformer.ResourceHandle, _utility.DefaultHumanPbdResource); } catch diff --git a/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs b/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs index 20db7e25..80892b0f 100644 --- a/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs +++ b/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs @@ -8,7 +8,6 @@ using OtterGui.Services; using Penumbra.Communication; using Penumbra.GameData; using Penumbra.Interop.Hooks.Resources; -using Penumbra.Interop.Services; using Penumbra.Interop.Structs; using Penumbra.Services; using CharacterUtility = Penumbra.Interop.Services.CharacterUtility; @@ -137,7 +136,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic _hairMaskState = new ModdedShaderPackageState(() => _modelRenderer.HairMaskShaderPackage, () => _modelRenderer.DefaultHairMaskShaderPackage); - _humanSetupScalingHook.SetupReplacements += SetupHSSReplacements; + _humanSetupScalingHook.SetupReplacements += SetupHssReplacements; _humanOnRenderMaterialHook = hooks.CreateHook("Human.OnRenderMaterial", vTables.HumanVTable[64], OnRenderHumanMaterial, !HookOverrides.Instance.PostProcessing.HumanOnRenderMaterial).Result; _modelRendererOnRenderMaterialHook = hooks.CreateHook("ModelRenderer.OnRenderMaterial", @@ -146,7 +145,8 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic _modelRendererUnkFuncHook = hooks.CreateHook("ModelRenderer.UnkFunc", Sigs.ModelRendererUnkFunc, ModelRendererUnkFuncDetour, !HookOverrides.Instance.PostProcessing.ModelRendererUnkFunc).Result; - _prepareColorTableHook = hooks.CreateHook("MaterialResourceHandle.PrepareColorTable", + _prepareColorTableHook = hooks.CreateHook( + "MaterialResourceHandle.PrepareColorTable", Sigs.PrepareColorSet, PrepareColorTableDetour, !HookOverrides.Instance.PostProcessing.PrepareColorTable).Result; @@ -160,7 +160,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic _modelRendererUnkFuncHook.Dispose(); _modelRendererOnRenderMaterialHook.Dispose(); _humanOnRenderMaterialHook.Dispose(); - _humanSetupScalingHook.SetupReplacements -= SetupHSSReplacements; + _humanSetupScalingHook.SetupReplacements -= SetupHssReplacements; _communicator.MtrlLoaded.Unsubscribe(OnMtrlLoaded); _resourceHandleDestructor.Unsubscribe(OnResourceHandleDestructor); @@ -188,14 +188,6 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic _characterOcclusionState.GetAndResetSlowPathCallDelta(), _hairMaskState.GetAndResetSlowPathCallDelta()); - private static bool IsMaterialWithShpk(MaterialResourceHandle* mtrlResource, ReadOnlySpan shpkName) - { - if (mtrlResource == null) - return false; - - return shpkName.SequenceEqual(mtrlResource->ShpkNameSpan); - } - private void OnMtrlLoaded(nint mtrlResourceHandle, nint gameObject) { var mtrl = (MaterialResourceHandle*)mtrlResourceHandle; @@ -203,9 +195,11 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic if (shpk == null) return; - var shpkName = mtrl->ShpkNameSpan; - var shpkState = GetStateForHumanSetup(shpkName) ?? GetStateForHumanRender(shpkName) ?? GetStateForModelRendererRender(shpkName) - ?? GetStateForModelRendererUnk(shpkName) ?? GetStateForColorTable(shpkName); + var shpkName = mtrl->ShpkNameSpan; + var shpkState = GetStateForHumanSetup(shpkName) + ?? GetStateForHumanRender(shpkName) + ?? GetStateForModelRendererRender(shpkName) + ?? GetStateForModelRendererUnk(shpkName) ?? GetStateForColorTable(shpkName); if (shpkState != null && shpk != shpkState.DefaultShaderPackage) shpkState.TryAddMaterial(mtrlResourceHandle); @@ -228,12 +222,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic => mtrlResource == null ? null : GetStateForHumanSetup(mtrlResource->ShpkNameSpan); private ModdedShaderPackageState? GetStateForHumanSetup(ReadOnlySpan shpkName) - { - if (CharacterStockingsShpkName.SequenceEqual(shpkName)) - return _characterStockingsState; - - return null; - } + => CharacterStockingsShpkName.SequenceEqual(shpkName) ? _characterStockingsState : null; [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private uint GetTotalMaterialCountForHumanSetup() @@ -243,12 +232,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic => mtrlResource == null ? null : GetStateForHumanRender(mtrlResource->ShpkNameSpan); private ModdedShaderPackageState? GetStateForHumanRender(ReadOnlySpan shpkName) - { - if (SkinShpkName.SequenceEqual(shpkName)) - return _skinState; - - return null; - } + => SkinShpkName.SequenceEqual(shpkName) ? _skinState : null; [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private uint GetTotalMaterialCountForHumanRender() @@ -305,18 +289,13 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic + _characterStockingsState.MaterialCount; private ModdedShaderPackageState? GetStateForColorTable(ReadOnlySpan shpkName) - { - if (CharacterLegacyShpkName.SequenceEqual(shpkName)) - return _characterLegacyState; - - return null; - } + => CharacterLegacyShpkName.SequenceEqual(shpkName) ? _characterLegacyState : null; [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private uint GetTotalMaterialCountForColorTable() => _characterLegacyState.MaterialCount; - private void SetupHSSReplacements(CharacterBase* drawObject, uint slotIndex, Span replacements, + private void SetupHssReplacements(CharacterBase* drawObject, uint slotIndex, Span replacements, ref int numReplacements, ref IDisposable? pbdDisposable, ref object? shpkLock) { // If we don't have any on-screen instances of modded characterstockings.shpk, we don't need the slow path at all. @@ -326,6 +305,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic var model = drawObject->Models[slotIndex]; if (model == null) return; + MaterialResourceHandle* mtrlResource = null; ModdedShaderPackageState? shpkState = null; foreach (var material in model->MaterialsSpan) @@ -340,6 +320,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic if (shpkState != null) break; } + if (shpkState == null || shpkState.MaterialCount == 0) return; @@ -348,7 +329,8 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic // This is less performance-critical than the others, as this is called by the game only on draw object creation and slot update. // There are still thread safety concerns as it might be called in other threads by plugins. shpkLock = shpkState; - replacements[numReplacements++] = new((nint)shpkState.ShaderPackageReference, (nint)mtrlResource->ShaderPackageResourceHandle, + replacements[numReplacements++] = new HumanSetupScalingHook.Replacement((nint)shpkState.ShaderPackageReference, + (nint)mtrlResource->ShaderPackageResourceHandle, (nint)shpkState.DefaultShaderPackage); } @@ -439,7 +421,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic // Same performance considerations as OnRenderHumanMaterial. lock (shpkState) { - var shpkReference = shpkState.ShaderPackageReference; + var shpkReference = shpkState.ShaderPackageReference; try { *shpkReference = mtrlResource->ShaderPackageResourceHandle; @@ -452,7 +434,7 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic } } - private MaterialResourceHandle* GetMaterialResourceHandle(ModelRendererStructs.UnkPayload* unkPayload) + private static MaterialResourceHandle* GetMaterialResourceHandle(ModelRendererStructs.UnkPayload* unkPayload) { // TODO ClientStructs-ify var unkPointer = *(nint*)((nint)unkPayload->ModelResourceHandle + 0xE8) + unkPayload->UnkIndex * 0x24; @@ -467,13 +449,14 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic if (mtrlResource->ShaderPackageResourceHandle == null) { - Penumbra.Log.Warning($"ShaderReplacementFixer found a MaterialResourceHandle with no shader package"); + Penumbra.Log.Warning("ShaderReplacementFixer found a MaterialResourceHandle with no shader package"); return null; } if (mtrlResource->ShaderPackageResourceHandle->ShaderPackage != unkPayload->ShaderWrapper->ShaderPackage) { - Penumbra.Log.Warning($"ShaderReplacementFixer found a MaterialResourceHandle (0x{(nint)mtrlResource:X}) with an inconsistent shader package (got 0x{(nint)mtrlResource->ShaderPackageResourceHandle->ShaderPackage:X}, expected 0x{(nint)unkPayload->ShaderWrapper->ShaderPackage:X})"); + Penumbra.Log.Warning( + $"ShaderReplacementFixer found a MaterialResourceHandle (0x{(nint)mtrlResource:X}) with an inconsistent shader package (got 0x{(nint)mtrlResource->ShaderPackageResourceHandle->ShaderPackage:X}, expected 0x{(nint)unkPayload->ShaderWrapper->ShaderPackage:X})"); return null; } diff --git a/Penumbra/Interop/Services/CharacterUtility.cs b/Penumbra/Interop/Services/CharacterUtility.cs index 4ab156a9..1641e42d 100644 --- a/Penumbra/Interop/Services/CharacterUtility.cs +++ b/Penumbra/Interop/Services/CharacterUtility.cs @@ -67,7 +67,7 @@ public unsafe class CharacterUtility : IDisposable, IRequiredService _framework.Update += LoadDefaultResources; } - /// We store the default data of the resources so we can always restore them. + /// We store the default data of the resources, so we can always restore them. private void LoadDefaultResources(object _) { if (Address == null)