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.
///
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
index 8688671d3b..b64423ec10 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, 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());
}
diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs
index b114ed9468..56c1965df8 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;
@@ -65,8 +66,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());
}