Browse Source

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

pull/1277/head
Jeremy Koritzinsky 9 years ago
parent
commit
617f35ce53
  1. 14
      src/Avalonia.Base/Utilities/Ref.cs
  2. 9
      src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs
  3. 8
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  4. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
  5. 66
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  6. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs
  7. 3
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs
  8. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs
  9. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
  10. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  11. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs
  12. 55
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  13. 2
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

14
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) + ">");
}

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>

8
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
src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs

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

66
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
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
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs

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

2
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.

5
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.

4
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
src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs

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

55
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();
}
}
}
}
}

2
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)

Loading…
Cancel
Save