From 0c55a8fe357010c7fcfc008831397e310d6297a8 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Thu, 24 Aug 2017 20:33:02 +0300 Subject: [PATCH 1/2] Defer changes to dirty visuals --- .../Rendering/DirtyVisuals.cs | 91 ++++++++++++++++--- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs b/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs index f14f5bc912..895b1679b0 100644 --- a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs +++ b/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs @@ -17,6 +17,8 @@ namespace Avalonia.Rendering { private SortedDictionary> _inner = new SortedDictionary>(); private Dictionary _index = new Dictionary(); + private List<(DeferredChange change, IVisual visual)> _deferredChanges = new List<(DeferredChange, IVisual)>(); + private bool _deferring; /// /// Gets the number of dirty visuals. @@ -29,10 +31,15 @@ namespace Avalonia.Rendering /// The dirty visual. public void Add(IVisual visual) { + if (_deferring) + { + _deferredChanges.Add((DeferredChange.Add, visual)); + return; + } + var distance = visual.CalculateDistanceFromAncestor(visual.VisualRoot); - int existingDistance; - if (_index.TryGetValue(visual, out existingDistance)) + if (_index.TryGetValue(visual, out var existingDistance)) { if (distance == existingDistance) { @@ -43,9 +50,7 @@ namespace Avalonia.Rendering _index.Remove(visual); } - List list; - - if (!_inner.TryGetValue(distance, out list)) + if (!_inner.TryGetValue(distance, out var list)) { list = new List(); _inner.Add(distance, list); @@ -60,6 +65,12 @@ namespace Avalonia.Rendering /// public void Clear() { + if (_deferring) + { + _deferredChanges.Add((DeferredChange.Clear, null)); + return; + } + _inner.Clear(); _index.Clear(); } @@ -68,19 +79,19 @@ namespace Avalonia.Rendering /// Removes a visual from the dirty list. /// /// The visual. - /// True if the visual was present in the list; otherwise false. - public bool Remove(IVisual visual) + public void Remove(IVisual visual) { - int distance; + if (_deferring) + { + _deferredChanges.Add((DeferredChange.Remove, visual)); + return; + } - if (_index.TryGetValue(visual, out distance)) + if (_index.TryGetValue(visual, out var distance)) { _inner[distance].Remove(visual); _index.Remove(visual); - return true; } - - return false; } /// @@ -89,19 +100,69 @@ namespace Avalonia.Rendering /// A collection of visuals. public IEnumerator GetEnumerator() { - foreach (var i in _inner) + using (DeferChanges()) { - foreach (var j in i.Value) + foreach (var i in _inner) { - yield return j; + foreach (var j in i.Value) + { + yield return j; + } } } } + private DeferDisposer DeferChanges() + { + _deferring = true; + return new DeferDisposer(this); + } + + private void EndDefer() + { + if (!_deferring) return; + + _deferring = false; + + foreach (var change in _deferredChanges) + { + switch (change.change) + { + case DeferredChange.Add: + Add(change.visual); + break; + case DeferredChange.Remove: + Remove(change.visual); + break; + case DeferredChange.Clear: + Clear(); + break; + } + } + + _deferredChanges.Clear(); + } + /// /// Gets the dirty visuals, in ascending order of distance to their root. /// /// A collection of visuals. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private struct DeferDisposer : IDisposable + { + private DirtyVisuals _parent; + + internal DeferDisposer(DirtyVisuals parent) => _parent = parent; + + public void Dispose() => _parent?.EndDefer(); + } + + private enum DeferredChange + { + Add, + Remove, + Clear + } } } From e069eb8014b931db4be695d89772c0ecb2175dd2 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Sat, 2 Sep 2017 15:48:28 +0300 Subject: [PATCH 2/2] PR comments --- .../Rendering/DirtyVisuals.cs | 79 ++++--------------- 1 file changed, 17 insertions(+), 62 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs b/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs index 895b1679b0..00bc236b9c 100644 --- a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs +++ b/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs @@ -17,8 +17,8 @@ namespace Avalonia.Rendering { private SortedDictionary> _inner = new SortedDictionary>(); private Dictionary _index = new Dictionary(); - private List<(DeferredChange change, IVisual visual)> _deferredChanges = new List<(DeferredChange, IVisual)>(); - private bool _deferring; + private List _deferredChanges = new List(); + private int _deferring; /// /// Gets the number of dirty visuals. @@ -31,9 +31,9 @@ namespace Avalonia.Rendering /// The dirty visual. public void Add(IVisual visual) { - if (_deferring) + if (_deferring > 0) { - _deferredChanges.Add((DeferredChange.Add, visual)); + _deferredChanges.Add(visual); return; } @@ -65,42 +65,23 @@ namespace Avalonia.Rendering /// public void Clear() { - if (_deferring) + if (_deferring > 0) { - _deferredChanges.Add((DeferredChange.Clear, null)); - return; + throw new InvalidOperationException("Cannot clear while enumerating"); } _inner.Clear(); _index.Clear(); } - /// - /// Removes a visual from the dirty list. - /// - /// The visual. - public void Remove(IVisual visual) - { - if (_deferring) - { - _deferredChanges.Add((DeferredChange.Remove, visual)); - return; - } - - if (_index.TryGetValue(visual, out var distance)) - { - _inner[distance].Remove(visual); - _index.Remove(visual); - } - } - /// /// Gets the dirty visuals, in ascending order of distance to their root. /// /// A collection of visuals. public IEnumerator GetEnumerator() { - using (DeferChanges()) + BeginDefer(); + try { foreach (var i in _inner) { @@ -110,34 +91,24 @@ namespace Avalonia.Rendering } } } + finally + { + EndDefer(); + } } - private DeferDisposer DeferChanges() + private void BeginDefer() { - _deferring = true; - return new DeferDisposer(this); + ++_deferring; } private void EndDefer() { - if (!_deferring) return; + if (--_deferring > 0) return; - _deferring = false; - - foreach (var change in _deferredChanges) + foreach (var visual in _deferredChanges) { - switch (change.change) - { - case DeferredChange.Add: - Add(change.visual); - break; - case DeferredChange.Remove: - Remove(change.visual); - break; - case DeferredChange.Clear: - Clear(); - break; - } + Add(visual); } _deferredChanges.Clear(); @@ -148,21 +119,5 @@ namespace Avalonia.Rendering /// /// A collection of visuals. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private struct DeferDisposer : IDisposable - { - private DirtyVisuals _parent; - - internal DeferDisposer(DirtyVisuals parent) => _parent = parent; - - public void Dispose() => _parent?.EndDefer(); - } - - private enum DeferredChange - { - Add, - Remove, - Clear - } } }