mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Add IncRef mode and debug DecRef check.
This commit is contained in:
parent
d445df256b
commit
1d7829593e
3 changed files with 46 additions and 16 deletions
|
|
@ -14,6 +14,10 @@ namespace Penumbra.Interop.Loader;
|
||||||
|
|
||||||
public unsafe partial class ResourceLoader
|
public unsafe partial class ResourceLoader
|
||||||
{
|
{
|
||||||
|
// If in debug mode, this logs any resource at refcount 0 that gets decremented again, and skips the decrement instead.
|
||||||
|
private delegate byte ResourceHandleDecRef( ResourceHandle* handle );
|
||||||
|
private readonly Hook<ResourceHandleDecRef> _decRefHook;
|
||||||
|
|
||||||
public delegate IntPtr ResourceHandleDestructor( ResourceHandle* handle );
|
public delegate IntPtr ResourceHandleDestructor( ResourceHandle* handle );
|
||||||
|
|
||||||
[Signature( "48 89 5C 24 ?? 57 48 83 EC ?? 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 89 01 B8",
|
[Signature( "48 89 5C 24 ?? 57 48 83 EC ?? 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 89 01 B8",
|
||||||
|
|
@ -24,8 +28,8 @@ public unsafe partial class ResourceLoader
|
||||||
{
|
{
|
||||||
if( handle != null )
|
if( handle != null )
|
||||||
{
|
{
|
||||||
PluginLog.Information( "[ResourceLoader] Destructing Resource Handle {Path:l} at 0x{Address:X} (Refcount {Refcount}) .", handle->FileName,
|
PluginLog.Information( "[ResourceLoader] Destructing Resource Handle {Path:l} at 0x{Address:X} (Refcount {Refcount}) .",
|
||||||
( ulong )handle, handle->RefCount );
|
handle->FileName, ( ulong )handle, handle->RefCount );
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResourceHandleDestructorHook!.Original( handle );
|
return ResourceHandleDestructorHook!.Original( handle );
|
||||||
|
|
@ -54,11 +58,13 @@ public unsafe partial class ResourceLoader
|
||||||
|
|
||||||
public void EnableDebug()
|
public void EnableDebug()
|
||||||
{
|
{
|
||||||
|
_decRefHook?.Enable();
|
||||||
ResourceLoaded += AddModifiedDebugInfo;
|
ResourceLoaded += AddModifiedDebugInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisableDebug()
|
public void DisableDebug()
|
||||||
{
|
{
|
||||||
|
_decRefHook?.Disable();
|
||||||
ResourceLoaded -= AddModifiedDebugInfo;
|
ResourceLoaded -= AddModifiedDebugInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,6 +213,18 @@ public unsafe partial class ResourceLoader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent resource management weirdness.
|
||||||
|
private byte ResourceHandleDecRefDetour( ResourceHandle* handle )
|
||||||
|
{
|
||||||
|
if( handle->RefCount != 0 )
|
||||||
|
{
|
||||||
|
return _decRefHook!.Original( handle );
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginLog.Error( $"Caught decrease of Reference Counter for {handle->FileName} at 0x{( ulong )handle:X} below 0." );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Logging functions for EnableFullLogging.
|
// Logging functions for EnableFullLogging.
|
||||||
private static void LogPath( Utf8GamePath path, bool synchronous )
|
private static void LogPath( Utf8GamePath path, bool synchronous )
|
||||||
=> PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" );
|
=> PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" );
|
||||||
|
|
|
||||||
|
|
@ -84,18 +84,6 @@ public unsafe partial class ResourceLoader
|
||||||
|
|
||||||
ResourceRequested?.Invoke( gamePath, isSync );
|
ResourceRequested?.Invoke( gamePath, isSync );
|
||||||
|
|
||||||
// Force metadata tables to load synchronously and not be able to be replaced.
|
|
||||||
switch( *resourceType )
|
|
||||||
{
|
|
||||||
case ResourceType.Eqp:
|
|
||||||
case ResourceType.Gmp:
|
|
||||||
case ResourceType.Eqdp:
|
|
||||||
case ResourceType.Cmp:
|
|
||||||
case ResourceType.Est:
|
|
||||||
PluginLog.Verbose( "Forced resource {gamePath} to be loaded synchronously.", gamePath );
|
|
||||||
return CallOriginalHandler( true, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk );
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no replacements are being made, we still want to be able to trigger the event.
|
// If no replacements are being made, we still want to be able to trigger the event.
|
||||||
var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash );
|
var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash );
|
||||||
PathResolved?.Invoke( gamePath, *resourceType, resolvedPath, data );
|
PathResolved?.Invoke( gamePath, *resourceType, resolvedPath, data );
|
||||||
|
|
@ -126,7 +114,7 @@ public unsafe partial class ResourceLoader
|
||||||
// Try all resolve path subscribers or use the default replacer.
|
// Try all resolve path subscribers or use the default replacer.
|
||||||
private (FullPath?, object?) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash )
|
private (FullPath?, object?) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash )
|
||||||
{
|
{
|
||||||
if( !DoReplacements )
|
if( !DoReplacements || IsInIncRef )
|
||||||
{
|
{
|
||||||
return ( null, null );
|
return ( null, null );
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +247,7 @@ public unsafe partial class ResourceLoader
|
||||||
ResourceHandleDestructorHook?.Dispose();
|
ResourceHandleDestructorHook?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ComputeHash( Utf8String path, GetResourceParameters* pGetResParams )
|
private static int ComputeHash( Utf8String path, GetResourceParameters* pGetResParams )
|
||||||
{
|
{
|
||||||
if( pGetResParams == null || !pGetResParams->IsPartialRead )
|
if( pGetResParams == null || !pGetResParams->IsPartialRead )
|
||||||
{
|
{
|
||||||
|
|
@ -276,4 +264,21 @@ public unsafe partial class ResourceLoader
|
||||||
Utf8String.FromStringUnsafe( pGetResParams->SegmentLength.ToString( "x" ), true )
|
Utf8String.FromStringUnsafe( pGetResParams->SegmentLength.ToString( "x" ), true )
|
||||||
).Crc32;
|
).Crc32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A resource with ref count 0 that gets incremented goes through GetResourceAsync again.
|
||||||
|
// This means, that if the path determined from that is different than the resources path,
|
||||||
|
// a different resource gets loaded or incremented, while the IncRef'd resource stays at 0.
|
||||||
|
// This causes some problems and is hopefully prevented with this.
|
||||||
|
public bool IsInIncRef { get; private set; } = false;
|
||||||
|
private readonly Hook< ResourceHandleDestructor > _incRefHook;
|
||||||
|
|
||||||
|
private IntPtr ResourceHandleIncRefDetour( ResourceHandle* handle )
|
||||||
|
{
|
||||||
|
var tmp = IsInIncRef;
|
||||||
|
IsInIncRef = true;
|
||||||
|
var ret = _incRefHook.Original( handle );
|
||||||
|
IsInIncRef = tmp;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
|
|
@ -82,6 +83,7 @@ public unsafe partial class ResourceLoader : IDisposable
|
||||||
ReadSqPackHook.Enable();
|
ReadSqPackHook.Enable();
|
||||||
GetResourceSyncHook.Enable();
|
GetResourceSyncHook.Enable();
|
||||||
GetResourceAsyncHook.Enable();
|
GetResourceAsyncHook.Enable();
|
||||||
|
_incRefHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisableHooks()
|
public void DisableHooks()
|
||||||
|
|
@ -95,11 +97,16 @@ public unsafe partial class ResourceLoader : IDisposable
|
||||||
ReadSqPackHook.Disable();
|
ReadSqPackHook.Disable();
|
||||||
GetResourceSyncHook.Disable();
|
GetResourceSyncHook.Disable();
|
||||||
GetResourceAsyncHook.Disable();
|
GetResourceAsyncHook.Disable();
|
||||||
|
_incRefHook.Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceLoader( Penumbra _ )
|
public ResourceLoader( Penumbra _ )
|
||||||
{
|
{
|
||||||
SignatureHelper.Initialise( this );
|
SignatureHelper.Initialise( this );
|
||||||
|
_decRefHook = new Hook< ResourceHandleDecRef >( ( IntPtr )FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle.fpDecRef,
|
||||||
|
ResourceHandleDecRefDetour );
|
||||||
|
_incRefHook = new Hook< ResourceHandleDestructor >(
|
||||||
|
( IntPtr )FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle.fpIncRef, ResourceHandleIncRefDetour );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event fired whenever a resource is requested.
|
// Event fired whenever a resource is requested.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue