From 5067ab2bb297f68aaaecff7e4255a60431eaeb04 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 18 Sep 2023 18:18:23 +0200 Subject: [PATCH] Add load state to resource watcher. --- Penumbra/Interop/Structs/ResourceHandle.cs | 12 +++ Penumbra/UI/ResourceWatcher/Record.cs | 6 ++ .../ResourceWatcher/ResourceWatcherTable.cs | 89 +++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/Penumbra/Interop/Structs/ResourceHandle.cs b/Penumbra/Interop/Structs/ResourceHandle.cs index 1b78e857..3cceb949 100644 --- a/Penumbra/Interop/Structs/ResourceHandle.cs +++ b/Penumbra/Interop/Structs/ResourceHandle.cs @@ -34,6 +34,15 @@ public unsafe struct ShaderPackageResourceHandle public ShaderPackage* ShaderPackage; } +public enum LoadState : byte +{ + Success = 0x07, + Async = 0x03, + Failure = 0x09, + FailedSubResource = 0x0A, + None = 0xFF, +} + [StructLayout(LayoutKind.Explicit)] public unsafe struct ResourceHandle { @@ -99,6 +108,9 @@ public unsafe struct ResourceHandle [FieldOffset(0x58)] public int FileNameLength; + [FieldOffset(0xA9)] + public LoadState LoadState; + [FieldOffset(0xAC)] public uint RefCount; diff --git a/Penumbra/UI/ResourceWatcher/Record.cs b/Penumbra/UI/ResourceWatcher/Record.cs index 1a25d722..0fc51f26 100644 --- a/Penumbra/UI/ResourceWatcher/Record.cs +++ b/Penumbra/UI/ResourceWatcher/Record.cs @@ -30,6 +30,7 @@ internal unsafe struct Record public OptionalBool Synchronously; public OptionalBool ReturnValue; public OptionalBool CustomLoad; + public LoadState LoadState; public static Record CreateRequest(ByteString path, bool sync) => new() @@ -47,6 +48,7 @@ internal unsafe struct Record ReturnValue = OptionalBool.Null, CustomLoad = OptionalBool.Null, AssociatedGameObject = string.Empty, + LoadState = LoadState.None, }; public static Record CreateDefaultLoad(ByteString path, ResourceHandle* handle, ModCollection collection, string associatedGameObject) @@ -67,6 +69,7 @@ internal unsafe struct Record ReturnValue = OptionalBool.Null, CustomLoad = false, AssociatedGameObject = associatedGameObject, + LoadState = handle->LoadState, }; } @@ -87,6 +90,7 @@ internal unsafe struct Record ReturnValue = OptionalBool.Null, CustomLoad = true, AssociatedGameObject = associatedGameObject, + LoadState = handle->LoadState, }; public static Record CreateDestruction(ResourceHandle* handle) @@ -107,6 +111,7 @@ internal unsafe struct Record ReturnValue = OptionalBool.Null, CustomLoad = OptionalBool.Null, AssociatedGameObject = string.Empty, + LoadState = handle->LoadState, }; } @@ -126,5 +131,6 @@ internal unsafe struct Record ReturnValue = ret, CustomLoad = custom, AssociatedGameObject = string.Empty, + LoadState = handle->LoadState, }; } diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs index 3789baf4..89dd42bb 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs @@ -5,7 +5,9 @@ using OtterGui.Classes; using OtterGui.Raii; using OtterGui.Table; using Penumbra.Enums; +using Penumbra.Interop.Structs; using Penumbra.String; +using Penumbra.UI.Classes; namespace Penumbra.UI.ResourceWatcher; @@ -24,6 +26,7 @@ internal sealed class ResourceWatcherTable : Table new ResourceCategoryColumn(config) { Label = "Category" }, new ResourceTypeColumn(config) { Label = "Type" }, new HandleColumn { Label = "Resource" }, + new LoadStateColumn { Label = "State" }, new RefCountColumn { Label = "#Ref" }, new DateColumn { Label = "Time" } ) @@ -241,6 +244,92 @@ internal sealed class ResourceWatcherTable : Table } } + private sealed class LoadStateColumn : ColumnFlags + { + public override float Width + => 50 * UiHelpers.Scale; + + [Flags] + public enum LoadStateFlag : byte + { + Success = 0x01, + Async = 0x02, + Failed = 0x04, + FailedSub = 0x08, + Unknown = 0x10, + None = 0xFF, + } + + protected override string[] Names + => new[] + { + "Loaded", + "Loading", + "Failed", + "Dependency Failed", + "Unknown", + "None", + }; + + public LoadStateColumn() + { + AllFlags = Enum.GetValues().Aggregate((v, f) => v | f); + _filterValue = AllFlags; + } + + private LoadStateFlag _filterValue; + + public override LoadStateFlag FilterValue + => _filterValue; + + protected override void SetValue(LoadStateFlag value, bool enable) + { + if (enable) + _filterValue |= value; + else + _filterValue &= ~value; + } + + public override bool FilterFunc(Record item) + => item.LoadState switch + { + LoadState.None => FilterValue.HasFlag(LoadStateFlag.None), + LoadState.Success => FilterValue.HasFlag(LoadStateFlag.Success), + LoadState.Async => FilterValue.HasFlag(LoadStateFlag.Async), + LoadState.Failure => FilterValue.HasFlag(LoadStateFlag.Failed), + LoadState.FailedSubResource => FilterValue.HasFlag(LoadStateFlag.FailedSub), + _ => FilterValue.HasFlag(LoadStateFlag.Unknown), + }; + + public override void DrawColumn(Record item, int _) + { + if (item.LoadState == LoadState.None) + return; + + var (icon, color, tt) = item.LoadState switch + { + LoadState.Success => (FontAwesomeIcon.CheckCircle, ColorId.IncreasedMetaValue.Value(), + $"Successfully loaded ({(byte)item.LoadState})."), + LoadState.Async => (FontAwesomeIcon.Clock, ColorId.FolderLine.Value(), $"Loading asynchronously ({(byte)item.LoadState})."), + LoadState.Failure => (FontAwesomeIcon.Times, ColorId.DecreasedMetaValue.Value(), + $"Failed to load ({(byte)item.LoadState})."), + LoadState.FailedSubResource => (FontAwesomeIcon.ExclamationCircle, ColorId.DecreasedMetaValue.Value(), + $"Dependencies failed to load ({(byte)item.LoadState})."), + _ => (FontAwesomeIcon.QuestionCircle, ColorId.UndefinedMod.Value(), $"Unknown state ({(byte)item.LoadState})."), + }; + using (var font = ImRaii.PushFont(UiBuilder.IconFont)) + { + using var c = ImRaii.PushColor(ImGuiCol.Text, color); + ImGui.TextUnformatted(icon.ToIconString()); + } + + ImGuiUtil.HoverTooltip(tt); + } + + public override int Compare(Record lhs, Record rhs) + => lhs.LoadState.CompareTo(rhs.LoadState); + } + private sealed class HandleColumn : ColumnString { public override float Width