mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Add performance monitor in debug compilations.
This commit is contained in:
parent
f2997102c7
commit
2f7b6e3d55
18 changed files with 204 additions and 233 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 1b61ce894209ebabe6cf2d8b7c120f5a6edbe86a
|
||||
Subproject commit 6a1e25a6c6aea6165c0a38771953e35550a1f9cf
|
||||
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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,15 +82,14 @@ 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 );
|
||||
|
||||
|
|
@ -100,13 +100,10 @@ public unsafe partial class ResourceLoader
|
|||
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;
|
||||
ret = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
|
||||
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )ret, gamePath, null, data );
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Replace the hash and path with the correct one for the replacement.
|
||||
*resourceHash = ComputeHash( resolvedPath.Value.InternalName, pGetResParams );
|
||||
|
|
@ -115,9 +112,6 @@ public unsafe partial class ResourceLoader
|
|||
ret = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
|
||||
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )ret, gamePath, resolvedPath.Value, data );
|
||||
|
||||
}
|
||||
}
|
||||
TimingManager.StopTimer( TimingType.GetResourceHandler );
|
||||
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 );
|
||||
return 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.
|
||||
byte ret = 0;
|
||||
var split = gamePath.Path.Split( ( byte )'|', 3, false );
|
||||
fileDescriptor->ResourceHandle->FileNameData = split[2].Path;
|
||||
fileDescriptor->ResourceHandle->FileNameLength = split[2].Length;
|
||||
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 ) );
|
||||
.Invoke( split[ 1 ], split[ 2 ], resourceManager, fileDescriptor, priority, isSync, out ret ) );
|
||||
|
||||
if( !funcFound )
|
||||
{
|
||||
ret = DefaultLoadResource( split[2], resourceManager, fileDescriptor, priority, isSync );
|
||||
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;
|
||||
}
|
||||
|
||||
TimingManager.StopTimer( TimingType.ReadSqPack );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,6 +111,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private IntPtr LoadCharacterSoundDetour( IntPtr character, int unk1, int unk2, IntPtr unk3, ulong unk4, int unk5, int unk6, ulong unk7 )
|
||||
{
|
||||
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 );
|
||||
|
|
@ -126,7 +128,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private ulong LoadTimelineResourcesDetour( IntPtr timeline )
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.TimelineResources );
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.TimelineResources );
|
||||
ulong ret;
|
||||
var old = _animationLoadData;
|
||||
try
|
||||
|
|
@ -152,8 +154,6 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
|
||||
_animationLoadData = old;
|
||||
|
||||
TimingManager.StopTimer( TimingType.TimelineResources );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +167,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private void CharacterBaseLoadAnimationDetour( IntPtr drawObject )
|
||||
{
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadCharacterBaseAnimation );
|
||||
var last = _animationLoadData;
|
||||
_animationLoadData = _drawObjectState.LastCreatedCollection.Valid
|
||||
? _drawObjectState.LastCreatedCollection
|
||||
|
|
@ -186,6 +187,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private void LoadSomePapDetour( IntPtr a1, int a2, IntPtr a3, int a4 )
|
||||
{
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadPap );
|
||||
var timelinePtr = a1 + 0x50;
|
||||
var last = _animationLoadData;
|
||||
if( timelinePtr != IntPtr.Zero )
|
||||
|
|
@ -207,6 +209,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private void SomeActionLoadDetour( IntPtr gameObject )
|
||||
{
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadAction );
|
||||
var last = _animationLoadData;
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject, true );
|
||||
_someActionLoadHook.Original( gameObject );
|
||||
|
|
@ -248,7 +251,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private IntPtr LoadCharacterVfxDetour( byte* vfxPath, VfxParams* vfxParams, byte unk1, byte unk2, float unk3, int unk4 )
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.LoadCharacterVfx );
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadCharacterVfx );
|
||||
var last = _animationLoadData;
|
||||
if( vfxParams != null && vfxParams->GameObjectId != unchecked( ( uint )-1 ) )
|
||||
{
|
||||
|
|
@ -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,7 +288,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
private IntPtr LoadAreaVfxDetour( uint vfxId, float* pos, GameObject* caster, float unk1, float unk2, byte unk3 )
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.LoadAreaVfx );
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.LoadAreaVfx );
|
||||
var last = _animationLoadData;
|
||||
if( caster != null )
|
||||
{
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,36 +71,26 @@ 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 );
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.IdentifyCollection );
|
||||
var gameObject = ( GameObject* )Dalamud.Objects.GetObjectAddress( 0 );
|
||||
ModCollection ret;
|
||||
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 )
|
||||
return CollectionByIdentifier( player )
|
||||
?? CheckYourself( player, gameObject )
|
||||
?? CollectionByAttributes( gameObject )
|
||||
?? Penumbra.CollectionManager.Default;
|
||||
}
|
||||
TimingManager.StopTimer( TimingType.IdentifyCollection );
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Check both temporary and permanent character collections. Temporary first.
|
||||
private static ModCollection? CollectionByIdentifier( ActorIdentifier identifier )
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ public class Penumbra : IDalamudPlugin
|
|||
public static IGamePathParser GamePathParser { get; private set; } = null!;
|
||||
public static StainManager StainManager { get; private set; } = null!;
|
||||
public static ItemData ItemData { get; private set; } = null!;
|
||||
public static PerformanceTracker< PerformanceType > Performance { get; private set; } = null!;
|
||||
|
||||
public static readonly List< Exception > ImcExceptions = new();
|
||||
|
||||
|
|
@ -86,10 +87,8 @@ public class Penumbra : IDalamudPlugin
|
|||
{
|
||||
try
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.TotalTime );
|
||||
TimingManager.StartTimer( TimingType.LaunchTime );
|
||||
|
||||
Dalamud.Initialize( pluginInterface );
|
||||
Performance = new PerformanceTracker< PerformanceType >( Dalamud.Framework );
|
||||
Log = new Logger();
|
||||
DevPenumbraExists = CheckDevPluginPenumbra();
|
||||
IsNotInstalledPenumbra = CheckIsNotInstalled();
|
||||
|
|
@ -165,7 +164,6 @@ public class Penumbra : IDalamudPlugin
|
|||
{
|
||||
ResidentResources.Reload();
|
||||
}
|
||||
TimingManager.StopTimer( TimingType.LaunchTime );
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -308,7 +306,7 @@ public class Penumbra : IDalamudPlugin
|
|||
ResourceLogger?.Dispose();
|
||||
ResourceLoader?.Dispose();
|
||||
CharacterUtility?.Dispose();
|
||||
TimingManager.StopAllTimers();
|
||||
Performance?.Dispose();
|
||||
}
|
||||
|
||||
// Collect all relevant files for penumbra configuration.
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Penumbra;
|
||||
|
||||
public enum TimingType
|
||||
{
|
||||
TotalTime,
|
||||
LaunchTime,
|
||||
DebugTimes,
|
||||
UiMainWindow,
|
||||
UiAdvancedWindow,
|
||||
GetResourceHandler,
|
||||
ReadSqPack,
|
||||
CharacterResolver,
|
||||
IdentifyCollection,
|
||||
CharacterBaseCreate,
|
||||
TimelineResources,
|
||||
LoadCharacterVfx,
|
||||
LoadAreaVfx,
|
||||
AddSubfile,
|
||||
SetResource,
|
||||
SetPathCollection,
|
||||
}
|
||||
|
||||
public static class TimingManager
|
||||
{
|
||||
public static readonly IReadOnlyList< ThreadLocal< Stopwatch > > StopWatches =
|
||||
#if DEBUG
|
||||
Enum.GetValues< TimingType >().Select( e => new ThreadLocal< Stopwatch >( () => new Stopwatch(), true ) ).ToArray();
|
||||
#else
|
||||
Array.Empty<ThreadLocal<Stopwatch>>();
|
||||
#endif
|
||||
|
||||
[Conditional( "DEBUG" )]
|
||||
public static void StartTimer( TimingType timingType )
|
||||
{
|
||||
var stopWatch = StopWatches[ ( int )timingType ].Value;
|
||||
stopWatch!.Start();
|
||||
}
|
||||
|
||||
[Conditional( "DEBUG" )]
|
||||
public static void StopTimer( TimingType timingType )
|
||||
{
|
||||
var stopWatch = StopWatches[ ( int )timingType ].Value;
|
||||
stopWatch!.Stop();
|
||||
}
|
||||
|
||||
[Conditional( "DEBUG" )]
|
||||
public static void StopAllTimers()
|
||||
{
|
||||
foreach( var threadWatch in StopWatches )
|
||||
{
|
||||
foreach( var stopWatch in threadWatch.Values )
|
||||
{
|
||||
stopWatch.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "DEBUG" )]
|
||||
public static void CreateTimingReport()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sb = new StringBuilder( 1024 );
|
||||
sb.AppendLine( "```" );
|
||||
foreach( var type in Enum.GetValues< TimingType >() )
|
||||
{
|
||||
var watches = StopWatches[ ( int )type ];
|
||||
var timeSum = watches.Values.Sum( w => w.ElapsedMilliseconds );
|
||||
|
||||
sb.AppendLine( $"{type,-20} - {timeSum,8} ms over {watches.Values.Count,2} Thread(s)" );
|
||||
}
|
||||
|
||||
sb.AppendLine( "```" );
|
||||
|
||||
ImGui.SetClipboardText( sb.ToString() );
|
||||
}
|
||||
catch( Exception ex )
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not create timing report:\n{ex}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,8 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
|
||||
public override void PreDraw()
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.UiAdvancedWindow );
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.UiAdvancedWindow );
|
||||
|
||||
var sb = new StringBuilder( 256 );
|
||||
|
||||
var redirections = 0;
|
||||
|
|
@ -126,7 +127,6 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
_allowReduplicate = redirections != _editor.AvailableFiles.Count || _editor.MissingFiles.Count > 0;
|
||||
sb.Append( WindowBaseLabel );
|
||||
WindowName = sb.ToString();
|
||||
TimingManager.StopTimer( TimingType.UiAdvancedWindow );
|
||||
}
|
||||
|
||||
public override void OnClose()
|
||||
|
|
@ -137,7 +137,8 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
|
||||
public override void Draw()
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.UiAdvancedWindow );
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.UiAdvancedWindow );
|
||||
|
||||
using var tabBar = ImRaii.TabBar( "##tabs" );
|
||||
if( !tabBar )
|
||||
{
|
||||
|
|
@ -155,7 +156,6 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
_materialTab.Draw();
|
||||
DrawTextureTab();
|
||||
_swapWindow.DrawItemSwapPanel();
|
||||
TimingManager.StopTimer( TimingType.UiAdvancedWindow );
|
||||
}
|
||||
|
||||
// A row of three buttonSizes and a help marker that can be used for material suffix changing.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
|
@ -15,6 +16,7 @@ using Penumbra.Interop.Loader;
|
|||
using Penumbra.Interop.Resolver;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.String;
|
||||
using Penumbra.Util;
|
||||
using CharacterUtility = Penumbra.Interop.CharacterUtility;
|
||||
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||
|
||||
|
|
@ -55,6 +57,7 @@ public partial class ConfigWindow
|
|||
}
|
||||
|
||||
DrawDebugTabGeneral();
|
||||
DrawPerformanceTab();
|
||||
ImGui.NewLine();
|
||||
DrawDebugTabReplacedResources();
|
||||
ImGui.NewLine();
|
||||
|
|
@ -109,6 +112,18 @@ public partial class ConfigWindow
|
|||
PrintValue( "Web Server Enabled", ( _window._penumbra.WebServer != null ).ToString() );
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void DrawPerformanceTab()
|
||||
{
|
||||
ImGui.NewLine();
|
||||
if( !ImGui.CollapsingHeader( "Performance" ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Penumbra.Performance.Draw( "##performance", "Enable Performance Tracking", PerformanceTypeExtensions.ToName );
|
||||
}
|
||||
|
||||
// Draw all resources currently replaced by Penumbra and (if existing) the resources they replace.
|
||||
// Resources are collected by iterating through the
|
||||
private static unsafe void DrawDebugTabReplacedResources()
|
||||
|
|
@ -249,7 +264,7 @@ public partial class ConfigWindow
|
|||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted( collection.ModCollection.Name );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted( collection.AssociatedGameObject.ToString("X") );
|
||||
ImGui.TextUnformatted( collection.AssociatedGameObject.ToString( "X" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -370,14 +370,6 @@ public partial class ConfigWindow
|
|||
{
|
||||
_window._penumbra.ForceChangelogOpen();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
ImGui.SetCursorPos( new Vector2( xPos, 5 * ImGui.GetFrameHeightWithSpacing() ) );
|
||||
if( ImGui.Button( "Copy Timings", new Vector2( width, 0 ) ) )
|
||||
{
|
||||
TimingManager.CreateTimingReport();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using ImGuiNET;
|
|||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.UI.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI;
|
||||
|
||||
|
|
@ -52,10 +53,10 @@ public sealed partial class ConfigWindow : Window, IDisposable
|
|||
|
||||
public override void Draw()
|
||||
{
|
||||
using var performance = Penumbra.Performance.Measure( PerformanceType.UiMainWindow );
|
||||
|
||||
try
|
||||
{
|
||||
TimingManager.StartTimer( TimingType.UiMainWindow );
|
||||
|
||||
if( Penumbra.ImcExceptions.Count > 0 )
|
||||
{
|
||||
DrawProblemWindow( $"There were {Penumbra.ImcExceptions.Count} errors while trying to load IMC files from the game data.\n"
|
||||
|
|
@ -103,7 +104,6 @@ public sealed partial class ConfigWindow : Window, IDisposable
|
|||
{
|
||||
Penumbra.Log.Error( $"Exception thrown during UI Render:\n{e}" );
|
||||
}
|
||||
TimingManager.StopTimer( TimingType.UiMainWindow );
|
||||
}
|
||||
|
||||
private static void DrawProblemWindow( string text, bool withExceptions )
|
||||
|
|
|
|||
60
Penumbra/Util/PerformanceType.cs
Normal file
60
Penumbra/Util/PerformanceType.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
namespace Penumbra.Util;
|
||||
|
||||
public enum PerformanceType
|
||||
{
|
||||
UiMainWindow,
|
||||
UiAdvancedWindow,
|
||||
CharacterResolver,
|
||||
IdentifyCollection,
|
||||
GetResourceHandler,
|
||||
ReadSqPack,
|
||||
CharacterBaseCreate,
|
||||
TimelineResources,
|
||||
LoadCharacterVfx,
|
||||
LoadAreaVfx,
|
||||
LoadSound,
|
||||
LoadAction,
|
||||
LoadCharacterBaseAnimation,
|
||||
LoadPap,
|
||||
LoadTextures,
|
||||
LoadShaders,
|
||||
LoadApricotResources,
|
||||
UpdateModels,
|
||||
GetEqp,
|
||||
SetupVisor,
|
||||
SetupCharacter,
|
||||
ChangeCustomize,
|
||||
DebugTimes,
|
||||
}
|
||||
|
||||
public static class PerformanceTypeExtensions
|
||||
{
|
||||
public static string ToName( this PerformanceType type )
|
||||
=> type switch
|
||||
{
|
||||
PerformanceType.UiMainWindow => "Main Interface Drawing",
|
||||
PerformanceType.UiAdvancedWindow => "Advanced Window Drawing",
|
||||
PerformanceType.GetResourceHandler => "GetResource Hook",
|
||||
PerformanceType.ReadSqPack => "ReadSqPack Hook",
|
||||
PerformanceType.CharacterResolver => "Resolving Characters",
|
||||
PerformanceType.IdentifyCollection => "Identifying Collections",
|
||||
PerformanceType.CharacterBaseCreate => "CharacterBaseCreate Hook",
|
||||
PerformanceType.TimelineResources => "LoadTimelineResources Hook",
|
||||
PerformanceType.LoadCharacterVfx => "LoadCharacterVfx Hook",
|
||||
PerformanceType.LoadAreaVfx => "LoadAreaVfx Hook",
|
||||
PerformanceType.LoadTextures => "LoadTextures Hook",
|
||||
PerformanceType.LoadShaders => "LoadShaders Hook",
|
||||
PerformanceType.LoadApricotResources => "LoadApricotFiles Hook",
|
||||
PerformanceType.UpdateModels => "UpdateModels Hook",
|
||||
PerformanceType.GetEqp => "GetEqp Hook",
|
||||
PerformanceType.SetupVisor => "SetupVisor Hook",
|
||||
PerformanceType.SetupCharacter => "SetupCharacter Hook",
|
||||
PerformanceType.ChangeCustomize => "ChangeCustomize Hook",
|
||||
PerformanceType.LoadSound => "LoadSound Hook",
|
||||
PerformanceType.LoadCharacterBaseAnimation => "LoadCharacterAnimation Hook",
|
||||
PerformanceType.LoadPap => "LoadPap Hook",
|
||||
PerformanceType.LoadAction => "LoadAction Hook",
|
||||
PerformanceType.DebugTimes => "Debug Tracking",
|
||||
_ => $"Unknown {( int )type}",
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue