浏览代码

Add ref-counting checks throughout the SceneGraph nodes. Because IDrawOperations can be shared between IVisualNodes, they are now also ref-counted.

Jeremy Koritzinsky 7 年之前
父节点
当前提交
617f35ce53

+ 10 - 4
src/Avalonia.Base/Utilities/Ref.cs

@@ -50,6 +50,7 @@ namespace Avalonia.Utilities
             public RefCounter(IDisposable item)
             {
                 _item = item;
+                _refs = 1;
             }
 
             public void AddRef()
@@ -101,8 +102,6 @@ namespace Avalonia.Utilities
             {
                 _item = item;
                 _counter = counter;
-                Interlocked.MemoryBarrier();
-                _counter.AddRef();
             }
 
             public void Dispose()
@@ -139,7 +138,11 @@ namespace Avalonia.Utilities
                 lock (_lock)
                 {
                     if (_item != null)
-                        return new Ref<T>(_item, _counter);
+                    {
+                        var newRef = new Ref<T>(_item, _counter);
+                        _counter.AddRef();
+                        return newRef;
+                    }
                     throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
                 }
             }
@@ -150,7 +153,10 @@ namespace Avalonia.Utilities
                 {
                     if (_item != null)
                     {
-                        return new Ref<TResult>((TResult)(object)_item, _counter);
+                        var castRef = new Ref<TResult>((TResult)(object)_item, _counter);
+                        Interlocked.MemoryBarrier();
+                        _counter.AddRef();
+                        return castRef;
                     }
                     throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
                 }

+ 0 - 9
src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs

@@ -37,15 +37,6 @@ namespace Avalonia.Media.Imaging
         /// </summary>
         public new IRef<IRenderTargetBitmapImpl> PlatformImpl { get; }
 
-        /// <summary>
-        /// Disposes of the bitmap.
-        /// </summary>
-        public void Dispose()
-        {
-            PlatformImpl.Dispose();
-            base.Dispose();
-        }
-
         /// <summary>
         /// Renders a visual to the <see cref="RenderTargetBitmap"/>.
         /// </summary>

+ 4 - 4
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -35,7 +35,7 @@ namespace Avalonia.Rendering
         private object _rendering = new object();
         private int _lastSceneId = -1;
         private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
-        private IDrawOperation _currentDraw;
+        private IRef<IDrawOperation> _currentDraw;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
@@ -159,13 +159,13 @@ namespace Avalonia.Rendering
         /// <inheritdoc/>
         Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
         {
-            return (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty;
+            return (_currentDraw as IRef<BrushDrawOperation>)?.Item.ChildScenes?[brush.Visual]?.Size ?? Size.Empty;
         }
 
         /// <inheritdoc/>
         void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
         {
-            var childScene = (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual];
+            var childScene = (_currentDraw as IRef<BrushDrawOperation>)?.Item.ChildScenes?[brush.Visual];
 
             if (childScene != null)
             {
@@ -253,7 +253,7 @@ namespace Avalonia.Rendering
                     foreach (var operation in node.DrawOperations)
                     {
                         _currentDraw = operation;
-                        operation.Render(context);
+                        operation.Item.Render(context);
                         _currentDraw = null;
                     }
 

+ 4 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs

@@ -60,5 +60,9 @@ namespace Avalonia.Rendering.SceneGraph
                 context.PopClip();
             }
         }
+
+        public void Dispose()
+        {
+        }
     }
 }

+ 33 - 33
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@@ -81,7 +81,7 @@ namespace Avalonia.Rendering.SceneGraph
         /// <inheritdoc/>
         public void Dispose()
         {
-            // Nothing to do here as we allocate no unmanaged resources.
+            _node?.Dispose();
         }
 
         /// <summary>
@@ -103,9 +103,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<GeometryNode>();
 
-            if (next == null || !next.Equals(Transform, brush, pen, geometry))
+            if (next == null || !next.Item.Equals(Transform, brush, pen, geometry))
             {
-                Add(new GeometryNode(Transform, brush, pen, geometry, CreateChildScene(brush)));
+                Add(RefCountable.Create(new GeometryNode(Transform, brush, pen, geometry, CreateChildScene(brush))));
             }
             else
             {
@@ -118,9 +118,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<ImageNode>();
 
-            if (next == null || !next.Equals(Transform, source, opacity, sourceRect, destRect))
+            if (next == null || !next.Item.Equals(Transform, source, opacity, sourceRect, destRect))
             {
-                Add(new ImageNode(Transform, source, opacity, sourceRect, destRect));
+                Add(RefCountable.Create(new ImageNode(Transform, source, opacity, sourceRect, destRect)));
             }
             else
             {
@@ -140,9 +140,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<LineNode>();
 
-            if (next == null || !next.Equals(Transform, pen, p1, p2))
+            if (next == null || !next.Item.Equals(Transform, pen, p1, p2))
             {
-                Add(new LineNode(Transform, pen, p1, p2, CreateChildScene(pen.Brush)));
+                Add(RefCountable.Create(new LineNode(Transform, pen, p1, p2, CreateChildScene(pen.Brush))));
             }
             else
             {
@@ -155,9 +155,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<RectangleNode>();
 
-            if (next == null || !next.Equals(Transform, null, pen, rect, cornerRadius))
+            if (next == null || !next.Item.Equals(Transform, null, pen, rect, cornerRadius))
             {
-                Add(new RectangleNode(Transform, null, pen, rect, cornerRadius, CreateChildScene(pen.Brush)));
+                Add(RefCountable.Create(new RectangleNode(Transform, null, pen, rect, cornerRadius, CreateChildScene(pen.Brush))));
             }
             else
             {
@@ -170,9 +170,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<TextNode>();
 
-            if (next == null || !next.Equals(Transform, foreground, origin, text))
+            if (next == null || !next.Item.Equals(Transform, foreground, origin, text))
             {
-                Add(new TextNode(Transform, foreground, origin, text, CreateChildScene(foreground)));
+                Add(RefCountable.Create(new TextNode(Transform, foreground, origin, text, CreateChildScene(foreground))));
             }
             else
             {
@@ -185,9 +185,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<RectangleNode>();
 
-            if (next == null || !next.Equals(Transform, brush, null, rect, cornerRadius))
+            if (next == null || !next.Item.Equals(Transform, brush, null, rect, cornerRadius))
             {
-                Add(new RectangleNode(Transform, brush, null, rect, cornerRadius, CreateChildScene(brush)));
+                Add(RefCountable.Create(new RectangleNode(Transform, brush, null, rect, cornerRadius, CreateChildScene(brush))));
             }
             else
             {
@@ -205,9 +205,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<ClipNode>();
 
-            if (next == null || !next.Equals(null))
+            if (next == null || !next.Item.Equals(null))
             {
-                Add(new ClipNode());
+                Add(RefCountable.Create(new ClipNode()));
             }
             else
             {
@@ -220,9 +220,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<GeometryClipNode>();
 
-            if (next == null || !next.Equals(null))
+            if (next == null || !next.Item.Equals(null))
             {
-                Add(new GeometryClipNode());
+                Add(RefCountable.Create((new GeometryClipNode())));
             }
             else
             {
@@ -235,9 +235,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<OpacityNode>();
 
-            if (next == null || !next.Equals(null))
+            if (next == null || !next.Item.Equals(null))
             {
-                Add(new OpacityNode());
+                Add(RefCountable.Create(new OpacityNode()));
             }
             else
             {
@@ -250,9 +250,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<OpacityMaskNode>();
 
-            if (next == null || !next.Equals(null, null))
+            if (next == null || !next.Item.Equals(null, null))
             {
-                Add(new OpacityMaskNode());
+                Add(RefCountable.Create(new OpacityMaskNode()));
             }
             else
             {
@@ -265,9 +265,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<ClipNode>();
 
-            if (next == null || !next.Equals(clip))
+            if (next == null || !next.Item.Equals(clip))
             {
-                Add(new ClipNode(clip));
+                Add(RefCountable.Create(new ClipNode(clip)));
             }
             else
             {
@@ -280,9 +280,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<GeometryClipNode>();
 
-            if (next == null || !next.Equals(clip))
+            if (next == null || !next.Item.Equals(clip))
             {
-                Add(new GeometryClipNode(clip));
+                Add(RefCountable.Create(new GeometryClipNode(clip)));
             }
             else
             {
@@ -295,9 +295,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<OpacityNode>();
 
-            if (next == null || !next.Equals(opacity))
+            if (next == null || !next.Item.Equals(opacity))
             {
-                Add(new OpacityNode(opacity));
+                Add(RefCountable.Create(new OpacityNode(opacity)));
             }
             else
             {
@@ -310,9 +310,9 @@ namespace Avalonia.Rendering.SceneGraph
         {
             var next = NextDrawAs<OpacityMaskNode>();
 
-            if (next == null || !next.Equals(mask, bounds))
+            if (next == null || !next.Item.Equals(mask, bounds))
             {
-                Add(new OpacityMaskNode(mask, bounds, CreateChildScene(mask)));
+                Add(RefCountable.Create(new OpacityMaskNode(mask, bounds, CreateChildScene(mask))));
             }
             else
             {
@@ -342,7 +342,7 @@ namespace Avalonia.Rendering.SceneGraph
 
                 foreach (var operation in Owner._node.DrawOperations)
                 {
-                    dirty.Add(operation.Bounds);
+                    dirty.Add(operation.Item.Bounds);
                 }
 
                 Owner._node = Node;
@@ -356,7 +356,7 @@ namespace Avalonia.Rendering.SceneGraph
             public int DrawOperationIndex { get; }
         }
 
-        private void Add(IDrawOperation  node)
+        private void Add(IRef<IDrawOperation> node)
         {
             if (_drawOperationindex < _node.DrawOperations.Count)
             {
@@ -370,9 +370,9 @@ namespace Avalonia.Rendering.SceneGraph
             ++_drawOperationindex;
         }
 
-        private T NextDrawAs<T>() where T : class, IDrawOperation
+        private IRef<T> NextDrawAs<T>() where T : class, IDrawOperation
         {
-            return _drawOperationindex < _node.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as T : null;
+            return _drawOperationindex < _node.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as IRef<T> : null;
         }
 
         private IDictionary<IVisual, Scene> CreateChildScene(IBrush brush)

+ 4 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs

@@ -22,5 +22,9 @@ namespace Avalonia.Rendering.SceneGraph
         public abstract bool HitTest(Point p);
 
         public abstract void Render(IDrawingContextImpl context);
+
+        public virtual void Dispose()
+        {
+        }
     }
 }

+ 3 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs

@@ -60,5 +60,8 @@ namespace Avalonia.Rendering.SceneGraph
                 context.PopGeometryClip();
             }
         }
+        public void Dispose()
+        {
+        }
     }
 }

+ 1 - 1
src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Rendering.SceneGraph
     /// <summary>
     /// Represents a node in the low-level scene graph that represents geometry.
     /// </summary>
-    public interface IDrawOperation
+    public interface IDrawOperation : IDisposable
     {
         /// <summary>
         /// Gets the bounds of the visible content in the node in global coordinates.

+ 3 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using Avalonia.Media;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering.SceneGraph
@@ -12,7 +13,7 @@ namespace Avalonia.Rendering.SceneGraph
     /// <summary>
     /// Represents a node in the low-level scene graph representing an <see cref="IVisual"/>.
     /// </summary>
-    public interface IVisualNode
+    public interface IVisualNode : IDisposable
     {
         /// <summary>
         /// Gets the visual to which the node relates.
@@ -66,7 +67,7 @@ namespace Avalonia.Rendering.SceneGraph
         /// <summary>
         /// Gets the drawing operations for the visual.
         /// </summary>
-        IReadOnlyList<IDrawOperation> DrawOperations { get; }
+        IReadOnlyList<IRef<IDrawOperation>> DrawOperations { get; }
 
         /// <summary>
         /// Sets up the drawing context for rendering the node's geometry.

+ 2 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Rendering.SceneGraph
     /// <summary>
     /// A node in the scene graph which represents an image draw.
     /// </summary>
-    internal class ImageNode : DrawOperation, IDisposable
+    internal class ImageNode : DrawOperation
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageNode"/> class.
@@ -89,7 +89,7 @@ namespace Avalonia.Rendering.SceneGraph
         /// <inheritdoc/>
         public override bool HitTest(Point p) => Bounds.Contains(p);
 
-        public void Dispose()
+        public override void Dispose()
         {
             Source?.Dispose();
         }

+ 4 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs

@@ -60,5 +60,9 @@ namespace Avalonia.Rendering.SceneGraph
                 context.PopOpacity();
             }
         }
+
+        public void Dispose()
+        {
+        }
     }
 }

+ 44 - 11
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@@ -3,8 +3,10 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using Avalonia.Media;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering.SceneGraph
@@ -15,12 +17,12 @@ namespace Avalonia.Rendering.SceneGraph
     internal class VisualNode : IVisualNode
     {
         private static readonly IReadOnlyList<IVisualNode> EmptyChildren = new IVisualNode[0];
-        private static readonly IReadOnlyList<IDrawOperation> EmptyDrawOperations = new IDrawOperation[0];
+        private static readonly IReadOnlyList<IRef<IDrawOperation>> EmptyDrawOperations = new IRef<IDrawOperation>[0];
 
         private Rect? _bounds;
         private double _opacity;
         private List<IVisualNode> _children;
-        private List<IDrawOperation> _drawOperations;
+        private List<IRef<IDrawOperation>> _drawOperations;
         private bool _drawOperationsCloned;
         private Matrix transformRestore;
 
@@ -101,7 +103,7 @@ namespace Avalonia.Rendering.SceneGraph
         public IReadOnlyList<IVisualNode> Children => _children ?? EmptyChildren;
 
         /// <inheritdoc/>
-        public IReadOnlyList<IDrawOperation> DrawOperations => _drawOperations ?? EmptyDrawOperations;
+        public IReadOnlyList<IRef<IDrawOperation>> DrawOperations => _drawOperations ?? EmptyDrawOperations;
 
         /// <summary>
         /// Adds a child to the <see cref="Children"/> collection.
@@ -117,10 +119,10 @@ namespace Avalonia.Rendering.SceneGraph
         /// Adds an operation to the <see cref="DrawOperations"/> collection.
         /// </summary>
         /// <param name="operation">The operation to add.</param>
-        public void AddDrawOperation(IDrawOperation operation)
+        public void AddDrawOperation(IRef<IDrawOperation> operation)
         {
             EnsureDrawOperationsCreated();
-            _drawOperations.Add(operation);
+            _drawOperations.Add(operation.Clone());
         }
 
         /// <summary>
@@ -131,6 +133,7 @@ namespace Avalonia.Rendering.SceneGraph
         {
             EnsureChildrenCreated();
             _children.Remove(child);
+            child.Dispose();
         }
 
         /// <summary>
@@ -141,7 +144,9 @@ namespace Avalonia.Rendering.SceneGraph
         public void ReplaceChild(int index, IVisualNode node)
         {
             EnsureChildrenCreated();
+            var old = _children[index];
             _children[index] = node;
+            old.Dispose();
         }
 
         /// <summary>
@@ -149,10 +154,15 @@ namespace Avalonia.Rendering.SceneGraph
         /// </summary>
         /// <param name="index">The opeation to be replaced.</param>
         /// <param name="operation">The operation to add.</param>
-        public void ReplaceDrawOperation(int index, IDrawOperation operation)
+        public void ReplaceDrawOperation(int index, IRef<IDrawOperation> operation)
         {
             EnsureDrawOperationsCreated();
-            _drawOperations[index] = operation;
+            var old = _drawOperations[index];
+            _drawOperations[index] = operation.Clone();
+            if (old is IDisposable disposable)
+            {
+                disposable.Dispose();
+            }
         }
 
         /// <summary>
@@ -165,6 +175,10 @@ namespace Avalonia.Rendering.SceneGraph
             if (first < _children?.Count)
             {
                 EnsureChildrenCreated();
+                for (int i = first; i < _children.Count - first; i++)
+                {
+                    _children[i].Dispose();
+                }
                 _children.RemoveRange(first, _children.Count - first);
             }
         }
@@ -179,6 +193,10 @@ namespace Avalonia.Rendering.SceneGraph
             if (first < _drawOperations?.Count)
             {
                 EnsureDrawOperationsCreated();
+                for (int i = first; i < _drawOperations.Count - first; i++)
+                {
+                    _drawOperations[i].Dispose();
+                }
                 _drawOperations.RemoveRange(first, _drawOperations.Count - first);
             }
         }
@@ -209,7 +227,7 @@ namespace Avalonia.Rendering.SceneGraph
         {
             foreach (var operation in DrawOperations)
             {
-                if (operation.HitTest(p) == true)
+                if (operation.Item.HitTest(p) == true)
                 {
                     return true;
                 }
@@ -280,7 +298,7 @@ namespace Avalonia.Rendering.SceneGraph
 
             foreach (var operation in DrawOperations)
             {
-                result = result.Union(operation.Bounds);
+                result = result.Union(operation.Item.Bounds);
             }
 
             _bounds = result;
@@ -299,13 +317,28 @@ namespace Avalonia.Rendering.SceneGraph
         {
             if (_drawOperations == null)
             {
-                _drawOperations = new List<IDrawOperation>();
+                _drawOperations = new List<IRef<IDrawOperation>>();
             }
             else if (_drawOperationsCloned)
             {
-                _drawOperations = new List<IDrawOperation>(_drawOperations);
+                _drawOperations = new List<IRef<IDrawOperation>>(_drawOperations.Select(op => op.Clone()));
                 _drawOperationsCloned = false;
             }
         }
+
+        public void Dispose()
+        {
+            foreach (var child in Children)
+            {
+                child.Dispose();
+            }
+            if (!_drawOperationsCloned)
+            {
+                foreach (var operation in DrawOperations)
+                {
+                    operation.Dispose();
+                } 
+            }
+        }
     }
 }

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -399,7 +399,7 @@ namespace Avalonia.Direct2D1.Media
                 return new ImageBrushImpl(
                     imageBrush,
                     _renderTarget,
-                    (BitmapImpl)imageBrush.Source.PlatformImpl,
+                    (BitmapImpl)imageBrush.Source.PlatformImpl.Item,
                     destinationSize);
             }
             else if (visualBrush != null)