mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add/improve ShaderReplacementFixer hooks
This commit is contained in:
parent
d630a3dff4
commit
fb58a9c271
10 changed files with 478 additions and 113 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit ac9d9c78ae0025489b80ce2e798cdaacb0b43947
|
||||
Subproject commit 2fd5aa44056a906df90c9a826d1d17f6fdafebff
|
||||
|
|
@ -80,6 +80,8 @@ public class HookOverrides
|
|||
public bool HumanCreateDeformer;
|
||||
public bool HumanOnRenderMaterial;
|
||||
public bool ModelRendererOnRenderMaterial;
|
||||
public bool ModelRendererUnkFunc;
|
||||
public bool PrepareColorTable;
|
||||
}
|
||||
|
||||
public struct ResourceLoadingHooks
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Services;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.PostProcessing;
|
||||
|
||||
// TODO: "SetupScaling" does not seem to only set up scaling -> find a better name?
|
||||
public unsafe class HumanSetupScalingHook : FastHook<HumanSetupScalingHook.Delegate>
|
||||
{
|
||||
private const int ReplacementCapacity = 2;
|
||||
|
||||
public event EventDelegate? SetupReplacements;
|
||||
|
||||
public HumanSetupScalingHook(HookManager hooks, CharacterBaseVTables vTables)
|
||||
{
|
||||
Task = hooks.CreateHook<Delegate>("Human.SetupScaling", vTables.HumanVTable[58], Detour,
|
||||
!HookOverrides.Instance.PostProcessing.HumanSetupScaling);
|
||||
}
|
||||
|
||||
private void Detour(CharacterBase* drawObject, uint slotIndex)
|
||||
{
|
||||
Span<Replacement> replacements = stackalloc Replacement[ReplacementCapacity];
|
||||
var numReplacements = 0;
|
||||
IDisposable? pbdDisposable = null;
|
||||
object? shpkLock = null;
|
||||
var releaseLock = false;
|
||||
|
||||
try
|
||||
{
|
||||
SetupReplacements?.Invoke(drawObject, slotIndex, replacements, ref numReplacements, ref pbdDisposable, ref shpkLock);
|
||||
if (shpkLock != null)
|
||||
{
|
||||
Monitor.Enter(shpkLock);
|
||||
releaseLock = true;
|
||||
}
|
||||
for (var i = 0; i < numReplacements; ++i)
|
||||
*(nint*)replacements[i].AddressToReplace = replacements[i].ValueToSet;
|
||||
Task.Result.Original(drawObject, slotIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
for (var i = numReplacements; i-- > 0;)
|
||||
*(nint*)replacements[i].AddressToReplace = replacements[i].ValueToRestore;
|
||||
if (releaseLock)
|
||||
Monitor.Exit(shpkLock!);
|
||||
pbdDisposable?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void Delegate(CharacterBase* drawObject, uint slotIndex);
|
||||
|
||||
public delegate void EventDelegate(CharacterBase* drawObject, uint slotIndex, Span<Replacement> replacements, ref int numReplacements,
|
||||
ref IDisposable? pbdDisposable, ref object? shpkLock);
|
||||
|
||||
public readonly record struct Replacement(nint AddressToReplace, nint ValueToSet, nint ValueToRestore);
|
||||
}
|
||||
|
|
@ -18,27 +18,26 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi
|
|||
public static readonly Utf8GamePath PreBoneDeformerPath =
|
||||
Utf8GamePath.FromSpan("chara/xls/boneDeformer/human.pbd"u8, MetaDataComputation.All, out var p) ? p : Utf8GamePath.Empty;
|
||||
|
||||
// Approximate name guesses.
|
||||
private delegate void CharacterBaseSetupScalingDelegate(CharacterBase* drawObject, uint slotIndex);
|
||||
private delegate void* CharacterBaseCreateDeformerDelegate(CharacterBase* drawObject, uint slotIndex);
|
||||
// Approximate name guess.
|
||||
private delegate void* CharacterBaseCreateDeformerDelegate(CharacterBase* drawObject, uint slotIndex);
|
||||
|
||||
private readonly Hook<CharacterBaseSetupScalingDelegate> _humanSetupScalingHook;
|
||||
private readonly Hook<CharacterBaseCreateDeformerDelegate> _humanCreateDeformerHook;
|
||||
|
||||
private readonly CharacterUtility _utility;
|
||||
private readonly CollectionResolver _collectionResolver;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly IFramework _framework;
|
||||
private readonly CharacterUtility _utility;
|
||||
private readonly CollectionResolver _collectionResolver;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly IFramework _framework;
|
||||
private readonly HumanSetupScalingHook _humanSetupScalingHook;
|
||||
|
||||
public PreBoneDeformerReplacer(CharacterUtility utility, CollectionResolver collectionResolver, ResourceLoader resourceLoader,
|
||||
HookManager hooks, IFramework framework, CharacterBaseVTables vTables)
|
||||
HookManager hooks, IFramework framework, CharacterBaseVTables vTables, HumanSetupScalingHook humanSetupScalingHook)
|
||||
{
|
||||
_utility = utility;
|
||||
_collectionResolver = collectionResolver;
|
||||
_resourceLoader = resourceLoader;
|
||||
_framework = framework;
|
||||
_humanSetupScalingHook = hooks.CreateHook<CharacterBaseSetupScalingDelegate>("HumanSetupScaling", vTables.HumanVTable[58], SetupScaling,
|
||||
!HookOverrides.Instance.PostProcessing.HumanSetupScaling).Result;
|
||||
_utility = utility;
|
||||
_collectionResolver = collectionResolver;
|
||||
_resourceLoader = resourceLoader;
|
||||
_framework = framework;
|
||||
_humanSetupScalingHook = humanSetupScalingHook;
|
||||
_humanSetupScalingHook.SetupReplacements += SetupHSSReplacements;
|
||||
_humanCreateDeformerHook = hooks.CreateHook<CharacterBaseCreateDeformerDelegate>("HumanCreateDeformer", vTables.HumanVTable[101],
|
||||
CreateDeformer, !HookOverrides.Instance.PostProcessing.HumanCreateDeformer).Result;
|
||||
}
|
||||
|
|
@ -46,7 +45,7 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi
|
|||
public void Dispose()
|
||||
{
|
||||
_humanCreateDeformerHook.Dispose();
|
||||
_humanSetupScalingHook.Dispose();
|
||||
_humanSetupScalingHook.SetupReplacements -= SetupHSSReplacements;
|
||||
}
|
||||
|
||||
private SafeResourceHandle GetPreBoneDeformerForCharacter(CharacterBase* drawObject)
|
||||
|
|
@ -58,22 +57,24 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi
|
|||
return cache.CustomResources.Get(ResourceCategory.Chara, ResourceType.Pbd, PreBoneDeformerPath, resolveData);
|
||||
}
|
||||
|
||||
private void SetupScaling(CharacterBase* drawObject, uint slotIndex)
|
||||
private void SetupHSSReplacements(CharacterBase* drawObject, uint slotIndex, Span<HumanSetupScalingHook.Replacement> replacements,
|
||||
ref int numReplacements, ref IDisposable? pbdDisposable, ref object? shpkLock)
|
||||
{
|
||||
if (!_framework.IsInFrameworkUpdateThread)
|
||||
Penumbra.Log.Warning(
|
||||
$"{nameof(PreBoneDeformerReplacer)}.{nameof(SetupScaling)}(0x{(nint)drawObject:X}, {slotIndex}) called out of framework thread");
|
||||
$"{nameof(PreBoneDeformerReplacer)}.{nameof(SetupHSSReplacements)}(0x{(nint)drawObject:X}, {slotIndex}) called out of framework thread");
|
||||
|
||||
using var preBoneDeformer = GetPreBoneDeformerForCharacter(drawObject);
|
||||
var preBoneDeformer = GetPreBoneDeformerForCharacter(drawObject);
|
||||
try
|
||||
{
|
||||
if (!preBoneDeformer.IsInvalid)
|
||||
_utility.Address->HumanPbdResource = (Structs.ResourceHandle*)preBoneDeformer.ResourceHandle;
|
||||
_humanSetupScalingHook.Original(drawObject, slotIndex);
|
||||
pbdDisposable = preBoneDeformer;
|
||||
replacements[numReplacements++] = new((nint)(&_utility.Address->HumanPbdResource), (nint)preBoneDeformer.ResourceHandle,
|
||||
_utility.DefaultHumanPbdResource);
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
_utility.Address->HumanPbdResource = (Structs.ResourceHandle*)_utility.DefaultHumanPbdResource;
|
||||
preBoneDeformer.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
|
|
@ -7,6 +8,8 @@ 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;
|
||||
using CSModelRenderer = FFXIVClientStructs.FFXIV.Client.Graphics.Render.ModelRenderer;
|
||||
|
|
@ -19,6 +22,12 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
public static ReadOnlySpan<byte> SkinShpkName
|
||||
=> "skin.shpk"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> CharacterStockingsShpkName
|
||||
=> "characterstockings.shpk"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> CharacterLegacyShpkName
|
||||
=> "characterlegacy.shpk"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> IrisShpkName
|
||||
=> "iris.shpk"u8;
|
||||
|
||||
|
|
@ -42,16 +51,26 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
private delegate nint ModelRendererOnRenderMaterialDelegate(CSModelRenderer* modelRenderer, ushort* outFlags,
|
||||
CSModelRenderer.OnRenderModelParams* param, Material* material, uint materialIndex);
|
||||
|
||||
private delegate void ModelRendererUnkFuncDelegate(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2,
|
||||
uint unk3, uint unk4, uint unk5);
|
||||
|
||||
private readonly Hook<CharacterBaseOnRenderMaterialDelegate> _humanOnRenderMaterialHook;
|
||||
|
||||
private readonly Hook<ModelRendererOnRenderMaterialDelegate> _modelRendererOnRenderMaterialHook;
|
||||
|
||||
private readonly Hook<ModelRendererUnkFuncDelegate> _modelRendererUnkFuncHook;
|
||||
|
||||
private readonly Hook<MaterialResourceHandle.Delegates.PrepareColorTable> _prepareColorTableHook;
|
||||
|
||||
private readonly ResourceHandleDestructor _resourceHandleDestructor;
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CharacterUtility _utility;
|
||||
private readonly ModelRenderer _modelRenderer;
|
||||
private readonly HumanSetupScalingHook _humanSetupScalingHook;
|
||||
|
||||
private readonly ModdedShaderPackageState _skinState;
|
||||
private readonly ModdedShaderPackageState _characterStockingsState;
|
||||
private readonly ModdedShaderPackageState _characterLegacyState;
|
||||
private readonly ModdedShaderPackageState _irisState;
|
||||
private readonly ModdedShaderPackageState _characterGlassState;
|
||||
private readonly ModdedShaderPackageState _characterTransparencyState;
|
||||
|
|
@ -64,6 +83,12 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
public uint ModdedSkinShpkCount
|
||||
=> _skinState.MaterialCount;
|
||||
|
||||
public uint ModdedCharacterStockingsShpkCount
|
||||
=> _characterStockingsState.MaterialCount;
|
||||
|
||||
public uint ModdedCharacterLegacyShpkCount
|
||||
=> _characterLegacyState.MaterialCount;
|
||||
|
||||
public uint ModdedIrisShpkCount
|
||||
=> _irisState.MaterialCount;
|
||||
|
||||
|
|
@ -83,16 +108,23 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
=> _hairMaskState.MaterialCount;
|
||||
|
||||
public ShaderReplacementFixer(ResourceHandleDestructor resourceHandleDestructor, CharacterUtility utility, ModelRenderer modelRenderer,
|
||||
CommunicatorService communicator, HookManager hooks, CharacterBaseVTables vTables)
|
||||
CommunicatorService communicator, HookManager hooks, CharacterBaseVTables vTables, HumanSetupScalingHook humanSetupScalingHook)
|
||||
{
|
||||
_resourceHandleDestructor = resourceHandleDestructor;
|
||||
_utility = utility;
|
||||
_modelRenderer = modelRenderer;
|
||||
_communicator = communicator;
|
||||
_humanSetupScalingHook = humanSetupScalingHook;
|
||||
|
||||
_skinState = new ModdedShaderPackageState(
|
||||
() => (ShaderPackageResourceHandle**)&_utility.Address->SkinShpkResource,
|
||||
() => (ShaderPackageResourceHandle*)_utility.DefaultSkinShpkResource);
|
||||
_characterStockingsState = new ModdedShaderPackageState(
|
||||
() => (ShaderPackageResourceHandle**)&_utility.Address->CharacterStockingsShpkResource,
|
||||
() => (ShaderPackageResourceHandle*)_utility.DefaultCharacterStockingsShpkResource);
|
||||
_characterLegacyState = new ModdedShaderPackageState(
|
||||
() => (ShaderPackageResourceHandle**)&_utility.Address->CharacterLegacyShpkResource,
|
||||
() => (ShaderPackageResourceHandle*)_utility.DefaultCharacterLegacyShpkResource);
|
||||
_irisState = new ModdedShaderPackageState(() => _modelRenderer.IrisShaderPackage, () => _modelRenderer.DefaultIrisShaderPackage);
|
||||
_characterGlassState = new ModdedShaderPackageState(() => _modelRenderer.CharacterGlassShaderPackage,
|
||||
() => _modelRenderer.DefaultCharacterGlassShaderPackage);
|
||||
|
|
@ -105,33 +137,50 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
_hairMaskState =
|
||||
new ModdedShaderPackageState(() => _modelRenderer.HairMaskShaderPackage, () => _modelRenderer.DefaultHairMaskShaderPackage);
|
||||
|
||||
_humanSetupScalingHook.SetupReplacements += SetupHSSReplacements;
|
||||
_humanOnRenderMaterialHook = hooks.CreateHook<CharacterBaseOnRenderMaterialDelegate>("Human.OnRenderMaterial", vTables.HumanVTable[64],
|
||||
OnRenderHumanMaterial, !HookOverrides.Instance.PostProcessing.HumanOnRenderMaterial).Result;
|
||||
_modelRendererOnRenderMaterialHook = hooks.CreateHook<ModelRendererOnRenderMaterialDelegate>("ModelRenderer.OnRenderMaterial",
|
||||
Sigs.ModelRendererOnRenderMaterial, ModelRendererOnRenderMaterialDetour,
|
||||
!HookOverrides.Instance.PostProcessing.ModelRendererOnRenderMaterial).Result;
|
||||
_modelRendererUnkFuncHook = hooks.CreateHook<ModelRendererUnkFuncDelegate>("ModelRenderer.UnkFunc",
|
||||
Sigs.ModelRendererUnkFunc, ModelRendererUnkFuncDetour,
|
||||
!HookOverrides.Instance.PostProcessing.ModelRendererUnkFunc).Result;
|
||||
_prepareColorTableHook = hooks.CreateHook<MaterialResourceHandle.Delegates.PrepareColorTable>("MaterialResourceHandle.PrepareColorTable",
|
||||
Sigs.PrepareColorSet, PrepareColorTableDetour,
|
||||
!HookOverrides.Instance.PostProcessing.PrepareColorTable).Result;
|
||||
|
||||
_communicator.MtrlLoaded.Subscribe(OnMtrlLoaded, MtrlLoaded.Priority.ShaderReplacementFixer);
|
||||
_resourceHandleDestructor.Subscribe(OnResourceHandleDestructor, ResourceHandleDestructor.Priority.ShaderReplacementFixer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_prepareColorTableHook.Dispose();
|
||||
_modelRendererUnkFuncHook.Dispose();
|
||||
_modelRendererOnRenderMaterialHook.Dispose();
|
||||
_humanOnRenderMaterialHook.Dispose();
|
||||
_humanSetupScalingHook.SetupReplacements -= SetupHSSReplacements;
|
||||
|
||||
_communicator.MtrlLoaded.Unsubscribe(OnMtrlLoaded);
|
||||
_resourceHandleDestructor.Unsubscribe(OnResourceHandleDestructor);
|
||||
|
||||
_hairMaskState.ClearMaterials();
|
||||
_characterOcclusionState.ClearMaterials();
|
||||
_characterTattooState.ClearMaterials();
|
||||
_characterTransparencyState.ClearMaterials();
|
||||
_characterGlassState.ClearMaterials();
|
||||
_irisState.ClearMaterials();
|
||||
_characterLegacyState.ClearMaterials();
|
||||
_characterStockingsState.ClearMaterials();
|
||||
_skinState.ClearMaterials();
|
||||
}
|
||||
|
||||
public (ulong Skin, ulong Iris, ulong CharacterGlass, ulong CharacterTransparency, ulong CharacterTattoo, ulong CharacterOcclusion, ulong
|
||||
HairMask) GetAndResetSlowPathCallDeltas()
|
||||
public (ulong Skin, ulong CharacterStockings, ulong CharacterLegacy, ulong Iris, ulong CharacterGlass, ulong CharacterTransparency, ulong
|
||||
CharacterTattoo, ulong CharacterOcclusion, ulong HairMask) GetAndResetSlowPathCallDeltas()
|
||||
=> (_skinState.GetAndResetSlowPathCallDelta(),
|
||||
_characterStockingsState.GetAndResetSlowPathCallDelta(),
|
||||
_characterLegacyState.GetAndResetSlowPathCallDelta(),
|
||||
_irisState.GetAndResetSlowPathCallDelta(),
|
||||
_characterGlassState.GetAndResetSlowPathCallDelta(),
|
||||
_characterTransparencyState.GetAndResetSlowPathCallDelta(),
|
||||
|
|
@ -155,7 +204,8 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
return;
|
||||
|
||||
var shpkName = mtrl->ShpkNameSpan;
|
||||
var shpkState = GetStateForHuman(shpkName) ?? GetStateForModelRenderer(shpkName);
|
||||
var shpkState = GetStateForHumanSetup(shpkName) ?? GetStateForHumanRender(shpkName) ?? GetStateForModelRendererRender(shpkName)
|
||||
?? GetStateForModelRendererUnk(shpkName) ?? GetStateForColorTable(shpkName);
|
||||
|
||||
if (shpkState != null && shpk != shpkState.DefaultShaderPackage)
|
||||
shpkState.TryAddMaterial(mtrlResourceHandle);
|
||||
|
|
@ -164,6 +214,8 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
private void OnResourceHandleDestructor(Structs.ResourceHandle* handle)
|
||||
{
|
||||
_skinState.TryRemoveMaterial(handle);
|
||||
_characterStockingsState.TryRemoveMaterial(handle);
|
||||
_characterLegacyState.TryRemoveMaterial(handle);
|
||||
_irisState.TryRemoveMaterial(handle);
|
||||
_characterGlassState.TryRemoveMaterial(handle);
|
||||
_characterTransparencyState.TryRemoveMaterial(handle);
|
||||
|
|
@ -172,10 +224,25 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
_hairMaskState.TryRemoveMaterial(handle);
|
||||
}
|
||||
|
||||
private ModdedShaderPackageState? GetStateForHuman(MaterialResourceHandle* mtrlResource)
|
||||
=> mtrlResource == null ? null : GetStateForHuman(mtrlResource->ShpkNameSpan);
|
||||
private ModdedShaderPackageState? GetStateForHumanSetup(MaterialResourceHandle* mtrlResource)
|
||||
=> mtrlResource == null ? null : GetStateForHumanSetup(mtrlResource->ShpkNameSpan);
|
||||
|
||||
private ModdedShaderPackageState? GetStateForHuman(ReadOnlySpan<byte> shpkName)
|
||||
private ModdedShaderPackageState? GetStateForHumanSetup(ReadOnlySpan<byte> shpkName)
|
||||
{
|
||||
if (CharacterStockingsShpkName.SequenceEqual(shpkName))
|
||||
return _characterStockingsState;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private uint GetTotalMaterialCountForHumanSetup()
|
||||
=> _characterStockingsState.MaterialCount;
|
||||
|
||||
private ModdedShaderPackageState? GetStateForHumanRender(MaterialResourceHandle* mtrlResource)
|
||||
=> mtrlResource == null ? null : GetStateForHumanRender(mtrlResource->ShpkNameSpan);
|
||||
|
||||
private ModdedShaderPackageState? GetStateForHumanRender(ReadOnlySpan<byte> shpkName)
|
||||
{
|
||||
if (SkinShpkName.SequenceEqual(shpkName))
|
||||
return _skinState;
|
||||
|
|
@ -184,17 +251,14 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private uint GetTotalMaterialCountForHuman()
|
||||
private uint GetTotalMaterialCountForHumanRender()
|
||||
=> _skinState.MaterialCount;
|
||||
|
||||
private ModdedShaderPackageState? GetStateForModelRenderer(MaterialResourceHandle* mtrlResource)
|
||||
=> mtrlResource == null ? null : GetStateForModelRenderer(mtrlResource->ShpkNameSpan);
|
||||
private ModdedShaderPackageState? GetStateForModelRendererRender(MaterialResourceHandle* mtrlResource)
|
||||
=> mtrlResource == null ? null : GetStateForModelRendererRender(mtrlResource->ShpkNameSpan);
|
||||
|
||||
private ModdedShaderPackageState? GetStateForModelRenderer(ReadOnlySpan<byte> shpkName)
|
||||
private ModdedShaderPackageState? GetStateForModelRendererRender(ReadOnlySpan<byte> shpkName)
|
||||
{
|
||||
if (IrisShpkName.SequenceEqual(shpkName))
|
||||
return _irisState;
|
||||
|
||||
if (CharacterGlassShpkName.SequenceEqual(shpkName))
|
||||
return _characterGlassState;
|
||||
|
||||
|
|
@ -204,9 +268,6 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
if (CharacterTattooShpkName.SequenceEqual(shpkName))
|
||||
return _characterTattooState;
|
||||
|
||||
if (CharacterOcclusionShpkName.SequenceEqual(shpkName))
|
||||
return _characterOcclusionState;
|
||||
|
||||
if (HairMaskShpkName.SequenceEqual(shpkName))
|
||||
return _hairMaskState;
|
||||
|
||||
|
|
@ -214,24 +275,93 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private uint GetTotalMaterialCountForModelRenderer()
|
||||
=> _irisState.MaterialCount
|
||||
+ _characterGlassState.MaterialCount
|
||||
private uint GetTotalMaterialCountForModelRendererRender()
|
||||
=> _characterGlassState.MaterialCount
|
||||
+ _characterTransparencyState.MaterialCount
|
||||
+ _characterTattooState.MaterialCount
|
||||
+ _characterOcclusionState.MaterialCount
|
||||
+ _hairMaskState.MaterialCount;
|
||||
|
||||
private ModdedShaderPackageState? GetStateForModelRendererUnk(MaterialResourceHandle* mtrlResource)
|
||||
=> mtrlResource == null ? null : GetStateForModelRendererUnk(mtrlResource->ShpkNameSpan);
|
||||
|
||||
private ModdedShaderPackageState? GetStateForModelRendererUnk(ReadOnlySpan<byte> shpkName)
|
||||
{
|
||||
if (IrisShpkName.SequenceEqual(shpkName))
|
||||
return _irisState;
|
||||
|
||||
if (CharacterOcclusionShpkName.SequenceEqual(shpkName))
|
||||
return _characterOcclusionState;
|
||||
|
||||
if (CharacterStockingsShpkName.SequenceEqual(shpkName))
|
||||
return _characterStockingsState;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private uint GetTotalMaterialCountForModelRendererUnk()
|
||||
=> _irisState.MaterialCount
|
||||
+ _characterOcclusionState.MaterialCount
|
||||
+ _characterStockingsState.MaterialCount;
|
||||
|
||||
private ModdedShaderPackageState? GetStateForColorTable(ReadOnlySpan<byte> shpkName)
|
||||
{
|
||||
if (CharacterLegacyShpkName.SequenceEqual(shpkName))
|
||||
return _characterLegacyState;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private uint GetTotalMaterialCountForColorTable()
|
||||
=> _characterLegacyState.MaterialCount;
|
||||
|
||||
private void SetupHSSReplacements(CharacterBase* drawObject, uint slotIndex, Span<HumanSetupScalingHook.Replacement> 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.
|
||||
if (!Enabled || GetTotalMaterialCountForHumanSetup() == 0)
|
||||
return;
|
||||
|
||||
var model = drawObject->Models[slotIndex];
|
||||
if (model == null)
|
||||
return;
|
||||
MaterialResourceHandle* mtrlResource = null;
|
||||
ModdedShaderPackageState? shpkState = null;
|
||||
foreach (var material in model->MaterialsSpan)
|
||||
{
|
||||
if (material.Value == null)
|
||||
continue;
|
||||
|
||||
mtrlResource = material.Value->MaterialResourceHandle;
|
||||
shpkState = GetStateForHumanSetup(mtrlResource);
|
||||
// Despite this function being called with what designates a model (and therefore potentially many materials),
|
||||
// we currently don't need to handle more than one modded ShPk.
|
||||
if (shpkState != null)
|
||||
break;
|
||||
}
|
||||
if (shpkState == null || shpkState.MaterialCount == 0)
|
||||
return;
|
||||
|
||||
shpkState.IncrementSlowPathCallDelta();
|
||||
|
||||
// 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,
|
||||
(nint)shpkState.DefaultShaderPackage);
|
||||
}
|
||||
|
||||
private nint OnRenderHumanMaterial(CharacterBase* human, CSModelRenderer.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 (!Enabled || GetTotalMaterialCountForHuman() == 0)
|
||||
if (!Enabled || GetTotalMaterialCountForHumanRender() == 0)
|
||||
return _humanOnRenderMaterialHook.Original(human, param);
|
||||
|
||||
var material = param->Model->Materials[param->MaterialIndex];
|
||||
var mtrlResource = material->MaterialResourceHandle;
|
||||
var shpkState = GetStateForHuman(mtrlResource);
|
||||
if (shpkState == null)
|
||||
var shpkState = GetStateForHumanRender(mtrlResource);
|
||||
if (shpkState == null || shpkState.MaterialCount == 0)
|
||||
return _humanOnRenderMaterialHook.Original(human, param);
|
||||
|
||||
shpkState.IncrementSlowPathCallDelta();
|
||||
|
|
@ -259,18 +389,18 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
private nint ModelRendererOnRenderMaterialDetour(CSModelRenderer* modelRenderer, ushort* outFlags,
|
||||
CSModelRenderer.OnRenderModelParams* param, Material* material, uint materialIndex)
|
||||
{
|
||||
// If we don't have any on-screen instances of modded characterglass.shpk, we don't need the slow path at all.
|
||||
if (!Enabled || GetTotalMaterialCountForModelRenderer() == 0)
|
||||
// If we don't have any on-screen instances of modded characterglass.shpk or others, we don't need the slow path at all.
|
||||
if (!Enabled || GetTotalMaterialCountForModelRendererRender() == 0)
|
||||
return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex);
|
||||
|
||||
var mtrlResource = material->MaterialResourceHandle;
|
||||
var shpkState = GetStateForModelRenderer(mtrlResource);
|
||||
if (shpkState == null)
|
||||
var shpkState = GetStateForModelRendererRender(mtrlResource);
|
||||
if (shpkState == null || shpkState.MaterialCount == 0)
|
||||
return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex);
|
||||
|
||||
shpkState.IncrementSlowPathCallDelta();
|
||||
|
||||
// Same performance considerations as above.
|
||||
// Same performance considerations as OnRenderHumanMaterial.
|
||||
lock (shpkState)
|
||||
{
|
||||
var shpkReference = shpkState.ShaderPackageReference;
|
||||
|
|
@ -286,6 +416,102 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
|||
}
|
||||
}
|
||||
|
||||
private void ModelRendererUnkFuncDetour(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2, uint unk3,
|
||||
uint unk4, uint unk5)
|
||||
{
|
||||
// If we don't have any on-screen instances of modded iris.shpk or others, we don't need the slow path at all.
|
||||
if (!Enabled || GetTotalMaterialCountForModelRendererUnk() == 0)
|
||||
{
|
||||
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);
|
||||
return;
|
||||
}
|
||||
|
||||
var mtrlResource = GetMaterialResourceHandle(unkPayload);
|
||||
var shpkState = GetStateForModelRendererUnk(mtrlResource);
|
||||
if (shpkState == null || shpkState.MaterialCount == 0)
|
||||
{
|
||||
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);
|
||||
return;
|
||||
}
|
||||
|
||||
shpkState.IncrementSlowPathCallDelta();
|
||||
|
||||
// Same performance considerations as OnRenderHumanMaterial.
|
||||
lock (shpkState)
|
||||
{
|
||||
var shpkReference = shpkState.ShaderPackageReference;
|
||||
try
|
||||
{
|
||||
*shpkReference = mtrlResource->ShaderPackageResourceHandle;
|
||||
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);
|
||||
}
|
||||
finally
|
||||
{
|
||||
*shpkReference = shpkState.DefaultShaderPackage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MaterialResourceHandle* GetMaterialResourceHandle(ModelRendererStructs.UnkPayload* unkPayload)
|
||||
{
|
||||
// TODO ClientStructs-ify
|
||||
var unkPointer = *(nint*)((nint)unkPayload->ModelResourceHandle + 0xE8) + unkPayload->UnkIndex * 0x24;
|
||||
var materialIndex = *(ushort*)(unkPointer + 8);
|
||||
var material = unkPayload->Params->Model->Materials[materialIndex];
|
||||
if (material == null)
|
||||
return null;
|
||||
|
||||
var mtrlResource = material->MaterialResourceHandle;
|
||||
if (mtrlResource == null)
|
||||
return null;
|
||||
|
||||
if (mtrlResource->ShaderPackageResourceHandle == null)
|
||||
{
|
||||
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})");
|
||||
return null;
|
||||
}
|
||||
|
||||
return mtrlResource;
|
||||
}
|
||||
|
||||
private Texture* PrepareColorTableDetour(MaterialResourceHandle* thisPtr, byte stain0Id, byte stain1Id)
|
||||
{
|
||||
// If we don't have any on-screen instances of modded characterlegacy.shpk, we don't need the slow path at all.
|
||||
if (!Enabled || GetTotalMaterialCountForColorTable() == 0)
|
||||
return _prepareColorTableHook.Original(thisPtr, stain0Id, stain1Id);
|
||||
|
||||
var material = thisPtr->Material;
|
||||
if (material == null)
|
||||
return _prepareColorTableHook.Original(thisPtr, stain0Id, stain1Id);
|
||||
|
||||
var shpkState = GetStateForColorTable(thisPtr->ShpkNameSpan);
|
||||
if (shpkState == null || shpkState.MaterialCount == 0)
|
||||
return _prepareColorTableHook.Original(thisPtr, stain0Id, stain1Id);
|
||||
|
||||
shpkState.IncrementSlowPathCallDelta();
|
||||
|
||||
// Same performance considerations as HumanSetupScalingDetour.
|
||||
lock (shpkState)
|
||||
{
|
||||
var shpkReference = shpkState.ShaderPackageReference;
|
||||
try
|
||||
{
|
||||
*shpkReference = thisPtr->ShaderPackageResourceHandle;
|
||||
return _prepareColorTableHook.Original(thisPtr, stain0Id, stain1Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
*shpkReference = shpkState.DefaultShaderPackage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ModdedShaderPackageState(ShaderPackageReferenceGetter referenceGetter, DefaultShaderPackageGetter defaultGetter)
|
||||
{
|
||||
// MaterialResourceHandle set
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ public unsafe class CharacterUtility : IDisposable, IRequiredService
|
|||
|
||||
public bool Ready { get; private set; }
|
||||
public event Action LoadingFinished;
|
||||
public nint DefaultHumanPbdResource { get; private set; }
|
||||
public nint DefaultTransparentResource { get; private set; }
|
||||
public nint DefaultDecalResource { get; private set; }
|
||||
public nint DefaultSkinShpkResource { get; private set; }
|
||||
public nint DefaultHumanPbdResource { get; private set; }
|
||||
public nint DefaultTransparentResource { get; private set; }
|
||||
public nint DefaultDecalResource { get; private set; }
|
||||
public nint DefaultSkinShpkResource { get; private set; }
|
||||
public nint DefaultCharacterStockingsShpkResource { get; private set; }
|
||||
public nint DefaultCharacterLegacyShpkResource { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The relevant indices depend on which meta manipulations we allow for.
|
||||
|
|
@ -108,6 +110,18 @@ public unsafe class CharacterUtility : IDisposable, IRequiredService
|
|||
anyMissing |= DefaultSkinShpkResource == nint.Zero;
|
||||
}
|
||||
|
||||
if (DefaultCharacterStockingsShpkResource == nint.Zero)
|
||||
{
|
||||
DefaultCharacterStockingsShpkResource = (nint)Address->CharacterStockingsShpkResource;
|
||||
anyMissing |= DefaultCharacterStockingsShpkResource == nint.Zero;
|
||||
}
|
||||
|
||||
if (DefaultCharacterLegacyShpkResource == nint.Zero)
|
||||
{
|
||||
DefaultCharacterLegacyShpkResource = (nint)Address->CharacterLegacyShpkResource;
|
||||
anyMissing |= DefaultCharacterLegacyShpkResource == nint.Zero;
|
||||
}
|
||||
|
||||
if (anyMissing)
|
||||
return;
|
||||
|
||||
|
|
@ -122,10 +136,12 @@ public unsafe class CharacterUtility : IDisposable, IRequiredService
|
|||
if (!Ready)
|
||||
return;
|
||||
|
||||
Address->HumanPbdResource = (ResourceHandle*)DefaultHumanPbdResource;
|
||||
Address->TransparentTexResource = (TextureResourceHandle*)DefaultTransparentResource;
|
||||
Address->DecalTexResource = (TextureResourceHandle*)DefaultDecalResource;
|
||||
Address->SkinShpkResource = (ResourceHandle*)DefaultSkinShpkResource;
|
||||
Address->HumanPbdResource = (ResourceHandle*)DefaultHumanPbdResource;
|
||||
Address->TransparentTexResource = (TextureResourceHandle*)DefaultTransparentResource;
|
||||
Address->DecalTexResource = (TextureResourceHandle*)DefaultDecalResource;
|
||||
Address->SkinShpkResource = (ResourceHandle*)DefaultSkinShpkResource;
|
||||
Address->CharacterStockingsShpkResource = (ResourceHandle*)DefaultCharacterStockingsShpkResource;
|
||||
Address->CharacterLegacyShpkResource = (ResourceHandle*)DefaultCharacterLegacyShpkResource;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Dalamud.Plugin.Services;
|
|||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using OtterGui.Services;
|
||||
using ModelRendererData = FFXIVClientStructs.FFXIV.Client.Graphics.Render.ModelRenderer;
|
||||
|
||||
namespace Penumbra.Interop.Services;
|
||||
|
||||
|
|
@ -9,46 +10,53 @@ public unsafe class ModelRenderer : IDisposable, IRequiredService
|
|||
{
|
||||
public bool Ready { get; private set; }
|
||||
|
||||
public ShaderPackageResourceHandle** IrisShaderPackage
|
||||
public ModelRendererData* Address
|
||||
=> Manager.Instance() switch
|
||||
{
|
||||
null => null,
|
||||
var renderManager => &renderManager->ModelRenderer.IrisShaderPackage,
|
||||
var renderManager => &renderManager->ModelRenderer,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle** IrisShaderPackage
|
||||
=> Address switch
|
||||
{
|
||||
null => null,
|
||||
var data => &data->IrisShaderPackage,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle** CharacterGlassShaderPackage
|
||||
=> Manager.Instance() switch
|
||||
=> Address switch
|
||||
{
|
||||
null => null,
|
||||
var renderManager => &renderManager->ModelRenderer.CharacterGlassShaderPackage,
|
||||
null => null,
|
||||
var data => &data->CharacterGlassShaderPackage,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle** CharacterTransparencyShaderPackage
|
||||
=> Manager.Instance() switch
|
||||
=> Address switch
|
||||
{
|
||||
null => null,
|
||||
var renderManager => &renderManager->ModelRenderer.CharacterTransparencyShaderPackage,
|
||||
null => null,
|
||||
var data => &data->CharacterTransparencyShaderPackage,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle** CharacterTattooShaderPackage
|
||||
=> Manager.Instance() switch
|
||||
=> Address switch
|
||||
{
|
||||
null => null,
|
||||
var renderManager => &renderManager->ModelRenderer.CharacterTattooShaderPackage,
|
||||
null => null,
|
||||
var data => &data->CharacterTattooShaderPackage,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle** CharacterOcclusionShaderPackage
|
||||
=> Manager.Instance() switch
|
||||
=> Address switch
|
||||
{
|
||||
null => null,
|
||||
var renderManager => &renderManager->ModelRenderer.CharacterOcclusionShaderPackage,
|
||||
null => null,
|
||||
var data => &data->CharacterOcclusionShaderPackage,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle** HairMaskShaderPackage
|
||||
=> Manager.Instance() switch
|
||||
=> Address switch
|
||||
{
|
||||
null => null,
|
||||
var renderManager => &renderManager->ModelRenderer.HairMaskShaderPackage,
|
||||
null => null,
|
||||
var data => &data->HairMaskShaderPackage,
|
||||
};
|
||||
|
||||
public ShaderPackageResourceHandle* DefaultIrisShaderPackage { get; private set; }
|
||||
|
|
@ -96,7 +104,7 @@ public unsafe class ModelRenderer : IDisposable, IRequiredService
|
|||
if (DefaultCharacterTransparencyShaderPackage == null)
|
||||
{
|
||||
DefaultCharacterTransparencyShaderPackage = *CharacterTransparencyShaderPackage;
|
||||
anyMissing |= DefaultCharacterTransparencyShaderPackage == null;
|
||||
anyMissing |= DefaultCharacterTransparencyShaderPackage == null;
|
||||
}
|
||||
|
||||
if (DefaultCharacterTattooShaderPackage == null)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,17 @@ namespace Penumbra.Interop.Structs;
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct CharacterUtilityData
|
||||
{
|
||||
public const int IndexHumanPbd = 63;
|
||||
public const int IndexTransparentTex = 79;
|
||||
public const int IndexDecalTex = 80;
|
||||
public const int IndexTileOrbArrayTex = 81;
|
||||
public const int IndexTileNormArrayTex = 82;
|
||||
public const int IndexSkinShpk = 83;
|
||||
public const int IndexGudStm = 94;
|
||||
public const int IndexLegacyStm = 95;
|
||||
public const int IndexSphereDArrayTex = 96;
|
||||
public const int IndexHumanPbd = 63;
|
||||
public const int IndexTransparentTex = 79;
|
||||
public const int IndexDecalTex = 80;
|
||||
public const int IndexTileOrbArrayTex = 81;
|
||||
public const int IndexTileNormArrayTex = 82;
|
||||
public const int IndexSkinShpk = 83;
|
||||
public const int IndexCharacterStockingsShpk = 84;
|
||||
public const int IndexCharacterLegacyShpk = 85;
|
||||
public const int IndexGudStm = 94;
|
||||
public const int IndexLegacyStm = 95;
|
||||
public const int IndexSphereDArrayTex = 96;
|
||||
|
||||
public static readonly MetaIndex[] EqdpIndices = Enum.GetNames<MetaIndex>()
|
||||
.Zip(Enum.GetValues<MetaIndex>())
|
||||
|
|
@ -111,6 +113,12 @@ public unsafe struct CharacterUtilityData
|
|||
[FieldOffset(8 + IndexSkinShpk * 8)]
|
||||
public ResourceHandle* SkinShpkResource;
|
||||
|
||||
[FieldOffset(8 + IndexCharacterStockingsShpk * 8)]
|
||||
public ResourceHandle* CharacterStockingsShpkResource;
|
||||
|
||||
[FieldOffset(8 + IndexCharacterLegacyShpk * 8)]
|
||||
public ResourceHandle* CharacterLegacyShpkResource;
|
||||
|
||||
[FieldOffset(8 + IndexGudStm * 8)]
|
||||
public ResourceHandle* GudStmResource;
|
||||
|
||||
|
|
|
|||
35
Penumbra/Interop/Structs/ModelRendererStructs.cs
Normal file
35
Penumbra/Interop/Structs/ModelRendererStructs.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
|
||||
public static unsafe class ModelRendererStructs
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x28)]
|
||||
public struct UnkShaderWrapper
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public void* Vtbl;
|
||||
|
||||
[FieldOffset(8)]
|
||||
public ShaderPackage* ShaderPackage;
|
||||
}
|
||||
|
||||
// Unknown size, this is allocated on FUN_1404446c0's stack (E8 ?? ?? ?? ?? FF C3 41 3B DE 72 ?? 48 C7 85)
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct UnkPayload
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ModelRenderer.OnRenderModelParams* Params;
|
||||
|
||||
[FieldOffset(8)]
|
||||
public ModelResourceHandle* ModelResourceHandle;
|
||||
|
||||
[FieldOffset(0x10)]
|
||||
public UnkShaderWrapper* ShaderWrapper;
|
||||
|
||||
[FieldOffset(0x1C)]
|
||||
public ushort UnkIndex;
|
||||
}
|
||||
}
|
||||
|
|
@ -833,20 +833,6 @@ public class DebugTab : Window, ITab, IUiService
|
|||
ImGui.TableSetupColumn("\u0394 Slow-Path Calls", ImGuiTableColumnFlags.WidthStretch, 0.2f);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("skin.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedSkinShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.Skin}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("iris.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedIrisShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.Iris}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("characterglass.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
|
|
@ -855,18 +841,11 @@ public class DebugTab : Window, ITab, IUiService
|
|||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterGlass}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("charactertransparency.shpk");
|
||||
ImGui.TextUnformatted("characterlegacy.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterTransparencyShpkCount}");
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterLegacyShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterTransparency}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("charactertattoo.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterTattooShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterTattoo}");
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterLegacy}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("characterocclusion.shpk");
|
||||
|
|
@ -875,12 +854,47 @@ public class DebugTab : Window, ITab, IUiService
|
|||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterOcclusion}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("characterstockings.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterStockingsShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterStockings}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("charactertattoo.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterTattooShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterTattoo}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("charactertransparency.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterTransparencyShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterTransparency}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("hairmask.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedHairMaskShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.HairMask}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("iris.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedIrisShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.Iris}");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted("skin.shpk");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedSkinShpkCount}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"{slowPathCallDeltas.Skin}");
|
||||
}
|
||||
|
||||
/// <summary> Draw information about the resident resource files. </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue