Add performance monitor in debug compilations.

This commit is contained in:
Ottermandias 2023-01-04 14:44:33 +01:00
parent f2997102c7
commit 2f7b6e3d55
18 changed files with 204 additions and 233 deletions

View file

@ -77,13 +77,11 @@ public unsafe partial class CharacterUtility
// Set the currently stored data of this resource to new values.
private void SetResourceInternal( IntPtr data, int length )
{
TimingManager.StartTimer( TimingType.SetResource );
if( Ready )
{
var resource = Penumbra.CharacterUtility.Address->Resource( GlobalIndex );
resource->SetData( data, length );
}
TimingManager.StopTimer( TimingType.SetResource );
}
// Reset the currently stored data of this resource to its default values.
@ -135,7 +133,6 @@ public unsafe partial class CharacterUtility
{
if( !Disposed )
{
TimingManager.StartTimer( TimingType.SetResource );
var list = List._entries;
var wasCurrent = ReferenceEquals( this, list.First?.Value );
list.Remove( this );
@ -162,7 +159,6 @@ public unsafe partial class CharacterUtility
}
Disposed = true;
TimingManager.StopTimer( TimingType.SetResource );
}
}
}

View file

@ -10,6 +10,7 @@ using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
namespace Penumbra.Interop.Loader;
@ -71,12 +72,13 @@ public unsafe partial class ResourceLoader
private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
ResolveData resolverInfo )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.DebugTimes );
if( manipulatedPath == null || manipulatedPath.Value.Crc64 == 0 )
{
return;
}
TimingManager.StartTimer( TimingType.DebugTimes );
// Got some incomprehensible null-dereference exceptions here when hot-reloading penumbra.
try
{
@ -97,7 +99,6 @@ public unsafe partial class ResourceLoader
{
Penumbra.Log.Error( e.ToString() );
}
TimingManager.StopTimer( TimingType.DebugTimes );
}
// Find a key in a StdMap.
@ -204,10 +205,16 @@ public unsafe partial class ResourceLoader
// Only used when the Replaced Resources Tab in the Debug tab is open.
public void UpdateDebugInfo()
{
TimingManager.StartTimer( TimingType.DebugTimes );
using var performance = Penumbra.Performance.Measure( PerformanceType.DebugTimes );
for( var i = 0; i < _debugList.Count; ++i )
{
var data = _debugList.Values[ i ];
var data = _debugList.Values[ i ];
if( data.OriginalPath.Path == null )
{
_debugList.RemoveAt( i-- );
continue;
}
var regularResource = FindResource( data.Category, data.Extension, ( uint )data.OriginalPath.Path.Crc32 );
var modifiedResource = FindResource( data.Category, data.Extension, ( uint )data.ManipulatedPath.InternalName.Crc32 );
if( modifiedResource == null )
@ -223,7 +230,6 @@ public unsafe partial class ResourceLoader
};
}
}
TimingManager.StopTimer( TimingType.DebugTimes );
}
// Prevent resource management weirdness.

View file

@ -11,6 +11,7 @@ using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
using FileMode = Penumbra.Interop.Structs.FileMode;
using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle;
@ -81,43 +82,36 @@ public unsafe partial class ResourceLoader
internal ResourceHandle* GetResourceHandler( bool isSync, ResourceManager* resourceManager, ResourceCategory* categoryId,
ResourceType* resourceType, int* resourceHash, byte* path, GetResourceParameters* pGetResParams, bool isUnk )
{
TimingManager.StartTimer( TimingType.GetResourceHandler );
ResourceHandle* ret = null;
using var performance = Penumbra.Performance.Measure( PerformanceType.GetResourceHandler );
ResourceHandle* ret;
if( !Utf8GamePath.FromPointer( path, out var gamePath ) )
{
Penumbra.Log.Error( "Could not create GamePath from resource path." );
ret = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
return CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
}
else
CompareHash( ComputeHash( gamePath.Path, pGetResParams ), *resourceHash, gamePath );
ResourceRequested?.Invoke( gamePath, isSync );
// If no replacements are being made, we still want to be able to trigger the event.
var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash );
PathResolved?.Invoke( gamePath, *resourceType, resolvedPath ?? ( gamePath.IsRooted() ? new FullPath( gamePath ) : null ), data );
if( resolvedPath == null )
{
CompareHash( ComputeHash( gamePath.Path, pGetResParams ), *resourceHash, gamePath );
ResourceRequested?.Invoke( gamePath, isSync );
// If no replacements are being made, we still want to be able to trigger the event.
var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash );
PathResolved?.Invoke( gamePath, *resourceType, resolvedPath ?? ( gamePath.IsRooted() ? new FullPath( gamePath ) : null ), data );
if( resolvedPath == null )
{
var retUnmodified =
CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )retUnmodified, gamePath, null, data );
ret = retUnmodified;
}
else
{
// Replace the hash and path with the correct one for the replacement.
*resourceHash = ComputeHash( resolvedPath.Value.InternalName, pGetResParams );
path = resolvedPath.Value.InternalName.Path;
ret = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )ret, gamePath, resolvedPath.Value, data );
}
ret = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )ret, gamePath, null, data );
return ret;
}
TimingManager.StopTimer( TimingType.GetResourceHandler );
// Replace the hash and path with the correct one for the replacement.
*resourceHash = ComputeHash( resolvedPath.Value.InternalName, pGetResParams );
path = resolvedPath.Value.InternalName.Path;
ret = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )ret, gamePath, resolvedPath.Value, data );
return ret;
}
@ -174,50 +168,51 @@ public unsafe partial class ResourceLoader
private byte ReadSqPackDetour( ResourceManager* resourceManager, SeFileDescriptor* fileDescriptor, int priority, bool isSync )
{
TimingManager.StartTimer( TimingType.ReadSqPack );
byte ret = 0;
using var performance = Penumbra.Performance.Measure( PerformanceType.ReadSqPack );
if( !DoReplacements )
{
ret = ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
return ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
}
else if( fileDescriptor == null || fileDescriptor->ResourceHandle == null )
if( fileDescriptor == null || fileDescriptor->ResourceHandle == null )
{
Penumbra.Log.Error( "Failure to load file from SqPack: invalid File Descriptor." );
ret = ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
return ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
}
else if( !Utf8GamePath.FromSpan( fileDescriptor->ResourceHandle->FileNameSpan(), out var gamePath, false ) || gamePath.Length == 0 )
if( !Utf8GamePath.FromSpan( fileDescriptor->ResourceHandle->FileNameSpan(), out var gamePath, false ) || gamePath.Length == 0 )
{
ret = ReadSqPackHook.Original(resourceManager, fileDescriptor, priority, isSync);
return ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
}
// Paths starting with a '|' are handled separately to allow for special treatment.
// They are expected to also have a closing '|'.
else if( ResourceLoadCustomization == null || gamePath.Path[ 0 ] != ( byte )'|' )
if( ResourceLoadCustomization == null || gamePath.Path[ 0 ] != ( byte )'|' )
{
ret = DefaultLoadResource( gamePath.Path, resourceManager, fileDescriptor, priority, isSync );
}
else
{
// Split the path into the special-treatment part (between the first and second '|')
// and the actual path.
var split = gamePath.Path.Split( ( byte )'|', 3, false );
fileDescriptor->ResourceHandle->FileNameData = split[2].Path;
fileDescriptor->ResourceHandle->FileNameLength = split[2].Length;
var funcFound = fileDescriptor->ResourceHandle->Category != ResourceCategory.Ui
&& ResourceLoadCustomization.GetInvocationList()
.Any( f => ( ( ResourceLoadCustomizationDelegate )f )
.Invoke( split[1], split[2], resourceManager, fileDescriptor, priority, isSync, out ret ) );
if( !funcFound )
{
ret = DefaultLoadResource( split[2], resourceManager, fileDescriptor, priority, isSync );
}
// Return original resource handle path so that they can be loaded separately.
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
return DefaultLoadResource( gamePath.Path, resourceManager, fileDescriptor, priority, isSync );
}
TimingManager.StopTimer( TimingType.ReadSqPack );
// Split the path into the special-treatment part (between the first and second '|')
// and the actual path.
byte ret = 0;
var split = gamePath.Path.Split( ( byte )'|', 3, false );
fileDescriptor->ResourceHandle->FileNameData = split[ 2 ].Path;
fileDescriptor->ResourceHandle->FileNameLength = split[ 2 ].Length;
var funcFound = fileDescriptor->ResourceHandle->Category != ResourceCategory.Ui
&& ResourceLoadCustomization.GetInvocationList()
.Any( f => ( ( ResourceLoadCustomizationDelegate )f )
.Invoke( split[ 1 ], split[ 2 ], resourceManager, fileDescriptor, priority, isSync, out ret ) );
if( !funcFound )
{
ret = DefaultLoadResource( split[ 2 ], resourceManager, fileDescriptor, priority, isSync );
}
// Return original resource handle path so that they can be loaded separately.
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
return ret;
}

View file

@ -6,6 +6,7 @@ using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
namespace Penumbra.Interop.Resolver;
@ -110,7 +111,8 @@ public unsafe partial class PathResolver
private IntPtr LoadCharacterSoundDetour( IntPtr character, int unk1, int unk2, IntPtr unk3, ulong unk4, int unk5, int unk6, ulong unk7 )
{
var last = _characterSoundData;
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadSound );
var last = _characterSoundData;
_characterSoundData = IdentifyCollection( ( GameObject* )character, true );
var ret = _loadCharacterSoundHook.Original( character, unk1, unk2, unk3, unk4, unk5, unk6, unk7 );
_characterSoundData = last;
@ -126,9 +128,9 @@ public unsafe partial class PathResolver
private ulong LoadTimelineResourcesDetour( IntPtr timeline )
{
TimingManager.StartTimer( TimingType.TimelineResources );
ulong ret;
var old = _animationLoadData;
using var performance = Penumbra.Performance.Measure( PerformanceType.TimelineResources );
ulong ret;
var old = _animationLoadData;
try
{
if( timeline != IntPtr.Zero )
@ -152,8 +154,6 @@ public unsafe partial class PathResolver
}
_animationLoadData = old;
TimingManager.StopTimer( TimingType.TimelineResources );
return ret;
}
@ -167,7 +167,8 @@ public unsafe partial class PathResolver
private void CharacterBaseLoadAnimationDetour( IntPtr drawObject )
{
var last = _animationLoadData;
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadCharacterBaseAnimation );
var last = _animationLoadData;
_animationLoadData = _drawObjectState.LastCreatedCollection.Valid
? _drawObjectState.LastCreatedCollection
: FindParent( drawObject, out var collection ) != null
@ -186,8 +187,9 @@ public unsafe partial class PathResolver
private void LoadSomePapDetour( IntPtr a1, int a2, IntPtr a3, int a4 )
{
var timelinePtr = a1 + 0x50;
var last = _animationLoadData;
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadPap );
var timelinePtr = a1 + 0x50;
var last = _animationLoadData;
if( timelinePtr != IntPtr.Zero )
{
var actorIdx = ( int )( *( *( ulong** )timelinePtr + 1 ) >> 3 );
@ -207,7 +209,8 @@ public unsafe partial class PathResolver
private void SomeActionLoadDetour( IntPtr gameObject )
{
var last = _animationLoadData;
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadAction );
var last = _animationLoadData;
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject, true );
_someActionLoadHook.Original( gameObject );
_animationLoadData = last;
@ -248,8 +251,8 @@ public unsafe partial class PathResolver
private IntPtr LoadCharacterVfxDetour( byte* vfxPath, VfxParams* vfxParams, byte unk1, byte unk2, float unk3, int unk4 )
{
TimingManager.StartTimer( TimingType.LoadCharacterVfx );
var last = _animationLoadData;
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadCharacterVfx );
var last = _animationLoadData;
if( vfxParams != null && vfxParams->GameObjectId != unchecked( ( uint )-1 ) )
{
var obj = vfxParams->GameObjectType switch
@ -267,6 +270,7 @@ public unsafe partial class PathResolver
{
_animationLoadData = ResolveData.Invalid;
}
var ret = _loadCharacterVfxHook.Original( vfxPath, vfxParams, unk1, unk2, unk3, unk4 );
#if DEBUG
var path = new ByteString( vfxPath );
@ -274,7 +278,6 @@ public unsafe partial class PathResolver
$"Load Character VFX: {path} {vfxParams->GameObjectId:X} {vfxParams->TargetCount} {unk1} {unk2} {unk3} {unk4} -> {ret:X} {_animationLoadData.ModCollection.Name} {_animationLoadData.AssociatedGameObject} {last.ModCollection.Name} {last.AssociatedGameObject}" );
#endif
_animationLoadData = last;
TimingManager.StopTimer( TimingType.LoadCharacterVfx );
return ret;
}
@ -285,8 +288,8 @@ public unsafe partial class PathResolver
private IntPtr LoadAreaVfxDetour( uint vfxId, float* pos, GameObject* caster, float unk1, float unk2, byte unk3 )
{
TimingManager.StartTimer( TimingType.LoadAreaVfx );
var last = _animationLoadData;
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadAreaVfx );
var last = _animationLoadData;
if( caster != null )
{
_animationLoadData = IdentifyCollection( caster, true );
@ -302,7 +305,6 @@ public unsafe partial class PathResolver
$"Load Area VFX: {vfxId}, {pos[ 0 ]} {pos[ 1 ]} {pos[ 2 ]} {( caster != null ? new ByteString( caster->GetName() ).ToString() : "Unknown" )} {unk1} {unk2} {unk3} -> {ret:X} {_animationLoadData.ModCollection.Name} {_animationLoadData.AssociatedGameObject} {last.ModCollection.Name} {last.AssociatedGameObject}" );
#endif
_animationLoadData = last;
TimingManager.StopTimer( TimingType.LoadAreaVfx );
return ret;
}

View file

@ -10,6 +10,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes;
using Penumbra.GameData.Enums;
using Penumbra.String.Classes;
using Penumbra.Util;
namespace Penumbra.Interop.Resolver;
@ -138,7 +139,8 @@ public unsafe partial class PathResolver
private IntPtr CharacterBaseCreateDetour( uint a, IntPtr b, IntPtr c, byte d )
{
TimingManager.StartTimer( TimingType.CharacterBaseCreate );
using var performance = Penumbra.Performance.Measure( PerformanceType.CharacterBaseCreate );
var meta = DisposableContainer.Empty;
if( LastGameObject != null )
{
@ -171,7 +173,7 @@ public unsafe partial class PathResolver
{
meta.Dispose();
}
TimingManager.StopTimer( TimingType.CharacterBaseCreate );
return ret;
}

View file

@ -8,6 +8,7 @@ using OtterGui;
using Penumbra.Collections;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.Util;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
@ -19,12 +20,13 @@ public unsafe partial class PathResolver
// Identify the correct collection for a GameObject by index and name.
public static ResolveData IdentifyCollection( GameObject* gameObject, bool useCache )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.IdentifyCollection );
if( gameObject == null )
{
return new ResolveData( Penumbra.CollectionManager.Default );
}
TimingManager.StartTimer( TimingType.IdentifyCollection );
try
{
if( useCache && IdentifiedCache.TryGetValue( gameObject, out var data ) )
@ -69,35 +71,25 @@ public unsafe partial class PathResolver
Penumbra.Log.Error( $"Error identifying collection:\n{e}" );
return Penumbra.CollectionManager.Default.ToResolveData( gameObject );
}
finally
{
TimingManager.StopTimer( TimingType.IdentifyCollection );
}
}
// Get the collection applying to the current player character
// or the default collection if no player exists.
public static ModCollection PlayerCollection()
{
TimingManager.StartTimer( TimingType.IdentifyCollection );
var gameObject = ( GameObject* )Dalamud.Objects.GetObjectAddress( 0 );
ModCollection ret;
using var performance = Penumbra.Performance.Measure( PerformanceType.IdentifyCollection );
var gameObject = ( GameObject* )Dalamud.Objects.GetObjectAddress( 0 );
if( gameObject == null )
{
ret = Penumbra.CollectionManager.ByType( CollectionType.Yourself )
return Penumbra.CollectionManager.ByType( CollectionType.Yourself )
?? Penumbra.CollectionManager.Default;
}
else
{
var player = Penumbra.Actors.GetCurrentPlayer();
ret = CollectionByIdentifier( player )
?? CheckYourself( player, gameObject )
?? CollectionByAttributes( gameObject )
?? Penumbra.CollectionManager.Default;
}
TimingManager.StopTimer( TimingType.IdentifyCollection );
return ret;
var player = Penumbra.Actors.GetCurrentPlayer();
return CollectionByIdentifier( player )
?? CheckYourself( player, gameObject )
?? CollectionByAttributes( gameObject )
?? Penumbra.CollectionManager.Default;
}
// Check both temporary and permanent character collections. Temporary first.

View file

@ -6,6 +6,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.Util;
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
using static Penumbra.GameData.Enums.GenderRace;
@ -97,6 +98,7 @@ public unsafe partial class PathResolver
{
return;
}
using var performance = Penumbra.Performance.Measure( PerformanceType.UpdateModels );
var collection = GetResolveData( drawObject );
using var eqp = collection.ModCollection.TemporarilySetEqpFile();
@ -134,7 +136,7 @@ public unsafe partial class PathResolver
{
return;
}
using var performance = Penumbra.Performance.Measure( PerformanceType.GetEqp );
var resolveData = GetResolveData( drawObject );
using var eqp = resolveData.ModCollection.TemporarilySetEqpFile();
_getEqpIndirectHook.Original( drawObject );
@ -150,6 +152,7 @@ public unsafe partial class PathResolver
private byte SetupVisorDetour( IntPtr drawObject, ushort modelId, byte visorState )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.SetupVisor );
var resolveData = GetResolveData( drawObject );
using var gmp = resolveData.ModCollection.TemporarilySetGmpFile();
return _setupVisorHook.Original( drawObject, modelId, visorState );
@ -169,6 +172,7 @@ public unsafe partial class PathResolver
}
else
{
using var performance = Penumbra.Performance.Measure( PerformanceType.SetupCharacter );
var resolveData = GetResolveData( drawObject );
using var cmp = resolveData.ModCollection.TemporarilySetCmpFile();
_rspSetupCharacterHook.Original( drawObject, unk2, unk3, unk4, unk5 );
@ -184,6 +188,7 @@ public unsafe partial class PathResolver
private bool ChangeCustomizeDetour( IntPtr human, IntPtr data, byte skipEquipment )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.ChangeCustomize );
_inChangeCustomize = true;
var resolveData = GetResolveData( human );
using var cmp = resolveData.ModCollection.TemporarilySetCmpFile();

View file

@ -95,7 +95,6 @@ public unsafe partial class PathResolver
// Special handling for paths so that we do not store non-owned temporary strings in the dictionary.
public void SetCollection( IntPtr gameObject, ByteString path, ModCollection collection )
{
TimingManager.StartTimer( TimingType.SetPathCollection );
if( _pathCollections.ContainsKey( path ) || path.IsOwned )
{
_pathCollections[ path ] = collection.ToResolveData( gameObject );
@ -104,7 +103,6 @@ public unsafe partial class PathResolver
{
_pathCollections[ path.Clone() ] = collection.ToResolveData( gameObject );
}
TimingManager.StopTimer( TimingType.SetPathCollection );
}
}
}

View file

@ -11,6 +11,7 @@ using Penumbra.Interop.Loader;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
namespace Penumbra.Interop.Resolver;
@ -113,9 +114,7 @@ public unsafe partial class PathResolver
case ResourceType.Avfx:
if( handle->FileSize == 0 )
{
TimingManager.StartTimer( TimingType.AddSubfile );
_subFileCollection[ ( IntPtr )handle ] = resolveData;
TimingManager.StopTimer( TimingType.AddSubfile );
}
break;
@ -128,9 +127,7 @@ public unsafe partial class PathResolver
{
case ResourceType.Mtrl:
case ResourceType.Avfx:
TimingManager.StartTimer( TimingType.AddSubfile );
_subFileCollection.TryRemove( ( IntPtr )handle, out _ );
TimingManager.StopTimer( TimingType.AddSubfile );
break;
}
}
@ -167,6 +164,7 @@ public unsafe partial class PathResolver
private byte LoadMtrlTexDetour( IntPtr mtrlResourceHandle )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadTextures );
_mtrlData = LoadFileHelper( mtrlResourceHandle );
var ret = _loadMtrlTexHook.Original( mtrlResourceHandle );
_mtrlData = ResolveData.Invalid;
@ -179,6 +177,7 @@ public unsafe partial class PathResolver
private byte LoadMtrlShpkDetour( IntPtr mtrlResourceHandle )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadShaders );
_mtrlData = LoadFileHelper( mtrlResourceHandle );
var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle );
_mtrlData = ResolveData.Invalid;
@ -204,6 +203,7 @@ public unsafe partial class PathResolver
private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 )
{
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadApricotResources );
_avfxData = LoadFileHelper( handle );
var ret = _apricotResourceLoadHook.Original( handle, unk1, unk2 );
_avfxData = ResolveData.Invalid;

View file

@ -10,6 +10,7 @@ using Penumbra.GameData.Enums;
using Penumbra.Interop.Loader;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
namespace Penumbra.Interop.Resolver;
@ -48,7 +49,7 @@ public partial class PathResolver : IDisposable
// The modified resolver that handles game path resolving.
private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, ResolveData) data )
{
TimingManager.StartTimer( TimingType.CharacterResolver );
using var performance = Penumbra.Performance.Measure( PerformanceType.CharacterResolver );
// Check if the path was marked for a specific collection,
// or if it is a file loaded by a material, and if we are currently in a material load,
// or if it is a face decal path and the current mod collection is set.
@ -72,7 +73,6 @@ public partial class PathResolver : IDisposable
// We also need to handle defaulted materials against a non-default collection.
var path = resolved == null ? gamePath.Path : resolved.Value.InternalName;
SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
TimingManager.StopTimer( TimingType.CharacterResolver );
return true;
}