diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Render.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Render.cs index 8d8b862bb3..fe2b88e264 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Render.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual/ServerCompositionVisual.Render.cs @@ -192,7 +192,12 @@ partial class ServerCompositionVisual if (visual.RenderOptions != default) _canvas.PopRenderOptions(); } - + + // A cached visual is always a leaf in the walk (children are skipped), so its + // PreSubgraph/PostSubgraph are adjacent. Reset here so the flag never leaks into a + // later sibling and causes its effect/mask/options pushes to be left unpopped. + _usedCache = false; + // If we are rendering to bitmap cache, PreSubgraph skipped those for the root visual if (!bitmapCacheRoot) { diff --git a/tests/Avalonia.RenderTests/Media/EffectTests.cs b/tests/Avalonia.RenderTests/Media/EffectTests.cs index c8b931dae8..d2c346c582 100644 --- a/tests/Avalonia.RenderTests/Media/EffectTests.cs +++ b/tests/Avalonia.RenderTests/Media/EffectTests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Shapes; +using Avalonia.Layout; using Avalonia.Media; using Xunit; #pragma warning disable CS0649 @@ -76,6 +77,55 @@ public class EffectTests : TestBase await RenderToFile(target); CompareImages(skipImmediate: true); } - + + [Fact] + public async Task CachedSiblingFollowedByEffect() + { + var target = new Border + { + Background = Brushes.White, + Width = 200, + Height = 200, + Child = new StackPanel + { + Orientation = Orientation.Horizontal, + Spacing = 10, + Margin = new Thickness(15), + Children = + { + new Rectangle + { + Width = 50, + Height = 50, + Fill = Brushes.Yellow, + CacheMode = new BitmapCache() + }, + new Rectangle + { + Width = 50, + Height = 50, + Fill = Brushes.Blue, + Effect = new DropShadowEffect + { + Opacity = 1, + OffsetX = 0, + OffsetY = 0, + Color = Colors.Black, + BlurRadius = 20 + } + }, + new Rectangle + { + Width = 50, + Height = 50, + Fill = Brushes.Green + } + } + } + }; + + await RenderToFile(target); + CompareImages(skipImmediate: true); + } } #endif diff --git a/tests/TestFiles/Skia/Media/Effects/CachedSiblingFollowedByEffect.expected.png b/tests/TestFiles/Skia/Media/Effects/CachedSiblingFollowedByEffect.expected.png new file mode 100644 index 0000000000..6c02b510a7 Binary files /dev/null and b/tests/TestFiles/Skia/Media/Effects/CachedSiblingFollowedByEffect.expected.png differ