From 5ce4a3ddba16a06c41bbb24c44260ee3b503c8b7 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sun, 19 Apr 2026 12:29:44 +0100 Subject: [PATCH] Do not update composition target when rendering is stopped (#21192) * Do not update composition target when rendering is stopped * Address copilot review --- .../Composition/CompositingRenderer.cs | 15 +++++--- .../Rendering/CompositorLifetimeTests.cs | 36 +++++++++++++++++++ .../CompositorTestServices.cs | 4 +-- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Rendering/CompositorLifetimeTests.cs diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 1bff835f17..c8efb9b931 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -2,18 +2,13 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using System.Numerics; using System.Threading; using System.Threading.Tasks; -using Avalonia.Collections; -using Avalonia.Collections.Pooled; using Avalonia.Diagnostics; -using Avalonia.Platform; using Avalonia.Platform.Surfaces; using Avalonia.Media; using Avalonia.Rendering.Composition.Drawing; using Avalonia.Threading; -using Avalonia.VisualTree; namespace Avalonia.Rendering.Composition; @@ -201,6 +196,13 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester { if(_updating) return; + + if (!CompositionTarget.IsEnabled) + { + _queuedUpdate = false; + return; + } + _updating = true; try { @@ -237,6 +239,9 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester return; CompositionTarget.IsEnabled = true; + + if (_dirty.Count > 0 || _recalculateChildren.Count > 0) + QueueUpdate(); } /// diff --git a/tests/Avalonia.Base.UnitTests/Rendering/CompositorLifetimeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/CompositorLifetimeTests.cs new file mode 100644 index 0000000000..03c84e664a --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Rendering/CompositorLifetimeTests.cs @@ -0,0 +1,36 @@ +using Avalonia.Rendering.Composition; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Xunit; + +namespace Avalonia.Base.UnitTests.Rendering; + +public class CompositorLifetimeTests : CompositorTestsBase +{ + [Fact] + public void InvalidateVisual_Does_Not_Update_RenderingTarget_When_Rendering_Stopped() + { + using var services = new CompositorTestServices(new Size(200, 200)); + + var presentationSource = services.TopLevel.GetPresentationSource(); + Assert.NotNull(presentationSource); + + var compositionTarget = ((CompositingRenderer)presentationSource.Renderer).CompositionTarget; + Assert.True(compositionTarget.IsEnabled); + Assert.Equal(new PixelSize(200, 200), compositionTarget.PixelSize); + + // Stop rendering and invalidate a visual: this should not result in an update + services.TopLevel.StopRendering(); + ((CompositorTestServices.TopLevelImpl)services.TopLevel.PlatformImpl!).ClientSize = new Size(300, 300); + services.TopLevel.InvalidateVisual(); + services.RunJobs(); + + Assert.Equal(new PixelSize(200, 200), compositionTarget.PixelSize); + + // Check that restarting rendering re-queues the pending invalidation + services.TopLevel.StartRendering(); + services.RunJobs(); + + Assert.Equal(new PixelSize(300, 300), compositionTarget.PixelSize); + } +} diff --git a/tests/Avalonia.UnitTests/CompositorTestServices.cs b/tests/Avalonia.UnitTests/CompositorTestServices.cs index cb2a84049c..7fd5ae6ad8 100644 --- a/tests/Avalonia.UnitTests/CompositorTestServices.cs +++ b/tests/Avalonia.UnitTests/CompositorTestServices.cs @@ -142,7 +142,7 @@ public class CompositorTestServices : IDisposable public void TriggerTick() => Tick?.Invoke(TimeSpan.Zero); } - class TopLevelImpl : ITopLevelImpl + public class TopLevelImpl : ITopLevelImpl { private readonly Compositor _compositor; @@ -159,7 +159,7 @@ public class CompositorTestServices : IDisposable public double DesktopScaling => 1; public IPlatformHandle? Handle => null; - public Size ClientSize { get; } + public Size ClientSize { get; set; } public double RenderScaling => 1; public IPlatformRenderSurface[] Surfaces { get; } = [new DummyFramebufferSurface()]; public Action? Input { get; set; }