Add handling for cached TMBs.

This commit is contained in:
Ottermandias 2024-12-26 00:06:51 +01:00
parent f679e0ccee
commit b3883c1306
9 changed files with 415 additions and 40 deletions

@ -1 +1 @@
Subproject commit ffc149cc8c169c2c6e838cbd138676f6fe4daeea Subproject commit 19355cfa0ec80e8d5a91de11ecffc49257b37b53

View file

@ -1,6 +1,5 @@
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Mods;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.String.Classes; using Penumbra.String.Classes;
@ -33,5 +32,8 @@ public sealed class ResolvedFileChanged()
{ {
/// <seealso cref="Api.DalamudSubstitutionProvider.OnResolvedFileChange"/> /// <seealso cref="Api.DalamudSubstitutionProvider.OnResolvedFileChange"/>
DalamudSubstitutionProvider = 0, DalamudSubstitutionProvider = 0,
/// <seealso cref="Interop.Services.SchedulerResourceManagementService.OnResolvedFileChange"/>
SchedulerResourceManagementService = 0,
} }
} }

View file

@ -49,6 +49,9 @@ public class GameState : IService
public void RestoreAnimationData(ResolveData old) public void RestoreAnimationData(ResolveData old)
=> _animationLoadData.Value = old; => _animationLoadData.Value = old;
public readonly ThreadLocal<bool> InLoadActionTmb = new(() => false);
public readonly ThreadLocal<bool> SkipTmbCache = new(() => false);
#endregion #endregion
#region Sound Data #region Sound Data

View file

@ -0,0 +1,53 @@
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
using JetBrains.Annotations;
using OtterGui.Services;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
using Penumbra.String;
namespace Penumbra.Interop.Hooks.Animation;
/// <summary> Load a cached TMB resource from SchedulerResourceManagement. </summary>
public sealed unsafe class GetCachedScheduleResource : FastHook<GetCachedScheduleResource.Delegate>
{
private readonly GameState _state;
public GetCachedScheduleResource(HookManager hooks, GameState state)
{
_state = state;
Task = hooks.CreateHook<Delegate>("Get Cached Schedule Resource", Sigs.GetCachedScheduleResource, Detour,
!HookOverrides.Instance.Animation.GetCachedScheduleResource);
}
public delegate SchedulerResource* Delegate(SchedulerResourceManagement* a, ScheduleResourceLoadData* b, byte useMap);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private SchedulerResource* Detour(SchedulerResourceManagement* a, ScheduleResourceLoadData* b, byte c)
{
if (_state.SkipTmbCache.Value)
{
Penumbra.Log.Verbose(
$"[GetCachedScheduleResource] Called with 0x{(ulong)a:X}, {b->Id}, {new CiByteString(b->Path, MetaDataComputation.None)}, {c} from LoadActionTmb with forced skipping of cache, returning NULL.");
return null;
}
var ret = Task.Result.Original(a, b, c);
Penumbra.Log.Excessive(
$"[GetCachedScheduleResource] Called with 0x{(ulong)a:X}, {b->Id}, {new CiByteString(b->Path, MetaDataComputation.None)}, {c}, returning 0x{(ulong)ret:X} ({(ret != null && Resource(ret) != null ? Resource(ret)->FileName().ToString() : "No Path")}).");
return ret;
}
public struct ScheduleResourceLoadData
{
[UsedImplicitly]
public byte* Path;
[UsedImplicitly]
public uint Id;
}
// #TODO: remove when fixed in CS.
public static ResourceHandle* Resource(SchedulerResource* r)
=> ((ResourceHandle**)r)[3];
}

View file

@ -0,0 +1,55 @@
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
using OtterGui.Services;
using Penumbra.GameData;
using Penumbra.Interop.Services;
using Penumbra.String;
namespace Penumbra.Interop.Hooks.Animation;
/// <summary> Load a Action TMB. </summary>
public sealed unsafe class LoadActionTmb : FastHook<LoadActionTmb.Delegate>
{
private readonly GameState _state;
private readonly SchedulerResourceManagementService _scheduler;
public LoadActionTmb(HookManager hooks, GameState state, SchedulerResourceManagementService scheduler)
{
_state = state;
_scheduler = scheduler;
Task = hooks.CreateHook<Delegate>("Load Action TMB", Sigs.LoadActionTmb, Detour, !HookOverrides.Instance.Animation.LoadActionTmb);
}
public delegate SchedulerResource* Delegate(SchedulerResourceManagement* scheduler,
GetCachedScheduleResource.ScheduleResourceLoadData* loadData, nint b, byte c, byte d, byte e);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private SchedulerResource* Detour(SchedulerResourceManagement* scheduler, GetCachedScheduleResource.ScheduleResourceLoadData* loadData,
nint b, byte c, byte d, byte e)
{
_state.InLoadActionTmb.Value = true;
SchedulerResource* ret;
if (ShouldSkipCache(loadData))
{
_state.SkipTmbCache.Value = true;
ret = Task.Result.Original(scheduler, loadData, b, c, d, 1);
Penumbra.Log.Verbose(
$"[LoadActionTMB] Called with 0x{(ulong)scheduler:X}, {loadData->Id}, {new CiByteString(loadData->Path, MetaDataComputation.None)}, 0x{b:X}, {c}, {d}, {e}, forced no-cache use, returned 0x{(ulong)ret:X} ({(ret != null && GetCachedScheduleResource.Resource(ret) != null ? GetCachedScheduleResource.Resource(ret)->FileName().ToString() : "No Path")}).");
_state.SkipTmbCache.Value = false;
}
else
{
ret = Task.Result.Original(scheduler, loadData, b, c, d, e);
Penumbra.Log.Excessive(
$"[LoadActionTMB] Called with 0x{(ulong)scheduler:X}, {loadData->Id}, {new CiByteString(loadData->Path)}, 0x{b:X}, {c}, {d}, {e}, returned 0x{(ulong)ret:X} ({(ret != null && GetCachedScheduleResource.Resource(ret) != null ? GetCachedScheduleResource.Resource(ret)->FileName().ToString() : "No Path")}).");
}
_state.InLoadActionTmb.Value = false;
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private bool ShouldSkipCache(GetCachedScheduleResource.ScheduleResourceLoadData* loadData)
=> _scheduler.Contains(loadData->Id);
}

View file

@ -43,6 +43,8 @@ public class HookOverrides
public bool SomeMountAnimation; public bool SomeMountAnimation;
public bool SomePapLoad; public bool SomePapLoad;
public bool SomeParasolAnimation; public bool SomeParasolAnimation;
public bool GetCachedScheduleResource;
public bool LoadActionTmb;
} }
public struct MetaHooks public struct MetaHooks

View file

@ -0,0 +1,92 @@
using System.Collections.Frozen;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
using Lumina.Excel.Sheets;
using OtterGui.Services;
using Penumbra.Collections;
using Penumbra.Communication;
using Penumbra.GameData;
using Penumbra.Mods.Editor;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.String.Classes;
namespace Penumbra.Interop.Services;
public unsafe class SchedulerResourceManagementService : IService, IDisposable
{
private static readonly CiByteString TmbExtension = new(".tmb"u8, MetaDataComputation.All);
private static readonly CiByteString FolderPrefix = new("chara/action/"u8, MetaDataComputation.All);
private readonly CommunicatorService _communicator;
private readonly FrozenDictionary<CiByteString, uint> _actionTmbs;
private readonly ConcurrentDictionary<uint, CiByteString> _listedTmbIds = [];
public bool Contains(uint tmbId)
=> _listedTmbIds.ContainsKey(tmbId);
public IReadOnlyDictionary<uint, CiByteString> ListedTmbs
=> _listedTmbIds;
public IReadOnlyDictionary<CiByteString, uint> ActionTmbs
=> _actionTmbs;
public SchedulerResourceManagementService(IGameInteropProvider interop, CommunicatorService communicator, IDataManager dataManager)
{
_communicator = communicator;
_actionTmbs = CreateActionTmbs(dataManager);
_communicator.ResolvedFileChanged.Subscribe(OnResolvedFileChange, ResolvedFileChanged.Priority.SchedulerResourceManagementService);
interop.InitializeFromAttributes(this);
}
private void OnResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath gamePath, FullPath oldPath,
FullPath newPath, IMod? mod)
{
switch (type)
{
case ResolvedFileChanged.Type.Added:
CheckFile(gamePath);
return;
case ResolvedFileChanged.Type.FullRecomputeFinished:
foreach (var path in collection.ResolvedFiles.Keys)
CheckFile(path);
return;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void CheckFile(Utf8GamePath gamePath)
{
if (!gamePath.Extension().Equals(TmbExtension))
return;
if (!gamePath.Path.StartsWith(FolderPrefix))
return;
var tmb = gamePath.Path.Substring(FolderPrefix.Length, gamePath.Length - FolderPrefix.Length - TmbExtension.Length).Clone();
if (_actionTmbs.TryGetValue(tmb, out var rowId))
_listedTmbIds[rowId] = tmb;
else
Penumbra.Log.Debug($"Action TMB {gamePath} encountered with no corresponding row ID.");
}
[Signature(Sigs.SchedulerResourceManagementInstance, ScanType = ScanType.StaticAddress)]
public readonly SchedulerResourceManagement** Address = null;
public SchedulerResourceManagement* Scheduler
=> *Address;
public void Dispose()
{
_listedTmbIds.Clear();
_communicator.ResolvedFileChanged.Unsubscribe(OnResolvedFileChange);
}
private static FrozenDictionary<CiByteString, uint> CreateActionTmbs(IDataManager dataManager)
{
var sheet = dataManager.GetExcelSheet<ActionTimeline>();
return sheet.Where(row => !row.Key.IsEmpty).DistinctBy(row => row.Key).ToFrozenDictionary(row => new CiByteString(row.Key, MetaDataComputation.All).Clone(), row => row.RowId);
}
}

View file

@ -69,38 +69,39 @@ public class Diagnostics(ServiceManager provider) : IUiService
public class DebugTab : Window, ITab, IUiService public class DebugTab : Window, ITab, IUiService
{ {
private readonly PerformanceTracker _performance; private readonly PerformanceTracker _performance;
private readonly Configuration _config; private readonly Configuration _config;
private readonly CollectionManager _collectionManager; private readonly CollectionManager _collectionManager;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly ValidityChecker _validityChecker; private readonly ValidityChecker _validityChecker;
private readonly HttpApi _httpApi; private readonly HttpApi _httpApi;
private readonly ActorManager _actors; private readonly ActorManager _actors;
private readonly StainService _stains; private readonly StainService _stains;
private readonly GlobalVariablesDrawer _globalVariablesDrawer; private readonly GlobalVariablesDrawer _globalVariablesDrawer;
private readonly ResourceManagerService _resourceManager; private readonly ResourceManagerService _resourceManager;
private readonly CollectionResolver _collectionResolver; private readonly CollectionResolver _collectionResolver;
private readonly DrawObjectState _drawObjectState; private readonly DrawObjectState _drawObjectState;
private readonly PathState _pathState; private readonly PathState _pathState;
private readonly SubfileHelper _subfileHelper; private readonly SubfileHelper _subfileHelper;
private readonly IdentifiedCollectionCache _identifiedCollectionCache; private readonly IdentifiedCollectionCache _identifiedCollectionCache;
private readonly CutsceneService _cutsceneService; private readonly CutsceneService _cutsceneService;
private readonly ModImportManager _modImporter; private readonly ModImportManager _modImporter;
private readonly ImportPopup _importPopup; private readonly ImportPopup _importPopup;
private readonly FrameworkManager _framework; private readonly FrameworkManager _framework;
private readonly TextureManager _textureManager; private readonly TextureManager _textureManager;
private readonly ShaderReplacementFixer _shaderReplacementFixer; private readonly ShaderReplacementFixer _shaderReplacementFixer;
private readonly RedrawService _redraws; private readonly RedrawService _redraws;
private readonly DictEmote _emotes; private readonly DictEmote _emotes;
private readonly Diagnostics _diagnostics; private readonly Diagnostics _diagnostics;
private readonly ObjectManager _objects; private readonly ObjectManager _objects;
private readonly IClientState _clientState; private readonly IClientState _clientState;
private readonly IDataManager _dataManager; private readonly IDataManager _dataManager;
private readonly IpcTester _ipcTester; private readonly IpcTester _ipcTester;
private readonly CrashHandlerPanel _crashHandlerPanel; private readonly CrashHandlerPanel _crashHandlerPanel;
private readonly TexHeaderDrawer _texHeaderDrawer; private readonly TexHeaderDrawer _texHeaderDrawer;
private readonly HookOverrideDrawer _hookOverrides; private readonly HookOverrideDrawer _hookOverrides;
private readonly RsfService _rsfService; private readonly RsfService _rsfService;
private readonly SchedulerResourceManagementService _schedulerService;
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects, public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects,
IClientState clientState, IDataManager dataManager, IClientState clientState, IDataManager dataManager,
@ -110,7 +111,8 @@ public class DebugTab : Window, ITab, IUiService
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework, CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework,
TextureManager textureManager, ShaderReplacementFixer shaderReplacementFixer, RedrawService redraws, DictEmote emotes, TextureManager textureManager, ShaderReplacementFixer shaderReplacementFixer, RedrawService redraws, DictEmote emotes,
Diagnostics diagnostics, IpcTester ipcTester, CrashHandlerPanel crashHandlerPanel, TexHeaderDrawer texHeaderDrawer, Diagnostics diagnostics, IpcTester ipcTester, CrashHandlerPanel crashHandlerPanel, TexHeaderDrawer texHeaderDrawer,
HookOverrideDrawer hookOverrides, RsfService rsfService, GlobalVariablesDrawer globalVariablesDrawer) HookOverrideDrawer hookOverrides, RsfService rsfService, GlobalVariablesDrawer globalVariablesDrawer,
SchedulerResourceManagementService schedulerService)
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse) : base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
{ {
IsOpen = true; IsOpen = true;
@ -148,6 +150,7 @@ public class DebugTab : Window, ITab, IUiService
_hookOverrides = hookOverrides; _hookOverrides = hookOverrides;
_rsfService = rsfService; _rsfService = rsfService;
_globalVariablesDrawer = globalVariablesDrawer; _globalVariablesDrawer = globalVariablesDrawer;
_schedulerService = schedulerService;
_objects = objects; _objects = objects;
_clientState = clientState; _clientState = clientState;
_dataManager = dataManager; _dataManager = dataManager;
@ -672,6 +675,22 @@ public class DebugTab : Window, ITab, IUiService
} }
} }
} }
using (var tmbCache = TreeNode("TMB Cache"))
{
if (tmbCache)
{
using var table = Table("###TmbTable", 2, ImGuiTableFlags.SizingFixedFit);
if (table)
{
foreach (var (id, name) in _schedulerService.ListedTmbs.OrderBy(kvp => kvp.Key))
{
ImUtf8.DrawTableColumn($"{id:D6}");
ImUtf8.DrawTableColumn(name.Span);
}
}
}
}
} }
private void DrawData() private void DrawData()
@ -680,6 +699,7 @@ public class DebugTab : Window, ITab, IUiService
return; return;
DrawEmotes(); DrawEmotes();
DrawActionTmbs();
DrawStainTemplates(); DrawStainTemplates();
DrawAtch(); DrawAtch();
} }
@ -739,6 +759,27 @@ public class DebugTab : Window, ITab, IUiService
ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing()); ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing());
} }
private void DrawActionTmbs()
{
using var mainTree = TreeNode("Action TMBs");
if (!mainTree)
return;
using var table = Table("##table", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
new Vector2(-1, 12 * ImGui.GetTextLineHeightWithSpacing()));
if (!table)
return;
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing());
var dummy = ImGuiClip.ClippedDraw(_schedulerService.ActionTmbs.OrderBy(r => r.Value), skips,
p =>
{
ImUtf8.DrawTableColumn($"{p.Value}");
ImUtf8.DrawTableColumn(p.Key.Span);
});
ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing());
}
private void DrawStainTemplates() private void DrawStainTemplates()
{ {
using var mainTree = TreeNode("Staining Templates"); using var mainTree = TreeNode("Staining Templates");
@ -1061,7 +1102,7 @@ public class DebugTab : Window, ITab, IUiService
} }
ImUtf8.HoverTooltip("Click to copy address to clipboard."u8); ImUtf8.HoverTooltip("Click to copy address to clipboard."u8);
} }
public static unsafe void DrawCopyableAddress(ReadOnlySpan<byte> label, nint address) public static unsafe void DrawCopyableAddress(ReadOnlySpan<byte> label, nint address)
=> DrawCopyableAddress(label, (void*)address); => DrawCopyableAddress(label, (void*)address);

View file

@ -1,12 +1,23 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Scheduler;
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
using FFXIVClientStructs.Interop;
using FFXIVClientStructs.STD;
using ImGuiNET; using ImGuiNET;
using OtterGui.Services; using OtterGui.Services;
using OtterGui.Text; using OtterGui.Text;
using Penumbra.Interop.Services; using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.String;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
namespace Penumbra.UI.Tabs.Debug; namespace Penumbra.UI.Tabs.Debug;
public unsafe class GlobalVariablesDrawer(CharacterUtility characterUtility, ResidentResourceManager residentResources) : IUiService public unsafe class GlobalVariablesDrawer(
CharacterUtility characterUtility,
ResidentResourceManager residentResources,
SchedulerResourceManagementService scheduler) : IUiService
{ {
/// <summary> Draw information about some game global variables. </summary> /// <summary> Draw information about some game global variables. </summary>
public void Draw() public void Draw()
@ -16,13 +27,22 @@ public unsafe class GlobalVariablesDrawer(CharacterUtility characterUtility, Res
if (!header) if (!header)
return; return;
DebugTab.DrawCopyableAddress("CharacterUtility"u8, characterUtility.Address); var actionManager = (ActionTimelineManager**)ActionTimelineManager.Instance();
DebugTab.DrawCopyableAddress("ResidentResourceManager"u8, residentResources.Address); DebugTab.DrawCopyableAddress("CharacterUtility"u8, characterUtility.Address);
DebugTab.DrawCopyableAddress("Device"u8, Device.Instance()); DebugTab.DrawCopyableAddress("ResidentResourceManager"u8, residentResources.Address);
DebugTab.DrawCopyableAddress("ScheduleManagement"u8, ScheduleManagement.Instance());
DebugTab.DrawCopyableAddress("ActionTimelineManager*"u8, actionManager);
DebugTab.DrawCopyableAddress("ActionTimelineManager"u8, actionManager != null ? *actionManager : null);
DebugTab.DrawCopyableAddress("SchedulerResourceManagement*"u8, scheduler.Address);
DebugTab.DrawCopyableAddress("SchedulerResourceManagement"u8, scheduler.Address != null ? *scheduler.Address : null);
DebugTab.DrawCopyableAddress("Device"u8, Device.Instance());
DrawCharacterUtility(); DrawCharacterUtility();
DrawResidentResources(); DrawResidentResources();
DrawSchedulerResourcesMap();
DrawSchedulerResourcesList();
} }
/// <summary> /// <summary>
/// Draw information about the character utility class from SE, /// Draw information about the character utility class from SE,
/// displaying all files, their sizes, the default files and the default sizes. /// displaying all files, their sizes, the default files and the default sizes.
@ -123,4 +143,111 @@ public unsafe class GlobalVariablesDrawer(CharacterUtility characterUtility, Res
ImUtf8.DrawTableColumn(length.ToString()); ImUtf8.DrawTableColumn(length.ToString());
} }
} }
private string _schedulerFilterList = string.Empty;
private string _schedulerFilterMap = string.Empty;
private CiByteString _schedulerFilterListU8 = CiByteString.Empty;
private CiByteString _schedulerFilterMapU8 = CiByteString.Empty;
private int _shownResourcesList = 0;
private int _shownResourcesMap = 0;
private void DrawSchedulerResourcesMap()
{
using var tree = ImUtf8.TreeNode("Scheduler Resources (Map)"u8);
if (!tree)
return;
if (scheduler.Address == null || scheduler.Scheduler == null)
return;
if (ImUtf8.InputText("##SchedulerMapFilter"u8, ref _schedulerFilterMap, "Filter..."u8))
_schedulerFilterMapU8 = CiByteString.FromString(_schedulerFilterMap, out var t, MetaDataComputation.All, false)
? t
: CiByteString.Empty;
ImUtf8.Text($"{_shownResourcesMap} / {scheduler.Scheduler->NumResources}");
using var table = ImUtf8.Table("##SchedulerMapResources"u8, 10, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
-Vector2.UnitX);
if (!table)
return;
var map = (StdMap<int, Pointer<SchedulerResource>>*)&scheduler.Scheduler->Unknown;
var total = 0;
_shownResourcesMap = 0;
foreach (var (key, resourcePtr) in *map)
{
var resource = resourcePtr.Value;
if (_schedulerFilterMap.Length is 0 || resource->Name.Buffer.IndexOf(_schedulerFilterMapU8.Span) >= 0)
{
ImUtf8.DrawTableColumn($"[{total:D4}]");
ImUtf8.DrawTableColumn($"{resource->Name.Unk1}");
ImUtf8.DrawTableColumn(new CiByteString(resource->Name.Buffer, MetaDataComputation.None).Span);
ImUtf8.DrawTableColumn($"{resource->Consumers}");
ImUtf8.DrawTableColumn($"{resource->Unk1}"); // key
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource:X}");
ImGui.TableNextColumn();
var resourceHandle = *((ResourceHandle**)resource + 3);
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resourceHandle:X}");
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable(resourceHandle->FileName().Span);
ImGui.TableNextColumn();
uint dataLength = 0;
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource->GetResourceData(&dataLength):X}");
ImUtf8.DrawTableColumn($"{dataLength}");
++_shownResourcesMap;
}
++total;
}
}
private void DrawSchedulerResourcesList()
{
using var tree = ImUtf8.TreeNode("Scheduler Resources (List)"u8);
if (!tree)
return;
if (scheduler.Address == null || scheduler.Scheduler == null)
return;
if (ImUtf8.InputText("##SchedulerListFilter"u8, ref _schedulerFilterList, "Filter..."u8))
_schedulerFilterListU8 = CiByteString.FromString(_schedulerFilterList, out var t, MetaDataComputation.All, false)
? t
: CiByteString.Empty;
ImUtf8.Text($"{_shownResourcesList} / {scheduler.Scheduler->NumResources}");
using var table = ImUtf8.Table("##SchedulerListResources"u8, 10, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
-Vector2.UnitX);
if (!table)
return;
var resource = scheduler.Scheduler->Begin;
var total = 0;
_shownResourcesList = 0;
while (resource != null && total < (int)scheduler.Scheduler->NumResources)
{
if (_schedulerFilterList.Length is 0 || resource->Name.Buffer.IndexOf(_schedulerFilterListU8.Span) >= 0)
{
ImUtf8.DrawTableColumn($"[{total:D4}]");
ImUtf8.DrawTableColumn($"{resource->Name.Unk1}");
ImUtf8.DrawTableColumn(new CiByteString(resource->Name.Buffer, MetaDataComputation.None).Span);
ImUtf8.DrawTableColumn($"{resource->Consumers}");
ImUtf8.DrawTableColumn($"{resource->Unk1}"); // key
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource:X}");
ImGui.TableNextColumn();
var resourceHandle = *((ResourceHandle**)resource + 3);
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resourceHandle:X}");
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable(resourceHandle->FileName().Span);
ImGui.TableNextColumn();
uint dataLength = 0;
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource->GetResourceData(&dataLength):X}");
ImUtf8.DrawTableColumn($"{dataLength}");
++_shownResourcesList;
}
resource = resource->Previous;
++total;
}
}
} }