From f78c5157c8fe5c8a9c03c496de44655f4749cf38 Mon Sep 17 00:00:00 2001 From: goat Date: Wed, 14 Dec 2022 21:57:21 +0100 Subject: [PATCH] feat: defer texturewrap dispose until the end of the frame --- Dalamud/Dalamud.csproj | 4 +- .../Interface/Internal/DalamudTextureWrap.cs | 54 +++++++++++++++++++ .../Interface/Internal/InterfaceManager.cs | 27 ++++++++-- 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 Dalamud/Interface/Internal/DalamudTextureWrap.cs diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 9eb0a7c7f..5e406acdb 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -131,7 +131,7 @@ - + @@ -143,7 +143,7 @@ - + diff --git a/Dalamud/Interface/Internal/DalamudTextureWrap.cs b/Dalamud/Interface/Internal/DalamudTextureWrap.cs new file mode 100644 index 000000000..346b7cd24 --- /dev/null +++ b/Dalamud/Interface/Internal/DalamudTextureWrap.cs @@ -0,0 +1,54 @@ +using System; + +using ImGuiScene; + +namespace Dalamud.Interface.Internal; + +/// +/// Safety harness for ImGuiScene textures that will defer destruction until +/// the end of the frame. +/// +public class DalamudTextureWrap : TextureWrap +{ + private readonly TextureWrap wrappedWrap; + + /// + /// Initializes a new instance of the class. + /// + /// The texture wrap to wrap. + internal DalamudTextureWrap(TextureWrap wrappingWrap) + { + this.wrappedWrap = wrappingWrap; + } + + /// + /// Gets the ImGui handle of the texture. + /// + public IntPtr ImGuiHandle => this.wrappedWrap.ImGuiHandle; + + /// + /// Gets the width of the texture. + /// + public int Width => this.wrappedWrap.Width; + + /// + /// Gets the height of the texture. + /// + public int Height => this.wrappedWrap.Height; + + /// + /// Queue the texture to be disposed once the frame ends. + /// + public void Dispose() + { + Service.Get().EnqueueDeferredDispose(this); + } + + /// + /// Actually dispose the wrapped texture. + /// + internal void RealDispose() + { + this.wrappedWrap.Dispose(); + } +} diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 5e6da15c1..41bd8e9c7 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -56,6 +56,8 @@ internal class InterfaceManager : IDisposable, IServiceType private readonly HashSet glyphRequests = new(); private readonly Dictionary loadedFontInfo = new(); + private readonly List deferredDisposeTextures = new(); + [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); @@ -242,7 +244,8 @@ internal class InterfaceManager : IDisposable, IServiceType try { - return this.scene?.LoadImage(filePath) ?? null; + var wrap = this.scene?.LoadImage(filePath); + return wrap != null ? new DalamudTextureWrap(wrap) : null; } catch (Exception ex) { @@ -264,7 +267,8 @@ internal class InterfaceManager : IDisposable, IServiceType try { - return this.scene?.LoadImage(imageData) ?? null; + var wrap = this.scene?.LoadImage(imageData); + return wrap != null ? new DalamudTextureWrap(wrap) : null; } catch (Exception ex) { @@ -289,7 +293,8 @@ internal class InterfaceManager : IDisposable, IServiceType try { - return this.scene?.LoadImageRaw(imageData, width, height, numChannels) ?? null; + var wrap = this.scene?.LoadImageRaw(imageData, width, height, numChannels); + return wrap != null ? new DalamudTextureWrap(wrap) : null; } catch (Exception ex) { @@ -395,6 +400,15 @@ internal class InterfaceManager : IDisposable, IServiceType return this.NewFontSizeRef(size, ranges); } + /// + /// Enqueue a texture to be disposed at the end of the frame. + /// + /// The texture. + public void EnqueueDeferredDispose(DalamudTextureWrap wrap) + { + this.deferredDisposeTextures.Add(wrap); + } + private static void ShowFontError(string path) { Util.Fatal($"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}", "Error"); @@ -549,6 +563,13 @@ internal class InterfaceManager : IDisposable, IServiceType this.RenderImGui(); + foreach (var texture in this.deferredDisposeTextures) + { + texture.RealDispose(); + } + + this.deferredDisposeTextures.Clear(); + return this.presentHook.Original(swapChain, syncInterval, presentFlags); }