From f8c8098ad57e57bcf7f518be41ffc752fce13d74 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 11 Apr 2020 18:36:19 +0200 Subject: [PATCH] Pool collections used by VisualNode. Ensure that when cloning we initialize collection sizes to avoid reallocations. --- .../Rendering/SceneGraph/Scene.cs | 17 ++++++-- .../Rendering/SceneGraph/SceneLayers.cs | 20 +++++++-- .../Rendering/SceneGraph/VisualNode.cs | 43 +++++++++++++++---- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs index b4bf4c799a..b2e827fa26 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs @@ -12,7 +12,7 @@ namespace Avalonia.Rendering.SceneGraph /// public class Scene : IDisposable { - private Dictionary _index; + private readonly Dictionary _index; /// /// Initializes a new instance of the class. @@ -83,7 +83,7 @@ namespace Avalonia.Rendering.SceneGraph /// The cloned scene. public Scene CloneScene() { - var index = new Dictionary(); + var index = new Dictionary(_index.Count); var root = Clone((VisualNode)Root, null, index); var result = new Scene(root, index, Layers.Clone(), Generation + 1) @@ -162,9 +162,18 @@ namespace Avalonia.Rendering.SceneGraph index.Add(result.Visual, result); - foreach (var child in source.Children) + int childCount = source.Children.Count; + + if (childCount > 0) { - result.AddChild(Clone((VisualNode)child, result, index)); + Span children = result.AddChildrenSpan(childCount); + + for (var i = 0; i < childCount; i++) + { + var child = source.Children[i]; + + children[i] = Clone((VisualNode)child, result, index); + } } return result; diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs index 5960b4f560..25f7383a1a 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs @@ -11,16 +11,28 @@ namespace Avalonia.Rendering.SceneGraph public class SceneLayers : IEnumerable { private readonly IVisual _root; - private readonly List _inner = new List(); - private readonly Dictionary _index = new Dictionary(); + private readonly List _inner; + private readonly Dictionary _index; /// /// Initializes a new instance of the class. /// /// The scene's root visual. - public SceneLayers(IVisual root) + public SceneLayers(IVisual root) : this(root, 0) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The scene's root visual. + /// Initial layer capacity. + public SceneLayers(IVisual root, int capacity) { _root = root; + + _inner = new List(capacity); + _index = new Dictionary(capacity); } /// @@ -84,7 +96,7 @@ namespace Avalonia.Rendering.SceneGraph /// The cloned layers. public SceneLayers Clone() { - var result = new SceneLayers(_root); + var result = new SceneLayers(_root, Count); foreach (var src in _inner) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index 82444a0c29..8cd1a47795 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; +using Avalonia.Collections.Pooled; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Utilities; @@ -19,8 +20,8 @@ namespace Avalonia.Rendering.SceneGraph private Rect? _bounds; private double _opacity; - private List _children; - private List> _drawOperations; + private PooledList _children; + private PooledList> _drawOperations; private IRef _drawOperationsRefCounter; private bool _drawOperationsCloned; private Matrix transformRestore; @@ -349,6 +350,18 @@ namespace Avalonia.Rendering.SceneGraph context.Transform = transformRestore; } + /// + /// Inserts default constructed children into collection and returns a span for the newly created range. + /// + /// Count of children that will be added. + /// + internal Span AddChildrenSpan(int count) + { + EnsureChildrenCreated(count); + + return _children.AddSpan(count); + } + private Rect CalculateBounds() { var result = new Rect(); @@ -362,11 +375,11 @@ namespace Avalonia.Rendering.SceneGraph return result; } - private void EnsureChildrenCreated() + private void EnsureChildrenCreated(int capacity = 0) { if (_children == null) { - _children = new List(); + _children = new PooledList(capacity); } } @@ -377,13 +390,21 @@ namespace Avalonia.Rendering.SceneGraph { if (_drawOperations == null) { - _drawOperations = new List>(); + _drawOperations = new PooledList>(); _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations)); _drawOperationsCloned = false; } else if (_drawOperationsCloned) { - _drawOperations = new List>(_drawOperations.Select(op => op.Clone())); + var oldDrawOperations = _drawOperations; + + _drawOperations = new PooledList>(oldDrawOperations.Count); + + foreach (var drawOperation in oldDrawOperations) + { + _drawOperations.Add(drawOperation.Clone()); + } + _drawOperationsRefCounter.Dispose(); _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations)); _drawOperationsCloned = false; @@ -397,14 +418,16 @@ namespace Avalonia.Rendering.SceneGraph /// /// Draw operations that need to be disposed. /// Disposable for given draw operations. - private static IDisposable CreateDisposeDrawOperations(List> drawOperations) + private static IDisposable CreateDisposeDrawOperations(PooledList> drawOperations) { - return Disposable.Create(() => + return Disposable.Create(drawOperations, operations => { - foreach (var operation in drawOperations) + foreach (var operation in operations) { operation.Dispose(); } + + operations.Dispose(); }); } @@ -414,6 +437,8 @@ namespace Avalonia.Rendering.SceneGraph { _drawOperationsRefCounter?.Dispose(); + _children?.Dispose(); + Disposed = true; } }