diff --git a/src/AcDream.App/Rendering/TextureCache.cs b/src/AcDream.App/Rendering/TextureCache.cs index 0e7ebcea..250a69e4 100644 --- a/src/AcDream.App/Rendering/TextureCache.cs +++ b/src/AcDream.App/Rendering/TextureCache.cs @@ -37,6 +37,12 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab private readonly Dictionary _handlesByRenderSurfaceId = new(); private readonly Dictionary _rsSizeById = new(); + // Ad-hoc handles produced by the public UploadRgba8(byte[],int,int,bool) wrapper + // (used by IconComposer for composited item icons). These are NOT stored in any + // of the keyed caches above, so Dispose must sweep this list to avoid leaking + // GL texture objects until process exit. + private readonly List _adhocHandles = new(); + private readonly Wb.BindlessSupport? _bindless; // Bindless / Texture2DArray parallel caches. Keys mirror the legacy three @@ -543,9 +549,16 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab } /// Uploads a raw RGBA8 byte array as a Texture2D. Used by - /// to upload CPU-composited icon layers. + /// to upload CPU-composited icon layers. + /// The returned handle is tracked in and deleted by + /// . Callers must NOT also store the handle in any of the + /// keyed caches — that would cause a double-delete on Dispose. public uint UploadRgba8(byte[] rgba, int width, int height, bool nearest = false) - => UploadRgba8(new DecodedTexture(rgba, width, height), nearest); + { + uint h = UploadRgba8(new DecodedTexture(rgba, width, height), nearest); + _adhocHandles.Add(h); + return h; + } private uint UploadRgba8(DecodedTexture decoded, bool nearest = false) { @@ -656,5 +669,17 @@ public sealed unsafe class TextureCache : Wb.ITextureCachePerInstance, IDisposab _gl.DeleteTexture(_magentaHandle); _magentaHandle = 0; } + + // RenderSurface (UI sprite) handles — pre-existing gap: this dict was populated + // by GetOrUploadRenderSurface but was not swept here before this fix. + foreach (var h in _handlesByRenderSurfaceId.Values) + _gl.DeleteTexture(h); + _handlesByRenderSurfaceId.Clear(); + + // Ad-hoc handles from the public UploadRgba8(byte[],int,int,bool) wrapper + // (IconComposer composited icons). Not stored in any keyed cache. + foreach (var h in _adhocHandles) + _gl.DeleteTexture(h); + _adhocHandles.Clear(); } }