diff --git a/Penumbra/Interop/ProcessThreadApi.cs b/Penumbra/Interop/ProcessThreadApi.cs new file mode 100644 index 00000000..5ee213d9 --- /dev/null +++ b/Penumbra/Interop/ProcessThreadApi.cs @@ -0,0 +1,7 @@ +namespace Penumbra.Interop; + +public static partial class ProcessThreadApi +{ + [LibraryImport("kernel32.dll")] + public static partial uint GetCurrentThreadId(); +} diff --git a/Penumbra/UI/ResourceWatcher/Record.cs b/Penumbra/UI/ResourceWatcher/Record.cs index b8730750..ba718bc9 100644 --- a/Penumbra/UI/ResourceWatcher/Record.cs +++ b/Penumbra/UI/ResourceWatcher/Record.cs @@ -1,6 +1,7 @@ using OtterGui.Classes; using Penumbra.Collections; using Penumbra.Enums; +using Penumbra.Interop; using Penumbra.Interop.Structs; using Penumbra.String; using Penumbra.String.Classes; @@ -34,6 +35,7 @@ internal unsafe struct Record public OptionalBool ReturnValue; public OptionalBool CustomLoad; public LoadState LoadState; + public uint OsThreadId; public static Record CreateRequest(CiByteString path, bool sync) @@ -54,6 +56,7 @@ internal unsafe struct Record AssociatedGameObject = string.Empty, LoadState = LoadState.None, Crc64 = 0, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; public static Record CreateRequest(CiByteString path, bool sync, FullPath fullPath, ResolveData resolve) @@ -74,6 +77,7 @@ internal unsafe struct Record AssociatedGameObject = string.Empty, LoadState = LoadState.None, Crc64 = fullPath.Crc64, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; public static Record CreateDefaultLoad(CiByteString path, ResourceHandle* handle, ModCollection collection, string associatedGameObject) @@ -96,6 +100,7 @@ internal unsafe struct Record AssociatedGameObject = associatedGameObject, LoadState = handle->LoadState, Crc64 = 0, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; } @@ -118,6 +123,7 @@ internal unsafe struct Record AssociatedGameObject = associatedGameObject, LoadState = handle->LoadState, Crc64 = path.Crc64, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; public static Record CreateDestruction(ResourceHandle* handle) @@ -140,6 +146,7 @@ internal unsafe struct Record AssociatedGameObject = string.Empty, LoadState = handle->LoadState, Crc64 = 0, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; } @@ -161,6 +168,7 @@ internal unsafe struct Record AssociatedGameObject = string.Empty, LoadState = handle->LoadState, Crc64 = 0, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; public static Record CreateResourceComplete(CiByteString path, ResourceHandle* handle, Utf8GamePath originalPath, ReadOnlySpan additionalData) @@ -181,6 +189,7 @@ internal unsafe struct Record AssociatedGameObject = string.Empty, LoadState = handle->LoadState, Crc64 = 0, + OsThreadId = ProcessThreadApi.GetCurrentThreadId(), }; private static CiByteString CombinedPath(CiByteString path, ReadOnlySpan additionalData) diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs index a58d74d1..009da842 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs @@ -30,7 +30,8 @@ internal sealed class ResourceWatcherTable : Table new LoadStateColumn { Label = "State" }, new RefCountColumn { Label = "#Ref" }, new DateColumn { Label = "Time" }, - new Crc64Column { Label = "Crc64" } + new Crc64Column { Label = "Crc64" }, + new OsThreadColumn { Label = "TID" } ) { } @@ -453,4 +454,19 @@ internal sealed class ResourceWatcherTable : Table public override int Compare(Record lhs, Record rhs) => lhs.RefCount.CompareTo(rhs.RefCount); } + + private sealed class OsThreadColumn : ColumnString + { + public override float Width + => 60 * UiHelpers.Scale; + + public override string ToName(Record item) + => item.OsThreadId.ToString(); + + public override void DrawColumn(Record item, int _) + => ImGuiUtil.RightAlign(ToName(item)); + + public override int Compare(Record lhs, Record rhs) + => lhs.OsThreadId.CompareTo(rhs.OsThreadId); + } }