mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 12:44:19 +01:00
Update ShaderReplacementFixer for 7.0
This commit is contained in:
parent
1284037554
commit
41d271213e
3 changed files with 359 additions and 70 deletions
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
@ -102,54 +147,99 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
||||||
if (shpk == null)
|
if (shpk == null)
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
*CharacterGlassShaderPackage = DefaultCharacterGlassShaderPackage;
|
*HairMaskShaderPackage = DefaultHairMaskShaderPackage;
|
||||||
|
*CharacterOcclusionShaderPackage = DefaultCharacterOcclusionShaderPackage;
|
||||||
|
*CharacterTattooShaderPackage = DefaultCharacterTattooShaderPackage;
|
||||||
|
*CharacterTransparencyShaderPackage = DefaultCharacterTransparencyShaderPackage;
|
||||||
|
*CharacterGlassShaderPackage = DefaultCharacterGlassShaderPackage;
|
||||||
|
*IrisShaderPackage = DefaultIrisShaderPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue