From ec164ea0c3aa1cfeef16bc9d9f920a40709c8efa Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 29 Aug 2022 14:48:11 +0300 Subject: [PATCH 1/3] Throw on attempt to invalidate a visual during a render pass --- .../Composition/CompositingRenderer.cs | 22 ++++++++++++- src/Avalonia.Base/Rendering/DirtyVisuals.cs | 33 ++++--------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 9aa3c25425..9742a6b3ba 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -29,6 +29,7 @@ public class CompositingRenderer : IRendererWithCompositor private bool _queuedUpdate; private Action _update; private Action _invalidateScene; + private bool _updating; internal CompositionTarget CompositionTarget; @@ -77,6 +78,8 @@ public class CompositingRenderer : IRendererWithCompositor /// public void AddDirty(IVisual visual) { + if (_updating) + throw new InvalidOperationException("Visual was invalidated during the render pass"); _dirty.Add((Visual)visual); QueueUpdate(); } @@ -107,6 +110,8 @@ public class CompositingRenderer : IRendererWithCompositor /// public void RecalculateChildren(IVisual visual) { + if (_updating) + throw new InvalidOperationException("Visual was invalidated during the render pass"); _recalculateChildren.Add((Visual)visual); QueueUpdate(); } @@ -191,7 +196,7 @@ public class CompositingRenderer : IRendererWithCompositor private void InvalidateScene() => SceneInvalidated?.Invoke(this, new SceneInvalidatedEventArgs(_root, new Rect(_root.ClientSize))); - private void Update() + private void UpdateCore() { _queuedUpdate = false; foreach (var visual in _dirty) @@ -240,6 +245,21 @@ public class CompositingRenderer : IRendererWithCompositor CompositionTarget.Scaling = _root.RenderScaling; Compositor.InvokeOnNextCommit(_invalidateScene); } + + private void Update() + { + if(_updating) + return; + _updating = true; + try + { + UpdateCore(); + } + finally + { + _updating = false; + } + } public void Resized(Size size) { diff --git a/src/Avalonia.Base/Rendering/DirtyVisuals.cs b/src/Avalonia.Base/Rendering/DirtyVisuals.cs index 00bc236b9c..999b12e810 100644 --- a/src/Avalonia.Base/Rendering/DirtyVisuals.cs +++ b/src/Avalonia.Base/Rendering/DirtyVisuals.cs @@ -17,8 +17,7 @@ namespace Avalonia.Rendering { private SortedDictionary> _inner = new SortedDictionary>(); private Dictionary _index = new Dictionary(); - private List _deferredChanges = new List(); - private int _deferring; + private int _enumerating; /// /// Gets the number of dirty visuals. @@ -31,10 +30,9 @@ namespace Avalonia.Rendering /// The dirty visual. public void Add(IVisual visual) { - if (_deferring > 0) + if (_enumerating > 0) { - _deferredChanges.Add(visual); - return; + throw new InvalidOperationException("Visual was invalidated during a render pass"); } var distance = visual.CalculateDistanceFromAncestor(visual.VisualRoot); @@ -65,7 +63,7 @@ namespace Avalonia.Rendering /// public void Clear() { - if (_deferring > 0) + if (_enumerating > 0) { throw new InvalidOperationException("Cannot clear while enumerating"); } @@ -80,7 +78,7 @@ namespace Avalonia.Rendering /// A collection of visuals. public IEnumerator GetEnumerator() { - BeginDefer(); + _enumerating++; try { foreach (var i in _inner) @@ -93,27 +91,10 @@ namespace Avalonia.Rendering } finally { - EndDefer(); + _enumerating--; } } - - private void BeginDefer() - { - ++_deferring; - } - - private void EndDefer() - { - if (--_deferring > 0) return; - - foreach (var visual in _deferredChanges) - { - Add(visual); - } - - _deferredChanges.Clear(); - } - + /// /// Gets the dirty visuals, in ascending order of distance to their root. /// From 7bc608d37beacfc71ce0a578352509769c1c258f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 Aug 2022 11:04:13 +0100 Subject: [PATCH 2/3] use composition renderer only on ios. --- .../Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs | 3 ++- src/iOS/Avalonia.iOS/AvaloniaView.cs | 5 +++-- src/iOS/Avalonia.iOS/Platform.cs | 7 +++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 8688671d3b..508950162d 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -7,6 +7,7 @@ using Avalonia.LinuxFramebuffer.Input; using Avalonia.LinuxFramebuffer.Output; using Avalonia.Platform; using Avalonia.Rendering; +using Avalonia.Rendering.Composition; namespace Avalonia.LinuxFramebuffer { @@ -32,7 +33,7 @@ namespace Avalonia.LinuxFramebuffer { var factory = AvaloniaLocator.Current.GetService(); var renderLoop = AvaloniaLocator.Current.GetService(); - return factory?.Create(root, renderLoop) ?? new DeferredRenderer(root, renderLoop); + return factory?.Create(root, renderLoop) ?? new CompositingRenderer(root, new Compositor(renderLoop, null)); } public void Dispose() diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index cc40401638..c4a2b5bcb1 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -10,6 +10,7 @@ using Avalonia.iOS.Storage; using Avalonia.Platform; using Avalonia.Platform.Storage; using Avalonia.Rendering; +using Avalonia.Rendering.Composition; using CoreAnimation; using Foundation; using ObjCRuntime; @@ -63,8 +64,8 @@ namespace Avalonia.iOS // No-op } - public IRenderer CreateRenderer(IRenderRoot root) => new DeferredRenderer(root, - AvaloniaLocator.Current.GetService()); + public IRenderer CreateRenderer(IRenderRoot root) => new CompositingRenderer(root, Platform.Compositor); + public void Invalidate(Rect rect) { diff --git a/src/iOS/Avalonia.iOS/Platform.cs b/src/iOS/Avalonia.iOS/Platform.cs index 2738e502de..67d037f9e5 100644 --- a/src/iOS/Avalonia.iOS/Platform.cs +++ b/src/iOS/Avalonia.iOS/Platform.cs @@ -6,6 +6,7 @@ using Avalonia.Input.Platform; using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; +using Avalonia.Rendering.Composition; namespace Avalonia { @@ -26,6 +27,8 @@ namespace Avalonia.iOS { public static EaglFeature GlFeature; public static DisplayLinkTimer Timer; + internal static Compositor Compositor { get; private set; } + class PlatformSettings : IPlatformSettings { /// @@ -57,6 +60,10 @@ namespace Avalonia.iOS .Bind().ToConstant(Timer) .Bind().ToConstant(new PlatformThreadingInterface()) .Bind().ToConstant(keyboard); + + Compositor = new Compositor( + AvaloniaLocator.Current.GetRequiredService(), + AvaloniaLocator.Current.GetService()); } From 15c855de29a8580d1653ce82df9a52f70a55a2ac Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 Aug 2022 13:33:35 +0100 Subject: [PATCH 3/3] fix framebuffer compositor platform. --- .../Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs | 2 +- .../LinuxFramebufferPlatform.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 508950162d..b64423ec10 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -33,7 +33,7 @@ namespace Avalonia.LinuxFramebuffer { var factory = AvaloniaLocator.Current.GetService(); var renderLoop = AvaloniaLocator.Current.GetService(); - return factory?.Create(root, renderLoop) ?? new CompositingRenderer(root, new Compositor(renderLoop, null)); + return factory?.Create(root, renderLoop) ?? new CompositingRenderer(root, LinuxFramebufferPlatform.Compositor); } public void Dispose() diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index a642766809..bf9452e191 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -15,6 +15,7 @@ using Avalonia.LinuxFramebuffer.Output; using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; +using Avalonia.Rendering.Composition; using Avalonia.Threading; using JetBrains.Annotations; @@ -26,6 +27,10 @@ namespace Avalonia.LinuxFramebuffer private static readonly Stopwatch St = Stopwatch.StartNew(); internal static uint Timestamp => (uint)St.ElapsedTicks; public static InternalPlatformThreadingInterface Threading; + + internal static Compositor Compositor { get; private set; } + + LinuxFramebufferPlatform(IOutputBackend backend) { _fb = backend; @@ -48,6 +53,10 @@ namespace Avalonia.LinuxFramebuffer .Bind().ToConstant(new KeyboardDevice()) .Bind().ToSingleton() .Bind().ToSingleton(); + + Compositor = new Compositor( + AvaloniaLocator.Current.GetRequiredService(), + AvaloniaLocator.Current.GetService()); }