From 51d158dc1b1c3f27f6868407307a0ff1ce5aea14 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Jun 2022 01:39:25 +0100 Subject: [PATCH 1/6] clear layout jobs instead of explicit layout call. --- src/Avalonia.Controls/TopLevel.cs | 7 ++++++- src/Avalonia.Controls/WindowBase.cs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 57fb82485c..31217c70a9 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -13,6 +13,7 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; +using Avalonia.Threading; using Avalonia.Utilities; using Avalonia.VisualTree; @@ -412,7 +413,11 @@ namespace Avalonia.Controls FrameSize = PlatformImpl!.FrameSize; Width = clientSize.Width; Height = clientSize.Height; - LayoutManager.ExecuteLayoutPass(); + + // Setting ClientSize and Width / Height above caused ExecuteLayoutPass to be queued. + // Instead of explicitly calling LayoutManager.ExecuteLayoutPass here, we clear the job queue. + Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); + Renderer?.Resized(clientSize); } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 12ba143c8a..5e827cc08f 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; +using Avalonia.Threading; using JetBrains.Annotations; namespace Avalonia.Controls @@ -219,7 +220,11 @@ namespace Avalonia.Controls { ClientSize = clientSize; FrameSize = PlatformImpl?.FrameSize; - LayoutManager.ExecuteLayoutPass(); + + // Setting ClientSize and Width / Height above caused ExecuteLayoutPass to be queued. + // Instead of explicitly calling LayoutManager.ExecuteLayoutPass here, we clear the job queue. + Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); + Renderer?.Resized(clientSize); } From 33c61fdac5272f6f7912e9a39d8a10c4530ba8a5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Jun 2022 01:41:18 +0100 Subject: [PATCH 2/6] reduce work done on osx native side. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 7f2bb128da..f133fa34f6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -298,14 +298,15 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso } @try { - lastSize = NSSize {x, y}; - - if (!_shown) { - BaseEvents->Resized(AvnSize{x, y}, reason); - } - else if(Window != nullptr) { - [Window setContentSize:lastSize]; - [Window invalidateShadow]; + if(x != lastSize.width || y != lastSize.height) { + lastSize = NSSize{x, y}; + + if (!_shown) { + BaseEvents->Resized(AvnSize{x, y}, reason); + } else if (Window != nullptr) { + [Window setContentSize:lastSize]; + [Window invalidateShadow]; + } } } @finally { From 96931e2203840f8e99679cffd2722e4958f44e0a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Jun 2022 11:29:55 +0100 Subject: [PATCH 3/6] prevent excess layout passes in layout manager to catch other scenarios causing excessive layout passes. --- src/Avalonia.Base/Layout/LayoutManager.cs | 14 ++++++++++++-- src/Avalonia.Controls/TopLevel.cs | 6 +----- src/Avalonia.Controls/WindowBase.cs | 6 +----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs index fc988a8d6c..446f135c83 100644 --- a/src/Avalonia.Base/Layout/LayoutManager.cs +++ b/src/Avalonia.Base/Layout/LayoutManager.cs @@ -28,7 +28,7 @@ namespace Avalonia.Layout public LayoutManager(ILayoutRoot owner) { _owner = owner ?? throw new ArgumentNullException(nameof(owner)); - _executeLayoutPass = ExecuteLayoutPass; + _executeLayoutPass = ExecuteQueuedLayoutPass; } public virtual event EventHandler? LayoutUpdated; @@ -94,6 +94,16 @@ namespace Avalonia.Layout QueueLayoutPass(); } + private void ExecuteQueuedLayoutPass() + { + if (!_queued) + { + return; + } + + ExecuteLayoutPass(); + } + /// public virtual void ExecuteLayoutPass() { @@ -319,8 +329,8 @@ namespace Avalonia.Layout { if (!_queued && !_running) { - Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout); _queued = true; + Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout); } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 31217c70a9..0bc4adf1b5 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -413,11 +413,7 @@ namespace Avalonia.Controls FrameSize = PlatformImpl!.FrameSize; Width = clientSize.Width; Height = clientSize.Height; - - // Setting ClientSize and Width / Height above caused ExecuteLayoutPass to be queued. - // Instead of explicitly calling LayoutManager.ExecuteLayoutPass here, we clear the job queue. - Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); - + LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 5e827cc08f..224aeea9e9 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -220,11 +220,7 @@ namespace Avalonia.Controls { ClientSize = clientSize; FrameSize = PlatformImpl?.FrameSize; - - // Setting ClientSize and Width / Height above caused ExecuteLayoutPass to be queued. - // Instead of explicitly calling LayoutManager.ExecuteLayoutPass here, we clear the job queue. - Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); - + LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } From 726ac748ed1136156e564dfec23a65b172e71a47 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Jun 2022 11:30:50 +0100 Subject: [PATCH 4/6] restore changes. --- src/Avalonia.Controls/TopLevel.cs | 1 - src/Avalonia.Controls/WindowBase.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 0bc4adf1b5..57fb82485c 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -13,7 +13,6 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; -using Avalonia.Threading; using Avalonia.Utilities; using Avalonia.VisualTree; diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 224aeea9e9..12ba143c8a 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -8,7 +8,6 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; -using Avalonia.Threading; using JetBrains.Annotations; namespace Avalonia.Controls From 0b4ea2b1eb09115951546e4992657e8b396e9316 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Jun 2022 11:47:40 +0100 Subject: [PATCH 5/6] add unit test. --- .../Layout/LayoutManagerTests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs index f0e8e1cd11..a097d395d8 100644 --- a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; using Avalonia.Layout; +using Avalonia.Threading; using Xunit; namespace Avalonia.Base.UnitTests.Layout @@ -421,5 +422,22 @@ namespace Avalonia.Base.UnitTests.Layout Assert.Equal(new Size(200, 200), control.Bounds.Size); Assert.Equal(new Size(200, 200), control.DesiredSize); } + + [Fact] + public void LayoutManager_Execute_Layout_Pass_Should_Clear_Queued_LayoutPasses() + { + var control = new LayoutTestControl(); + var root = new LayoutTestRoot { Child = control }; + + int layoutCount = 0; + root.LayoutUpdated += (sender, args) => layoutCount++; + + root.LayoutManager.InvalidateArrange(control); + root.LayoutManager.ExecuteInitialLayoutPass(); + + Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); + + Assert.Equal(1, layoutCount); + } } } From 236d10bf64b03e52a93183f6adbb26fcdf09ac04 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Jun 2022 11:48:44 +0100 Subject: [PATCH 6/6] discard unused params --- tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs index a097d395d8..37e07c244e 100644 --- a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs @@ -430,7 +430,7 @@ namespace Avalonia.Base.UnitTests.Layout var root = new LayoutTestRoot { Child = control }; int layoutCount = 0; - root.LayoutUpdated += (sender, args) => layoutCount++; + root.LayoutUpdated += (_, _) => layoutCount++; root.LayoutManager.InvalidateArrange(control); root.LayoutManager.ExecuteInitialLayoutPass();