瀏覽代碼

Bunch of memory dispose fixes

Krzysztof Krysiński 5 月之前
父節點
當前提交
e69197ccbd

+ 5 - 2
src/ChunkyImageLib/ChunkPool.cs

@@ -11,6 +11,7 @@ internal class ChunkPool
 
     private static object lockObj = new();
     private static ChunkPool? instance;
+
     /// <summary>
     /// The instance of the <see cref="ChunkPool"/>
     /// </summary>
@@ -25,6 +26,7 @@ internal class ChunkPool
                     instance ??= new ChunkPool();
                 }
             }
+
             return instance;
         }
     }
@@ -39,7 +41,8 @@ internal class ChunkPool
     /// </summary>
     /// <param name="resolution">The resolution for the chunk</param>
     /// <param name="chunkCs"></param>
-    internal Chunk? Get(ChunkResolution resolution, ColorSpace chunkCs) => GetBag(resolution, chunkCs).TryTake(out Chunk? item) ? item : null;
+    internal Chunk? Get(ChunkResolution resolution, ColorSpace chunkCs) =>
+        GetBag(resolution, chunkCs).TryTake(out Chunk? item) ? item : null;
 
     private ConcurrentBag<Chunk> GetBag(ChunkResolution resolution, ColorSpace colorSpace)
     {
@@ -60,7 +63,7 @@ internal class ChunkPool
     {
         var chunks = GetBag(chunk.Resolution, chunk.ColorSpace);
         //a race condition can cause the count to go above 200, but likely not by much
-        if (chunks.Count < 200)
+        if (chunks.Count <= 32)
             chunks.Add(chunk);
         else
             chunk.Surface.Dispose();

+ 1 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -267,11 +267,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     public override void Dispose()
     {
         base.Dispose();
-
-        foreach (var workingSurface in workingSurfaces)
-        {
-            workingSurface.Value.Dispose();
-        }
+        fullResrenderedSurface.Dispose();
     }
 
     IReadOnlyChunkyImage IReadOnlyImageNode.GetLayerImageAtFrame(int frame) => GetLayerImageAtFrame(frame);

+ 12 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

@@ -185,4 +185,16 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
     {
         RenderContent(context, drawOnto, false);
     }
+
+    public override void Dispose()
+    {
+        base.Dispose();
+        if (workingSurfaces != null)
+        {
+            foreach (var workingSurface in workingSurfaces.Values)
+            {
+                workingSurface?.Dispose();
+            }
+        }
+    }
 }

+ 3 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs

@@ -348,8 +348,10 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
 
     public override void Dispose()
     {
-        Output.Value = null;
         base.Dispose();
+        renderedMask?.Dispose();
+        EmbeddedMask?.Dispose();
+        Output.Value = null;
         maskPaint.Dispose();
         blendPaint.Dispose();
     }

+ 35 - 17
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -13,7 +13,7 @@ using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Rendering;
 
-public class DocumentRenderer : IPreviewRenderable
+public class DocumentRenderer : IPreviewRenderable, IDisposable
 {
     private Queue<RenderRequest> renderRequests = new();
     private Texture renderTexture;
@@ -25,9 +25,9 @@ public class DocumentRenderer : IPreviewRenderable
 
     private IReadOnlyDocument Document { get; }
     public bool IsBusy { get; private set; }
-    
+
     private bool isExecuting = false;
-    
+
     public void UpdateChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime)
     {
         try
@@ -109,27 +109,29 @@ public class DocumentRenderer : IPreviewRenderable
         toRenderOn.Canvas.Save();
         toRenderOn.Canvas.SetMatrix(Matrix3X3.Identity);
 
-        RenderContext context = new(renderTexture.DrawingSurface, frameTime, resolution, Document.Size, Document.ProcessingColorSpace);
+        RenderContext context = new(renderTexture.DrawingSurface, frameTime, resolution, Document.Size,
+            Document.ProcessingColorSpace);
         context.FullRerender = true;
 
         node.RenderForOutput(context, toRenderOn, null);
-        
+
         renderTexture.DrawingSurface.Canvas.Restore();
         toRenderOn.Canvas.Restore();
-        
+
         IsBusy = false;
     }
 
-    public async Task RenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn, RenderContext context,
+    public async Task RenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn,
+        RenderContext context,
         string elementToRenderName)
     {
         if (previewRenderable is Node { IsDisposed: true }) return;
         TaskCompletionSource<bool> tcs = new();
         RenderRequest request = new(tcs, context, renderOn, previewRenderable, elementToRenderName);
-        
+
         renderRequests.Enqueue(request);
         ExecuteRenderRequests();
-        
+
         await tcs.Task;
     }
 
@@ -203,7 +205,8 @@ public class DocumentRenderer : IPreviewRenderable
         return true;
     }
 
-    public void RenderDocument(DrawingSurface toRenderOn, KeyFrameTime frameTime, VecI renderSize, string? customOutput = null)
+    public void RenderDocument(DrawingSurface toRenderOn, KeyFrameTime frameTime, VecI renderSize,
+        string? customOutput = null)
     {
         IsBusy = true;
 
@@ -247,8 +250,8 @@ public class DocumentRenderer : IPreviewRenderable
 
     private void ExecuteRenderRequests()
     {
-        if(isExecuting) return;
-        
+        if (isExecuting) return;
+
         isExecuting = true;
         while (renderRequests.Count > 0)
         {
@@ -273,7 +276,7 @@ public class DocumentRenderer : IPreviewRenderable
                 request.TaskCompletionSource.SetException(e);
             }
         }
-        
+
         isExecuting = false;
     }
 
@@ -315,6 +318,19 @@ public class DocumentRenderer : IPreviewRenderable
 
         return found ?? (membersOnlyGraph.OutputNode as IRenderInput)?.Background;
     }
+
+    public void Dispose()
+    {
+        renderTexture?.Dispose();
+        renderTexture = null;
+
+        foreach (var request in renderRequests)
+        {
+            if (request.TaskCompletionSource == null) continue;
+
+            request.TaskCompletionSource.TrySetCanceled();
+        }
+    }
 }
 
 public struct RenderRequest
@@ -325,16 +341,18 @@ public struct RenderRequest
     public IPreviewRenderable? PreviewRenderable { get; set; }
     public string ElementToRenderName { get; set; }
     public TaskCompletionSource<bool> TaskCompletionSource { get; set; }
-    
-    public RenderRequest(TaskCompletionSource<bool> completionSource, RenderContext context, DrawingSurface renderOn, IReadOnlyNodeGraph nodeGraph)
+
+    public RenderRequest(TaskCompletionSource<bool> completionSource, RenderContext context, DrawingSurface renderOn,
+        IReadOnlyNodeGraph nodeGraph)
     {
         TaskCompletionSource = completionSource;
         Context = context;
         RenderOn = renderOn;
         NodeGraph = nodeGraph;
     }
-    
-    public RenderRequest(TaskCompletionSource<bool> completionSource, RenderContext context, DrawingSurface renderOn, IPreviewRenderable previewRenderable, string elementToRenderName)
+
+    public RenderRequest(TaskCompletionSource<bool> completionSource, RenderContext context, DrawingSurface renderOn,
+        IPreviewRenderable previewRenderable, string elementToRenderName)
     {
         TaskCompletionSource = completionSource;
         Context = context;

+ 1 - 1
src/PixiEditor/Models/Handlers/IAnimationHandler.cs

@@ -2,7 +2,7 @@
 
 namespace PixiEditor.Models.Handlers;
 
-internal interface IAnimationHandler
+internal interface IAnimationHandler : IDisposable
 {
     public IReadOnlyCollection<ICelHandler> KeyFrames { get; }
     public int ActiveFrameBindable { get; set; }

+ 1 - 1
src/PixiEditor/Models/Handlers/ICelHandler.cs

@@ -4,7 +4,7 @@ using PixiEditor.Models.Rendering;
 
 namespace PixiEditor.Models.Handlers;
 
-internal interface ICelHandler
+internal interface ICelHandler : IDisposable
 {
     public PreviewPainter? PreviewPainter { get; set; }
     public int StartFrameBindable { get; }

+ 1 - 1
src/PixiEditor/Models/Handlers/INodeHandler.cs

@@ -11,7 +11,7 @@ using Drawie.Numerics;
 
 namespace PixiEditor.Models.Handlers;
 
-public interface INodeHandler : INotifyPropertyChanged
+public interface INodeHandler : INotifyPropertyChanged, IDisposable
 {
     public Guid Id { get; }
     public string NodeNameBindable { get; set; }

+ 11 - 1
src/PixiEditor/Models/Rendering/PreviewPainter.cs

@@ -14,7 +14,7 @@ using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.Models.Rendering;
 
-public class PreviewPainter
+public class PreviewPainter : IDisposable
 {
     public string ElementToRenderName { get; set; }
     public IPreviewRenderable PreviewRenderable { get; set; }
@@ -126,6 +126,8 @@ public class PreviewPainter
         RepaintDirty();
     }
 
+
+
     private void RepaintDirty()
     {
         var dirtyArray = dirtyTextures.ToArray();
@@ -211,6 +213,14 @@ public class PreviewPainter
                 });
         }
     }
+
+    public void Dispose()
+    {
+        foreach (var texture in renderTextures)
+        {
+            texture.Value.Dispose();
+        }
+    }
 }
 
 public class PainterInstance

+ 9 - 1
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -14,7 +14,7 @@ using PixiEditor.Models.Handlers;
 
 namespace PixiEditor.Models.Rendering;
 
-internal class SceneRenderer
+internal class SceneRenderer : IDisposable
 {
     public const double ZoomDiffToRerender = 20;
     public IReadOnlyDocument Document { get; }
@@ -246,4 +246,12 @@ internal class SceneRenderer
             finalGraph.Execute(onionContext);
         }
     }
+
+    public void Dispose()
+    {
+        foreach (var texture in cachedTextures)
+        {
+            texture.Value?.Dispose();
+        }
+    }
 }

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs

@@ -15,7 +15,7 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
         var encoder = Config.Encoder;
         surfaceFactory.Config = Config;
 
-        Surface surface = new Surface(original.LatestSize);
+        using Surface surface = new Surface(original.LatestSize);
         original.DrawMostUpToDateRegionOn(
             new RectI(0, 0, original.LatestSize.X,
                 original.LatestSize.Y), ChunkResolution.Full, surface.DrawingSurface, new VecI(0, 0), new Paint());
@@ -38,6 +38,7 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
             original = new ChunkyImage(surface.Size, Config.ProcessingColorSpace);
             original.EnqueueDrawImage(VecI.Zero, surface);
             original.CommitChanges();
+            surface.Dispose();
             return true;
         }
 

+ 8 - 0
src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs

@@ -471,4 +471,12 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
         return false;
     }
+
+    public void Dispose()
+    {
+        foreach (var cel in allCels)
+        {
+            cel.Dispose();
+        }
+    }
 }

+ 6 - 0
src/PixiEditor/ViewModels/Document/CelViewModel.cs

@@ -140,4 +140,10 @@ internal abstract class CelViewModel : ObservableObject, ICelHandler
     {
         return frame >= StartFrameBindable && frame < StartFrameBindable + DurationBindable;
     }
+
+    public void Dispose()
+    {
+        PreviewPainter?.Dispose();
+        PreviewPainter = null;
+    }
 }

+ 9 - 2
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -73,6 +73,14 @@ internal partial class DocumentViewModel
 
         AddNodes(doc.NodeGraph, graph, nodeIdMap, keyFrameIdMap, serializationConfig, factories);
 
+        var preview = TryRenderWholeImage(0);
+        byte[]? previewBytes = null;
+        if (preview.IsT1)
+        {
+            previewBytes = preview.AsT1.DrawingSurface.Snapshot().Encode().AsSpan().ToArray();
+            preview.AsT1.Dispose();
+        }
+
         var document = new PixiDocument
         {
             SerializerName = "PixiEditor",
@@ -83,8 +91,7 @@ internal partial class DocumentViewModel
             Swatches = ToCollection(Swatches),
             Palette = ToCollection(Palette),
             Graph = graph,
-            PreviewImage =
-                (TryRenderWholeImage(0).Value as Surface)?.DrawingSurface.Snapshot().Encode().AsSpan().ToArray(),
+            PreviewImage = previewBytes,
             ReferenceLayer = GetReferenceLayer(doc, serializationConfig),
             AnimationData = ToAnimationData(doc.AnimationData, doc.NodeGraph, nodeIdMap, keyFrameIdMap),
             ImageEncoderUsed = encoder.EncodedFormatName,

+ 4 - 0
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -1153,6 +1153,10 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
     public void Dispose()
     {
+        NodeGraph.Dispose();
+        Renderer.Dispose();
+        SceneRenderer.Dispose();
+        AnimationDataViewModel.Dispose();
         Internals.ChangeController.TryStopActiveExecutor();
         Internals.Tracker.Dispose();
         Internals.Tracker.Document.Dispose();

+ 9 - 1
src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs

@@ -16,7 +16,7 @@ using PixiEditor.ViewModels.Nodes;
 
 namespace PixiEditor.ViewModels.Document;
 
-internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
+internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposable
 {
     public DocumentViewModel DocumentViewModel { get; }
     public ObservableCollection<INodeHandler> AllNodes { get; } = new();
@@ -374,4 +374,12 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
             }
         }
     }
+
+    public void Dispose()
+    {
+        foreach (var node in AllNodes)
+        {
+            node.Dispose();
+        }
+    }
 }

+ 12 - 4
src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs

@@ -19,7 +19,6 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
 {
     public StructureMemberViewModel()
     {
-        
     }
 
     private bool isVisible;
@@ -44,10 +43,10 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
 
     public RectD? TightBounds => Internals.Tracker.Document.FindMember(Id)
         ?.GetTightBounds(Document.AnimationDataViewModel.ActiveFrameBindable);
-    
+
     public ShapeCorners TransformationCorners => Internals.Tracker.Document.FindMember(Id)
         ?.GetTransformationCorners(Document.AnimationDataViewModel.ActiveFrameBindable) ?? new ShapeCorners();
-    
+
     public void SetMaskIsVisible(bool maskIsVisible)
     {
         this.maskIsVisible = maskIsVisible;
@@ -114,7 +113,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
     {
         get => hasMask;
     }
-    
+
     private float opacity;
 
     public void SetOpacity(float newOpacity)
@@ -161,6 +160,15 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
     }
 
     IDocument IStructureMemberHandler.Document => Document;
+
+    public override void Dispose()
+    {
+        base.Dispose();
+        PreviewPainter?.Dispose();
+        MaskPreviewPainter?.Dispose();
+        PreviewPainter = null;
+        MaskPreviewPainter = null;
+    }
 }
 
 public static class StructureMemberViewModel

+ 5 - 0
src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs

@@ -380,6 +380,11 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
         }
     }
 
+    public virtual void Dispose()
+    {
+        ResultPainter?.Dispose();
+    }
+
     public NodePropertyViewModel FindInputProperty(string propName)
     {
         return Inputs.FirstOrDefault(x => x.PropertyName == propName) as NodePropertyViewModel;

+ 4 - 0
src/PixiEditor/Views/MainView.axaml.cs

@@ -46,6 +46,10 @@ public partial class MainView : UserControl
                 OpenGlInitDummy.IsVisible = false;
             });
         }
+        else
+        {
+            OpenGlInitDummy.IsVisible = false;
+        }
 
         if (DataContext is ViewModelMain vm)
         {