From b3810fc08c6a1cb7dc3bcb836a4dc85d43829fd8 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 22 Jul 2026 18:46:56 +0500 Subject: [PATCH] Fixed a bug with stale _usedCache value that wasn't reset properly (#21823) --- .../ServerCompositionVisual.Render.cs | 7 ++- .../Avalonia.RenderTests/Media/EffectTests.cs | 52 +++++++++++++++++- ...CachedSiblingFollowedByEffect.expected.png | Bin 0 -> 2255 bytes 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/TestFiles/Skia/Media/Effects/CachedSiblingFollowedByEffect.expected.png 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 0000000000000000000000000000000000000000..6c02b510a77e7ced83f3fe3c518654e902f77b38 GIT binary patch literal 2255 zcmdUx`8yj}7spdes7eX-p-fg35*(2}%CbmTzD@=Jl(Udd4n8TRd~OJ}#O1rJnb9Nt z11P`3moY{YzWVmMCwRROW{2G`Kn>ESt#AJN&n{pTD^?SWBE>`43Qv~Ww&Vf$8wzsD z{}wIQLtjn(&TCtq&a{M_lZdyo?7`!ye@V&EThICsIrb_euoA7`g4l@jOOowPHp+}~ zm^UiDl&M3n?#VQf;`ue4zYXd^>w7EvgLIlsKKdPO`V%md@5iqbhj6Gr3MKK78wk4K zk~6G;uohReTC^5@h;DY6)H{GTkrQj?r7LTbi!BOcYSiA2RtFgVK3S;JoUMIn?y{rO zYynWh+jzufI5!j9Pg2<7NvvedMaXeP#F zu_Tl$bd5TT3^dOlQhz%1Y({ULBRgBfjxW89G;nWrdpoM{8GcZM>~GC{4#*076pP!$ zz~37Rx_jsiJ_Qlk!^`;}7B>^4WnHd|n>)+h!bUxp8R8NB8`XZdAiZXl-Gb~FwqS94 zI*y9(G4SgiZPd`oslXOjKPjoInZoE&@^zKGiY=nzzhuxl8qzwL?@6;t(|&}x<}Bhz zgu7;z4ihJC@vG(1>^`9g;Rm(Ip5*}wFE}PiAw@F(q<`L1W`7`Rg%SwCaXjMZ2;+mc z-{%;paw@XI9F@FMxxKTE4uak`ZtQYumxp*KX7+?Yke*Q~N+n^)K_|f^#Z@R8%bnFj z2EW;jf=L-ae0aTRX)CDyf;K;#nVQrUIxJvGTjzT*?=hJY6S#bkKA*-PJ(8r(gGwdP zs?TT1T++ppL(Ck+ymQvTp#VxGMv)N5A?1iqV=7Vxd*HGnO4`aGjnt9fum%6k$WE{ZVT(^&xIdh1@EKpc_h)>NDGmyIn>Hulf!b7t-90Q9bvQ{;I*O@9I)okFzHaJX;D2?7-d>CkQi*J zR@p);Sj^0)UE8@_T!oZ@GcfLin1lAnV?w?uTF#n6GxGTY4s*CaXJNbKE>)?I*W6 zt?n7q=&&o01ZZ>T0*?`rF)(%+OgM7wjjEct3Oo^p?3BLUR~JbiXKplNi=!a70tBl1 zCT%K3gQxfX>K!WBdAdqeR>D?_&D#TfDY4ZdGEBACtloRCh65&yt48W9mgmQQqE9WA z-v#wGAGpfe%wq{^6L)=!Efykjm}4;HJ6-xjADhaee$eeQPKVK_6*pKA-sAzv8rFwl%?CKOW)zd^P) zCi(fZ(j?WaH5FSU+I9W@{^YhDZ92{=O#V#)kf1n&X1|V4Z=Wq7Z5Sy~pA$l}t6CPw z5GT_)qviG?15Xoxx=S}pF^L!*%Mxwo9qYfzH1dc_4p4hN7<^}>TV_wRzRCJ{l