From dd44ec447f6c9238fad0d696f5ed7fa2ce08d043 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 14 Mar 2026 02:17:52 +0500 Subject: [PATCH] Use the correct flag to determine if extra dirty rect needs to be combined with existing one # Conflicts: # src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.ComputedProperties.cs --- ...verCompositionVisual.ComputedProperties.cs | 6 ++--- .../ServerCompositionVisual.DirtyInputs.cs | 2 +- .../ServerCompositionVisual.Update.cs | 6 ++--- .../Rendering/CompositorInvalidationTests.cs | 25 +++++++++++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.ComputedProperties.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.ComputedProperties.cs index 7a9ed98787..34b57008b9 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.ComputedProperties.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.ComputedProperties.cs @@ -50,7 +50,7 @@ partial class ServerCompositionVisual private LtrbRect? _ownClipRect; - private bool _hasExtraDirtyRect; + private bool _needsToAddExtraDirtyRectToDirtyRegion; private LtrbRect _extraDirtyRect; public virtual LtrbRect? ComputeOwnContentBounds() => null; @@ -107,7 +107,7 @@ partial class ServerCompositionVisual _isDirtyForRender |= fDirtyForRender; // If node itself is dirty for render, we don't need to keep track of extra dirty rects - _hasExtraDirtyRect = !fDirtyForRender && (_hasExtraDirtyRect || fAdditionalDirtyRegion); + _needsToAddExtraDirtyRectToDirtyRegion = !fDirtyForRender && (_needsToAddExtraDirtyRectToDirtyRegion || fAdditionalDirtyRegion); } public void RecomputeOwnProperties() @@ -161,4 +161,4 @@ partial class ServerCompositionVisual _ownBoundsDirty = _clipSizeDirty = _combinedTransformDirty = _compositionFieldsDirty = false; PropagateFlags(setDirtyBounds, setDirtyForRender, setHasExtraDirtyRect); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.DirtyInputs.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.DirtyInputs.cs index 75f0185b84..8b2d8195b2 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.DirtyInputs.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.DirtyInputs.cs @@ -163,7 +163,7 @@ partial class ServerCompositionVisual protected void AddExtraDirtyRect(LtrbRect rect) { - _extraDirtyRect = _hasExtraDirtyRect ? _extraDirtyRect.Union(rect) : rect; + _extraDirtyRect = _delayPropagateHasExtraDirtyRects ? _extraDirtyRect.Union(rect) : rect; _delayPropagateHasExtraDirtyRects = true; EnqueueOwnPropertiesRecompute(); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Update.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Update.cs index 336f26ac7c..daaba3fa2d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Update.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Update.cs @@ -56,7 +56,7 @@ internal partial class ServerCompositionVisual private bool NeedToPushBoundsAffectingProperties(ServerCompositionVisual node) { - return (node._isDirtyForRenderInSubgraph || node._hasExtraDirtyRect || node._contentChanged); + return (node._isDirtyForRenderInSubgraph || node._needsToAddExtraDirtyRectToDirtyRegion || node._contentChanged); } public void PreSubgraph(ServerCompositionVisual node, out bool visitChildren) @@ -141,7 +141,7 @@ internal partial class ServerCompositionVisual // specified before the tranform, i.e. in inner space, hence we have to pick them // up before we pop the transform from the transform stack. // - if (node._hasExtraDirtyRect) + if (node._needsToAddExtraDirtyRectToDirtyRegion) { AddToDirtyRegion(node._extraDirtyRect); } @@ -167,7 +167,7 @@ internal partial class ServerCompositionVisual node._isDirtyForRender = false; node._isDirtyForRenderInSubgraph = false; node._needsBoundingBoxUpdate = false; - node._hasExtraDirtyRect = false; + node._needsToAddExtraDirtyRectToDirtyRegion = false; node._contentChanged = false; } diff --git a/tests/Avalonia.Base.UnitTests/Rendering/CompositorInvalidationTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/CompositorInvalidationTests.cs index 699f450223..ef0e01a104 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/CompositorInvalidationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CompositorInvalidationTests.cs @@ -38,6 +38,31 @@ public class CompositorInvalidationTests : CompositorTestsBase s.AssertRects(new Rect(30, 50, 20, 10)); } } + + [Fact] + public void Sibling_Controls_Should_Invalidate_Union_Rect_When_Removed() + { + using (var s = new CompositorCanvas()) + { + var control = new Border() + { + Background = Brushes.Red, Width = 20, Height = 10, + [Canvas.LeftProperty] = 30, [Canvas.TopProperty] = 10 + }; + var control2 = new Border() + { + Background = Brushes.Blue, Width = 20, Height = 10, + [Canvas.LeftProperty] = 30, [Canvas.TopProperty] = 50 + }; + s.Canvas.Children.Add(control); + s.Canvas.Children.Add(control2); + s.RunJobs(); + s.Events.Rects.Clear(); + s.Canvas.Children.Remove(control); + s.Canvas.Children.Remove(control2); + s.AssertRects(new Rect(30, 10, 20, 50)); + } + } [Fact] public void Control_Should_Invalidate_Both_Own_Rects_When_Moved()