Update ShaderReplacementFixer for 7.0

This commit is contained in:
Exter-N 2024-07-05 23:59:22 +02:00
parent 1284037554
commit 41d271213e
3 changed files with 359 additions and 70 deletions

View file

@ -19,9 +19,24 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
public static ReadOnlySpan<byte> SkinShpkName public static ReadOnlySpan<byte> SkinShpkName
=> "skin.shpk"u8; => "skin.shpk"u8;
public static ReadOnlySpan<byte> IrisShpkName
=> "iris.shpk"u8;
public static ReadOnlySpan<byte> CharacterGlassShpkName public static ReadOnlySpan<byte> CharacterGlassShpkName
=> "characterglass.shpk"u8; => "characterglass.shpk"u8;
public static ReadOnlySpan<byte> CharacterTransparencyShpkName
=> "charactertransparency.shpk"u8;
public static ReadOnlySpan<byte> CharacterTattooShpkName
=> "charactertattoo.shpk"u8;
public static ReadOnlySpan<byte> CharacterOcclusionShpkName
=> "characterocclusion.shpk"u8;
public static ReadOnlySpan<byte> HairMaskShpkName
=> "hairmask.shpk"u8;
private delegate nint CharacterBaseOnRenderMaterialDelegate(CharacterBase* drawObject, CSModelRenderer.OnRenderMaterialParams* param); private delegate nint CharacterBaseOnRenderMaterialDelegate(CharacterBase* drawObject, CSModelRenderer.OnRenderMaterialParams* param);
private delegate nint ModelRendererOnRenderMaterialDelegate(CSModelRenderer* modelRenderer, ushort* outFlags, private delegate nint ModelRendererOnRenderMaterialDelegate(CSModelRenderer* modelRenderer, ushort* outFlags,
@ -36,26 +51,36 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
private readonly CharacterUtility _utility; private readonly CharacterUtility _utility;
private readonly ModelRenderer _modelRenderer; private readonly ModelRenderer _modelRenderer;
// MaterialResourceHandle set private readonly ModdedShaderPackageState _skinState;
private readonly ConcurrentSet<nint> _moddedSkinShpkMaterials = new(); private readonly ModdedShaderPackageState _irisState;
private readonly ConcurrentSet<nint> _moddedCharacterGlassShpkMaterials = new(); private readonly ModdedShaderPackageState _characterGlassState;
private readonly ModdedShaderPackageState _characterTransparencyState;
private readonly object _skinLock = new(); private readonly ModdedShaderPackageState _characterTattooState;
private readonly object _characterGlassLock = new(); private readonly ModdedShaderPackageState _characterOcclusionState;
private readonly ModdedShaderPackageState _hairMaskState;
// ConcurrentDictionary.Count uses a lock in its current implementation.
private int _moddedSkinShpkCount;
private int _moddedCharacterGlassShpkCount;
private ulong _skinSlowPathCallDelta;
private ulong _characterGlassSlowPathCallDelta;
public bool Enabled { get; internal set; } = true; public bool Enabled { get; internal set; } = true;
public int ModdedSkinShpkCount public uint ModdedSkinShpkCount
=> _moddedSkinShpkCount; => _skinState.MaterialCount;
public int ModdedCharacterGlassShpkCount public uint ModdedIrisShpkCount
=> _moddedCharacterGlassShpkCount; => _irisState.MaterialCount;
public uint ModdedCharacterGlassShpkCount
=> _characterGlassState.MaterialCount;
public uint ModdedCharacterTransparencyShpkCount
=> _characterTransparencyState.MaterialCount;
public uint ModdedCharacterTattooShpkCount
=> _characterTattooState.MaterialCount;
public uint ModdedCharacterOcclusionShpkCount
=> _characterOcclusionState.MaterialCount;
public uint ModdedHairMaskShpkCount
=> _hairMaskState.MaterialCount;
public ShaderReplacementFixer(ResourceHandleDestructor resourceHandleDestructor, CharacterUtility utility, ModelRenderer modelRenderer, public ShaderReplacementFixer(ResourceHandleDestructor resourceHandleDestructor, CharacterUtility utility, ModelRenderer modelRenderer,
CommunicatorService communicator, HookManager hooks, CharacterBaseVTables vTables) CommunicatorService communicator, HookManager hooks, CharacterBaseVTables vTables)
@ -64,7 +89,18 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
_utility = utility; _utility = utility;
_modelRenderer = modelRenderer; _modelRenderer = modelRenderer;
_communicator = communicator; _communicator = communicator;
_humanOnRenderMaterialHook = hooks.CreateHook<CharacterBaseOnRenderMaterialDelegate>("Human.OnRenderMaterial", vTables.HumanVTable[62],
_skinState = new(
() => (ShaderPackageResourceHandle**)&_utility.Address->SkinShpkResource,
() => (ShaderPackageResourceHandle*)_utility.DefaultSkinShpkResource);
_irisState = new(() => _modelRenderer.IrisShaderPackage, () => _modelRenderer.DefaultIrisShaderPackage);
_characterGlassState = new(() => _modelRenderer.CharacterGlassShaderPackage, () => _modelRenderer.DefaultCharacterGlassShaderPackage);
_characterTransparencyState = new(() => _modelRenderer.CharacterTransparencyShaderPackage, () => _modelRenderer.DefaultCharacterTransparencyShaderPackage);
_characterTattooState = new(() => _modelRenderer.CharacterTattooShaderPackage, () => _modelRenderer.DefaultCharacterTattooShaderPackage);
_characterOcclusionState = new(() => _modelRenderer.CharacterOcclusionShaderPackage, () => _modelRenderer.DefaultCharacterOcclusionShaderPackage);
_hairMaskState = new(() => _modelRenderer.HairMaskShaderPackage, () => _modelRenderer.DefaultHairMaskShaderPackage);
_humanOnRenderMaterialHook = hooks.CreateHook<CharacterBaseOnRenderMaterialDelegate>("Human.OnRenderMaterial", vTables.HumanVTable[64],
OnRenderHumanMaterial, HookSettings.PostProcessingHooks).Result; OnRenderHumanMaterial, HookSettings.PostProcessingHooks).Result;
_modelRendererOnRenderMaterialHook = hooks.CreateHook<ModelRendererOnRenderMaterialDelegate>("ModelRenderer.OnRenderMaterial", _modelRendererOnRenderMaterialHook = hooks.CreateHook<ModelRendererOnRenderMaterialDelegate>("ModelRenderer.OnRenderMaterial",
Sigs.ModelRendererOnRenderMaterial, ModelRendererOnRenderMaterialDetour, HookSettings.PostProcessingHooks).Result; Sigs.ModelRendererOnRenderMaterial, ModelRendererOnRenderMaterialDetour, HookSettings.PostProcessingHooks).Result;
@ -78,14 +114,23 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
_humanOnRenderMaterialHook.Dispose(); _humanOnRenderMaterialHook.Dispose();
_communicator.MtrlShpkLoaded.Unsubscribe(OnMtrlShpkLoaded); _communicator.MtrlShpkLoaded.Unsubscribe(OnMtrlShpkLoaded);
_resourceHandleDestructor.Unsubscribe(OnResourceHandleDestructor); _resourceHandleDestructor.Unsubscribe(OnResourceHandleDestructor);
_moddedCharacterGlassShpkMaterials.Clear(); _hairMaskState.ClearMaterials();
_moddedSkinShpkMaterials.Clear(); _characterOcclusionState.ClearMaterials();
_moddedCharacterGlassShpkCount = 0; _characterTattooState.ClearMaterials();
_moddedSkinShpkCount = 0; _characterTransparencyState.ClearMaterials();
_characterGlassState.ClearMaterials();
_irisState.ClearMaterials();
_skinState.ClearMaterials();
} }
public (ulong Skin, ulong CharacterGlass) GetAndResetSlowPathCallDeltas() public (ulong Skin, ulong Iris, ulong CharacterGlass, ulong CharacterTransparency, ulong CharacterTattoo, ulong CharacterOcclusion, ulong HairMask) GetAndResetSlowPathCallDeltas()
=> (Interlocked.Exchange(ref _skinSlowPathCallDelta, 0), Interlocked.Exchange(ref _characterGlassSlowPathCallDelta, 0)); => (_skinState.GetAndResetSlowPathCallDelta(),
_irisState.GetAndResetSlowPathCallDelta(),
_characterGlassState.GetAndResetSlowPathCallDelta(),
_characterTransparencyState.GetAndResetSlowPathCallDelta(),
_characterTattooState.GetAndResetSlowPathCallDelta(),
_characterOcclusionState.GetAndResetSlowPathCallDelta(),
_hairMaskState.GetAndResetSlowPathCallDelta());
private static bool IsMaterialWithShpk(MaterialResourceHandle* mtrlResource, ReadOnlySpan<byte> shpkName) private static bool IsMaterialWithShpk(MaterialResourceHandle* mtrlResource, ReadOnlySpan<byte> shpkName)
{ {
@ -103,53 +148,98 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
return; return;
var shpkName = mtrl->ShpkNameSpan; var shpkName = mtrl->ShpkNameSpan;
var shpkState = GetStateForHuman(shpkName) ?? GetStateForModelRenderer(shpkName);
if (SkinShpkName.SequenceEqual(shpkName) && (nint)shpk != _utility.DefaultSkinShpkResource) if (shpkState != null && shpk != shpkState.DefaultShaderPackage)
if (_moddedSkinShpkMaterials.TryAdd(mtrlResourceHandle)) shpkState.TryAddMaterial(mtrlResourceHandle);
Interlocked.Increment(ref _moddedSkinShpkCount);
if (CharacterGlassShpkName.SequenceEqual(shpkName) && shpk != _modelRenderer.DefaultCharacterGlassShaderPackage)
if (_moddedCharacterGlassShpkMaterials.TryAdd(mtrlResourceHandle))
Interlocked.Increment(ref _moddedCharacterGlassShpkCount);
} }
private void OnResourceHandleDestructor(Structs.ResourceHandle* handle) private void OnResourceHandleDestructor(Structs.ResourceHandle* handle)
{ {
if (_moddedSkinShpkMaterials.TryRemove((nint)handle)) _skinState.TryRemoveMaterial(handle);
Interlocked.Decrement(ref _moddedSkinShpkCount); _irisState.TryRemoveMaterial(handle);
_characterGlassState.TryRemoveMaterial(handle);
if (_moddedCharacterGlassShpkMaterials.TryRemove((nint)handle)) _characterTransparencyState.TryRemoveMaterial(handle);
Interlocked.Decrement(ref _moddedCharacterGlassShpkCount); _characterTattooState.TryRemoveMaterial(handle);
_characterOcclusionState.TryRemoveMaterial(handle);
_hairMaskState.TryRemoveMaterial(handle);
} }
private ModdedShaderPackageState? GetStateForHuman(MaterialResourceHandle* mtrlResource)
=> mtrlResource == null ? null : GetStateForHuman(mtrlResource->ShpkNameSpan);
private ModdedShaderPackageState? GetStateForHuman(ReadOnlySpan<byte> shpkName)
{
if (SkinShpkName.SequenceEqual(shpkName))
return _skinState;
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private uint GetTotalMaterialCountForHuman()
=> _skinState.MaterialCount;
private ModdedShaderPackageState? GetStateForModelRenderer(MaterialResourceHandle* mtrlResource)
=> mtrlResource == null ? null : GetStateForModelRenderer(mtrlResource->ShpkNameSpan);
private ModdedShaderPackageState? GetStateForModelRenderer(ReadOnlySpan<byte> shpkName)
{
if (IrisShpkName.SequenceEqual(shpkName))
return _irisState;
if (CharacterGlassShpkName.SequenceEqual(shpkName))
return _characterGlassState;
if (CharacterTransparencyShpkName.SequenceEqual(shpkName))
return _characterTransparencyState;
if (CharacterTattooShpkName.SequenceEqual(shpkName))
return _characterTattooState;
if (CharacterOcclusionShpkName.SequenceEqual(shpkName))
return _characterOcclusionState;
if (HairMaskShpkName.SequenceEqual(shpkName))
return _hairMaskState;
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private uint GetTotalMaterialCountForModelRenderer()
=> _irisState.MaterialCount + _characterGlassState.MaterialCount + _characterTransparencyState.MaterialCount + _characterTattooState.MaterialCount + _characterOcclusionState.MaterialCount + _hairMaskState.MaterialCount;
private nint OnRenderHumanMaterial(CharacterBase* human, CSModelRenderer.OnRenderMaterialParams* param) 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 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 || GetTotalMaterialCountForHuman() == 0)
return _humanOnRenderMaterialHook.Original(human, param); return _humanOnRenderMaterialHook.Original(human, param);
var material = param->Model->Materials[param->MaterialIndex]; var material = param->Model->Materials[param->MaterialIndex];
var mtrlResource = material->MaterialResourceHandle; var mtrlResource = material->MaterialResourceHandle;
if (!IsMaterialWithShpk(mtrlResource, SkinShpkName)) var shpkState = GetStateForHuman(mtrlResource);
if (shpkState == null)
return _humanOnRenderMaterialHook.Original(human, param); return _humanOnRenderMaterialHook.Original(human, param);
Interlocked.Increment(ref _skinSlowPathCallDelta); shpkState.IncrementSlowPathCallDelta();
// 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 frame rate 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 (shpkState)
{ {
var shpkReference = shpkState.ShaderPackageReference;
try try
{ {
_utility.Address->SkinShpkResource = (Structs.ResourceHandle*)mtrlResource->ShaderPackageResourceHandle; *shpkReference = mtrlResource->ShaderPackageResourceHandle;
return _humanOnRenderMaterialHook.Original(human, param); return _humanOnRenderMaterialHook.Original(human, param);
} }
finally finally
{ {
_utility.Address->SkinShpkResource = (Structs.ResourceHandle*)_utility.DefaultSkinShpkResource; *shpkReference = shpkState.DefaultShaderPackage;
} }
} }
} }
@ -158,27 +248,91 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
CSModelRenderer.OnRenderModelParams* param, Material* material, uint materialIndex) 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 we don't have any on-screen instances of modded characterglass.shpk, we don't need the slow path at all.
if (!Enabled || _moddedCharacterGlassShpkCount == 0) if (!Enabled || GetTotalMaterialCountForModelRenderer() == 0)
return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex); return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex);
var mtrlResource = material->MaterialResourceHandle; var mtrlResource = material->MaterialResourceHandle;
if (!IsMaterialWithShpk(mtrlResource, CharacterGlassShpkName)) var shpkState = GetStateForModelRenderer(mtrlResource);
if (shpkState == null)
return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex); return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex);
Interlocked.Increment(ref _characterGlassSlowPathCallDelta); shpkState.IncrementSlowPathCallDelta();
// Same performance considerations as above. // Same performance considerations as above.
lock (_characterGlassLock) lock (shpkState)
{ {
var shpkReference = shpkState.ShaderPackageReference;
try try
{ {
*_modelRenderer.CharacterGlassShaderPackage = mtrlResource->ShaderPackageResourceHandle; *shpkReference = mtrlResource->ShaderPackageResourceHandle;
return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex); return _modelRendererOnRenderMaterialHook.Original(modelRenderer, outFlags, param, material, materialIndex);
} }
finally finally
{ {
*_modelRenderer.CharacterGlassShaderPackage = _modelRenderer.DefaultCharacterGlassShaderPackage; *shpkReference = shpkState.DefaultShaderPackage;
} }
} }
} }
private sealed class ModdedShaderPackageState(ShaderPackageReferenceGetter referenceGetter, DefaultShaderPackageGetter defaultGetter)
{
// MaterialResourceHandle set
private readonly ConcurrentSet<nint> _materials = new();
// ConcurrentDictionary.Count uses a lock in its current implementation.
private uint _materialCount = 0;
private ulong _slowPathCallDelta = 0;
public uint MaterialCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _materialCount;
}
public ShaderPackageResourceHandle** ShaderPackageReference
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => referenceGetter();
}
public ShaderPackageResourceHandle* DefaultShaderPackage
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => defaultGetter();
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void TryAddMaterial(nint mtrlResourceHandle)
{
if (_materials.TryAdd(mtrlResourceHandle))
Interlocked.Increment(ref _materialCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void TryRemoveMaterial(Structs.ResourceHandle* handle)
{
if (_materials.TryRemove((nint)handle))
Interlocked.Decrement(ref _materialCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void ClearMaterials()
{
_materials.Clear();
_materialCount = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void IncrementSlowPathCallDelta()
=> Interlocked.Increment(ref _slowPathCallDelta);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public ulong GetAndResetSlowPathCallDelta()
=> Interlocked.Exchange(ref _slowPathCallDelta, 0);
}
private delegate ShaderPackageResourceHandle* DefaultShaderPackageGetter();
private delegate ShaderPackageResourceHandle** ShaderPackageReferenceGetter();
} }

View file

@ -9,6 +9,13 @@ public unsafe class ModelRenderer : IDisposable, IRequiredService
{ {
public bool Ready { get; private set; } public bool Ready { get; private set; }
public ShaderPackageResourceHandle** IrisShaderPackage
=> Manager.Instance() switch
{
null => null,
var renderManager => &renderManager->ModelRenderer.IrisShaderPackage,
};
public ShaderPackageResourceHandle** CharacterGlassShaderPackage public ShaderPackageResourceHandle** CharacterGlassShaderPackage
=> Manager.Instance() switch => Manager.Instance() switch
{ {
@ -16,8 +23,46 @@ public unsafe class ModelRenderer : IDisposable, IRequiredService
var renderManager => &renderManager->ModelRenderer.CharacterGlassShaderPackage, var renderManager => &renderManager->ModelRenderer.CharacterGlassShaderPackage,
}; };
public ShaderPackageResourceHandle** CharacterTransparencyShaderPackage
=> Manager.Instance() switch
{
null => null,
var renderManager => &renderManager->ModelRenderer.CharacterTransparencyShaderPackage,
};
public ShaderPackageResourceHandle** CharacterTattooShaderPackage
=> Manager.Instance() switch
{
null => null,
var renderManager => &renderManager->ModelRenderer.CharacterTattooShaderPackage,
};
public ShaderPackageResourceHandle** CharacterOcclusionShaderPackage
=> Manager.Instance() switch
{
null => null,
var renderManager => &renderManager->ModelRenderer.CharacterOcclusionShaderPackage,
};
public ShaderPackageResourceHandle** HairMaskShaderPackage
=> Manager.Instance() switch
{
null => null,
var renderManager => &renderManager->ModelRenderer.HairMaskShaderPackage,
};
public ShaderPackageResourceHandle* DefaultIrisShaderPackage { get; private set; }
public ShaderPackageResourceHandle* DefaultCharacterGlassShaderPackage { get; private set; } public ShaderPackageResourceHandle* DefaultCharacterGlassShaderPackage { get; private set; }
public ShaderPackageResourceHandle* DefaultCharacterTransparencyShaderPackage { get; private set; }
public ShaderPackageResourceHandle* DefaultCharacterTattooShaderPackage { get; private set; }
public ShaderPackageResourceHandle* DefaultCharacterOcclusionShaderPackage { get; private set; }
public ShaderPackageResourceHandle* DefaultHairMaskShaderPackage { get; private set; }
private readonly IFramework _framework; private readonly IFramework _framework;
public ModelRenderer(IFramework framework) public ModelRenderer(IFramework framework)
@ -36,12 +81,42 @@ public unsafe class ModelRenderer : IDisposable, IRequiredService
var anyMissing = false; var anyMissing = false;
if (DefaultIrisShaderPackage == null)
{
DefaultIrisShaderPackage = *IrisShaderPackage;
anyMissing |= DefaultIrisShaderPackage == null;
}
if (DefaultCharacterGlassShaderPackage == null) if (DefaultCharacterGlassShaderPackage == null)
{ {
DefaultCharacterGlassShaderPackage = *CharacterGlassShaderPackage; DefaultCharacterGlassShaderPackage = *CharacterGlassShaderPackage;
anyMissing |= DefaultCharacterGlassShaderPackage == null; anyMissing |= DefaultCharacterGlassShaderPackage == null;
} }
if (DefaultCharacterTransparencyShaderPackage == null)
{
DefaultCharacterTransparencyShaderPackage = *CharacterTransparencyShaderPackage;
anyMissing |= DefaultCharacterTransparencyShaderPackage == null;
}
if (DefaultCharacterTattooShaderPackage == null)
{
DefaultCharacterTattooShaderPackage = *CharacterTattooShaderPackage;
anyMissing |= DefaultCharacterTattooShaderPackage == null;
}
if (DefaultCharacterOcclusionShaderPackage == null)
{
DefaultCharacterOcclusionShaderPackage = *CharacterOcclusionShaderPackage;
anyMissing |= DefaultCharacterOcclusionShaderPackage == null;
}
if (DefaultHairMaskShaderPackage == null)
{
DefaultHairMaskShaderPackage = *HairMaskShaderPackage;
anyMissing |= DefaultHairMaskShaderPackage == null;
}
if (anyMissing) if (anyMissing)
return; return;
@ -55,7 +130,12 @@ public unsafe class ModelRenderer : IDisposable, IRequiredService
if (!Ready) if (!Ready)
return; return;
*HairMaskShaderPackage = DefaultHairMaskShaderPackage;
*CharacterOcclusionShaderPackage = DefaultCharacterOcclusionShaderPackage;
*CharacterTattooShaderPackage = DefaultCharacterTattooShaderPackage;
*CharacterTransparencyShaderPackage = DefaultCharacterTransparencyShaderPackage;
*CharacterGlassShaderPackage = DefaultCharacterGlassShaderPackage; *CharacterGlassShaderPackage = DefaultCharacterGlassShaderPackage;
*IrisShaderPackage = DefaultIrisShaderPackage;
} }
public void Dispose() public void Dispose()

View file

@ -177,6 +177,8 @@ public class DebugTab : Window, ITab, IUiService
ImGui.NewLine(); ImGui.NewLine();
DrawDebugCharacterUtility(); DrawDebugCharacterUtility();
ImGui.NewLine(); ImGui.NewLine();
DrawShaderReplacementFixer();
ImGui.NewLine();
DrawData(); DrawData();
ImGui.NewLine(); ImGui.NewLine();
DrawResourceProblems(); DrawResourceProblems();
@ -711,27 +713,6 @@ public class DebugTab : Window, ITab, IUiService
if (!ImGui.CollapsingHeader("Character Utility")) if (!ImGui.CollapsingHeader("Character Utility"))
return; return;
var enableShaderReplacementFixer = _shaderReplacementFixer.Enabled;
if (ImGui.Checkbox("Enable Shader Replacement Fixer", ref enableShaderReplacementFixer))
_shaderReplacementFixer.Enabled = enableShaderReplacementFixer;
if (enableShaderReplacementFixer)
{
ImGui.SameLine();
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
var slowPathCallDeltas = _shaderReplacementFixer.GetAndResetSlowPathCallDeltas();
ImGui.SameLine();
ImGui.TextUnformatted($"\u0394 Slow-Path Calls for skin.shpk: {slowPathCallDeltas.Skin}");
ImGui.SameLine();
ImGui.TextUnformatted($"characterglass.shpk: {slowPathCallDeltas.CharacterGlass}");
ImGui.SameLine();
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
ImGui.SameLine();
ImGui.TextUnformatted($"Materials with Modded skin.shpk: {_shaderReplacementFixer.ModdedSkinShpkCount}");
ImGui.SameLine();
ImGui.TextUnformatted($"characterglass.shpk: {_shaderReplacementFixer.ModdedCharacterGlassShpkCount}");
}
using var table = Table("##CharacterUtility", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, using var table = Table("##CharacterUtility", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
-Vector2.UnitX); -Vector2.UnitX);
if (!table) if (!table)
@ -786,6 +767,80 @@ public class DebugTab : Window, ITab, IUiService
} }
} }
private void DrawShaderReplacementFixer()
{
if (!ImGui.CollapsingHeader("Shader Replacement Fixer"))
return;
var enableShaderReplacementFixer = _shaderReplacementFixer.Enabled;
if (ImGui.Checkbox("Enable Shader Replacement Fixer", ref enableShaderReplacementFixer))
_shaderReplacementFixer.Enabled = enableShaderReplacementFixer;
if (!enableShaderReplacementFixer)
return;
using var table = Table("##ShaderReplacementFixer", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
-Vector2.UnitX);
if (!table)
return;
var slowPathCallDeltas = _shaderReplacementFixer.GetAndResetSlowPathCallDeltas();
ImGui.TableSetupColumn("Shader Package Name", ImGuiTableColumnFlags.WidthStretch, 0.6f);
ImGui.TableSetupColumn("Materials with Modded ShPk", ImGuiTableColumnFlags.WidthStretch, 0.2f);
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();
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterGlassShpkCount}");
ImGui.TableNextColumn();
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterGlass}");
ImGui.TableNextColumn();
ImGui.TextUnformatted("charactertransparency.shpk");
ImGui.TableNextColumn();
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterTransparencyShpkCount}");
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.TableNextColumn();
ImGui.TextUnformatted("characterocclusion.shpk");
ImGui.TableNextColumn();
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedCharacterOcclusionShpkCount}");
ImGui.TableNextColumn();
ImGui.TextUnformatted($"{slowPathCallDeltas.CharacterOcclusion}");
ImGui.TableNextColumn();
ImGui.TextUnformatted("hairmask.shpk");
ImGui.TableNextColumn();
ImGui.TextUnformatted($"{_shaderReplacementFixer.ModdedHairMaskShpkCount}");
ImGui.TableNextColumn();
ImGui.TextUnformatted($"{slowPathCallDeltas.HairMask}");
}
/// <summary> Draw information about the resident resource files. </summary> /// <summary> Draw information about the resident resource files. </summary>
private unsafe void DrawDebugResidentResources() private unsafe void DrawDebugResidentResources()
{ {