Pārlūkot izejas kodu

Removed legacy renderers

Nikita Tsukanov 3 gadi atpakaļ
vecāks
revīzija
6bdf0eacc7
80 mainītis faili ar 499 papildinājumiem un 7978 dzēšanām
  1. 1 2
      samples/ControlCatalog.NetCore/Program.cs
  2. 0 4
      samples/RenderDemo/App.xaml.cs
  3. 5 12
      src/Android/Avalonia.Android/AndroidPlatform.cs
  4. 1 10
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  5. 0 4
      src/Avalonia.Base/Avalonia.Base.csproj
  6. 8 0
      src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs
  7. 6 0
      src/Avalonia.Base/Point.cs
  8. 27 0
      src/Avalonia.Base/Rect.cs
  9. 12 3
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  10. 0 3
      src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
  11. 4 5
      src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
  12. 3 0
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  13. 0 780
      src/Avalonia.Base/Rendering/DeferredRenderer.cs
  14. 16 0
      src/Avalonia.Base/Rendering/ICustomHitTest.cs
  15. 0 33
      src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs
  16. 0 6
      src/Avalonia.Base/Rendering/IRenderRoot.cs
  17. 0 16
      src/Avalonia.Base/Rendering/IRendererFactory.cs
  18. 11 237
      src/Avalonia.Base/Rendering/ImmediateRenderer.cs
  19. 1 3
      src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs
  20. 0 48
      src/Avalonia.Base/Rendering/RenderLayer.cs
  21. 0 69
      src/Avalonia.Base/Rendering/RenderLayers.cs
  22. 0 482
      src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  23. 0 24
      src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs
  24. 0 105
      src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs
  25. 0 352
      src/Avalonia.Base/Rendering/SceneGraph/Scene.cs
  26. 0 485
      src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs
  27. 0 73
      src/Avalonia.Base/Rendering/SceneGraph/SceneLayer.cs
  28. 0 206
      src/Avalonia.Base/Rendering/SceneGraph/SceneLayers.cs
  29. 0 448
      src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs
  30. 1 17
      src/Avalonia.Base/Visual.cs
  31. 15 3
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  32. 1 3
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  33. 10 4
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  34. 0 5
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  35. 1 9
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  36. 1 3
      src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
  37. 1 3
      src/Avalonia.Controls/Primitives/OverlayLayer.cs
  38. 6 10
      src/Avalonia.Controls/Primitives/Popup.cs
  39. 22 10
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  40. 0 6
      src/Avalonia.Controls/TopLevel.cs
  41. 11 3
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  42. 11 3
      src/Avalonia.Diagnostics/Diagnostics/Conventions.cs
  43. 18 14
      src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml.cs
  44. 4 4
      src/Avalonia.Diagnostics/Diagnostics/VisualExtensions.cs
  45. 0 1
      src/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs
  46. 1 3
      src/Avalonia.Headless/AvaloniaHeadlessPlatform.cs
  47. 2 5
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  48. 3 10
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  49. 0 14
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  50. 3 20
      src/Avalonia.Native/WindowImplBase.cs
  51. 0 120
      src/Avalonia.X11/X11ImmediateRendererProxy.cs
  52. 2 18
      src/Avalonia.X11/X11Platform.cs
  53. 2 19
      src/Avalonia.X11/X11Window.cs
  54. 1 10
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  55. 2 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  56. 2 18
      src/Windows/Avalonia.Win32/Win32Platform.cs
  57. 2 26
      src/Windows/Avalonia.Win32/WindowImpl.cs
  58. 2 1
      tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs
  59. 15 15
      tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs
  60. 1 172
      tests/Avalonia.Base.UnitTests/Rendering/CompositorTestsBase.cs
  61. 1 2
      tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs
  62. 0 790
      tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs
  63. 0 577
      tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs
  64. 0 273
      tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs
  65. 0 458
      tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs
  66. 0 225
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs
  67. 0 1087
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  68. 0 258
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
  69. 0 35
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs
  70. 0 33
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs
  71. 0 123
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs
  72. 0 52
      tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs
  73. 19 34
      tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs
  74. 1 1
      tests/Avalonia.RenderTests/Controls/AdornerTests.cs
  75. 1 26
      tests/Avalonia.RenderTests/TestBase.cs
  76. 32 39
      tests/Avalonia.Skia.UnitTests/HitTesting.cs
  77. 3 4
      tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
  78. 207 0
      tests/Avalonia.UnitTests/CompositorTestServices.cs
  79. BIN
      tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png
  80. BIN
      tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png

+ 1 - 2
samples/ControlCatalog.NetCore/Program.cs

@@ -55,8 +55,7 @@ namespace ControlCatalog.NetCore
                 return builder
                     .UseHeadless(new AvaloniaHeadlessPlatformOptions
                     {
-                        UseHeadlessDrawing = true,
-                        UseCompositor = true
+                        UseHeadlessDrawing = true
                     })
                     .AfterSetup(_ =>
                     {

+ 0 - 4
samples/RenderDemo/App.xaml.cs

@@ -29,10 +29,6 @@ namespace RenderDemo
                .With(new Win32PlatformOptions
                {
                    OverlayPopups = true,
-               })
-               .With(new X11PlatformOptions
-               {
-                   UseCompositor = true
                })
                 .UsePlatformDetect()
                 .LogToTrace();

+ 5 - 12
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -32,7 +32,6 @@ namespace Avalonia.Android
         public static AndroidPlatformOptions Options { get; private set; }
 
         internal static Compositor Compositor { get; private set; }
-        internal static PlatformRenderInterfaceContextManager RenderInterface { get; private set; }
 
         public static void Initialize()
         {
@@ -55,16 +54,11 @@ namespace Avalonia.Android
                 EglPlatformGraphics.TryInitialize();
             }
             
-            if (Options.UseCompositor)
-            {
-                Compositor = new Compositor(
-                    AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(),
-                    AvaloniaLocator.Current.GetService<IPlatformGraphics>());
-            }
-            else
-                RenderInterface =
-                    new PlatformRenderInterfaceContextManager(AvaloniaLocator.Current
-                        .GetService<IPlatformGraphics>());
+            Compositor = new Compositor(
+                AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(),
+                AvaloniaLocator.Current.GetService<IPlatformGraphics>());
+            
+
         }
     }
 
@@ -72,6 +66,5 @@ namespace Avalonia.Android
     {
         public bool UseDeferredRendering { get; set; } = false;
         public bool UseGpu { get; set; } = true;
-        public bool UseCompositor { get; set; } = true;
     }
 }

+ 1 - 10
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -109,16 +109,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         public IEnumerable<object> Surfaces => new object[] { _gl, _framebuffer, Handle };
 
         public IRenderer CreateRenderer(IRenderRoot root) =>
-            AndroidPlatform.Options.UseCompositor
-                ? new CompositingRenderer(root, AndroidPlatform.Compositor, () => Surfaces)
-                : AndroidPlatform.Options.UseDeferredRendering
-                    ? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(),
-                            () => AndroidPlatform.RenderInterface.CreateRenderTarget(Surfaces),
-                            AndroidPlatform.RenderInterface)
-                        { RenderOnlyOnRenderThread = true }
-                    : new ImmediateRenderer((Visual)root,
-                        () => AndroidPlatform.RenderInterface.CreateRenderTarget(Surfaces),
-                        AndroidPlatform.RenderInterface);
+            new CompositingRenderer(root, AndroidPlatform.Compositor, () => Surfaces);
 
         public virtual void Hide()
         {

+ 0 - 4
src/Avalonia.Base/Avalonia.Base.csproj

@@ -70,8 +70,4 @@
   <ItemGroup Label="Build dependency">
     <ProjectReference Include="$(MSBuildThisFileDirectory)\..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj" SetTargetFramework="TargetFramework=netstandard2.0" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" />
   </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="Rendering\Composition\Utils" />
-  </ItemGroup>
 </Project>

+ 8 - 0
src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs

@@ -105,5 +105,13 @@ namespace Avalonia.Input.TextInput
             UnsubscribeFromParents();
             UpdateMatrix();
         }
+
+        public static IDisposable Track(Visual visual, Action<Visual, Matrix?> cb)
+        {
+            var rv = new TransformTrackingHelper();
+            rv.MatrixChanged += () => cb(visual, rv.Matrix);
+            rv.SetVisual(visual);
+            return rv;
+        }
     }
 }

+ 6 - 0
src/Avalonia.Base/Point.cs

@@ -251,6 +251,12 @@ namespace Avalonia
         /// <param name="transform">The transform.</param>
         /// <returns>The transformed point.</returns>
         public Point Transform(Matrix transform) => transform.Transform(this);
+        
+        internal Point Transform(Matrix4x4 matrix)
+        {
+            var vec = Vector2.Transform(new Vector2((float)X, (float)Y), matrix);
+            return new Point(vec.X, vec.Y);
+        }
 
         /// <summary>
         /// Returns a new point with the specified X coordinate.

+ 27 - 0
src/Avalonia.Base/Rect.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Globalization;
+using System.Numerics;
 using Avalonia.Animation.Animators;
 using Avalonia.Utilities;
 
@@ -441,6 +442,32 @@ namespace Avalonia
 
             return new Rect(new Point(left, top), new Point(right, bottom));
         }
+        
+        internal Rect TransformToAABB(Matrix4x4 matrix)
+        {
+            ReadOnlySpan<Point> points = stackalloc Point[4]
+            {
+                TopLeft.Transform(matrix),
+                TopRight.Transform(matrix),
+                BottomRight.Transform(matrix),
+                BottomLeft.Transform(matrix)
+            };
+
+            var left = double.MaxValue;
+            var right = double.MinValue;
+            var top = double.MaxValue;
+            var bottom = double.MinValue;
+
+            foreach (var p in points)
+            {
+                if (p.X < left) left = p.X;
+                if (p.X > right) right = p.X;
+                if (p.Y < top) top = p.Y;
+                if (p.Y > bottom) bottom = p.Y;
+            }
+
+            return new Rect(new Point(left, top), new Point(right, bottom));
+        }
 
         /// <summary>
         /// Translates the rectangle by an offset.

+ 12 - 3
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@@ -83,8 +83,16 @@ public class CompositingRenderer : IRendererWithCompositor
     }
 
     /// <inheritdoc/>
-    public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool>? filter)
+    public IEnumerable<Visual> HitTest(Point p, Visual? root, Func<Visual, bool>? filter)
     {
+        CompositionVisual? rootVisual = null;
+        if (root != null)
+        {
+            if (root.CompositionVisual == null)
+                yield break;
+            rootVisual = root.CompositionVisual;
+        }
+        
         Func<CompositionVisual, bool>? f = null;
         if (filter != null)
             f = v =>
@@ -93,8 +101,8 @@ public class CompositingRenderer : IRendererWithCompositor
                     return filter(dlv.Visual);
                 return true;
             };
-        
-        var res = CompositionTarget.TryHitTest(p, f);
+
+        var res = CompositionTarget.TryHitTest(p, rootVisual, f);
         if(res == null)
             yield break;
         foreach(var v in res)
@@ -257,6 +265,7 @@ public class CompositingRenderer : IRendererWithCompositor
         _recalculateChildren.Clear();
         CompositionTarget.Size = _root.ClientSize;
         CompositionTarget.Scaling = _root.RenderScaling;
+        TriggerSceneInvalidatedOnBatchCompletion(_compositor.RequestCommitAsync());
     }
 
     private async void TriggerSceneInvalidatedOnBatchCompletion(Task batchCompletion)

+ 0 - 3
src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs

@@ -61,9 +61,6 @@ internal class CompositionDrawListVisual : CompositionContainerVisual
             return false;
         if (custom != null)
         {
-            // Simulate the old behavior
-            // TODO: Change behavior once legacy renderers are removed
-            pt += new Point(Offset.X, Offset.Y);
             return custom.HitTest(pt);
         }
 

+ 4 - 5
src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs

@@ -28,16 +28,15 @@ namespace Avalonia.Rendering.Composition
         /// <summary>
         /// Attempts to perform a hit-tst
         /// </summary>
-        /// <param name="point"></param>
-        /// <param name="filter"></param>
         /// <returns></returns>
-        public PooledList<CompositionVisual>? TryHitTest(Point point, Func<CompositionVisual, bool>? filter)
+        public PooledList<CompositionVisual>? TryHitTest(Point point, CompositionVisual? root, Func<CompositionVisual, bool>? filter)
         {
             Server.Readback.NextRead();
-            if (Root == null)
+            root ??= Root;
+            if (root == null)
                 return null;
             var res = new PooledList<CompositionVisual>();
-            HitTestCore(Root, point, res, filter);
+            HitTestCore(root, point, res, filter);
             return res;
         }
 

+ 3 - 0
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@@ -37,6 +37,8 @@ namespace Avalonia.Rendering.Composition
         private List<Action> _pendingServerCompositorJobs = new();
 
         internal IEasing DefaultEasing { get; }
+
+        internal event Action? AfterCommit;
         
 
         /// <summary>
@@ -88,6 +90,7 @@ namespace Avalonia.Rendering.Composition
             {
                 if (_invokeBeforeCommitWrite.Count > 0)
                     RequestCommitAsync();
+                AfterCommit?.Invoke();
             }
         }
         

+ 0 - 780
src/Avalonia.Base/Rendering/DeferredRenderer.cs

@@ -1,780 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Avalonia.Logging;
-using Avalonia.Media;
-using Avalonia.Media.Immutable;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Threading;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering
-{
-    /// <summary>
-    /// A renderer which renders the state of the visual tree to an intermediate scene graph
-    /// representation which is then rendered on a rendering thread.
-    /// </summary>
-    public class DeferredRenderer : RendererBase, IRenderer, IRenderLoopTask, IVisualBrushRenderer
-    {
-        private readonly IDispatcher? _dispatcher;
-        private readonly IRenderLoop? _renderLoop;
-        private readonly Func<IRenderTarget>? _renderTargetFactory;
-        private readonly PlatformRenderInterfaceContextManager? _renderInterface;
-        private readonly Visual _root;
-        private readonly ISceneBuilder _sceneBuilder;
-
-        private bool _running;
-        private bool _disposed;
-        private volatile IRef<Scene>? _scene;
-        private DirtyVisuals? _dirty;
-        private HashSet<Visual>? _recalculateChildren;
-        private IRef<IRenderTargetBitmapImpl>? _overlay;
-        private int _lastSceneId = -1;
-        private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
-        private IRef<IDrawOperation>? _currentDraw;
-        private readonly IDeferredRendererLock _lock;
-        private readonly object _sceneLock = new object();
-        private readonly object _startStopLock = new object();
-        private readonly object _renderLoopIsRenderingLock = new object();
-        private readonly Action _updateSceneIfNeededDelegate;
-        private List<Action>? _pendingRenderThreadJobs;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
-        /// </summary>
-        /// <param name="root">The control to render.</param>
-        /// <param name="renderLoop">The render loop.</param>
-        /// <param name="renderTargetFactory">The target render factory.</param>
-        /// <param name="renderInterface">The Platform Render Context.</param>
-        /// <param name="sceneBuilder">The scene builder to use. Optional.</param>
-        /// <param name="dispatcher">The dispatcher to use. Optional.</param>
-        /// <param name="rendererLock">Lock object used before trying to access render target</param>
-        public DeferredRenderer(
-            IRenderRoot root,
-            IRenderLoop renderLoop,
-            Func<IRenderTarget> renderTargetFactory,
-            PlatformRenderInterfaceContextManager? renderInterface = null,
-            ISceneBuilder? sceneBuilder = null,
-            IDispatcher? dispatcher = null,
-            IDeferredRendererLock? rendererLock = null) : base(true)
-        {
-            _dispatcher = dispatcher ?? Dispatcher.UIThread;
-            _root = root as Visual ?? throw new ArgumentNullException(nameof(root));
-            _sceneBuilder = sceneBuilder ?? new SceneBuilder();
-            Layers = new RenderLayers();
-            _renderLoop = renderLoop;
-            _renderTargetFactory = renderTargetFactory;
-            _renderInterface = renderInterface;
-            _lock = rendererLock ?? new ManagedDeferredRendererLock();
-            _updateSceneIfNeededDelegate = UpdateSceneIfNeeded;
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
-        /// </summary>
-        /// <param name="root">The control to render.</param>
-        /// <param name="renderTarget">The render target.</param>
-        /// <param name="sceneBuilder">The scene builder to use. Optional.</param>
-        /// <remarks>
-        /// This constructor is intended to be used for unit testing.
-        /// </remarks>
-        public DeferredRenderer(
-            Visual root,
-            IRenderTarget renderTarget,
-            ISceneBuilder? sceneBuilder = null) : base(true)
-        {
-            _root = root ?? throw new ArgumentNullException(nameof(root));
-            RenderTarget = renderTarget ?? throw new ArgumentNullException(nameof(renderTarget));
-            _sceneBuilder = sceneBuilder ?? new SceneBuilder();
-            Layers = new RenderLayers();
-            _lock = new ManagedDeferredRendererLock();
-            _updateSceneIfNeededDelegate = UpdateSceneIfNeeded;
-        }
-
-        /// <inheritdoc/>
-        public bool DrawFps { get; set; }
-
-        /// <inheritdoc/>
-        public bool DrawDirtyRects { get; set; }
-
-        /// <summary>
-        /// Gets or sets a path to which rendered frame should be rendered for debugging.
-        /// </summary>
-        public string? DebugFramesPath { get; set; }
-
-        /// <summary>
-        /// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered
-        /// </summary>
-        public bool RenderOnlyOnRenderThread { get; set; }
-
-        /// <inheritdoc/>
-        public event EventHandler<SceneInvalidatedEventArgs>? SceneInvalidated;
-
-        /// <summary>
-        /// Gets the render layers.
-        /// </summary>
-        internal RenderLayers Layers { get; }
-
-        /// <summary>
-        /// Gets the current render target.
-        /// </summary>
-        internal IRenderTarget? RenderTarget { get; private set; }
-
-        /// <inheritdoc/>
-        public void AddDirty(Visual visual)
-        {
-            _dirty?.Add(visual);
-        }
-
-        /// <summary>
-        /// Disposes of the renderer and detaches from the render loop.
-        /// </summary>
-        public void Dispose()
-        {
-            lock (_sceneLock)
-            {
-                if (_disposed)
-                    return;
-                _disposed = true;
-                var scene = _scene;
-                _scene = null;
-                scene?.Dispose();
-            }
-
-            Stop();
-            // Wait for any in-progress rendering to complete
-            lock(_renderLoopIsRenderingLock){}
-            DisposeRenderTarget();
-        }
-
-        public void RecalculateChildren(Visual visual) => _recalculateChildren?.Add(visual);
-
-        void DisposeRenderTarget()
-        {
-            using (var l = _lock.TryLock())
-            {
-                if(l == null)
-                {
-                    // We are still trying to render on the render thread, try again a bit later
-                    DispatcherTimer.RunOnce(DisposeRenderTarget, TimeSpan.FromMilliseconds(50),
-                        DispatcherPriority.Background);
-                    return;
-                }
-
-                Layers.Clear();
-                RenderTarget?.Dispose();
-                RenderTarget = null;
-            }
-        }
-
-        /// <inheritdoc/>
-        public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool>? filter)
-        {
-            EnsureCanHitTest();
-
-            //It's safe to access _scene here without a lock since
-            //it's only changed from UI thread which we are currently on
-            return _scene?.Item.HitTest(p, root, filter) ?? Enumerable.Empty<Visual>();
-        }
-
-        /// <inheritdoc/>
-        public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool>? filter)
-        {
-            EnsureCanHitTest();
-
-            //It's safe to access _scene here without a lock since
-            //it's only changed from UI thread which we are currently on
-            return _scene?.Item.HitTestFirst(p, root, filter);
-        }
-
-        /// <inheritdoc/>
-        public void Paint(Rect rect)
-        {
-            if (RenderOnlyOnRenderThread)
-            {
-                // Renderer is stopped and doesn't tick on the render thread
-                // This indicates a bug somewhere in our code
-                // (currently happens when a window gets minimized with Show desktop on Windows 10)
-                if(!_running)
-                    return;
-                
-                while (true)
-                {
-                    Scene? scene;
-                    bool? updated;
-                    lock (_sceneLock)
-                    {
-                        updated = UpdateScene();
-                        scene = _scene?.Item;
-                    }
-                    
-                    // Renderer is in invalid state, skip drawing
-                    if(updated == null)
-                        return;
-
-                    // Wait for the scene to be rendered or disposed
-                    scene?.Rendered.Wait();
-                    
-                    // That was an up-to-date scene, we can return immediately
-                    if (updated == true)
-                        return;
-                }
-            }
-            else
-            {
-                var t = (IRenderLoopTask)this;
-                if (t.NeedsUpdate)
-                    UpdateScene();
-                if (_scene?.Item != null)
-                    Render(true);
-            }
-        }
-
-        /// <inheritdoc/>
-        public void Resized(Size size)
-        {
-        }
-
-        /// <inheritdoc/>
-        public void Start()
-        {
-            lock (_startStopLock)
-            {
-                if (!_running && _renderLoop != null)
-                {
-                    _renderLoop.Add(this);
-                    _running = true;
-                }
-            }
-        }
-
-        /// <inheritdoc/>
-        public void Stop()
-        {
-            lock (_startStopLock)
-            {
-                if (_running && _renderLoop != null)
-                {
-                    _renderLoop.Remove(this);
-                    _running = false;
-                }
-            }
-        }
-
-        public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType)
-        {
-            if (_renderInterface == null)
-                return new((object?)null);
-            
-            var tcs = new TaskCompletionSource<object?>();
-            _pendingRenderThreadJobs ??= new();
-            _pendingRenderThreadJobs.Add(() =>
-            {
-                try
-                {
-                    using (_renderInterface.EnsureCurrent())
-                    {
-                        tcs.TrySetResult(_renderInterface.Value.TryGetFeature(featureType));
-                    }
-                }
-                catch (Exception e)
-                {
-                    tcs.TrySetException(e);
-                }
-            });
-            return new ValueTask<object?>(tcs.Task);
-        }
-
-        bool NeedsUpdate => _dirty == null || _dirty.Count > 0;
-        bool IRenderLoopTask.NeedsUpdate => NeedsUpdate;
-
-        void IRenderLoopTask.Update(TimeSpan time) => UpdateScene();
-
-        void IRenderLoopTask.Render()
-        {
-            lock (_renderLoopIsRenderingLock)
-            {
-                lock(_startStopLock)
-                    if(!_running)
-                        return;
-                Render(false);
-            }
-        }
-
-        static Scene? TryGetChildScene(IRef<IDrawOperation>? op) => (op?.Item as BrushDrawOperation)?.Aux as Scene;
-        
-        /// <inheritdoc/>
-        Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
-        {
-            return TryGetChildScene(_currentDraw)?.Size ?? default;
-        }
-
-        /// <inheritdoc/>
-        void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
-        {
-            var childScene = TryGetChildScene(_currentDraw);
-
-            if (childScene != null)
-            {
-                Render(context, (VisualNode)childScene.Root, null, new Rect(childScene.Size));
-            }
-        }
-
-        internal void UnitTestUpdateScene() => UpdateScene();
-
-        internal void UnitTestRender() => Render(false);
-
-        internal Scene? UnitTestScene() => _scene?.Item;
-
-        private void EnsureCanHitTest()
-        {
-            if (_renderLoop == null && (_dirty == null || _dirty.Count > 0))
-            {
-                // When unit testing the renderLoop may be null, so update the scene manually.
-                UpdateScene();
-            }
-        }
-
-        internal void Render(bool forceComposite)
-        {
-            using (var l = _lock.TryLock())
-            {
-                if (l == null)
-                    return;
-
-                IDrawingContextImpl? context = null;
-                try
-                {
-                    try
-                    {
-                        var (scene, updated) = UpdateRenderLayersAndConsumeSceneIfNeeded(ref context);
-                        if (updated)
-                            FpsTick();
-                        using (scene)
-                        {
-                            if (scene?.Item != null)
-                            {
-                                try
-                                {
-                                    var overlay = DrawDirtyRects || DrawFps;
-                                    if (DrawDirtyRects)
-                                        _dirtyRectsDisplay.Tick();
-                                    if (overlay)
-                                        RenderOverlay(scene.Item, ref context);
-                                    if (updated || forceComposite || overlay)
-                                        RenderComposite(scene.Item, ref context);
-                                }
-                                finally
-                                {
-                                    try
-                                    {
-                                        if(scene.Item.RenderThreadJobs!=null)
-                                            foreach (var job in scene.Item.RenderThreadJobs)
-                                                job();
-                                    }
-                                    finally
-                                    {
-                                        scene.Item.MarkAsRendered();
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    finally
-                    {
-                        context?.Dispose();
-                    }
-                }
-                catch (RenderTargetCorruptedException ex)
-                {
-                    Logger.TryGet(LogEventLevel.Information, LogArea.Animations)?.Log(this, "Render target was corrupted. Exception: {0}", ex);
-                    RenderTarget?.Dispose();
-                    RenderTarget = null;
-                }
-            }
-        }
-
-        private (IRef<Scene>? scene, bool updated) UpdateRenderLayersAndConsumeSceneIfNeeded(ref IDrawingContextImpl? context,
-            bool recursiveCall = false)
-        {
-            IRef<Scene>? sceneRef;
-            lock (_sceneLock)
-                sceneRef = _scene?.Clone();
-            if (sceneRef == null)
-                return (null, false);
-            using (sceneRef)
-            {
-                var scene = sceneRef.Item;
-                if (scene.Generation != _lastSceneId)
-                {
-                    EnsureDrawingContext(ref context);
-
-                    Layers.Update(scene, context);
-
-                    RenderToLayers(scene);
-
-                    if (DebugFramesPath != null)
-                    {
-                        SaveDebugFrames(scene.Generation);
-                    }
-
-                    lock (_sceneLock)
-                        _lastSceneId = scene.Generation;
-
-
-                    var isUiThread = Dispatcher.UIThread.CheckAccess();
-                    // We have consumed the previously available scene, but there might be some dirty 
-                    // rects since the last update. *If* we are on UI thread, we can force immediate scene
-                    // rebuild before rendering anything on-screen
-                    // We are calling the same method recursively here 
-                    if (!recursiveCall && isUiThread && NeedsUpdate)
-                    {
-                        UpdateScene();
-                        var (rs, _) = UpdateRenderLayersAndConsumeSceneIfNeeded(ref context, true);
-                        return (rs, true);
-                    }
-
-                    // We are rendering a new scene version, so it's highly likely
-                    // that there is already a pending update for animations
-                    // So we are scheduling an update call so UI thread could prepare a scene before
-                    // the next render timer tick
-                    if (!recursiveCall && !isUiThread)
-                        Dispatcher.UIThread.Post(_updateSceneIfNeededDelegate, DispatcherPriority.Render);
-
-                    // Indicate that we have updated the layers
-                    return (sceneRef.Clone(), true);
-                }
-
-                // Just return scene, layers weren't updated
-                return (sceneRef.Clone(), false);
-            }
-
-        }
-
-
-        private void Render(IDrawingContextImpl context, VisualNode node, Visual? layer, Rect clipBounds)
-        {
-            if (layer == null || node.LayerRoot == layer)
-            {
-                clipBounds = node.ClipBounds.Intersect(clipBounds);
-
-                if (!clipBounds.IsDefault && node.Opacity > 0)
-                {
-                    var isLayerRoot = node.Visual == layer;
-
-                    node.BeginRender(context, isLayerRoot);
-
-                    var drawOperations = node.DrawOperations;
-                    var drawOperationsCount = drawOperations.Count;
-                    for (int i = 0; i < drawOperationsCount; i++)
-                    {
-                        var operation = drawOperations[i];
-                        _currentDraw = operation;
-                        operation.Item.Render(context);
-                        _currentDraw = null;
-                    }
-
-                    var children = node.Children;
-                    var childrenCount = children.Count;
-                    for (int i = 0; i < childrenCount; i++)
-                    {
-                        var child = children[i];
-                        Render(context, (VisualNode)child, layer, clipBounds);
-                    }
-
-                    node.EndRender(context, isLayerRoot);
-                }
-            }
-        }
-
-        private void RenderToLayers(Scene scene)
-        {
-            foreach (var layer in scene.Layers)
-            {
-                var renderLayer = Layers[layer.LayerRoot];
-                if (layer.Dirty.IsEmpty && !renderLayer.IsEmpty)
-                    continue;
-                var renderTarget = renderLayer.Bitmap;
-                var node = (VisualNode?)scene.FindNode(layer.LayerRoot);
-
-                if (node != null)
-                {
-                    using (var context = renderTarget.Item.CreateDrawingContext(this))
-                    {
-                        if (renderLayer.IsEmpty)
-                        {
-                            // Render entire layer root node
-                            context.Clear(Colors.Transparent);
-                            context.Transform = Matrix.Identity;
-                            context.PushClip(node.ClipBounds);
-                            Render(context, node, layer.LayerRoot, node.ClipBounds);
-                            context.PopClip();
-                            if (DrawDirtyRects)
-                            {
-                                _dirtyRectsDisplay.Add(node.ClipBounds);
-                            }
-
-                            renderLayer.IsEmpty = false;
-                        }
-                        else
-                        {
-                            var scale = scene.Scaling;
-
-                            foreach (var rect in layer.Dirty)
-                            {
-                                var snappedRect = SnapToDevicePixels(rect, scale);
-                                context.Transform = Matrix.Identity;
-                                context.PushClip(snappedRect);
-                                context.Clear(Colors.Transparent);
-                                Render(context, node, layer.LayerRoot, snappedRect);
-                                context.PopClip();
-
-                                if (DrawDirtyRects)
-                                {
-                                    _dirtyRectsDisplay.Add(snappedRect);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        private static Rect SnapToDevicePixels(Rect rect, double scale)
-        {
-            return new Rect(
-                new Point(
-                    Math.Floor(rect.X * scale) / scale,
-                    Math.Floor(rect.Y * scale) / scale),
-                new Point(
-                    Math.Ceiling(rect.Right * scale) / scale,
-                    Math.Ceiling(rect.Bottom * scale) / scale));
-        }
-
-        private void RenderOverlay(Scene scene, ref IDrawingContextImpl? parentContent)
-        {
-            EnsureDrawingContext(ref parentContent);
-
-            if (DrawDirtyRects)
-            {
-                var overlay = GetOverlay(parentContent, scene.Size, scene.Scaling);
-
-                using (var context = overlay.Item.CreateDrawingContext(this))
-                {
-                    context.Clear(Colors.Transparent);
-                    RenderDirtyRects(context);
-                }
-            }
-            else
-            {
-                _overlay?.Dispose();
-                _overlay = null;
-            }
-        }
-
-        private void RenderDirtyRects(IDrawingContextImpl context)
-        {
-            foreach (var r in _dirtyRectsDisplay)
-            {
-                var brush = new ImmutableSolidColorBrush(Colors.Magenta, r.Opacity);
-                context.DrawRectangle(brush,null, r.Rect);
-            }
-        }
-
-        private void RenderComposite(Scene scene, ref IDrawingContextImpl? context)
-        {
-            EnsureDrawingContext(ref context);
-
-            context.Clear(Colors.Transparent);
-
-            var clientRect = new Rect(scene.Size);
-
-            var firstLayer = true;
-            foreach (var layer in scene.Layers)
-            {
-                var bitmap = Layers[layer.LayerRoot].Bitmap;
-                var sourceRect = new Rect(0, 0, bitmap.Item.PixelSize.Width, bitmap.Item.PixelSize.Height);
-
-                if (layer.GeometryClip != null)
-                {
-                    context.PushGeometryClip(layer.GeometryClip);
-                }
-
-                if (layer.OpacityMask == null)
-                {
-                    if (firstLayer && bitmap.Item.CanBlit)
-                        bitmap.Item.Blit(context);
-                    else
-                        context.DrawBitmap(bitmap, layer.Opacity, sourceRect, clientRect);
-                }
-                else
-                {
-                    context.DrawBitmap(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
-                }
-
-                if (layer.GeometryClip != null)
-                {
-                    context.PopGeometryClip();
-                }
-
-                firstLayer = false;
-            }
-
-            if (_overlay != null)
-            {
-                var sourceRect = new Rect(0, 0, _overlay.Item.PixelSize.Width, _overlay.Item.PixelSize.Height);
-                context.DrawBitmap(_overlay, 0.5, sourceRect, clientRect);
-            }
-
-            if (DrawFps)
-            {
-                using (var c = new DrawingContext(context, false))
-                {
-                    RenderFps(c, clientRect, scene.Layers.Count);
-                }
-            }
-        }
-
-        private void EnsureDrawingContext([NotNull] ref IDrawingContextImpl? context)
-        {
-            if (context != null)
-            {
-                return;
-            }
-
-            if (RenderTarget?.IsCorrupted == true)
-            {
-                RenderTarget!.Dispose();
-                RenderTarget = null;
-            }
-
-            if (RenderTarget == null)
-            {
-                RenderTarget = _renderTargetFactory!();
-            }
-
-            context = RenderTarget.CreateDrawingContext(this);
-        }
-
-        private void UpdateSceneIfNeeded()
-        {
-            if(NeedsUpdate)
-                UpdateScene();
-        }
-        
-        private bool? UpdateScene()
-        {
-            Dispatcher.UIThread.VerifyAccess();
-            using var noPump = NonPumpingLockHelper.Use();
-            lock (_sceneLock)
-            {
-                if (_disposed)
-                    return null;
-                if (_scene?.Item.Generation > _lastSceneId)
-                    return false;
-            }
-            if (_root.IsVisible)
-            {
-                var sceneRef = RefCountable.Create(_scene?.Item.CloneScene() ?? new Scene(_root)
-                {
-                    RenderThreadJobs = _pendingRenderThreadJobs
-                });
-                _pendingRenderThreadJobs = null;
-                var scene = sceneRef.Item;
-
-                if (_dirty == null)
-                {
-                    _dirty = new DirtyVisuals();
-                    _recalculateChildren = new HashSet<Visual>();
-                    _sceneBuilder.UpdateAll(scene);
-                }
-                else
-                {
-                    foreach (var visual in _recalculateChildren!)
-                    {
-                        var node = scene.FindNode(visual);
-                        ((VisualNode?)node)?.SortChildren(scene);
-                    }
-
-                    _recalculateChildren.Clear();
-
-                    foreach (var visual in _dirty)
-                    {
-                        _sceneBuilder.Update(scene, visual);
-                    }
-                }
-
-                lock (_sceneLock)
-                {
-                    var oldScene = _scene;
-                    _scene = sceneRef;
-                    oldScene?.Dispose();
-                }
-
-                _dirty.Clear();
-
-                if (SceneInvalidated != null)
-                {
-                    var rect = new Rect();
-
-                    foreach (var layer in scene.Layers)
-                    {
-                        foreach (var dirty in layer.Dirty)
-                        {
-                            rect = rect.Union(dirty);
-                        }
-                    }
-
-                    SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect));
-                }
-
-                return true;
-            }
-            else
-            {
-                lock (_sceneLock)
-                {
-                    var oldScene = _scene;
-                    _scene = null;
-                    oldScene?.Dispose();
-                }
-
-                return null;
-            }
-        }
-
-        private IRef<IRenderTargetBitmapImpl> GetOverlay(
-            IDrawingContextImpl parentContext,
-            Size size,
-            double scaling)
-        {
-            var pixelSize = size * scaling;
-
-            if (_overlay == null ||
-                _overlay.Item.PixelSize.Width != pixelSize.Width ||
-                _overlay.Item.PixelSize.Height != pixelSize.Height)
-            {
-                _overlay?.Dispose();
-                _overlay = RefCountable.Create(parentContext.CreateLayer(size));
-            }
-
-            return _overlay;
-        }
-
-        private void SaveDebugFrames(int id)
-        {
-            var index = 0;
-
-            foreach (var layer in Layers)
-            {
-                var fileName = Path.Combine(DebugFramesPath ?? string.Empty, FormattableString.Invariant($"frame-{id}-layer-{index++}.png"));
-                layer.Bitmap.Item.Save(fileName);
-            }
-        }
-    }
-}

+ 16 - 0
src/Avalonia.Base/Rendering/ICustomHitTest.cs

@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Rendering
+{
+
+    /// <summary>
+    /// Allows customization of hit-testing
+    /// </summary>
+    public interface ICustomHitTest
+    {
+        /// <param name="point">The point to hit test in global coordinate space.</param>
+        bool HitTest(Point point);
+    }
+}

+ 0 - 33
src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs

@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering
-{
-    /// <summary>
-    /// An interface to allow non-templated controls to customize their hit-testing
-    /// when using a renderer with a simple hit-testing algorithm without a scene graph,
-    /// such as <see cref="ImmediateRenderer" />
-    /// </summary>
-    public interface ICustomSimpleHitTest
-    {
-        /// <param name="point">The point to hit test in global coordinate space.</param>
-        bool HitTest(Point point);
-    }
-
-    /// <summary>
-    /// Allows customization of hit-testing for all renderers.
-    /// </summary>
-    public interface ICustomHitTest : ICustomSimpleHitTest
-    {
-    }
-
-    public static class CustomSimpleHitTestExtensions
-    {
-        public static bool HitTestCustom(this Visual visual, Point point)
-            => (visual as ICustomSimpleHitTest)?.HitTest(point) ?? visual.TransformedBounds?.Contains(point) == true;
-
-        public static bool HitTestCustom(this IEnumerable<Visual> children, Point point)
-            => children.Any(ctrl => ctrl.HitTestCustom(point));
-    }
-}

+ 0 - 6
src/Avalonia.Base/Rendering/IRenderRoot.cs

@@ -25,12 +25,6 @@ namespace Avalonia.Rendering
         /// </summary>
         double RenderScaling { get; }
 
-        /// <summary>
-        /// Adds a rectangle to the window's dirty region.
-        /// </summary>
-        /// <param name="rect">The rectangle.</param>
-        void Invalidate(Rect rect);
-
         /// <summary>
         /// Converts a point from screen to client coordinates.
         /// </summary>

+ 0 - 16
src/Avalonia.Base/Rendering/IRendererFactory.cs

@@ -1,16 +0,0 @@
-
-namespace Avalonia.Rendering
-{
-    /// <summary>
-    /// Defines the interface for a renderer factory.
-    /// </summary>
-    public interface IRendererFactory
-    {
-        /// <summary>
-        /// Creates a renderer.
-        /// </summary>
-        /// <param name="root">The root visual.</param>
-        /// <param name="renderLoop">The render loop.</param>
-        IRenderer Create(IRenderRoot root, IRenderLoop renderLoop);
-    }
-}

+ 11 - 237
src/Avalonia.Base/Rendering/ImmediateRenderer.cs

@@ -10,102 +10,12 @@ using Avalonia.VisualTree;
 namespace Avalonia.Rendering
 {
     /// <summary>
-    /// A renderer which renders the state of the visual tree without an intermediate scene graph
-    /// representation.
+    /// This class is used to render the visual tree into a DrawingContext by doing
+    /// a simple tree traversal.
+    /// It's currently used mostly for RenderTargetBitmap.Render and VisualBrush
     /// </summary>
-    /// <remarks>
-    /// The immediate renderer supports only clip-bound-based hit testing; a control's geometry is
-    /// not taken into account.
-    /// </remarks>
-    public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer
+    internal class ImmediateRenderer : IVisualBrushRenderer//, IRenderer
     {
-        private readonly Visual _root;
-        private readonly Func<IRenderTarget> _renderTargetFactory;
-        private readonly PlatformRenderInterfaceContextManager? _renderContext;
-        private readonly IRenderRoot? _renderRoot;
-        private bool _updateTransformedBounds = true;
-        private IRenderTarget? _renderTarget;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ImmediateRenderer"/> class.
-        /// </summary>
-        /// <param name="root">The control to render.</param>
-        /// <param name="renderTargetFactory">The target render factory.</param>
-        /// <param name="renderContext">The render contex.</param>
-        public ImmediateRenderer(Visual root, Func<IRenderTarget> renderTargetFactory, 
-            PlatformRenderInterfaceContextManager? renderContext = null)
-        {
-            _root = root ?? throw new ArgumentNullException(nameof(root));
-            _renderTargetFactory = renderTargetFactory;
-            _renderContext = renderContext;
-            _renderRoot = root as IRenderRoot;
-        }
-
-        private ImmediateRenderer(Visual root, Func<IRenderTarget> renderTargetFactory, bool updateTransformedBounds)
-        {
-            _root = root ?? throw new ArgumentNullException(nameof(root));
-            _renderTargetFactory = renderTargetFactory;
-            _renderRoot = root as IRenderRoot;
-            _updateTransformedBounds = updateTransformedBounds;
-        }
-
-        /// <inheritdoc/>
-        public bool DrawFps { get; set; }
-
-        /// <inheritdoc/>
-        public bool DrawDirtyRects { get; set; }
-
-        /// <inheritdoc/>
-        public event EventHandler<SceneInvalidatedEventArgs>? SceneInvalidated;
-
-        /// <inheritdoc/>
-        public void Paint(Rect rect)
-        {
-            if (_renderTarget == null)
-            {
-                _renderTarget = _renderTargetFactory();
-            }
-
-            try
-            {
-                using (var context = new DrawingContext(_renderTarget.CreateDrawingContext(this)))
-                {
-                    context.PlatformImpl.Clear(Colors.Transparent);
-
-                    using (context.PushTransformContainer())
-                    {
-                        Render(context, _root, _root.Bounds);
-                    }
-
-                    if (DrawDirtyRects)
-                    {
-                        var color = (uint)new Random().Next(0xffffff) | 0x44000000;
-                        context.FillRectangle(
-                            new SolidColorBrush(color),
-                            rect);
-                    }
-
-                    if (DrawFps)
-                    {
-                        RenderFps(context, _root.Bounds, null);
-                    }
-                }
-            }
-            catch (RenderTargetCorruptedException ex)
-            {
-                Logger.TryGet(LogEventLevel.Information, LogArea.Animations)?.Log(this, "Render target was corrupted. Exception: {0}", ex);
-                _renderTarget.Dispose();
-                _renderTarget = null;
-            }
-
-            SceneInvalidated?.Invoke(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect));
-        }
-
-        /// <inheritdoc/>
-        public void Resized(Size size)
-        {
-        }
-
         /// <summary>
         /// Renders a visual to a render target.
         /// </summary>
@@ -113,11 +23,8 @@ namespace Avalonia.Rendering
         /// <param name="target">The render target.</param>
         public static void Render(Visual visual, IRenderTarget target)
         {
-            using (var renderer = new ImmediateRenderer(visual, () => target, updateTransformedBounds: false))
-            using (var context = new DrawingContext(target.CreateDrawingContext(renderer)))
-            {
-                renderer.Render(context, visual, visual.Bounds);
-            }
+            using var context = new DrawingContext(target.CreateDrawingContext(new ImmediateRenderer()));
+            Render(context, visual, visual.Bounds);
         }
 
         /// <summary>
@@ -127,77 +34,9 @@ namespace Avalonia.Rendering
         /// <param name="context">The drawing context.</param>
         public static void Render(Visual visual, DrawingContext context)
         {
-            using (var renderer = new ImmediateRenderer(visual, 
-                       () => throw new InvalidOperationException("This is not supposed to be called"),
-                       updateTransformedBounds: false))
-            {
-                renderer.Render(context, visual, visual.Bounds);
-            }
+            Render(context, visual, visual.Bounds);
         }
-
-        /// <inheritdoc/>
-        public void AddDirty(Visual visual)
-        {
-            if (!visual.Bounds.IsDefault)
-            {
-                var m = visual.TransformToVisual(_root);
-
-                if (m.HasValue)
-                {
-                    var bounds = new Rect(visual.Bounds.Size).TransformToAABB(m.Value);
-
-                    //use transformedbounds as previous render state of the visual bounds
-                    //so we can invalidate old and new bounds of a control in case it moved/shrinked
-                    if (visual.TransformedBounds.HasValue)
-                    {
-                        var trb = visual.TransformedBounds.Value;
-                        var trBounds = trb.Bounds.TransformToAABB(trb.Transform);
-
-                        if (trBounds != bounds)
-                        {
-                            _renderRoot?.Invalidate(trBounds);
-                        }
-                    }
-
-                    _renderRoot?.Invalidate(bounds);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Ends the operation of the renderer.
-        /// </summary>
-        public void Dispose()
-        {
-            _renderTarget?.Dispose();
-        }
-
-        /// <inheritdoc/>
-        public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool> filter)
-        {
-            return HitTest(root, p, filter);
-        }
-
-        public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool> filter)
-        {
-            return HitTest(root, p, filter).FirstOrDefault();
-        }
-
-        /// <inheritdoc/>
-        public void RecalculateChildren(Visual visual) => AddDirty(visual);
-
-        /// <inheritdoc/>
-        public void Start()
-        {
-        }
-
-        /// <inheritdoc/>
-        public void Stop()
-        {
-        }
-
-        public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType) =>
-            new(_renderContext?.Value?.TryGetFeature(featureType));
+        
 
         /// <inheritdoc/>
         Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
@@ -215,18 +54,7 @@ namespace Avalonia.Rendering
 
         internal static void Render(Visual visual, DrawingContext context, bool updateTransformedBounds)
         {
-            using var renderer = new ImmediateRenderer(visual, 
-                () => throw new InvalidOperationException("This is not supposed to be called"),
-                updateTransformedBounds);
-            renderer.Render(context, visual, visual.Bounds);
-        }
-
-        private static void ClearTransformedBounds(Visual visual)
-        {
-            foreach (var e in visual.GetSelfAndVisualDescendants())
-            {
-                visual.SetTransformedBounds(null);
-            }
+            Render(context, visual, visual.Bounds);
         }
 
         private static Rect GetTransformedBounds(Visual visual)
@@ -244,45 +72,8 @@ namespace Avalonia.Rendering
             }
         }
 
-        private static IEnumerable<Visual> HitTest(
-           Visual visual,
-           Point p,
-           Func<Visual, bool>? filter)
-        {
-            _ = visual ?? throw new ArgumentNullException(nameof(visual));
-
-            if (filter?.Invoke(visual) != false)
-            {
-                bool containsPoint;
-
-                if (visual is ICustomSimpleHitTest custom)
-                {
-                    containsPoint = custom.HitTest(p);
-                }
-                else
-                {
-                    containsPoint = visual.TransformedBounds?.Contains(p) == true;
-                }
-
-                if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0)
-                {
-                    foreach (var child in visual.VisualChildren.SortByZIndex())
-                    {
-                        foreach (var result in HitTest(child, p, filter))
-                        {
-                            yield return result;
-                        }
-                    }
-                }
 
-                if (containsPoint)
-                {
-                    yield return visual;
-                }
-            }
-        }
-
-        private void Render(DrawingContext context, Visual visual, Rect clipRect)
+        private static void Render(DrawingContext context, Visual visual, Rect clipRect)
         {
             var opacity = visual.Opacity;
             var clipToBounds = visual.ClipToBounds;
@@ -338,15 +129,7 @@ namespace Avalonia.Rendering
                 using (context.PushTransformContainer())
                 {
                     visual.Render(context);
-
-#pragma warning disable 0618
-                    var transformed =
-                        new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform);
-#pragma warning restore 0618
-
-                    if (_updateTransformedBounds)
-                        visual.SetTransformedBounds(transformed);
-
+                    
                     var childrenEnumerable = visual.HasNonUniformZIndexChildren
                         ? visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)
                         : (IEnumerable<Visual>)visual.VisualChildren;
@@ -362,18 +145,9 @@ namespace Avalonia.Rendering
                                 : clipRect;
                             Render(context, child, childClipRect);
                         }
-                        else if (_updateTransformedBounds)
-                        {
-                            ClearTransformedBounds(child);
-                        }
                     }
                 }
             }
-
-            if (!visual.IsVisible && _updateTransformedBounds)
-            {
-                ClearTransformedBounds(visual);
-            }
         }
     }
 }

+ 1 - 3
src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs

@@ -6,9 +6,7 @@ using Avalonia.Reactive;
 
 namespace Avalonia.Rendering;
 
-[Unstable]
-// TODO: Make it internal once legacy renderers are removed
-public class PlatformRenderInterfaceContextManager
+internal class PlatformRenderInterfaceContextManager
 {
     private readonly IPlatformGraphics? _graphics;
     private IPlatformRenderInterfaceContext? _backend;

+ 0 - 48
src/Avalonia.Base/Rendering/RenderLayer.cs

@@ -1,48 +0,0 @@
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering
-{
-    public class RenderLayer
-    {
-        public RenderLayer(
-            IDrawingContextImpl drawingContext,
-            Size size,
-            double scaling,
-            Visual layerRoot)
-        {
-            Bitmap = RefCountable.Create(drawingContext.CreateLayer(size));
-            Size = size;
-            Scaling = scaling;
-            LayerRoot = layerRoot;
-            IsEmpty = true;
-        }
-
-        public IRef<IDrawingContextLayerImpl> Bitmap { get; private set; }
-        public bool IsEmpty { get; set; }
-        public double Scaling { get; private set; }
-        public Size Size { get; private set; }
-        public Visual LayerRoot { get; }
-
-        public void RecreateBitmap(IDrawingContextImpl drawingContext, Size size, double scaling)
-        {
-            if (Size != size || Scaling != scaling)
-            {
-                var resized = RefCountable.Create(drawingContext.CreateLayer(size));
-
-                using (var context = resized.Item.CreateDrawingContext(null))
-                {
-                    Bitmap.Dispose();
-                    context.Clear(default);
-                    
-                    Bitmap = resized;
-                    Scaling = scaling;
-                    Size = size;
-                    IsEmpty = true;
-                }
-            }
-        }
-    }
-}

+ 0 - 69
src/Avalonia.Base/Rendering/RenderLayers.cs

@@ -1,69 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-
-namespace Avalonia.Rendering
-{
-    public class RenderLayers : IEnumerable<RenderLayer>
-    {
-        private readonly List<RenderLayer> _inner = new List<RenderLayer>();
-        private readonly Dictionary<Visual, RenderLayer> _index = new Dictionary<Visual, RenderLayer>();
-        
-        public int Count => _inner.Count;
-        public RenderLayer this[Visual layerRoot] => _index[layerRoot];
-
-        public void Update(Scene scene, IDrawingContextImpl context)
-        {
-            for (var i = scene.Layers.Count - 1; i >= 0; --i)
-            {
-                var src = scene.Layers[i];
-
-                if (!_index.TryGetValue(src.LayerRoot, out var layer))
-                {
-                    layer = new RenderLayer(context, scene.Size, scene.Scaling, src.LayerRoot);
-                    _inner.Add(layer);
-                    _index.Add(src.LayerRoot, layer);
-                }
-                else
-                {
-                    layer.RecreateBitmap(context, scene.Size, scene.Scaling);
-                }
-            }
-
-            for (var i = 0; i < _inner.Count;)
-            {
-                var layer = _inner[i];
-
-                if (!scene.Layers.Exists(layer.LayerRoot))
-                {
-                    layer.Bitmap.Dispose();
-                    _inner.RemoveAt(i);
-                    _index.Remove(layer.LayerRoot);
-                }
-                else
-                    i++;
-            }
-        }
-
-        public void Clear()
-        {
-            foreach (var layer in _index.Values)
-            {
-                layer.Bitmap.Dispose();
-            }
-
-            _index.Clear();
-            _inner.Clear();
-        }
-
-        public bool TryGetValue(Visual layerRoot, [NotNullWhen(true)] out RenderLayer? value)
-        {
-            return _index.TryGetValue(layerRoot, out value);
-        }
-
-        public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
-        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-    }
-}

+ 0 - 482
src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@@ -1,482 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Numerics;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Utilities;
-using Avalonia.Media.Imaging;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// A drawing context which builds a scene graph.
-    /// </summary>
-    internal class DeferredDrawingContextImpl : IDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport
-    {
-        private readonly ISceneBuilder _sceneBuilder;
-        private VisualNode? _node;
-        private int _childIndex;
-        private int _drawOperationindex;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="DeferredDrawingContextImpl"/> class.
-        /// </summary>
-        /// <param name="sceneBuilder">
-        /// A scene builder used for constructing child scenes for visual brushes.
-        /// </param>
-        /// <param name="layers">The scene layers.</param>
-        public DeferredDrawingContextImpl(ISceneBuilder sceneBuilder, SceneLayers layers)
-        {
-            _sceneBuilder = sceneBuilder;
-            Layers = layers;
-        }
-
-        /// <inheritdoc/>
-        public Matrix Transform { get; set; } = Matrix.Identity;
-
-        /// <summary>
-        /// Gets the layers in the scene being built.
-        /// </summary>
-        public SceneLayers Layers { get; }
-
-        /// <summary>
-        /// Informs the drawing context of the visual node that is about to be rendered.
-        /// </summary>
-        /// <param name="node">The visual node.</param>
-        /// <returns>
-        /// An object which when disposed will commit the changes to visual node.
-        /// </returns>
-        public UpdateState BeginUpdate(VisualNode node)
-        {
-            _ = node ?? throw new ArgumentNullException(nameof(node));
-
-            if (_node != null)
-            {
-                if (_childIndex < _node.Children.Count)
-                {
-                    _node.ReplaceChild(_childIndex, node);
-                }
-                else
-                {
-                    _node.AddChild(node);
-                }
-
-                ++_childIndex;
-            }
-
-            var state = new UpdateState(this, _node, _childIndex, _drawOperationindex);
-            _node = node;
-            _childIndex = _drawOperationindex = 0;
-            return state;
-        }
-
-        /// <inheritdoc/>
-        public void Clear(Color color)
-        {
-            // Cannot clear a deferred scene.
-        }
-
-        /// <inheritdoc/>
-        public void Dispose()
-        {
-            // Nothing to do here since we allocate no unmanaged resources.
-        }
-
-        /// <summary>
-        /// Removes any remaining drawing operations from the visual node.
-        /// </summary>
-        /// <remarks>
-        /// Drawing operations are updated in place, overwriting existing drawing operations if
-        /// they are different. Once drawing has completed for the current visual node, it is
-        /// possible that there are stale drawing operations at the end of the list. This method
-        /// trims these stale drawing operations.
-        /// </remarks>
-        public void TrimChildren()
-        {
-            _node!.TrimChildren(_childIndex);
-        }
-
-        /// <inheritdoc/>
-        public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry)
-        {
-            var next = NextDrawAs<GeometryNode>();
-
-            if (next == null || !next.Item.Equals(Transform, brush, pen, geometry))
-            {
-                Add(new GeometryNode(Transform, brush, pen, geometry, CreateChildScene(brush)));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
-        {
-            var next = NextDrawAs<ImageNode>();
-
-            if (next == null || !next.Item.Equals(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode))
-            {
-                Add(new ImageNode(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
-        {
-            // This method is currently only used to composite layers so shouldn't be called here.
-            throw new NotSupportedException();
-        }
-
-        /// <inheritdoc/>
-        public void DrawLine(IPen pen, Point p1, Point p2)
-        {
-            var next = NextDrawAs<LineNode>();
-
-            if (next == null || !next.Item.Equals(Transform, pen, p1, p2))
-            {
-                Add(new LineNode(Transform, pen, p1, p2, CreateChildScene(pen.Brush)));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect,
-            BoxShadows boxShadows = default)
-        {
-            var next = NextDrawAs<RectangleNode>();
-
-            if (next == null || !next.Item.Equals(Transform, brush, pen, rect, boxShadows))
-            {
-                Add(new RectangleNode(Transform, brush, pen, rect, boxShadows, CreateChildScene(brush)));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void DrawRectangle(IExperimentalAcrylicMaterial material, RoundedRect rect)
-        {
-            var next = NextDrawAs<ExperimentalAcrylicNode>();
-
-            if (next == null || !next.Item.Equals(Transform, material, rect))
-            {
-                Add(new ExperimentalAcrylicNode(Transform, material, rect));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect)
-        {
-            var next = NextDrawAs<EllipseNode>();
-
-            if (next == null || !next.Item.Equals(Transform, brush, pen, rect))
-            {
-                Add(new EllipseNode(Transform, brush, pen, rect, CreateChildScene(brush)));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        public void Custom(ICustomDrawOperation custom)
-        {
-            var next = NextDrawAs<CustomDrawOperation>();
-            if (next == null || !next.Item.Equals(Transform, custom))
-                Add(new CustomDrawOperation(custom, Transform));
-            else
-                ++_drawOperationindex;
-        }
-
-        public object? GetFeature(Type t) => null;
-
-        /// <inheritdoc/>
-        public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun)
-        {
-            var next = NextDrawAs<GlyphRunNode>();
-
-            if (next == null || !next.Item.Equals(Transform, foreground, glyphRun))
-            {
-                Add(new GlyphRunNode(Transform, foreground, glyphRun, CreateChildScene(foreground)));
-            }
-
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-        public IDrawingContextLayerImpl CreateLayer(Size size)
-        {
-            throw new NotSupportedException("Creating layers on a deferred drawing context not supported");
-        }
-
-        /// <inheritdoc/>
-        public void PopClip()
-        {
-            var next = NextDrawAs<ClipNode>();
-
-            if (next == null || !next.Item.Equals(null))
-            {
-                Add(new ClipNode());
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PopGeometryClip()
-        {
-            var next = NextDrawAs<GeometryClipNode>();
-
-            if (next == null || !next.Item.Equals(null))
-            {
-                Add(new GeometryClipNode());
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PopBitmapBlendMode()
-        {
-            var next = NextDrawAs<BitmapBlendModeNode>();
-
-            if (next == null || !next.Item.Equals(null))
-            {
-                Add(new BitmapBlendModeNode());
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-        
-        /// <inheritdoc/>
-        public void PopOpacity()
-        {
-            var next = NextDrawAs<OpacityNode>();
-
-            if (next == null || !next.Item.Equals(null))
-            {
-                Add(new OpacityNode());
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PopOpacityMask()
-        {
-            var next = NextDrawAs<OpacityMaskNode>();
-
-            if (next == null || !next.Item.Equals(null, null))
-            {
-                Add(new OpacityMaskNode());
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PushClip(Rect clip)
-        {
-            var next = NextDrawAs<ClipNode>();
-
-            if (next == null || !next.Item.Equals(Transform, clip))
-            {
-                Add(new ClipNode(Transform, clip));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc />
-        public void PushClip(RoundedRect clip)
-        {
-            var next = NextDrawAs<ClipNode>();
-
-            if (next == null || !next.Item.Equals(Transform, clip))
-            {
-                Add(new ClipNode(Transform, clip));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PushGeometryClip(IGeometryImpl? clip)
-        {
-            if (clip is null)
-                return;
-
-            var next = NextDrawAs<GeometryClipNode>();
-
-            if (next == null || !next.Item.Equals(Transform, clip))
-            {
-                Add(new GeometryClipNode(Transform, clip));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PushOpacity(double opacity)
-        {
-            var next = NextDrawAs<OpacityNode>();
-
-            if (next == null || !next.Item.Equals(opacity))
-            {
-                Add(new OpacityNode(opacity));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PushOpacityMask(IBrush mask, Rect bounds)
-        {
-            var next = NextDrawAs<OpacityMaskNode>();
-
-            if (next == null || !next.Item.Equals(mask, bounds))
-            {
-                Add(new OpacityMaskNode(mask, bounds, CreateChildScene(mask)));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-
-        /// <inheritdoc/>
-        public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
-        {
-            var next = NextDrawAs<BitmapBlendModeNode>();
-
-            if (next == null || !next.Item.Equals(blendingMode))
-            {
-                Add(new BitmapBlendModeNode(blendingMode));
-            }
-            else
-            {
-                ++_drawOperationindex;
-            }
-        }
-        
-        public readonly struct UpdateState : IDisposable
-        {
-            public UpdateState(
-                DeferredDrawingContextImpl owner,
-                VisualNode? node,
-                int childIndex,
-                int drawOperationIndex)
-            {
-                Owner = owner;
-                Node = node;
-                ChildIndex = childIndex;
-                DrawOperationIndex = drawOperationIndex;
-            }
-
-            public void Dispose()
-            {
-                Owner._node!.TrimDrawOperations(Owner._drawOperationindex);
-
-                var dirty = Owner.Layers.GetOrAdd(Owner._node.LayerRoot!).Dirty;
-
-                var drawOperations = Owner._node.DrawOperations;
-                var drawOperationsCount = drawOperations.Count;
-
-                for (var i = 0; i < drawOperationsCount; i++)
-                {
-                    dirty.Add(drawOperations[i].Item.Bounds);
-                }
-
-                Owner._node = Node;
-                Owner._childIndex = ChildIndex;
-                Owner._drawOperationindex = DrawOperationIndex;
-            }
-
-            public DeferredDrawingContextImpl Owner { get; }
-            public VisualNode? Node { get; }
-            public int ChildIndex { get; }
-            public int DrawOperationIndex { get; }
-        }
-
-        private void Add<T>(T node) where T : class, IDrawOperation
-        {
-            using (var refCounted = RefCountable.Create(node))
-            {
-                Add(refCounted);
-            }
-        }
-
-        private void Add(IRef<IDrawOperation> node)
-        {
-            if (_drawOperationindex < _node!.DrawOperations.Count)
-            {
-                _node.ReplaceDrawOperation(_drawOperationindex, node);
-            }
-            else
-            {
-                _node.AddDrawOperation(node);
-            }
-
-            ++_drawOperationindex;
-        }
-
-        private IRef<T>? NextDrawAs<T>() where T : class, IDrawOperation
-        {
-            return _drawOperationindex < _node!.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as IRef<T> : null;
-        }
-
-        private IDisposable? CreateChildScene(IBrush? brush)
-        {
-            var visualBrush = brush as VisualBrush;
-
-            if (visualBrush != null)
-            {
-                var visual = visualBrush.Visual;
-
-                if (visual != null)
-                {
-                    (visual as IVisualBrushInitialize)?.EnsureInitialized();
-                    var scene = new Scene(visual);
-                    _sceneBuilder.UpdateAll(scene);
-                    return scene;
-                }
-            }
-
-            return null;
-        }
-    }
-}

+ 0 - 24
src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs

@@ -1,24 +0,0 @@
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// Builds a scene graph from a visual tree.
-    /// </summary>
-    public interface ISceneBuilder
-    {
-        /// <summary>
-        /// Builds the initial scene graph for a visual tree.
-        /// </summary>
-        /// <param name="scene">The scene to build.</param>
-        void UpdateAll(Scene scene);
-
-        /// <summary>
-        /// Updates the visual (and potentially its children) in a scene.
-        /// </summary>
-        /// <param name="scene">The scene.</param>
-        /// <param name="visual">The visual to update.</param>
-        /// <returns>True if changes were made, otherwise false.</returns>
-        bool Update(Scene scene, Visual visual);
-    }
-}

+ 0 - 105
src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs

@@ -1,105 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Platform;
-using Avalonia.Utilities;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// Represents a node in the low-level scene graph representing a <see cref="Visual"/>.
-    /// </summary>
-    public interface IVisualNode : IDisposable
-    {
-        /// <summary>
-        /// Gets the visual to which the node relates.
-        /// </summary>
-        Visual Visual { get; }
-
-        /// <summary>
-        /// Gets the parent scene graph node.
-        /// </summary>
-        IVisualNode? Parent { get; }
-
-        /// <summary>
-        /// Gets the transform for the node from global to control coordinates.
-        /// </summary>
-        Matrix Transform { get; }
-
-        /// <summary>
-        /// Gets the corner radius of visual. Contents are clipped to this radius.
-        /// </summary>
-        CornerRadius ClipToBoundsRadius { get; }
-
-        /// <summary>
-        /// Gets the bounds of the node's geometry in global coordinates.
-        /// </summary>
-        Rect Bounds { get; }
-
-        /// <summary>
-        /// Gets the clip bounds for the node in global coordinates.
-        /// </summary>
-        Rect ClipBounds { get; }
-
-        /// <summary>
-        /// Gets the layout bounds for the node in global coordinates.
-        /// </summary>
-        Rect LayoutBounds { get; }
-
-        /// <summary>
-        /// Whether the node is clipped to <see cref="ClipBounds"/>.
-        /// </summary>
-        bool ClipToBounds { get; }
-
-        /// <summary>
-        /// Gets the node's clip geometry, if any.
-        /// </summary>
-        IGeometryImpl? GeometryClip { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether one of the node's ancestors has a geometry clip.
-        /// </summary>
-        bool HasAncestorGeometryClip { get; }
-
-        /// <summary>
-        /// Gets the child scene graph nodes.
-        /// </summary>
-        IReadOnlyList<IVisualNode> Children { get; }
-
-        /// <summary>
-        /// Gets the drawing operations for the visual.
-        /// </summary>
-        IReadOnlyList<IRef<IDrawOperation>> DrawOperations { get; }
-
-        /// <summary>
-        /// Gets the opacity of the scene graph node.
-        /// </summary>
-        double Opacity { get; }
-
-        /// <summary>
-        /// Sets up the drawing context for rendering the node's geometry.
-        /// </summary>
-        /// <param name="context">The drawing context.</param>
-        /// <param name="skipOpacity">Whether to skip pushing the control's opacity.</param>
-        void BeginRender(IDrawingContextImpl context, bool skipOpacity);
-
-        /// <summary>
-        /// Resets the drawing context after rendering the node's geometry.
-        /// </summary>
-        /// <param name="context">The drawing context.</param>
-        /// <param name="skipOpacity">Whether to skip popping the control's opacity.</param>
-        void EndRender(IDrawingContextImpl context, bool skipOpacity);
-
-        /// <summary>
-        /// Hit test the geometry in this node.
-        /// </summary>
-        /// <param name="p">The point in global coordinates.</param>
-        /// <returns>True if the point hits the node's geometry; otherwise false.</returns>
-        /// <remarks>
-        /// This method does not recurse to child <see cref="IVisualNode"/>s, if you want
-        /// to hit test children they must be hit tested manually.
-        /// </remarks>
-        bool HitTest(Point p);
-
-        bool Disposed { get; }
-    }
-}

+ 0 - 352
src/Avalonia.Base/Rendering/SceneGraph/Scene.cs

@@ -1,352 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Avalonia.Collections.Pooled;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// Represents a scene graph used by the <see cref="DeferredRenderer"/>.
-    /// </summary>
-    public class Scene : IDisposable
-    {
-        private readonly Dictionary<Visual, IVisualNode> _index;
-        private readonly TaskCompletionSource<bool> _rendered = new TaskCompletionSource<bool>();
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Scene"/> class.
-        /// </summary>
-        /// <param name="rootVisual">The root visual to draw.</param>
-        public Scene(Visual rootVisual)
-            : this(
-                new VisualNode(rootVisual, null),
-                new Dictionary<Visual, IVisualNode>(),
-                new SceneLayers(rootVisual),
-                0)
-        {
-            _index.Add(rootVisual, Root);
-        }
-
-        private Scene(VisualNode root, Dictionary<Visual, IVisualNode> index, SceneLayers layers, int generation)
-        {
-            _ = root ?? throw new ArgumentNullException(nameof(root));
-
-            var renderRoot = root.Visual as IRenderRoot;
-
-            _index = index;
-            Root = root;
-            Layers = layers;
-            Generation = generation;
-            root.LayerRoot = root.Visual;
-        }
-
-        public Task Rendered => _rendered.Task;
-
-        /// <summary>
-        /// Gets a value identifying the scene's generation. This is incremented each time the scene is cloned.
-        /// </summary>
-        public int Generation { get; }
-
-        /// <summary>
-        /// Gets the layers for the scene.
-        /// </summary>
-        public SceneLayers Layers { get; }
-
-        /// <summary>
-        /// Gets the root node of the scene graph.
-        /// </summary>
-        public IVisualNode Root { get; }
-
-        /// <summary>
-        /// Gets or sets the size of the scene in device independent pixels.
-        /// </summary>
-        public Size Size { get; set; }
-
-        /// <summary>
-        /// Gets or sets the scene scaling.
-        /// </summary>
-        public double Scaling { get; set; } = 1;
-
-        /// <summary>
-        /// Adds a node to the scene index.
-        /// </summary>
-        /// <param name="node">The node.</param>
-        public void Add(IVisualNode node)
-        {
-            _ = node ?? throw new ArgumentNullException(nameof(node));
-
-            _index.Add(node.Visual, node);
-        }
-
-        /// <summary>
-        /// Clones the scene.
-        /// </summary>
-        /// <returns>The cloned scene.</returns>
-        public Scene CloneScene()
-        {
-            var index = new Dictionary<Visual, IVisualNode>(_index.Count);
-            var root = Clone((VisualNode)Root, null, index);
-
-            var result = new Scene(root, index, Layers.Clone(), Generation + 1)
-            {
-                Size = Size,
-                Scaling = Scaling,
-            };
-
-            return result;
-        }
-
-        public void Dispose()
-        {
-            _rendered.TrySetResult(false);
-            foreach (var node in _index.Values)
-            {
-                node.Dispose();
-            }
-        }
-
-        /// <summary>
-        /// Tries to find a node in the scene graph representing the specified visual.
-        /// </summary>
-        /// <param name="visual">The visual.</param>
-        /// <returns>
-        /// The node representing the visual or null if it could not be found.
-        /// </returns>
-        public IVisualNode? FindNode(Visual visual)
-        {
-            _index.TryGetValue(visual, out var node);
-            return node;
-        }
-
-        /// <summary>
-        /// Gets the visuals at a point in the scene.
-        /// </summary>
-        /// <param name="p">The point.</param>
-        /// <param name="root">The root of the subtree to search.</param>
-        /// <param name="filter">A filter. May be null.</param>
-        /// <returns>The visuals at the specified point.</returns>
-        public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool>? filter)
-        {
-            var node = FindNode(root);
-            return (node != null) ? new HitTestEnumerable(node, filter, p, Root) : Enumerable.Empty<Visual>();
-        }
-
-        /// <summary>
-        /// Gets the visual at a point in the scene.
-        /// </summary>
-        /// <param name="p">The point.</param>
-        /// <param name="root">The root of the subtree to search.</param>
-        /// <param name="filter">A filter. May be null.</param>
-        /// <returns>The visual at the specified point.</returns>
-        public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool>? filter)
-        {
-            var node = FindNode(root);
-            return (node != null) ? HitTestFirst(node, p, filter) : null;
-        }
-
-        /// <summary>
-        /// Removes a node from the scene index.
-        /// </summary>
-        /// <param name="node">The node.</param>
-        public void Remove(IVisualNode node)
-        {
-            _ = node ?? throw new ArgumentNullException(nameof(node));
-
-            _index.Remove(node.Visual);
-
-            node.Dispose();
-        }
-
-        private VisualNode Clone(VisualNode source, IVisualNode? parent, Dictionary<Visual, IVisualNode> index)
-        {
-            var result = source.Clone(parent);
-
-            index.Add(result.Visual, result);
-
-            var children = source.Children;
-            var childrenCount = children.Count;
-
-            if (childrenCount > 0)
-            {
-                result.TryPreallocateChildren(childrenCount);
-
-                for (var i = 0; i < childrenCount; i++)
-                {
-                    var child = children[i];
-
-                    result.AddChild(Clone((VisualNode)child, result, index));
-                }
-            }
-
-            return result;
-        }
-
-        private Visual HitTestFirst(IVisualNode root, Point p, Func<Visual, bool>? filter)
-        {
-            using var enumerator = new HitTestEnumerator(root, filter, p, Root);
-
-            enumerator.MoveNext();
-
-            return enumerator.Current;
-        }
-
-        private class HitTestEnumerable : IEnumerable<Visual>
-        {
-            private readonly IVisualNode _root;
-            private readonly Func<Visual, bool>? _filter;
-            private readonly IVisualNode _sceneRoot;
-            private readonly Point _point;
-            
-            public HitTestEnumerable(IVisualNode root, Func<Visual, bool>? filter, Point point, IVisualNode sceneRoot)
-            {
-                _root = root;
-                _filter = filter;
-                _point = point;
-                _sceneRoot = sceneRoot;
-            }
-
-            public IEnumerator<Visual> GetEnumerator()
-            {
-                return new HitTestEnumerator(_root, _filter, _point, _sceneRoot);
-            }
-
-            IEnumerator IEnumerable.GetEnumerator()
-            {
-                return GetEnumerator();
-            }
-        }
-
-        private struct HitTestEnumerator : IEnumerator<Visual>
-        {
-            private readonly PooledStack<Entry> _nodeStack;
-            private readonly Func<Visual, bool>? _filter;
-            private readonly IVisualNode _sceneRoot;
-            private Visual? _current;
-            private readonly Point _point;
-
-            public HitTestEnumerator(IVisualNode root, Func<Visual, bool>? filter, Point point, IVisualNode sceneRoot)
-            {
-                _nodeStack = new PooledStack<Entry>();
-                _nodeStack.Push(new Entry(root, false, null, true));
-
-                _filter = filter;
-                _point = point;
-                _sceneRoot = sceneRoot;
-
-                _current = null;
-            }
-
-            public bool MoveNext()
-            {
-                while (_nodeStack.Count > 0)
-                {
-                    (var wasVisited, var isRoot, IVisualNode node, Rect? clip) = _nodeStack.Pop();
-
-                    if (wasVisited && isRoot)
-                    {
-                        break;
-                    }
-
-                    var children = node.Children;
-                    int childCount = children.Count;
-
-                    if (childCount == 0 || wasVisited)
-                    {
-                        if ((wasVisited || FilterAndClip(node, ref clip)) &&
-                            (node.Visual is ICustomHitTest custom ? custom.HitTest(_point) : node.HitTest(_point)))
-                        {
-                            _current = node.Visual;
-
-                            return true;
-                        }
-                    }
-                    else if (FilterAndClip(node, ref clip))
-                    {
-                        _nodeStack.Push(new Entry(node, true, null));
-
-                        for (var i = 0; i < childCount; i++)
-                        {
-                            _nodeStack.Push(new Entry(children[i], false, clip));
-                        }
-                    }
-                }
-
-                return false;
-            }
-
-            public void Reset()
-            {
-                throw new NotSupportedException();
-            }
-
-            public Visual Current => _current!;
-
-            object IEnumerator.Current => Current;
-
-            public void Dispose()
-            {
-                _nodeStack.Dispose();
-            }
-
-            private bool FilterAndClip(IVisualNode node, ref Rect? clip)
-            {
-                if (_filter?.Invoke(node.Visual) != false && node.Visual.IsAttachedToVisualTree)
-                {
-                    var clipped = false;
-
-                    if (node.ClipToBounds)
-                    {
-                        clip = clip == null ? node.ClipBounds : clip.Value.Intersect(node.ClipBounds);
-                        clipped = !clip.Value.ContainsExclusive(_point);
-                    }
-
-                    if (node.GeometryClip != null)
-                    {
-                        var controlPoint = _sceneRoot.Visual.TranslatePoint(_point, node.Visual);
-                        clipped = !node.GeometryClip.FillContains(controlPoint!.Value);
-                    }
-
-                    if (!clipped && node.Visual is ICustomHitTest custom)
-                    {
-                        clipped = !custom.HitTest(_point);
-                    }
-
-                    return !clipped;
-                }
-
-                return false;
-            }
-
-            private readonly struct Entry
-            {
-                public readonly bool WasVisited;
-                public readonly bool IsRoot;
-                public readonly IVisualNode Node;
-                public readonly Rect? Clip;
-
-                public Entry(IVisualNode node, bool wasVisited, Rect? clip, bool isRoot = false)
-                {
-                    Node = node;
-                    WasVisited = wasVisited;
-                    IsRoot = isRoot;
-                    Clip = clip;
-                }
-
-                public void Deconstruct(out bool wasVisited, out bool isRoot, out IVisualNode node, out Rect? clip)
-                {
-                    wasVisited = WasVisited;
-                    isRoot = IsRoot;
-                    node = Node;
-                    clip = Clip;
-                }
-            }
-        }
-
-        public void MarkAsRendered() => _rendered.TrySetResult(true);
-
-        public List<Action>? RenderThreadJobs { get; set; }
-    }
-}

+ 0 - 485
src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs

@@ -1,485 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Threading;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// Builds a scene graph from a visual tree.
-    /// </summary>
-    public class SceneBuilder : ISceneBuilder
-    {
-        /// <inheritdoc/>
-        public void UpdateAll(Scene scene)
-        {
-            _ = scene ?? throw new ArgumentNullException(nameof(scene));
-            Dispatcher.UIThread.VerifyAccess();
-
-            UpdateSize(scene);
-            scene.Layers.GetOrAdd(scene.Root.Visual);
-
-            using (var impl = new DeferredDrawingContextImpl(this, scene.Layers))
-            using (var context = new DrawingContext(impl))
-            {
-                var clip = new Rect(scene.Root.Visual.Bounds.Size);
-                Update(context, scene, (VisualNode)scene.Root, clip, true);
-            }
-        }
-
-        /// <inheritdoc/>
-        public bool Update(Scene scene, Visual visual)
-        {
-            _ = scene ?? throw new ArgumentNullException(nameof(scene));
-            _ = visual ?? throw new ArgumentNullException(nameof(visual));
-
-            Dispatcher.UIThread.VerifyAccess();
-
-            if (!scene.Root.Visual.IsVisible)
-            {
-                throw new AvaloniaInternalException("Cannot update the scene for an invisible root visual.");
-            }
-
-            var node = (VisualNode?)scene.FindNode(visual);
-
-            if (visual == scene.Root.Visual)
-            {
-                UpdateSize(scene);
-            }
-
-            if (visual.VisualRoot == scene.Root.Visual)
-            {
-                if (node?.Parent != null &&
-                    visual.VisualParent != null &&
-                    node.Parent.Visual != visual.VisualParent)
-                {
-                    // The control has changed parents. Remove the node and recurse into the new parent node.
-                    ((VisualNode)node.Parent).RemoveChild(node);
-                    Deindex(scene, node);
-                    node = (VisualNode?)scene.FindNode(visual.VisualParent);
-                }
-
-                if (visual.IsVisible)
-                {
-                    // If the node isn't yet part of the scene, find the nearest ancestor that is.
-                    node = node ?? FindExistingAncestor(scene, visual);
-
-                    // We don't need to do anything if this part of the tree has already been fully
-                    // updated.
-                    if (node != null && !node.SubTreeUpdated)
-                    {
-                        // If the control we've been asked to update isn't part of the scene then
-                        // we're carrying out an add operation, so recurse and add all the
-                        // descendents too.
-                        var recurse = node.Visual != visual;
-
-                        using (var impl = new DeferredDrawingContextImpl(this, scene.Layers))
-                        using (var context = new DrawingContext(impl))
-                        {
-                            var clip = new Rect(scene.Root.Visual.Bounds.Size);
-
-                            if (node.Parent != null)
-                            {
-                                context.PushPostTransform(node.Parent.Transform);
-                                clip = node.Parent.ClipBounds;
-                            }
-
-                            using (context.PushTransformContainer())
-                            {
-                                Update(context, scene, node, clip, recurse);
-                            }
-                        }
-
-                        return true;
-                    }
-                }
-                else
-                {
-                    if (node != null)
-                    {
-                        // The control has been hidden so remove it from its parent and deindex the
-                        // node and its descendents.
-                        ((VisualNode?)node.Parent)?.RemoveChild(node);
-                        Deindex(scene, node);
-                        return true;
-                    }
-                }
-            }
-            else if (node != null)
-            {
-                // The control has been removed so remove it from its parent and deindex the
-                // node and its descendents.
-                var trim = FindFirstDeadAncestor(scene, node);
-                ((VisualNode)trim.Parent!).RemoveChild(trim);
-                Deindex(scene, trim);
-                return true;
-            }
-
-            return false;
-        }
-
-        private static VisualNode? FindExistingAncestor(Scene scene, Visual visual)
-        {
-            var node = scene.FindNode(visual);
-
-            while (node == null && visual.IsVisible)
-            {
-                var parent = visual.VisualParent;
-
-                if (parent is null)
-                    return null;
-
-                visual = parent;
-                node = scene.FindNode(visual);
-            }
-
-            return visual.IsVisible ? (VisualNode?)node : null;
-        }
-
-        private static VisualNode FindFirstDeadAncestor(Scene scene, IVisualNode node)
-        {
-            var parent = node.Parent;
-
-            while (parent!.Visual.VisualRoot == null)
-            {
-                node = parent;
-                parent = node.Parent;
-            }
-
-            return (VisualNode)node;
-        }
-
-        private static object GetOrCreateChildNode(Scene scene, Visual child, VisualNode parent)
-        {
-            var result = (VisualNode?)scene.FindNode(child);
-
-            if (result != null && result.Parent != parent)
-            {
-                Deindex(scene, result);
-                ((VisualNode?)result.Parent)?.RemoveChild(result);
-                result = null;
-            }
-
-            return result ?? CreateNode(scene, child, parent);
-        }
-
-        private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse)
-        {
-            var visual = node.Visual;
-            var opacity = visual.Opacity;
-            var clipToBounds = visual.ClipToBounds;
-#pragma warning disable CS0618 // Type or member is obsolete
-            var clipToBoundsRadius = visual is IVisualWithRoundRectClip roundRectClip ?
-                roundRectClip.ClipToBoundsRadius :
-                default;
-#pragma warning restore CS0618 // Type or member is obsolete
-
-            var bounds = new Rect(visual.Bounds.Size);
-            var contextImpl = (DeferredDrawingContextImpl)context.PlatformImpl;
-
-            contextImpl.Layers.Find(node.LayerRoot!)?.Dirty.Add(node.Bounds);
-
-            if (visual.IsVisible)
-            {
-                var m = node != scene.Root ? 
-                    Matrix.CreateTranslation(visual.Bounds.Position) :
-                    Matrix.Identity;
-
-                var renderTransform = Matrix.Identity;
-
-                // this should be calculated BEFORE renderTransform
-                if (visual.HasMirrorTransform)
-                {
-                    var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0);
-                    renderTransform *= mirrorMatrix;
-                }
-
-                if (visual.RenderTransform != null)
-                {
-                    var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height));
-                    var offset = Matrix.CreateTranslation(origin);
-                    var finalTransform = (-offset) * visual.RenderTransform.Value * (offset);
-                    renderTransform *= finalTransform;
-                }
-
-                m = renderTransform * m;
-
-                using (contextImpl.BeginUpdate(node))
-                using (context.PushPostTransform(m))
-                using (context.PushTransformContainer())
-                {
-                    var globalBounds = bounds.TransformToAABB(contextImpl.Transform);
-                    var clipBounds = clipToBounds ?
-                        globalBounds.Intersect(clip) :
-                        clip;
-
-                    forceRecurse = forceRecurse ||
-                        node.ClipBounds != clipBounds ||
-                        node.Opacity != opacity ||
-                        node.Transform != contextImpl.Transform;
-
-                    node.Transform = contextImpl.Transform;
-                    node.ClipBounds = clipBounds;
-                    node.ClipToBounds = clipToBounds;
-                    node.LayoutBounds = globalBounds;
-                    node.ClipToBoundsRadius = clipToBoundsRadius;
-                    node.GeometryClip = visual.Clip?.PlatformImpl;
-                    node.Opacity = opacity;
-
-                    // TODO: Check equality between node.OpacityMask and visual.OpacityMask before assigning.
-                    node.OpacityMask = visual.OpacityMask?.ToImmutable();
-
-                    if (ShouldStartLayer(visual))
-                    {
-                        if (node.LayerRoot != visual)
-                        {
-                            MakeLayer(scene, node);
-                        }
-                        else
-                        {
-                            UpdateLayer(node, scene.Layers[node.LayerRoot]);
-                        }
-                    }
-                    else if (node.LayerRoot == node.Visual && node.Parent != null)
-                    {
-                        ClearLayer(scene, node);
-                    }
-
-                    if (node.ClipToBounds)
-                    {
-                        clip = clip.Intersect(node.ClipBounds);
-                    }
-
-                    try
-                    {
-                        visual.Render(context);
-                    }
-                    catch { }
-
-                    var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
-                    visual.SetTransformedBounds(transformed);
-
-                    if (forceRecurse)
-                    {
-                        var visualChildren = (IList<Visual>) visual.VisualChildren;
-
-                        node.TryPreallocateChildren(visualChildren.Count);
-
-                        if (visualChildren.Count == 1)
-                        {
-                            var childNode = GetOrCreateChildNode(scene, visualChildren[0], node);
-                            Update(context, scene, (VisualNode)childNode, clip, forceRecurse);
-                        }
-                        else if (visualChildren.Count > 1)
-                        {
-                            var count = visualChildren.Count;
-
-                            if (visual.HasNonUniformZIndexChildren)
-                            {
-                                var sortedChildren = new (Visual visual, int index)[count];
-
-                                for (var i = 0; i < count; i++)
-                                {
-                                    sortedChildren[i] = (visualChildren[i], i);
-                                }
-
-                                // Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements.
-                                Array.Sort(sortedChildren, (lhs, rhs) =>
-                                {
-                                    var result = ZIndexComparer.Instance.Compare(lhs.visual, rhs.visual);
-
-                                    return result == 0 ? lhs.index.CompareTo(rhs.index) : result;
-                                });
-
-                                foreach (var child in sortedChildren)
-                                {
-                                    var childNode = GetOrCreateChildNode(scene, child.Item1, node);
-                                    Update(context, scene, (VisualNode)childNode, clip, forceRecurse);
-                                }
-                            }
-                            else
-                                foreach (var child in visualChildren)
-                                {
-                                    var childNode = GetOrCreateChildNode(scene, child, node);
-                                    Update(context, scene, (VisualNode)childNode, clip, forceRecurse);
-                                }
-                        }
-
-                        node.SubTreeUpdated = true;
-                        contextImpl.TrimChildren();
-                    }
-                }
-            }
-            else
-            {
-                contextImpl.BeginUpdate(node).Dispose();
-            }
-        }
-
-        private static void UpdateSize(Scene scene)
-        {
-            var renderRoot = scene.Root.Visual as IRenderRoot;
-            var newSize = renderRoot?.ClientSize ?? scene.Root.Visual.Bounds.Size;
-
-            scene.Scaling = renderRoot?.RenderScaling ?? 1;
-
-            if (scene.Size != newSize)
-            {
-                var oldSize = scene.Size;
-
-                scene.Size = newSize;
-
-                Rect horizontalDirtyRect = default;
-                Rect verticalDirtyRect = default;
-
-                if (newSize.Width > oldSize.Width)
-                {
-                    horizontalDirtyRect = new Rect(oldSize.Width, 0, newSize.Width - oldSize.Width, oldSize.Height);
-                }
-
-                if (newSize.Height > oldSize.Height)
-                {
-                    verticalDirtyRect = new Rect(0, oldSize.Height, newSize.Width, newSize.Height - oldSize.Height);
-                }
-
-                foreach (var layer in scene.Layers)
-                {
-                    layer.Dirty.Add(horizontalDirtyRect);
-                    layer.Dirty.Add(verticalDirtyRect);
-                }
-            }
-        }
-
-        private static VisualNode CreateNode(Scene scene, Visual visual, VisualNode parent)
-        {
-            var node = new VisualNode(visual, parent);
-            node.LayerRoot = parent.LayerRoot;
-            scene.Add(node);
-            return node;
-        }
-
-        private static void Deindex(Scene scene, VisualNode node)
-        {
-            var nodeChildren = node.Children;
-            var nodeChildrenCount = nodeChildren.Count;
-
-            for (var i = 0; i < nodeChildrenCount; i++)
-            {
-                if (nodeChildren[i] is VisualNode visual)
-                {
-                    Deindex(scene, visual);
-                }
-            }
-
-            scene.Remove(node);
-
-            node.SubTreeUpdated = true;
-
-            scene.Layers[node.LayerRoot!].Dirty.Add(node.Bounds);
-
-            node.Visual.SetTransformedBounds(null);
-
-            if (node.LayerRoot == node.Visual && node.Visual != scene.Root.Visual)
-            {
-                scene.Layers.Remove(node.LayerRoot);
-            }
-        }
-
-        private static void ClearLayer(Scene scene, VisualNode node)
-        {
-            var parent = (VisualNode)node.Parent!;
-            var oldLayerRoot = node.LayerRoot;
-            var newLayerRoot = parent.LayerRoot!;
-            var existingDirtyRects = scene.Layers[node.LayerRoot!].Dirty;
-            var newDirtyRects = scene.Layers[newLayerRoot].Dirty;
-
-            existingDirtyRects.Coalesce();
-
-            foreach (var r in existingDirtyRects)
-            {
-                newDirtyRects.Add(r);
-            }
-
-            var oldLayer = scene.Layers[oldLayerRoot!];
-            PropagateLayer(node, scene.Layers[newLayerRoot], oldLayer);
-            scene.Layers.Remove(oldLayer);
-        }
-
-        private static void MakeLayer(Scene scene, VisualNode node)
-        {
-            var oldLayerRoot = node.LayerRoot!;
-            var layer = scene.Layers.Add(node.Visual);
-            var oldLayer = scene.Layers[oldLayerRoot!];
-
-            UpdateLayer(node, layer);
-            PropagateLayer(node, layer, scene.Layers[oldLayerRoot]);
-        }
-
-        private static void UpdateLayer(VisualNode node, SceneLayer layer)
-        {
-            layer.Opacity = node.Visual.Opacity;
-
-            if (node.Visual.OpacityMask != null)
-            {
-                layer.OpacityMask = node.Visual.OpacityMask?.ToImmutable();
-                layer.OpacityMaskRect = node.ClipBounds;
-            }
-            else
-            {
-                layer.OpacityMask = null;
-                layer.OpacityMaskRect = default;
-            }
-
-            layer.GeometryClip = node.HasAncestorGeometryClip ?
-                CreateLayerGeometryClip(node) :
-                null;
-        }
-
-        private static void PropagateLayer(VisualNode node, SceneLayer layer, SceneLayer oldLayer)
-        {
-            node.LayerRoot = layer.LayerRoot;
-
-            layer.Dirty.Add(node.Bounds);
-            oldLayer.Dirty.Add(node.Bounds);
-
-            foreach (VisualNode child in node.Children)
-            {
-                // If the child is not the start of a new layer, recurse.
-                if (child.LayerRoot != child.Visual)
-                {
-                    PropagateLayer(child, layer, oldLayer);
-                }
-            }
-        }
-
-        // HACK: Disabled layers because they're broken in current renderer. See #2244.
-        private static bool ShouldStartLayer(Visual visual) => false;
-
-        private static IGeometryImpl? CreateLayerGeometryClip(VisualNode node)
-        {
-            IGeometryImpl? result = null;
-            VisualNode? n = node;
-
-            for (;;)
-            {
-                n = (VisualNode?)n!.Parent;
-
-                if (n == null || (n.GeometryClip == null && !n.HasAncestorGeometryClip))
-                {
-                    break;
-                }
-
-                if (n?.GeometryClip != null)
-                {
-                    var transformed = n.GeometryClip.WithTransform(n.Transform);
-
-                    result = result == null ? transformed : result.Intersect(transformed);
-                }
-            }
-
-            return result;
-        }
-    }
-}

+ 0 - 73
src/Avalonia.Base/Rendering/SceneGraph/SceneLayer.cs

@@ -1,73 +0,0 @@
-using Avalonia.Media;
-using Avalonia.Platform;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// Represents a layer in a <see cref="Scene"/>.
-    /// </summary>
-    public class SceneLayer
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SceneLayer"/> class.
-        /// </summary>
-        /// <param name="layerRoot">The visual at the root of the layer.</param>
-        /// <param name="distanceFromRoot">The distance from the scene root.</param>
-        public SceneLayer(Visual layerRoot, int distanceFromRoot)
-        {
-            LayerRoot = layerRoot;
-            Dirty = new DirtyRects();
-            DistanceFromRoot = distanceFromRoot;
-        }
-
-        /// <summary>
-        /// Clones the layer.
-        /// </summary>
-        /// <returns>The cloned layer.</returns>
-        public SceneLayer Clone()
-        {
-            return new SceneLayer(LayerRoot, DistanceFromRoot)
-            {
-                Opacity = Opacity,
-                OpacityMask = OpacityMask,
-                OpacityMaskRect = OpacityMaskRect,
-                GeometryClip = GeometryClip,
-            };
-        }
-
-        /// <summary>
-        /// Gets the visual at the root of the layer.
-        /// </summary>
-        public Visual LayerRoot { get; }
-
-        /// <summary>
-        /// Gets the distance of the layer root from the root of the scene.
-        /// </summary>
-        public int DistanceFromRoot { get; }
-
-        /// <summary>
-        /// Gets or sets the opacity of the layer.
-        /// </summary>
-        public double Opacity { get; set; } = 1;
-
-        /// <summary>
-        /// Gets or sets the opacity mask for the layer.
-        /// </summary>
-        public IBrush? OpacityMask { get; set; }
-
-        /// <summary>
-        /// Gets or sets the target rectangle for the layer opacity mask.
-        /// </summary>
-        public Rect OpacityMaskRect { get; set; }
-
-        /// <summary>
-        /// Gets the layer's geometry clip.
-        /// </summary>
-        public IGeometryImpl? GeometryClip { get; set; }
-
-        /// <summary>
-        /// Gets the dirty rectangles for the layer.
-        /// </summary>
-        internal DirtyRects Dirty { get; }
-    }
-}

+ 0 - 206
src/Avalonia.Base/Rendering/SceneGraph/SceneLayers.cs

@@ -1,206 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// Holds a collection of layers for a <see cref="Scene"/>.
-    /// </summary>
-    public class SceneLayers : IEnumerable<SceneLayer>
-    {
-        private readonly Visual _root;
-        private readonly List<SceneLayer> _inner;
-        private readonly Dictionary<Visual, SceneLayer> _index;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SceneLayers"/> class.
-        /// </summary>
-        /// <param name="root">The scene's root visual.</param>
-        public SceneLayers(Visual root) : this(root, 0)
-        {
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SceneLayers"/> class.
-        /// </summary>
-        /// <param name="root">The scene's root visual.</param>
-        /// <param name="capacity">Initial layer capacity.</param>
-        public SceneLayers(Visual root, int capacity)
-        {
-            _root = root;
-
-            _inner = new List<SceneLayer>(capacity);
-            _index = new Dictionary<Visual, SceneLayer>(capacity);
-        }
-
-        /// <summary>
-        /// Gets the number of layers in the scene.
-        /// </summary>
-        public int Count => _inner.Count;
-
-        /// <summary>
-        /// Gets a value indicating whether any of the layers have a dirty region.
-        /// </summary>
-        public bool HasDirty
-        {
-            get
-            {
-                foreach (var layer in _inner)
-                {
-                    if (!layer.Dirty.IsEmpty)
-                    {
-                        return true;
-                    }
-                }
-
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Gets a layer by index.
-        /// </summary>
-        /// <param name="index">The index of the layer.</param>
-        /// <returns>The layer.</returns>
-        public SceneLayer this[int index] => _inner[index];
-
-        /// <summary>
-        /// Gets a layer by its root visual.
-        /// </summary>
-        /// <param name="visual">The layer's root visual.</param>
-        /// <returns>The layer.</returns>
-        public SceneLayer this[Visual visual] => _index[visual];
-
-        /// <summary>
-        /// Adds a layer to the scene.
-        /// </summary>
-        /// <param name="layerRoot">The root visual of the layer.</param>
-        /// <returns>The created layer.</returns>
-        public SceneLayer Add(Visual layerRoot)
-        {
-            _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot));
-
-            var distance = layerRoot.CalculateDistanceFromAncestor(_root);
-            var layer = new SceneLayer(layerRoot, distance);
-            var insert = FindInsertIndex(layer);
-            _index.Add(layerRoot, layer);
-            _inner.Insert(insert, layer);
-            return layer;
-        }
-
-        /// <summary>
-        /// Makes a deep clone of the layers.
-        /// </summary>
-        /// <returns>The cloned layers.</returns>
-        public SceneLayers Clone()
-        {
-            var result = new SceneLayers(_root, Count);
-
-            foreach (var src in _inner)
-            {
-                var dest = src.Clone();
-                result._index.Add(dest.LayerRoot, dest);
-                result._inner.Add(dest);
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// Tests whether a layer exists with the specified root visual.
-        /// </summary>
-        /// <param name="layerRoot">The root visual.</param>
-        /// <returns>
-        /// True if a layer exists with the specified root visual, otherwise false.
-        /// </returns>
-        public bool Exists(Visual layerRoot)
-        {
-            _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot));
-
-            return _index.ContainsKey(layerRoot);
-        }
-
-        /// <summary>
-        /// Tries to find a layer with the specified root visual.
-        /// </summary>
-        /// <param name="layerRoot">The root visual.</param>
-        /// <returns>The layer if found, otherwise null.</returns>
-        public SceneLayer? Find(Visual layerRoot)
-        {
-            _index.TryGetValue(layerRoot, out var result);
-            return result;
-        }
-
-        /// <summary>
-        /// Gets an existing layer or creates a new one if no existing layer is found.
-        /// </summary>
-        /// <param name="layerRoot">The root visual.</param>
-        /// <returns>The layer.</returns>
-        public SceneLayer GetOrAdd(Visual layerRoot)
-        {
-            _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot));
-
-            if (!_index.TryGetValue(layerRoot, out var result))
-            {
-                result = Add(layerRoot);
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// Removes a layer from the scene.
-        /// </summary>
-        /// <param name="layerRoot">The root visual.</param>
-        /// <returns>True if a matching layer was removed, otherwise false.</returns>
-        public bool Remove(Visual layerRoot)
-        {
-            _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot));
-
-            if (_index.TryGetValue(layerRoot, out var layer))
-            {
-                Remove(layer);
-            }
-
-            return layer != null;
-        }
-
-        /// <summary>
-        /// Removes a layer from the scene.
-        /// </summary>
-        /// <param name="layer">The layer.</param>
-        /// <returns>True if the layer was part of the scene, otherwise false.</returns>
-        public bool Remove(SceneLayer layer)
-        {
-            _ = layer ?? throw new ArgumentNullException(nameof(layer));
-
-            _index.Remove(layer.LayerRoot);
-            return _inner.Remove(layer);
-        }
-
-        /// <inheritdoc/>
-        public IEnumerator<SceneLayer> GetEnumerator() => _inner.GetEnumerator();
-
-        /// <inheritdoc/>
-        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
-        private int FindInsertIndex(SceneLayer insert)
-        {
-            var index = 0;
-
-            foreach (var layer in _inner)
-            {
-                if (layer.DistanceFromRoot > insert.DistanceFromRoot)
-                {
-                    break;
-                }
-
-                ++index;
-            }
-
-            return index;
-        }
-    }
-}

+ 0 - 448
src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs

@@ -1,448 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Avalonia.Reactive;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Rendering.SceneGraph
-{
-    /// <summary>
-    /// A node in the low-level scene graph representing an <see cref="Visual"/>.
-    /// </summary>
-    internal class VisualNode : IVisualNode
-    {
-        private static readonly IReadOnlyList<IVisualNode> EmptyChildren = Array.Empty<IVisualNode>();
-        private static readonly IReadOnlyList<IRef<IDrawOperation>> EmptyDrawOperations = Array.Empty<IRef<IDrawOperation>>();
-
-        private Rect? _bounds;
-        private double _opacity;
-        private List<IVisualNode>? _children;
-        private List<IRef<IDrawOperation>>? _drawOperations;
-        private IRef<IDisposable>? _drawOperationsRefCounter;
-        private bool _drawOperationsCloned;
-        private Matrix transformRestore;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="VisualNode"/> class.
-        /// </summary>
-        /// <param name="visual">The visual that this node represents.</param>
-        /// <param name="parent">The parent scene graph node, if any.</param>
-        public VisualNode(Visual visual, IVisualNode? parent)
-        {
-            Visual = visual ?? throw new ArgumentNullException(nameof(visual));
-            Parent = parent;
-            HasAncestorGeometryClip = parent != null && 
-                (parent.HasAncestorGeometryClip || parent.GeometryClip != null);
-        }
-
-        /// <inheritdoc/>
-        public Visual Visual { get; }
-
-        /// <inheritdoc/>
-        public IVisualNode? Parent { get; }
-
-        /// <inheritdoc/>
-        public CornerRadius ClipToBoundsRadius { get; set; }
-
-        /// <inheritdoc/>
-        public Matrix Transform { get; set; }
-
-        /// <inheritdoc/>
-        public Rect Bounds => _bounds ?? CalculateBounds();
-
-        /// <inheritdoc/>
-        public Rect ClipBounds { get; set; }
-
-        /// <inheritdoc/>
-        public Rect LayoutBounds { get; set; }
-
-        /// <inheritdoc/>
-        public bool ClipToBounds { get; set; }
-
-        /// <inheritdoc/>
-        public IGeometryImpl? GeometryClip { get; set; }
-
-        /// <inheritdoc/>
-        public bool HasAncestorGeometryClip { get; }
-
-        /// <inheritdoc/>
-        public double Opacity
-        {
-            get { return _opacity; }
-            set
-            {
-                if (_opacity != value)
-                {
-                    _opacity = value;
-                    OpacityChanged = true;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the opacity mask for the scene graph node.
-        /// </summary>
-        public IBrush? OpacityMask { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether this node in the scene graph has already
-        /// been updated in the current update pass.
-        /// </summary>
-        public bool SubTreeUpdated { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether the <see cref="Opacity"/> property has changed.
-        /// </summary>
-        public bool OpacityChanged { get; private set; }
-
-        public Visual? LayerRoot { get; set; }
-
-        /// <inheritdoc/>
-        public IReadOnlyList<IVisualNode> Children => _children ?? EmptyChildren;
-
-        /// <inheritdoc/>
-        public IReadOnlyList<IRef<IDrawOperation>> DrawOperations => _drawOperations ?? EmptyDrawOperations;
-
-        /// <summary>
-        /// Adds a child to the <see cref="Children"/> collection.
-        /// </summary>
-        /// <param name="child">The child to add.</param>
-        public void AddChild(IVisualNode child)
-        {
-            if (child.Disposed)
-            {
-                throw new ObjectDisposedException("Visual node for {node.Visual}");
-            }
-
-            if (child.Parent != this)
-            {
-                throw new AvaloniaInternalException("VisualNode added to wrong parent.");
-            }
-
-            EnsureChildrenCreated();
-            _children.Add(child);
-        }
-
-        /// <summary>
-        /// Adds an operation to the <see cref="DrawOperations"/> collection.
-        /// </summary>
-        /// <param name="operation">The operation to add.</param>
-        public void AddDrawOperation(IRef<IDrawOperation> operation)
-        {
-            EnsureDrawOperationsCreated();
-            _drawOperations.Add(operation.Clone());
-        }
-
-        /// <summary>
-        /// Removes a child from the <see cref="Children"/> collection.
-        /// </summary>
-        /// <param name="child">The child to remove.</param>
-        public void RemoveChild(IVisualNode child)
-        {
-            EnsureChildrenCreated();
-            _children.Remove(child);
-        }
-
-        /// <summary>
-        /// Replaces a child in the <see cref="Children"/> collection.
-        /// </summary>
-        /// <param name="index">The child to be replaced.</param>
-        /// <param name="node">The child to add.</param>
-        public void ReplaceChild(int index, IVisualNode node)
-        {
-            if (node.Disposed)
-            {
-                throw new ObjectDisposedException("Visual node for {node.Visual}");
-            }
-
-            if (node.Parent != this)
-            {
-                throw new AvaloniaInternalException("VisualNode added to wrong parent.");
-            }
-
-            EnsureChildrenCreated();
-            _children[index] = node;
-        }
-
-        /// <summary>
-        /// Replaces an item in the <see cref="DrawOperations"/> collection.
-        /// </summary>
-        /// <param name="index">The operation to be replaced.</param>
-        /// <param name="operation">The operation to add.</param>
-        public void ReplaceDrawOperation(int index, IRef<IDrawOperation> operation)
-        {
-            EnsureDrawOperationsCreated();
-            var old = _drawOperations[index];
-            _drawOperations[index] = operation.Clone();
-            old.Dispose();
-        }
-
-        /// <summary>
-        /// Sorts the <see cref="Children"/> collection according to the order of the visual's
-        /// children and their z-index.
-        /// </summary>
-        /// <param name="scene">The scene that the node is a part of.</param>
-        public void SortChildren(Scene scene)
-        {
-            if (_children == null || _children.Count <= 1)
-            {
-                return;
-            }
-
-            var keys = new List<long>(Visual.VisualChildren.Count);
-
-            for (var i = 0; i < Visual.VisualChildren.Count; ++i)
-            {
-                var child = Visual.VisualChildren[i];
-                var zIndex = child.ZIndex;
-                keys.Add(((long)zIndex << 32) + i);
-            }
-
-            keys.Sort();
-            _children.Clear();
-
-            foreach (var i in keys)
-            {
-                var child = Visual.VisualChildren[(int)(i & 0xffffffff)];
-                var node = scene.FindNode(child);
-
-                if (node != null)
-                {
-                    _children.Add(node);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Removes items in the <see cref="Children"/> collection from the specified index
-        /// to the end.
-        /// </summary>
-        /// <param name="first">The index of the first child to be removed.</param>
-        public void TrimChildren(int first)
-        {
-            if (first < _children?.Count)
-            {
-                EnsureChildrenCreated();
-                for (int i = first; i < _children.Count; i++)
-                {
-                    _children[i].Dispose();
-                }
-                _children.RemoveRange(first, _children.Count - first);
-            }
-        }
-
-        /// <summary>
-        /// Removes items in the <see cref="DrawOperations"/> collection from the specified index
-        /// to the end.
-        /// </summary>
-        /// <param name="first">The index of the first operation to be removed.</param>
-        public void TrimDrawOperations(int first)
-        {
-            if (first < _drawOperations?.Count)
-            {
-                EnsureDrawOperationsCreated();
-                for (int i = first; i < _drawOperations.Count; i++)
-                {
-                    _drawOperations[i].Dispose();
-                }
-                _drawOperations.RemoveRange(first, _drawOperations.Count - first);
-            }
-        }
-
-        /// <summary>
-        /// Makes a copy of the node
-        /// </summary>
-        /// <param name="parent">The new parent node.</param>
-        /// <returns>A cloned node.</returns>
-        public VisualNode Clone(IVisualNode? parent)
-        {
-            return new VisualNode(Visual, parent)
-            {
-                Transform = Transform,
-                ClipBounds = ClipBounds,
-                ClipToBoundsRadius =  ClipToBoundsRadius,
-                ClipToBounds = ClipToBounds,
-                LayoutBounds = LayoutBounds,
-                GeometryClip = GeometryClip,
-                _opacity = Opacity,
-                OpacityMask = OpacityMask,
-                _drawOperations = _drawOperations,
-                _drawOperationsRefCounter = _drawOperationsRefCounter?.Clone(),
-                _drawOperationsCloned = true,
-                LayerRoot= LayerRoot,
-            };
-        }
-
-        /// <inheritdoc/>
-        public bool HitTest(Point p)
-        {
-            var drawOperations = DrawOperations;
-            var drawOperationsCount = drawOperations.Count;
-
-            for (var i = 0; i < drawOperationsCount; i++)
-            {
-                var operation = drawOperations[i];
-
-                if (operation?.Item?.HitTest(p) == true)
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        /// <inheritdoc/>
-        public void BeginRender(IDrawingContextImpl context, bool skipOpacity)
-        {
-            transformRestore = context.Transform;
-
-            if (ClipToBounds)
-            {
-                context.Transform = Matrix.Identity;
-                if (ClipToBoundsRadius.IsDefault)
-                    context.PushClip(ClipBounds);
-                else
-                    context.PushClip(new RoundedRect(ClipBounds, ClipToBoundsRadius));
-            }
-
-            context.Transform = Transform;
-
-            if (Opacity != 1 && !skipOpacity)
-            {
-                context.PushOpacity(Opacity);
-            }
-
-            if (GeometryClip != null)
-            {
-                context.PushGeometryClip(GeometryClip);
-            }
-
-            if (OpacityMask != null)
-            {
-                context.PushOpacityMask(OpacityMask, LayoutBounds);
-            }
-        }
-
-        /// <inheritdoc/>
-        public void EndRender(IDrawingContextImpl context, bool skipOpacity)
-        {
-            if (OpacityMask != null)
-            {
-                context.PopOpacityMask();
-            }
-
-            if (GeometryClip != null)
-            {
-                context.PopGeometryClip();
-            }
-
-            if (Opacity != 1 && !skipOpacity)
-            {
-                context.PopOpacity();
-            }
-
-            if (ClipToBounds)
-            {
-                context.Transform = Matrix.Identity;
-                context.PopClip();
-            }
-
-            context.Transform = transformRestore;
-        }
-
-        internal void TryPreallocateChildren(int count)
-        {
-            if (count == 0)
-            {
-                return;
-            }
-
-            EnsureChildrenCreated(count);
-        }
-
-        private Rect CalculateBounds()
-        {
-            var result = new Rect();
-
-            if (_drawOperations != null)
-            {
-                foreach (var operation in _drawOperations)
-                {
-                    result = result.Union(operation.Item.Bounds);
-                }
-            }
-
-            _bounds = result;
-            return result;
-        }
-
-        [MemberNotNull(nameof(_children))]
-        private void EnsureChildrenCreated(int capacity = 0)
-        {
-            if (_children == null)
-            {
-                _children = new List<IVisualNode>(capacity);
-            }
-        }
-
-        /// <summary>
-        /// Ensures that this node draw operations have been created and are mutable (in case we are using cloned operations).
-        /// </summary>
-        [MemberNotNull(nameof(_drawOperations))]
-        private void EnsureDrawOperationsCreated()
-        {
-            if (_drawOperations == null)
-            {
-                _drawOperations = new List<IRef<IDrawOperation>>();
-                _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations));
-                _drawOperationsCloned = false;
-            }
-            else if (_drawOperationsCloned)
-            {
-                var oldDrawOperations = _drawOperations;
-
-                _drawOperations = new List<IRef<IDrawOperation>>(oldDrawOperations.Count);
-
-                foreach (var drawOperation in oldDrawOperations)
-                {
-                    _drawOperations.Add(drawOperation.Clone());
-                }
-
-                _drawOperationsRefCounter?.Dispose();
-                _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations));
-                _drawOperationsCloned = false;
-            }
-        }
-
-        /// <summary>
-        /// Creates disposable that will dispose all items in passed draw operations after being disposed.
-        /// It is crucial that we don't capture current <see cref="VisualNode"/> instance
-        /// as draw operations can be cloned and may persist across subsequent scenes.
-        /// </summary>
-        /// <param name="drawOperations">Draw operations that need to be disposed.</param>
-        /// <returns>Disposable for given draw operations.</returns>
-        private static IDisposable CreateDisposeDrawOperations(List<IRef<IDrawOperation>> drawOperations)
-        {
-            return Disposable.Create(drawOperations, operations =>
-            {
-                foreach (var operation in operations)
-                {
-                    operation.Dispose();
-                }
-            });
-        }
-
-        public bool Disposed { get; private set; }
-
-        public void Dispose()
-        {
-            _drawOperationsRefCounter?.Dispose();
-
-            Disposed = true;
-        }
-    }
-}

+ 1 - 17
src/Avalonia.Base/Visual.cs

@@ -36,12 +36,7 @@ namespace Avalonia
         /// </summary>
         public static readonly DirectProperty<Visual, Rect> BoundsProperty =
             AvaloniaProperty.RegisterDirect<Visual, Rect>(nameof(Bounds), o => o.Bounds);
-
-        public static readonly DirectProperty<Visual, TransformedBounds?> TransformedBoundsProperty =
-            AvaloniaProperty.RegisterDirect<Visual, TransformedBounds?>(
-                nameof(TransformedBounds),
-                o => o.TransformedBounds);
-
+        
         /// <summary>
         /// Defines the <see cref="ClipToBounds"/> property.
         /// </summary>
@@ -116,7 +111,6 @@ namespace Avalonia
                 (s, h) => s.Invalidated -= h);
 
         private Rect _bounds;
-        private TransformedBounds? _transformedBounds;
         private IRenderRoot? _visualRoot;
         private Visual? _visualParent;
         private bool _hasMirrorTransform;
@@ -172,11 +166,6 @@ namespace Avalonia
             protected set { SetAndRaise(BoundsProperty, ref _bounds, value); }
         }
 
-        /// <summary>
-        /// Gets the bounds of the control relative to the window, accounting for rendering transforms.
-        /// </summary>
-        public TransformedBounds? TransformedBounds => _transformedBounds;
-
         /// <summary>
         /// Gets or sets a value indicating whether the control should be clipped to its bounds.
         /// </summary>
@@ -523,11 +512,6 @@ namespace Avalonia
             return CompositionVisual;
         }
 
-        internal void SetTransformedBounds(TransformedBounds? value)
-        {
-            SetAndRaise(TransformedBoundsProperty, ref _transformedBounds, value);
-        }
-
         /// <summary>
         /// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method 
         /// for this control and all of its visual descendants.

+ 15 - 3
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@@ -1,10 +1,13 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.Metadata;
 using Avalonia.Platform;
 using Avalonia.Rendering;
+using Avalonia.Rendering.Composition;
+using Avalonia.Threading;
 
 namespace Avalonia.Controls.Embedding.Offscreen
 {
@@ -13,7 +16,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
     {
         private double _scaling = 1;
         private Size _clientSize;
-        private PlatformRenderInterfaceContextManager _renderInterface = new(null);
+        private ManualRenderTimer _manualRenderTimer = new();
 
         public IInputRoot? InputRoot { get; private set; }
         public bool IsDisposed { get; private set; }
@@ -23,10 +26,19 @@ namespace Avalonia.Controls.Embedding.Offscreen
             IsDisposed = true;
         }
 
+        class ManualRenderTimer : IRenderTimer
+        {
+            static Stopwatch St = Stopwatch.StartNew(); 
+            public event Action<TimeSpan>? Tick;
+            public bool RunsInBackground => false;
+            public void TriggerTick() => Tick?.Invoke(St.Elapsed);
+        }
+
+
         public IRenderer CreateRenderer(IRenderRoot root) =>
-            new ImmediateRenderer((Visual)root, () => _renderInterface.CreateRenderTarget(Surfaces), _renderInterface);
+            new CompositingRenderer(root, new Compositor(new RenderLoop(_manualRenderTimer, Dispatcher.UIThread), null),
+                () => Surfaces);
 
-        public abstract void Invalidate(Rect rect);
         public abstract IEnumerable<object> Surfaces { get; }
 
         public Size ClientSize

+ 1 - 3
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Controls.Notifications
     /// </summary>
     [TemplatePart("PART_Items", typeof(Panel))]
     [PseudoClasses(":topleft", ":topright", ":bottomleft", ":bottomright")]
-    public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest
+    public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager
     {
         private IList? _items;
 
@@ -160,7 +160,5 @@ namespace Avalonia.Controls.Notifications
             PseudoClasses.Set(":bottomleft", position == NotificationPosition.BottomLeft);
             PseudoClasses.Set(":bottomright", position == NotificationPosition.BottomRight);
         }
-
-        public bool HitTest(Point point) => VisualChildren.HitTestCustom(point);
     }
 }

+ 10 - 4
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@@ -350,13 +350,19 @@ namespace Avalonia.Controls.Platform
         {
             // HACK: #8179 needs to be addressed to correctly implement it in the PointerPressed method.
             var item = GetMenuItem(e.Source as Control) as MenuItem;
-            if (item?.TransformedBounds == null)
-            {
+
+            if (item == null)
                 return;
-            }
+            
+            var serverTransform = item?.CompositionVisual?.TryGetServerGlobalTransform();
+            if (serverTransform == null)
+                return;
+            
             var point = e.GetCurrentPoint(null);
+            var transformedPoint = point.Position.Transform(serverTransform.Value);
 
-            if (point.Properties.IsLeftButtonPressed && item.TransformedBounds.Value.Contains(point.Position) == false)
+            if (point.Properties.IsLeftButtonPressed && 
+                new Rect(item!.Bounds.Size).Contains(transformedPoint) == false)
             {
                 e.Pointer.Capture(null);
             }

+ 0 - 5
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@@ -111,11 +111,6 @@ namespace Avalonia.Platform
         /// <param name="root">The toplevel.</param>
         IRenderer CreateRenderer(IRenderRoot root);
 
-        /// <summary>
-        /// Invalidates a rect on the toplevel.
-        /// </summary>
-        void Invalidate(Rect rect);
-
         /// <summary>
         /// Sets the <see cref="IInputRoot"/> for the toplevel.
         /// </summary>

+ 1 - 9
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Controls.Primitives
     /// <remarks>
     /// TODO: Need to track position of adorned elements and move the adorner if they move.
     /// </remarks>
-    public class AdornerLayer : Canvas, ICustomSimpleHitTest
+    public class AdornerLayer : Canvas
     {
         /// <summary>
         /// Allows for getting and setting of the adorned element.
@@ -305,17 +305,9 @@ namespace Avalonia.Controls.Primitives
                         info.Bounds = new TransformedBounds(new Rect(adorned.Bounds.Size), new Rect(adorned.Bounds.Size), Matrix.Identity);
                         InvalidateMeasure();
                     });
-                else
-                    info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x =>
-                    {
-                        info.Bounds = x;
-                        InvalidateMeasure();
-                    });
             }
         }
 
-        public bool HitTest(Point point) => Children.HitTestCustom(point);
-
         private class AdornedElementInfo
         {
             public IDisposable? Subscription { get; set; }

+ 1 - 3
src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs

@@ -4,7 +4,7 @@ using Avalonia.VisualTree;
 
 namespace Avalonia.Controls.Primitives
 {
-    public class ChromeOverlayLayer : Panel, ICustomSimpleHitTest
+    public class ChromeOverlayLayer : Panel
     {
         public static Panel? GetOverlayLayer(Visual visual)
         {
@@ -26,7 +26,5 @@ namespace Avalonia.Controls.Primitives
         {
             base.Children.Add(c);
         }
-
-        public bool HitTest(Point point) => Children.HitTestCustom(point);
     }
 }

+ 1 - 3
src/Avalonia.Controls/Primitives/OverlayLayer.cs

@@ -4,7 +4,7 @@ using Avalonia.VisualTree;
 
 namespace Avalonia.Controls.Primitives
 {
-    public class OverlayLayer : Canvas, ICustomSimpleHitTest
+    public class OverlayLayer : Canvas
     {
         public Size AvailableSize { get; private set; }
         public static OverlayLayer? GetOverlayLayer(Visual visual)
@@ -22,8 +22,6 @@ namespace Avalonia.Controls.Primitives
             return null;
         }
 
-        public bool HitTest(Point point) => Children.HitTestCustom(point);
-
         protected override Size MeasureOverride(Size availableSize)
         {
             foreach (Control child in Children)

+ 6 - 10
src/Avalonia.Controls/Primitives/Popup.cs

@@ -8,6 +8,7 @@ using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives.PopupPositioning;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
+using Avalonia.Input.TextInput;
 using Avalonia.Layout;
 using Avalonia.LogicalTree;
 using Avalonia.Metadata;
@@ -378,11 +379,8 @@ namespace Avalonia.Controls.Primitives
 
             if (InheritsTransform && placementTarget is Control c)
             {
-                SubscribeToEventHandler<Control, EventHandler<AvaloniaPropertyChangedEventArgs>>(
-                    c,
-                    PlacementTargetPropertyChanged,
-                    (x, handler) => x.PropertyChanged += handler,
-                    (x, handler) => x.PropertyChanged -= handler).DisposeWith(handlerCleanup);
+                TransformTrackingHelper.Track(c, PlacementTargetTransformChanged)
+                    .DisposeWith(handlerCleanup);
             }
             else
             {
@@ -872,13 +870,11 @@ namespace Avalonia.Controls.Primitives
                 Close();
             }
         }
-        
-        private void PlacementTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+
+        private void PlacementTargetTransformChanged(Visual v, Matrix? matrix)
         {
-            if (_openState is not null && e.Property == Visual.TransformedBoundsProperty)
-            {
+            if (_openState is not null)
                 UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget);
-            }
         }
 
         private void WindowLostFocus()

+ 22 - 10
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@@ -11,6 +11,7 @@ using Avalonia.Platform;
 using Avalonia.Remote.Protocol;
 using Avalonia.Remote.Protocol.Input;
 using Avalonia.Remote.Protocol.Viewport;
+using Avalonia.Rendering;
 using Avalonia.Threading;
 using Key = Avalonia.Input.Key;
 using PixelFormat = Avalonia.Platform.PixelFormat;
@@ -19,7 +20,7 @@ using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat;
 namespace Avalonia.Controls.Remote.Server
 {
     [Unstable]
-    public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface
+    internal class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface, ITopLevelImpl
     {
         private readonly IAvaloniaRemoteTransportConnection _transport;
         private LockedFramebuffer? _framebuffer;
@@ -28,7 +29,8 @@ namespace Avalonia.Controls.Remote.Server
         private long _lastReceivedFrame = -1;
         private long _nextFrameNumber = 1;
         private ClientViewportAllocatedMessage? _pendingAllocation;
-        private bool _invalidated;
+        private bool _queuedNextRender;
+        private bool _inRender;
         private Vector _dpi = new Vector(96, 96);
         private ProtocolPixelFormat[]? _supportedFormats;
 
@@ -38,6 +40,14 @@ namespace Avalonia.Controls.Remote.Server
             _transport.OnMessage += OnMessage;
 
             KeyboardDevice = AvaloniaLocator.Current.GetRequiredService<IKeyboardDevice>();
+            QueueNextRender();
+        }
+
+        IRenderer ITopLevelImpl.CreateRenderer(IRenderRoot root)
+        {
+            var r = (IRendererWithCompositor)base.CreateRenderer(root);
+            r.Compositor.AfterCommit += QueueNextRender;
+            return r;
         }
 
         private static RawPointerEventType GetAvaloniaEventType (Avalonia.Remote.Protocol.Input.MouseButton button, bool pressed)
@@ -125,7 +135,7 @@ namespace Avalonia.Controls.Remote.Server
                     lock(_lock)
                     {
                         _dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY);
-                        _invalidated = true;
+                        _queuedNextRender = true;
                     }
                     
                     Dispatcher.UIThread.Post(RenderIfNeeded);
@@ -315,7 +325,7 @@ namespace Avalonia.Controls.Remote.Server
         {
             lock (_lock)
             {
-                if (_lastReceivedFrame != _lastSentFrame || !_invalidated || _supportedFormats == null)
+                if (_lastReceivedFrame != _lastSentFrame || !_queuedNextRender || _supportedFormats == null)
                     return;
 
             }
@@ -327,23 +337,25 @@ namespace Avalonia.Controls.Remote.Server
                     format = fmt;
                     break;
                 }
-            
+
+            _inRender = true;
             var frame = RenderFrame((int) ClientSize.Width, (int) ClientSize.Height, format);
             lock (_lock)
             {
                 _lastSentFrame = _nextFrameNumber++;
                 frame.SequenceId = _lastSentFrame;
-                _invalidated = false;
+                _queuedNextRender = false;
             }
+            _inRender = false;
             _transport.Send(frame);
         }
 
-        public override void Invalidate(Rect rect)
+        private void QueueNextRender()
         {
-            if (!IsDisposed)
+            if (!_inRender && !IsDisposed)
             {
-                _invalidated = true;
-                Dispatcher.UIThread.Post(RenderIfNeeded);
+                _queuedNextRender = true;
+                DispatcherTimer.RunOnce(RenderIfNeeded, TimeSpan.FromMilliseconds(2), DispatcherPriority.Background);
             }
         }
 

+ 0 - 6
src/Avalonia.Controls/TopLevel.cs

@@ -385,12 +385,6 @@ namespace Avalonia.Controls
             ?? (PlatformImpl as ITopLevelImplWithStorageProvider)?.StorageProvider
             ?? throw new InvalidOperationException("StorageProvider platform implementation is not available.");
         
-        /// <inheritdoc/>
-        void IRenderRoot.Invalidate(Rect rect)
-        {
-            PlatformImpl?.Invalidate(rect);
-        }
-        
         /// <inheritdoc/>
         Point IRenderRoot.PointToClient(PixelPoint p)
         {

+ 11 - 3
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -14,6 +14,8 @@ using Avalonia.Platform;
 using Avalonia.Platform.Storage;
 using Avalonia.Platform.Storage.FileIO;
 using Avalonia.Rendering;
+using Avalonia.Rendering.Composition;
+using Avalonia.Threading;
 
 namespace Avalonia.DesignerSupport.Remote
 {
@@ -61,9 +63,15 @@ namespace Avalonia.DesignerSupport.Remote
                     }));
         }
 
-        public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer((Visual)root, () =>
-            new PlatformRenderInterfaceContextManager(null)
-                .CreateRenderTarget(Surfaces));
+        class DummyRenderTimer : IRenderTimer
+        {
+            public event Action<TimeSpan> Tick;
+            public bool RunsInBackground => false;
+        }
+
+        public IRenderer CreateRenderer(IRenderRoot root) =>
+            new CompositingRenderer(root,
+                new Compositor(new RenderLoop(new DummyRenderTimer(), Dispatcher.UIThread), null), () => Surfaces);
         
         public void Dispose()
         {

+ 11 - 3
src/Avalonia.Diagnostics/Diagnostics/Conventions.cs

@@ -1,12 +1,20 @@
 using System;
+using System.IO;
 
 namespace Avalonia.Diagnostics
 {
     internal static class Conventions
     {
-        public static string DefaultScreenshotsRoot =>
-             System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures, Environment.SpecialFolderOption.Create),
-                "Screenshots");
+        public static string DefaultScreenshotsRoot
+        {
+            get
+            {
+                var dir = System.IO.Path.Combine(
+                    Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Screenshots");
+                Directory.CreateDirectory(dir);
+                return dir;
+            }
+        }
 
         public static IScreenshotHandler DefaultScreenshotHandler { get; } =
             new Screenshots.FilePickerHandler();

+ 18 - 14
src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml.cs

@@ -40,15 +40,14 @@ namespace Avalonia.Diagnostics.Views
 
             _layoutRoot = this.GetControl<Grid>("LayoutRoot");
 
-            void SubscribeToBounds(Visual visual)
+            Visual? visual = _contentArea;
+            while (visual != null && !ReferenceEquals(visual, this))
             {
-                visual.GetPropertyChangedObservable(TransformedBoundsProperty)
+                visual.GetPropertyChangedObservable(BoundsProperty)
                     .Subscribe(UpdateSizeGuidelines);
+                visual = visual.VisualParent;
             }
-
-            SubscribeToBounds(_borderArea);
-            SubscribeToBounds(_paddingArea);
-            SubscribeToBounds(_contentArea);
+            
         }
 
         private void InitializeComponent()
@@ -56,22 +55,27 @@ namespace Avalonia.Diagnostics.Views
             AvaloniaXamlLoader.Load(this);
         }
 
-        private void UpdateSizeGuidelines(AvaloniaPropertyChangedEventArgs e)
+        private void UpdateSizeGuidelines(AvaloniaPropertyChangedEventArgs _)
         {
             void UpdateGuidelines(Visual area)
             {
-                if (area.TransformedBounds is TransformedBounds bounds)
+                // That's what TransformedBounds.Bounds actually was.
+                // The code below doesn't really make sense to me, so I've just changed v.TransformedBounds.Bounds
+                // to GetPseudoTransformedBounds
+                Rect GetPseudoTransformedBounds(Visual v) => new(v.Bounds.Size);
+                var bounds = GetPseudoTransformedBounds(area);
+                
                 {
                     // Horizontal guideline
                     {
-                        var sizeArea = TranslateToRoot((_horizontalSize.TransformedBounds ?? default).Bounds.BottomLeft,
+                        var sizeArea = TranslateToRoot(GetPseudoTransformedBounds(_horizontalSize).BottomLeft,
                             _horizontalSize);
 
-                        var start = TranslateToRoot(bounds.Bounds.BottomLeft, area);
+                        var start = TranslateToRoot(bounds.BottomLeft, area);
 
                         SetPosition(_horizontalSizeBegin, start);
 
-                        var end = TranslateToRoot(bounds.Bounds.BottomRight, area);
+                        var end = TranslateToRoot(bounds.BottomRight, area);
 
                         SetPosition(_horizontalSizeEnd, end.WithX(end.X - 1));
 
@@ -83,13 +87,13 @@ namespace Avalonia.Diagnostics.Views
 
                     // Vertical guideline
                     {
-                        var sizeArea = TranslateToRoot((_verticalSize.TransformedBounds ?? default).Bounds.TopRight, _verticalSize);
+                        var sizeArea = TranslateToRoot(GetPseudoTransformedBounds(_verticalSize).TopRight, _verticalSize);
 
-                        var start = TranslateToRoot(bounds.Bounds.TopRight, area);
+                        var start = TranslateToRoot(bounds.TopRight, area);
 
                         SetPosition(_verticalSizeBegin, start);
 
-                        var end = TranslateToRoot(bounds.Bounds.BottomRight, area);
+                        var end = TranslateToRoot(bounds.BottomRight, area);
 
                         SetPosition(_verticalSizeEnd, end.WithY(end.Y - 1));
 

+ 4 - 4
src/Avalonia.Diagnostics/Diagnostics/VisualExtensions.cs

@@ -17,11 +17,11 @@ namespace Avalonia.Diagnostics
         /// <param name="dpi">Dpi quality.</param>
         public static void RenderTo(this Control source, Stream destination, double dpi = 96)
         {
-            if (source.TransformedBounds == null)
-            {
+            var transform = source.CompositionVisual?.TryGetServerGlobalTransform();
+            if (transform == null)
                 return;
-            }
-            var rect = source.TransformedBounds.Value.Clip;
+
+            var rect = new Rect(source.Bounds.Size).TransformToAABB(transform.Value);
             var top = rect.TopLeft;
             var pixelSize = new PixelSize((int)rect.Width, (int)rect.Height);
             var dpiVector = new Vector(dpi, dpi);

+ 0 - 1
src/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs

@@ -21,7 +21,6 @@ namespace Avalonia
             return builder
                 .UseHeadless(new AvaloniaHeadlessPlatformOptions
                 {
-                    UseCompositor = true,
                     UseHeadlessDrawing = false
                 })
                 .AfterSetup(_ =>

+ 1 - 3
src/Avalonia.Headless/AvaloniaHeadlessPlatform.cs

@@ -72,8 +72,7 @@ namespace Avalonia.Headless
                 .Bind<ITextShaperImpl>().ToSingleton<HeadlessTextShaperStub>()
                 .Bind<IWindowingPlatform>().ToConstant(new HeadlessWindowingPlatform())
                 .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
-            if (opts.UseCompositor)
-                Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null);
+            Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null);
         }
 
 
@@ -88,7 +87,6 @@ namespace Avalonia.Headless
 
     public class AvaloniaHeadlessPlatformOptions
     {
-        public bool UseCompositor { get; set; } = true;
         public bool UseHeadlessDrawing { get; set; } = true;
     }
 

+ 2 - 5
src/Avalonia.Headless/HeadlessWindowImpl.cs

@@ -54,11 +54,8 @@ namespace Avalonia.Headless
         public Action<Size, PlatformResizeReason> Resized { get; set; }
         public Action<double> ScalingChanged { get; set; }
 
-        public IRenderer CreateRenderer(IRenderRoot root)
-            => AvaloniaHeadlessPlatform.Compositor != null
-                ? new CompositingRenderer(root, AvaloniaHeadlessPlatform.Compositor, () => Surfaces)
-                : new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(),
-                    () => new PlatformRenderInterfaceContextManager(null).CreateRenderTarget(Surfaces), null);
+        public IRenderer CreateRenderer(IRenderRoot root) =>
+            new CompositingRenderer(root, AvaloniaHeadlessPlatform.Compositor, () => Surfaces);
 
         public void Invalidate(Rect rect)
         {

+ 3 - 10
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -24,8 +24,7 @@ namespace Avalonia.Native
         static extern IntPtr CreateAvaloniaNative();
 
         internal static readonly KeyboardDevice KeyboardDevice = new KeyboardDevice();
-        internal static Compositor? Compositor { get; private set; }
-        internal static PlatformRenderInterfaceContextManager? RenderInterface { get; private set; }
+        internal static Compositor Compositor { get; private set; } = null!;
 
         public static AvaloniaNativePlatform Initialize(IntPtr factory, AvaloniaNativePlatformOptions options)
         {
@@ -140,14 +139,8 @@ namespace Avalonia.Native
                     // ignored
                 }
             }
-
-
-            if (_options.UseDeferredRendering && _options.UseCompositor)
-            {
-                Compositor = new Compositor(renderLoop, _platformGl);
-            }
-            else
-                RenderInterface = new PlatformRenderInterfaceContextManager(_platformGl);
+            
+            Compositor = new Compositor(renderLoop, _platformGl);
         }
 
         public ITrayIconImpl CreateTrayIcon()

+ 0 - 14
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@@ -30,20 +30,6 @@ namespace Avalonia
     /// </summary>
     public class AvaloniaNativePlatformOptions
     {
-        /// <summary>
-        /// Deferred renderer would be used when set to true. Immediate renderer when set to false. The default value is true.
-        /// </summary>
-        /// <remarks>
-        /// Avalonia has two rendering modes: Immediate and Deferred rendering.
-        /// Immediate re-renders the whole scene when some element is changed on the scene. Deferred re-renders only changed elements.
-        /// </remarks>
-        public bool UseDeferredRendering { get; set; } = true;
-        
-        /// <summary>
-        /// Enables new compositing rendering with UWP-like API
-        /// </summary>
-        public bool UseCompositor { get; set; } = true;
-
         /// <summary>
         /// Determines whether to use GPU for rendering in your project. The default value is true.
         /// </summary>

+ 3 - 20
src/Avalonia.Native/WindowImplBase.cs

@@ -53,7 +53,6 @@ namespace Avalonia.Native
         protected IInputRoot _inputRoot;
         IAvnWindowBase _native;
         private object _syncRoot = new object();
-        private bool _deferredRendering = false;
         private bool _gpu = false;
         private readonly MouseDevice _mouse;
         private readonly IKeyboardDevice _keyboard;
@@ -69,7 +68,6 @@ namespace Avalonia.Native
         {
             _factory = factory;
             _gpu = opts.UseGpu && glFeature != null;
-            _deferredRendering = opts.UseDeferredRendering;
 
             _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
             _mouse = new MouseDevice();
@@ -365,25 +363,10 @@ namespace Avalonia.Native
 
         public IRenderer CreateRenderer(IRenderRoot root)
         {
-            var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
-            var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
-            if (customRendererFactory != null)
-                return customRendererFactory.Create(root, loop);
-            
-            if (_deferredRendering)
+            return new CompositingRenderer(root, AvaloniaNativePlatform.Compositor, () => Surfaces)
             {
-                if (AvaloniaNativePlatform.Compositor != null)
-                    return new CompositingRenderer(root, AvaloniaNativePlatform.Compositor, () => Surfaces)
-                    {
-                        RenderOnlyOnRenderThread = false
-                    };
-                return new DeferredRenderer(root, loop,
-                    () => AvaloniaNativePlatform.RenderInterface!.CreateRenderTarget(Surfaces),
-                    AvaloniaNativePlatform.RenderInterface);
-            }
-
-            return new ImmediateRenderer((Visual)root,
-                () => AvaloniaNativePlatform.RenderInterface!.CreateRenderTarget(Surfaces), AvaloniaNativePlatform.RenderInterface);
+                RenderOnlyOnRenderThread = false
+            };
         }
 
         public virtual void Dispose()

+ 0 - 120
src/Avalonia.X11/X11ImmediateRendererProxy.cs

@@ -1,120 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.Threading;
-using Avalonia.VisualTree;
-
-namespace Avalonia.X11
-{
-    public class X11ImmediateRendererProxy : IRenderer, IRenderLoopTask
-    {
-        private readonly IRenderLoop _loop;
-        private ImmediateRenderer _renderer;
-        private bool _invalidated;
-        private bool _running;
-        private object _lock = new object();
-
-        public X11ImmediateRendererProxy(Visual root, IRenderLoop loop, Func<IRenderTarget> renderTargetFactory,
-            PlatformRenderInterfaceContextManager renderContext)
-        {
-            _loop = loop;
-            _renderer = new ImmediateRenderer(root, renderTargetFactory, renderContext);
-        }
-
-        public void Dispose()
-        {
-            _running = false;
-            _renderer.Dispose();
-        }
-
-        public bool DrawFps
-        {
-            get => _renderer.DrawFps;
-            set => _renderer.DrawFps = value;
-        }
-
-        public bool DrawDirtyRects
-        {
-            get => _renderer.DrawDirtyRects;
-            set => _renderer.DrawDirtyRects = value;
-        }
-
-        public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated
-        {
-            add => _renderer.SceneInvalidated += value;
-            remove => _renderer.SceneInvalidated -= value;
-        }
-
-        public void AddDirty(Visual visual)
-        {
-            lock (_lock)
-                _invalidated = true;
-            _renderer.AddDirty(visual);
-        }
-
-        public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool> filter)
-        {
-            return _renderer.HitTest(p, root, filter);
-        }
-
-        public Visual HitTestFirst(Point p, Visual root, Func<Visual, bool> filter)
-        {
-            return _renderer.HitTestFirst(p, root, filter);
-        }
-
-        public void RecalculateChildren(Visual visual)
-        {
-            _renderer.RecalculateChildren(visual);
-        }
-
-        public void Resized(Size size)
-        {
-            _renderer.Resized(size);
-        }
-
-        public void Paint(Rect rect)
-        {
-            _invalidated = false;
-            _renderer.Paint(rect);
-        }
-
-        public void Start()
-        {
-            _running = true;
-            _loop.Add(this);
-            _renderer.Start();
-        }
-
-        public void Stop()
-        {
-            _running = false;
-            _loop.Remove(this);
-            _renderer.Stop();
-        }
-
-        public ValueTask<object> TryGetRenderInterfaceFeature(Type featureType) =>
-            _renderer.TryGetRenderInterfaceFeature(featureType);
-
-        public bool NeedsUpdate => false;
-        public void Update(TimeSpan time)
-        {
-            
-        }
-
-        public void Render()
-        {
-            if (_invalidated)
-            {
-                lock (_lock)
-                    _invalidated = false;
-                Dispatcher.UIThread.Post(() =>
-                {
-                    if (_running)
-                        Paint(new Rect(0, 0, 100000, 100000));
-                });
-            }
-        }
-    }
-}

+ 2 - 18
src/Avalonia.X11/X11Platform.cs

@@ -30,7 +30,6 @@ namespace Avalonia.X11
         public X11Info Info { get; private set; }
         public IX11Screens X11Screens { get; private set; }
         public Compositor Compositor { get; private set; }
-        public PlatformRenderInterfaceContextManager RenderInterface { get; private set; }
         public IScreenImpl Screens { get; private set; }
         public X11PlatformOptions Options { get; private set; }
         public IntPtr OrphanedWindow { get; private set; }
@@ -103,12 +102,8 @@ namespace Avalonia.X11
             }
 
             var gl = AvaloniaLocator.Current.GetService<IPlatformGraphics>();
-            
-            if (options.UseCompositor)
-                Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), gl);
-            else
-                RenderInterface = new(gl);
 
+            Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), gl);
         }
 
         public IntPtr DeferredDisplay { get; set; }
@@ -226,18 +221,7 @@ namespace Avalonia
         /// The default value is true.
         /// </summary>
         public bool UseDBusFilePicker { get; set; } = true;
-
-        /// <summary>
-        /// Deferred renderer would be used when set to true. Immediate renderer when set to false. The default value is true.
-        /// </summary>
-        /// <remarks>
-        /// Avalonia has two rendering modes: Immediate and Deferred rendering.
-        /// Immediate re-renders the whole scene when some element is changed on the scene. Deferred re-renders only changed elements.
-        /// </remarks>
-        public bool UseDeferredRendering { get; set; } = true;
-
-        public bool UseCompositor { get; set; } = true;
-
+        
         /// <summary>
         /// Determines whether to use IME.
         /// IME would be enabled by default if the current user input language is one of the following: Mandarin, Japanese, Vietnamese or Korean.

+ 2 - 19
src/Avalonia.X11/X11Window.cs

@@ -384,25 +384,8 @@ namespace Avalonia.X11
         public Action<PixelPoint> PositionChanged { get; set; }
         public Action LostFocus { get; set; }
 
-        public IRenderer CreateRenderer(IRenderRoot root)
-        {
-            var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
-            var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
-
-            if (customRendererFactory != null)
-                return customRendererFactory.Create(root, loop);
-
-            return _platform.Options.UseDeferredRendering
-                ? _platform.Options.UseCompositor
-                    ? new CompositingRenderer(root, this._platform.Compositor, () => Surfaces)
-                    : new DeferredRenderer(root, loop, () => _platform.RenderInterface.CreateRenderTarget(Surfaces), _platform.RenderInterface)
-                    {
-                        RenderOnlyOnRenderThread = true
-                    }
-                : new X11ImmediateRendererProxy((Visual)root, loop,
-                    () => _platform.RenderInterface.CreateRenderTarget(Surfaces),
-                    _platform.RenderInterface);
-        }
+        public IRenderer CreateRenderer(IRenderRoot root) =>
+            new CompositingRenderer(root, _platform.Compositor, () => Surfaces);
 
         void OnEvent(ref XEvent ev)
         {

+ 1 - 10
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -24,16 +24,12 @@ namespace Avalonia.LinuxFramebuffer
             _inputBackend = inputBackend;
 
             Surfaces = new object[] { _outputBackend };
-
-            Invalidate(default);
             _inputBackend.Initialize(this, e => Input?.Invoke(e));
         }
 
         public IRenderer CreateRenderer(IRenderRoot root)
         {
-            var factory = AvaloniaLocator.Current.GetService<IRendererFactory>();
-            var renderLoop = AvaloniaLocator.Current.GetService<IRenderLoop>();
-            return factory?.Create(root, renderLoop) ?? new CompositingRenderer(root, LinuxFramebufferPlatform.Compositor, () => Surfaces);
+            return new CompositingRenderer(root, LinuxFramebufferPlatform.Compositor, () => Surfaces);
         }
 
         public void Dispose()
@@ -41,11 +37,6 @@ namespace Avalonia.LinuxFramebuffer
             throw new NotSupportedException();
         }
 
-
-        public void Invalidate(Rect rect)
-        {
-        }
-
         public void SetInputRoot(IInputRoot inputRoot)
         {
             InputRoot = inputRoot;

+ 2 - 2
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@@ -26,8 +26,8 @@ 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; }
+
+        internal static Compositor Compositor { get; private set; } = null!;
         
         
         LinuxFramebufferPlatform(IOutputBackend backend)

+ 2 - 18
src/Windows/Avalonia.Win32/Win32Platform.cs

@@ -42,17 +42,6 @@ namespace Avalonia
     /// </summary>
     public class Win32PlatformOptions
     {
-        /// <summary>
-        /// Deferred renderer would be used when set to true. Immediate renderer when set to false. The default value is true.
-        /// </summary>
-        /// <remarks>
-        /// Avalonia has two rendering modes: Immediate and Deferred rendering.
-        /// Immediate re-renders the whole scene when some element is changed on the scene. Deferred re-renders only changed elements.
-        /// </remarks>
-        public bool UseDeferredRendering { get; set; } = true;
-
-        public bool UseCompositor { get; set; } = true;
-
         /// <summary>
         /// Enables ANGLE for Windows. For every Windows version that is above Windows 7, the default is true otherwise it's false.
         /// </summary>
@@ -137,12 +126,10 @@ namespace Avalonia.Win32
         /// </summary>
         public static Version WindowsVersion { get; } = RtlGetVersion();
 
-        public static bool UseDeferredRendering => Options.UseDeferredRendering;
         internal static bool UseOverlayPopups => Options.OverlayPopups;
         public static Win32PlatformOptions Options { get; private set; }
         
         internal static Compositor Compositor { get; private set; }
-        internal static PlatformRenderInterfaceContextManager RenderInterface { get; private set; }
 
         public static void Initialize()
         {
@@ -181,11 +168,8 @@ namespace Avalonia.Win32
             
             if (OleContext.Current != null)
                 AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
-
-            if (Options.UseCompositor)
-                Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), platformGraphics);
-            else
-                RenderInterface = new PlatformRenderInterfaceContextManager(platformGraphics);
+            
+            Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), platformGraphics);
         }
 
         public bool HasMessages()

+ 2 - 26
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -558,32 +558,8 @@ namespace Avalonia.Win32
             _maxSize = maxSize;
         }
 
-        public IRenderer CreateRenderer(IRenderRoot root)
-        {
-            var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
-            var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
-
-            if (customRendererFactory != null)
-                return customRendererFactory.Create(root, loop);
-
-            if (Win32Platform.Compositor != null)
-                return new CompositingRenderer(root, Win32Platform.Compositor, () => Surfaces);
-
-            return Win32Platform.UseDeferredRendering
-                ? _isUsingComposition
-                    ? new DeferredRenderer(root, loop, 
-                        () => Win32Platform.RenderInterface.CreateRenderTarget(Surfaces),
-                        Win32Platform.RenderInterface)
-                    {
-                        RenderOnlyOnRenderThread = true
-                    }
-                    : (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock,
-                        renderTargetFactory: () => Win32Platform.RenderInterface.CreateRenderTarget(Surfaces),
-                        renderInterface: Win32Platform.RenderInterface)
-                : new ImmediateRenderer((Visual)root, 
-                    () => Win32Platform.RenderInterface.CreateRenderTarget(Surfaces),
-                    Win32Platform.RenderInterface);
-        }
+        public IRenderer CreateRenderer(IRenderRoot root) =>
+            new CompositingRenderer(root, Win32Platform.Compositor, () => Surfaces);
 
         public void Resize(Size value, PlatformResizeReason reason)
         {

+ 2 - 1
tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs

@@ -9,6 +9,7 @@ using Avalonia.Rendering.Composition;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
 using Avalonia.Threading;
+using Avalonia.UnitTests;
 using Xunit;
 using Xunit.Sdk;
 
@@ -67,7 +68,7 @@ public class CompositionAnimationTests
     public void GenericCheck(AnimationData data)
     {
         var compositor =
-            new Compositor(new RenderLoop(new CompositorTestsBase.ManualRenderTimer(), new Dispatcher(null)), null);
+            new Compositor(new RenderLoop(new CompositorTestServices.ManualRenderTimer(), new Dispatcher(null)), null);
         var target = compositor.CreateSolidColorVisual();
         var ani = new ScalarKeyFrameAnimation(null);
         foreach (var frame in data.Frames)

+ 15 - 15
tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs

@@ -20,7 +20,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Find_Controls_At_Point()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             var border = new Border
             {
@@ -40,7 +40,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Find_Empty_Controls_At_Point()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             var border = new Border
             {
@@ -59,7 +59,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Find_Invisible_Controls_At_Point()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Border visible, border;
             s.TopLevel.Content = border = new Border
@@ -91,7 +91,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     public void HitTest_Should_Find_Zero_Opacity_Controls_At_Point(bool parent, bool child)
     {
         
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Border visible, border;
             s.TopLevel.Content = border = new Border
@@ -118,7 +118,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Find_Control_Outside_Point()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             var border = new Border
             {
@@ -138,7 +138,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Return_Top_Controls_First()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Panel container = new Panel
             {
@@ -173,7 +173,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Return_Top_Controls_First_With_ZIndex()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Panel container = new Panel
             {
@@ -219,7 +219,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Find_Control_Translated_Outside_Parent_Bounds()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Border target;
             Panel container = new Panel
@@ -259,7 +259,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Find_Control_Outside_Parent_Bounds_When_Clipped()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Border target;
             Panel container = new Panel
@@ -299,7 +299,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Find_Control_Outside_Scroll_Viewport()
     {
-        using (var s = new CompositorServices(new Size(100, 200)))
+        using (var s = new CompositorTestServices(new Size(100, 200)))
         {
             Border target;
             Border item1;
@@ -373,7 +373,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Find_Path_When_Outside_Fill()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Path path = new Path
             {
@@ -392,7 +392,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Respect_Geometry_Clip()
     {
-        using (var s = new CompositorServices(new Size(400, 400)))
+        using (var s = new CompositorTestServices(new Size(400, 400)))
         {
             Canvas canvas;
             Border border = new Border
@@ -422,7 +422,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Accommodate_ICustomHitTest()
     {
-        using (var s = new CompositorServices(new Size(300, 200)))
+        using (var s = new CompositorTestServices(new Size(300, 200)))
         {
             Border border = new CustomHitTestBorder
             {
@@ -445,7 +445,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Should_Not_Hit_Controls_Next_Pixel()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Border targetRectangle;
 
@@ -470,7 +470,7 @@ public class CompositorHitTestingTests : CompositorTestsBase
     [Fact]
     public void HitTest_Filter_Should_Filter_Out_Children()
     {
-        using (var s = new CompositorServices(new Size(200, 200)))
+        using (var s = new CompositorTestServices(new Size(200, 200)))
         {
             Border child, parent;
             s.TopLevel.Content = parent = new Border

+ 1 - 172
tests/Avalonia.Base.UnitTests/Rendering/CompositorTestsBase.cs

@@ -24,178 +24,7 @@ namespace Avalonia.Base.UnitTests.Rendering;
 
 public class CompositorTestsBase
 {
-    public class DebugEvents : ICompositionTargetDebugEvents
-    {
-        public List<Rect> Rects = new();
-
-        public void RectInvalidated(Rect rc)
-        {
-            Rects.Add(rc);
-        }
-
-        public void Reset()
-        {
-            Rects.Clear();
-        }
-    }
-
-    public class ManualRenderTimer : IRenderTimer
-    {
-        public event Action<TimeSpan> Tick;
-        public bool RunsInBackground => false;
-        public void TriggerTick() => Tick?.Invoke(TimeSpan.Zero);
-        public Task TriggerBackgroundTick() => Task.Run(TriggerTick);
-    }
-
-    class TopLevelImpl : ITopLevelImpl
-    {
-        private readonly Compositor _compositor;
-        public CompositingRenderer Renderer { get; private set; }
-
-        public TopLevelImpl(Compositor compositor, Size clientSize)
-        {
-            ClientSize = clientSize;
-            _compositor = compositor;
-        }
-
-        public void Dispose()
-        {
-
-        }
-
-        public Size ClientSize { get; }
-        public Size? FrameSize { get; }
-        public double RenderScaling => 1;
-        public IEnumerable<object> Surfaces { get; } = Array.Empty<object>();
-        public Action<RawInputEventArgs> Input { get; set; }
-        public Action<Rect> Paint { get; set; }
-        public Action<Size, PlatformResizeReason> Resized { get; set; }
-        public Action<double> ScalingChanged { get; set; }
-        public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
-
-        public IRenderer CreateRenderer(IRenderRoot root)
-        {
-            return Renderer = new CompositingRenderer(root, _compositor, () => Surfaces);
-        }
-
-        public void Invalidate(Rect rect)
-        {
-        }
-
-        public void SetInputRoot(IInputRoot inputRoot)
-        {
-        }
-
-        public Point PointToClient(PixelPoint point) => default;
-
-        public PixelPoint PointToScreen(Point point) => new();
-
-        public void SetCursor(ICursorImpl cursor)
-        {
-        }
-
-        public Action Closed { get; set; }
-        public Action LostFocus { get; set; }
-        public IMouseDevice MouseDevice { get; } = new MouseDevice();
-        public IPopupImpl CreatePopup() => throw new NotImplementedException();
-
-        public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
-        {
-        }
-
-        public WindowTransparencyLevel TransparencyLevel { get; }
-        public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
-
-        public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
-    }
-
-    protected class CompositorServices : IDisposable
-    {
-        private readonly IDisposable _app;
-        public Compositor Compositor { get; }
-        public ManualRenderTimer Timer { get; } = new();
-        public EmbeddableControlRoot TopLevel { get; }
-        public CompositingRenderer Renderer { get; } = null!;
-        public DebugEvents Events { get; } = new();
-
-        public void Dispose()
-        {
-            TopLevel.Renderer.Stop();
-            TopLevel.Dispose();
-            _app.Dispose();
-        }
-
-        public CompositorServices(Size? size = null)
-        {
-            _app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
-            try
-            {
-                AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(Timer);
-                AvaloniaLocator.CurrentMutable.Bind<IRenderLoop>()
-                    .ToConstant(new RenderLoop(Timer, Dispatcher.UIThread));
-
-                Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null);
-                var impl = new TopLevelImpl(Compositor, size ?? new Size(1000, 1000));
-                TopLevel = new EmbeddableControlRoot(impl)
-                {
-                    Template = new FuncControlTemplate((parent, scope) =>
-                    {
-                        var presenter = new ContentPresenter
-                        {
-                            [~ContentPresenter.ContentProperty] = new TemplateBinding(ContentControl.ContentProperty)
-                        };
-                        scope.Register("PART_ContentPresenter", presenter);
-                        return presenter;
-                    })
-                };
-                Renderer = impl.Renderer;
-                TopLevel.Prepare();
-                TopLevel.Renderer.Start();
-                RunJobs();
-                Renderer.CompositionTarget.Server.DebugEvents = Events;
-            }
-            catch
-            {
-                _app.Dispose();
-                throw;
-            }
-        }
-
-        public void RunJobs()
-        {
-            Dispatcher.UIThread.RunJobs();
-            Timer.TriggerTick();
-            Dispatcher.UIThread.RunJobs();
-        }
-
-        public void AssertRects(params Rect[] rects)
-        {
-            RunJobs();
-            var toAssert = rects.Select(x => x.ToString()).Distinct().OrderBy(x => x);
-            var invalidated = Events.Rects.Select(x => x.ToString()).Distinct().OrderBy(x => x);
-            Assert.Equal(toAssert, invalidated);
-            Events.Rects.Clear();
-        }
-
-        public void AssertHitTest(double x, double y, Func<Visual, bool> filter, params object[] expected)
-            => AssertHitTest(new Point(x, y), filter, expected);
-        public void AssertHitTest(Point pt, Func<Visual, bool> filter, params object[] expected)
-        {
-            RunJobs();
-            var tested = Renderer.HitTest(pt, TopLevel, filter);
-            Assert.Equal(expected, tested);
-        }
-        
-        public void AssertHitTestFirst(Point pt, Func<Visual, bool> filter, object expected)
-        {
-            RunJobs();
-            var tested = Renderer.HitTest(pt, TopLevel, filter).First();
-            Assert.Equal(expected, tested);
-        }
-    }
-
-
-    protected class CompositorCanvas : CompositorServices
+    protected class CompositorCanvas : CompositorTestServices
     {
         public Canvas Canvas { get; } = new();
 

+ 1 - 2
tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs

@@ -8,8 +8,7 @@ namespace Avalonia.Base.UnitTests.Rendering
         public bool HitTest(Point point)
         {
             // Move hit testing window halfway to the left
-            return Bounds
-                .WithX(Bounds.X - Bounds.Width / 2)  
+            return new Rect( -Bounds.Width / 2,0, Bounds.Width, Bounds.Height)
                 .Contains(point);
         }
     }

+ 0 - 790
tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs

@@ -1,790 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reactive.Subjects;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Threading;
-using Avalonia.UnitTests;
-using Avalonia.Media.Imaging;
-using Avalonia.VisualTree;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering
-{
-    public class DeferredRendererTests
-    {
-        [Fact]
-        public void First_Frame_Calls_SceneBuilder_UpdateAll()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var root = new TestRoot();
-                var sceneBuilder = MockSceneBuilder(root);
-
-                CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object);
-
-                sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()));
-            }
-        }
-
-        [Fact]
-        public void Frame_Does_Not_Call_SceneBuilder_If_No_Dirty_Controls()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-                var root = new TestRoot();
-                var sceneBuilder = MockSceneBuilder(root);
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    sceneBuilder: sceneBuilder.Object);
-
-                target.Start();
-                IgnoreFirstFrame(target, sceneBuilder);
-                RunFrame(target);
-
-                sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()), Times.Never);
-                sceneBuilder.Verify(x => x.Update(It.IsAny<Scene>(), It.IsAny<Visual>()), Times.Never);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_Dirty_Controls_In_Order()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                Border border;
-                Decorator decorator;
-                Canvas canvas;
-
-                var root = new TestRoot
-                {
-                    Child = decorator = new Decorator
-                    {
-                        Child = border = new Border { Child = canvas = new Canvas() }
-                    }
-                };
-
-                var sceneBuilder = MockSceneBuilder(root);
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder.Object,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                target.Start();
-                IgnoreFirstFrame(target, sceneBuilder);
-                target.AddDirty(border);
-                target.AddDirty(canvas);
-                target.AddDirty(root);
-                target.AddDirty(decorator);
-
-                var result = new List<Visual>();
-
-                sceneBuilder.Setup(x => x.Update(It.IsAny<Scene>(), It.IsAny<Visual>()))
-                    .Callback<Scene, Visual>((_, v) => result.Add(v));
-
-                RunFrame(target);
-
-                Assert.Equal(new List<Visual> { root, decorator, border, canvas }, result);
-            }
-        }
-
-        [Fact]
-        public void Should_Add_Dirty_Rect_On_Child_Remove()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                Decorator decorator;
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = decorator = new Decorator
-                    {
-                        Child = border = new Border { Width = 50, Height = 50, Background = Brushes.Red, },
-                    }
-                };
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                target.Start();
-                RunFrame(target);
-
-                decorator.Child = null;
-
-                RunFrame(target);
-
-                var scene = target.UnitTestScene();
-                var stackNode = scene.FindNode(decorator);
-                var dirty = scene.Layers[0].Dirty.ToList();
-
-                Assert.Equal(1, dirty.Count);
-                Assert.Equal(new Rect(25, 25, 50, 50), dirty[0]);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_VisualNode_Order_On_Child_Remove_Insert()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                StackPanel stack;
-                Canvas canvas1;
-                Canvas canvas2;
-
-                var root = new TestRoot
-                {
-                    Child = stack = new StackPanel
-                    {
-                        Children = { (canvas1 = new Canvas()), (canvas2 = new Canvas()), }
-                    }
-                };
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                target.Start();
-                RunFrame(target);
-
-                stack.Children.Remove(canvas2);
-                stack.Children.Insert(0, canvas2);
-
-                RunFrame(target);
-
-                var scene = target.UnitTestScene();
-                var stackNode = scene.FindNode(stack);
-
-                Assert.Same(stackNode.Children[0].Visual, canvas2);
-                Assert.Same(stackNode.Children[1].Visual, canvas1);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_VisualNode_Order_On_Child_Move()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                StackPanel stack;
-                Canvas canvas1;
-                Canvas canvas2;
-
-                var root = new TestRoot
-                {
-                    Child = stack = new StackPanel
-                    {
-                        Children = { (canvas1 = new Canvas()), (canvas2 = new Canvas()), }
-                    }
-                };
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                target.Start();
-                RunFrame(target);
-
-                stack.Children.Move(1, 0);
-
-                RunFrame(target);
-
-                var scene = target.UnitTestScene();
-                var stackNode = scene.FindNode(stack);
-
-                Assert.Same(stackNode.Children[0].Visual, canvas2);
-                Assert.Same(stackNode.Children[1].Visual, canvas1);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_VisualNode_Order_On_ZIndex_Change()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                StackPanel stack;
-                Canvas canvas1;
-                Canvas canvas2;
-
-                var root = new TestRoot
-                {
-                    Child = stack = new StackPanel
-                    {
-                        Children =
-                        {
-                            (canvas1 = new Canvas { ZIndex = 1 }), (canvas2 = new Canvas { ZIndex = 2 }),
-                        }
-                    }
-                };
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                target.Start();
-                RunFrame(target);
-
-                canvas1.ZIndex = 3;
-
-                RunFrame(target);
-
-                var scene = target.UnitTestScene();
-                var stackNode = scene.FindNode(stack);
-
-                Assert.Same(stackNode.Children[0].Visual, canvas2);
-                Assert.Same(stackNode.Children[1].Visual, canvas1);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_VisualNode_Order_On_ZIndex_Change_With_Dirty_Ancestor()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                StackPanel stack;
-                Canvas canvas1;
-                Canvas canvas2;
-
-                var root = new TestRoot
-                {
-                    Child = stack = new StackPanel
-                    {
-                        Children =
-                        {
-                            (canvas1 = new Canvas { ZIndex = 1 }), (canvas2 = new Canvas { ZIndex = 2 }),
-                        }
-                    }
-                };
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                target.Start();
-                RunFrame(target);
-
-                root.InvalidateVisual();
-                canvas1.ZIndex = 3;
-
-                RunFrame(target);
-
-                var scene = target.UnitTestScene();
-                var stackNode = scene.FindNode(stack);
-
-                Assert.Same(stackNode.Children[0].Visual, canvas2);
-                Assert.Same(stackNode.Children[1].Visual, canvas1);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_VisualNodes_When_Child_Moved_To_New_Parent()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                Decorator moveFrom;
-                Decorator moveTo;
-                Canvas moveMe;
-
-                var root = new TestRoot
-                {
-                    Child = new StackPanel
-                    {
-                        Children =
-                        {
-                            (moveFrom = new Decorator { Child = moveMe = new Canvas(), }),
-                            (moveTo = new Decorator()),
-                        }
-                    }
-                };
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                target.Start();
-                RunFrame(target);
-
-                moveFrom.Child = null;
-                moveTo.Child = moveMe;
-
-                RunFrame(target);
-
-                var scene = target.UnitTestScene();
-                var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
-                var moveToNode = (VisualNode)scene.FindNode(moveTo);
-
-                Assert.Empty(moveFromNode.Children);
-                Assert.Equal(1, moveToNode.Children.Count);
-                Assert.Same(moveMe, moveToNode.Children[0].Visual);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_VisualNodes_When_Child_Moved_To_New_Parent_And_New_Root()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var dispatcher = new ImmediateDispatcher();
-                var loop = new Mock<IRenderLoop>();
-
-                Decorator moveFrom;
-                Decorator moveTo;
-                Canvas moveMe;
-
-                var root = new TestRoot
-                {
-                    Child = new StackPanel
-                    {
-                        Children = { (moveFrom = new Decorator { Child = moveMe = new Canvas(), }) }
-                    }
-                };
-
-                var otherRoot = new TestRoot { Child = new StackPanel { Children = { (moveTo = new Decorator()) } } };
-
-                var sceneBuilder = new SceneBuilder();
-
-                var target = new DeferredRenderer(
-                    root,
-                    loop.Object,
-                    sceneBuilder: sceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                var otherSceneBuilder = new SceneBuilder();
-
-                var otherTarget = new DeferredRenderer(
-                    otherRoot,
-                    loop.Object,
-                    sceneBuilder: otherSceneBuilder,
-                    renderTargetFactory: root.CreateRenderTarget,
-                    dispatcher: dispatcher);
-
-                root.Renderer = target;
-                otherRoot.Renderer = otherTarget;
-
-                target.Start();
-                otherTarget.Start();
-
-                RunFrame(target);
-                RunFrame(otherTarget);
-
-                moveFrom.Child = null;
-                moveTo.Child = moveMe;
-
-                RunFrame(target);
-                RunFrame(otherTarget);
-
-                var scene = target.UnitTestScene();
-                var otherScene = otherTarget.UnitTestScene();
-
-                var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
-                var moveToNode = (VisualNode)otherScene.FindNode(moveTo);
-
-                Assert.Empty(moveFromNode.Children);
-                Assert.Equal(1, moveToNode.Children.Count);
-                Assert.Same(moveMe, moveToNode.Children[0].Visual);
-            }
-        }
-
-        [Fact]
-        public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var root = new TestRoot
-                {
-                    Width = 100, Height = 100, Child = new Border { Background = Brushes.Red, Opacity = 0.5, }
-                };
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var target = CreateTargetAndRunFrame(root);
-                var context = GetLayerContext(target, root);
-                var animation = new BehaviorSubject<double>(0.5);
-
-                context.Verify(x => x.PushOpacity(0.5), Times.Once);
-                context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), default), Times.Once);
-                context.Verify(x => x.PopOpacity(), Times.Once);
-            }
-        }
-
-        [Fact]
-        public void Should_Not_Draw_Controls_With_0_Opacity()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new Border
-                    {
-                        Background = Brushes.Red,
-                        Opacity = 0,
-                        Child = new Border { Background = Brushes.Green, }
-                    }
-                };
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var target = CreateTargetAndRunFrame(root);
-                var context = GetLayerContext(target, root);
-                var animation = new BehaviorSubject<double>(0.5);
-
-                context.Verify(x => x.PushOpacity(0.5), Times.Never);
-                context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), default), Times.Never);
-                context.Verify(x => x.PopOpacity(), Times.Never);
-            }
-        }
-
-        [Fact]
-        public void Should_Push_Opacity_Mask()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new Border { Background = Brushes.Red, OpacityMask = Brushes.Green, }
-                };
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var target = CreateTargetAndRunFrame(root);
-                var context = GetLayerContext(target, root);
-                var animation = new BehaviorSubject<double>(0.5);
-
-                context.Verify(x => x.PushOpacityMask(Brushes.Green, new Rect(0, 0, 100, 100)), Times.Once);
-                context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), default), Times.Once);
-                context.Verify(x => x.PopOpacityMask(), Times.Once);
-            }
-        }
-
-        [Fact]
-        public void Should_Create_Layer_For_Root()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var root = new TestRoot();
-                var rootLayer = new Mock<IRenderTargetBitmapImpl>();
-
-                var sceneBuilder = new Mock<ISceneBuilder>();
-
-                sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
-                    .Callback<Scene>(scene =>
-                    {
-                        scene.Size = root.ClientSize;
-                        scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
-                    });
-
-                var renderInterface = new Mock<IPlatformRenderInterface>();
-                var target = CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object);
-
-                Assert.Single(target.Layers);
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Should_Create_And_Delete_Layers_For_Controls_With_Animated_Opacity()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new Border
-                    {
-                        Background = Brushes.Red,
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Green, Child = new Canvas(), Opacity = 0.9,
-                        }
-                    }
-                };
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var timer = new Mock<IRenderTimer>();
-                var target = CreateTargetAndRunFrame(root, timer);
-
-                Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-                RunFrame(target);
-
-                Assert.Equal(new Visual[] { root, border }, target.Layers.Select(x => x.LayerRoot));
-
-                animation.OnCompleted();
-                RunFrame(target);
-
-                Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Should_Not_Create_Layer_For_Childless_Control_With_Animated_Opacity()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new Border
-                    {
-                        Background = Brushes.Red, Child = border = new Border { Background = Brushes.Green, }
-                    }
-                };
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var timer = new Mock<IRenderTimer>();
-                var target = CreateTargetAndRunFrame(root, timer);
-
-                Assert.Single(target.Layers);
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Should_Not_Push_Opacity_For_Transparent_Layer_Root_Control()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = border = new Border { Background = Brushes.Red, Child = new Canvas(), }
-                };
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var target = CreateTargetAndRunFrame(root);
-                var context = GetLayerContext(target, border);
-
-                context.Verify(x => x.PushOpacity(0.5), Times.Never);
-                context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), default), Times.Once);
-                context.Verify(x => x.PopOpacity(), Times.Never);
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Should_Draw_Transparent_Layer_With_Correct_Opacity()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = border = new Border { Background = Brushes.Red, Child = new Canvas(), }
-                };
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var target = CreateTargetAndRunFrame(root);
-                var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null));
-                var borderLayer = target.Layers[border].Bitmap;
-
-                context.Verify(x => x.DrawBitmap(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>(),
-                    BitmapInterpolationMode.Default));
-            }
-        }
-
-        [Fact]
-        public void Can_Dirty_Control_In_SceneInvalidated()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border1;
-                Border border2;
-
-                var root = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new StackPanel
-                    {
-                        Children =
-                        {
-                            (border1 = new Border { Background = Brushes.Red, Child = new Canvas(), }),
-                            (border2 = new Border { Background = Brushes.Red, Child = new Canvas(), }),
-                        }
-                    }
-                };
-
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var target = CreateTargetAndRunFrame(root);
-                var invalidated = false;
-
-                target.SceneInvalidated += (s, e) =>
-                {
-                    invalidated = true;
-                    target.AddDirty(border2);
-                };
-
-                target.AddDirty(border1);
-                target.Paint(new Rect(root.DesiredSize));
-
-                Assert.True(invalidated);
-                Assert.True(((IRenderLoopTask)target).NeedsUpdate);
-            }
-        }
-
-        private DeferredRenderer CreateTargetAndRunFrame(
-            TestRoot root,
-            Mock<IRenderTimer> timer = null,
-            ISceneBuilder sceneBuilder = null,
-            IDispatcher dispatcher = null)
-        {
-            timer = timer ?? new Mock<IRenderTimer>();
-            dispatcher = dispatcher ?? new ImmediateDispatcher();
-            var target = new DeferredRenderer(
-                root,
-                new RenderLoop(timer.Object, dispatcher),
-                sceneBuilder: sceneBuilder,
-                renderTargetFactory: root.CreateRenderTarget,
-                dispatcher: dispatcher);
-            root.Renderer = target;
-            target.Start();
-            RunFrame(target);
-            return target;
-        }
-
-        private Mock<IDrawingContextImpl> GetLayerContext(DeferredRenderer renderer, Control layerRoot)
-        {
-            return Mock.Get(renderer.Layers[layerRoot].Bitmap.Item.CreateDrawingContext(null));
-        }
-
-        private void IgnoreFirstFrame(IRenderLoopTask task, Mock<ISceneBuilder> sceneBuilder)
-        {
-            RunFrame(task);
-            sceneBuilder.Invocations.Clear();
-        }
-
-        private void RunFrame(IRenderLoopTask task)
-        {
-            task.Update(TimeSpan.Zero);
-            task.Render();
-        }
-
-        private IRenderTargetBitmapImpl CreateLayer()
-        {
-            return Mock.Of<IRenderTargetBitmapImpl>(x =>
-                x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>()) == Mock.Of<IDrawingContextImpl>());
-        }
-
-        private Mock<ISceneBuilder> MockSceneBuilder(IRenderRoot root)
-        {
-            var result = new Mock<ISceneBuilder>();
-            result.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
-                .Callback<Scene>(x => x.Layers.Add((Visual)root).Dirty.Add(new Rect(root.ClientSize)));
-            return result;
-        }
-    }
-}

+ 0 - 577
tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs

@@ -1,577 +0,0 @@
-using System;
-using System.Linq;
-using Avalonia.Controls;
-using Avalonia.Controls.Presenters;
-using Avalonia.Controls.Shapes;
-using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering
-{
-    public class DeferredRendererTests_HitTesting
-    {
-        [Fact]
-        public void HitTest_Should_Find_Controls_At_Point()
-        {
-            using (TestApplication())
-            {
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(new[] { root.Child }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Empty_Controls_At_Point()
-        {
-            using (TestApplication())
-            {
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Empty(result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Invisible_Controls_At_Point()
-        {
-            using (TestApplication())
-            {
-                Border visible;
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center,
-                        IsVisible = false,
-                        Child = visible = new Border
-                        {
-                            Background = Brushes.Red,
-                            HorizontalAlignment = HorizontalAlignment.Stretch,
-                            VerticalAlignment = VerticalAlignment.Stretch,
-                        }
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Empty(result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Control_Outside_Point()
-        {
-            using (TestApplication())
-            {
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(10, 10), root, null);
-
-                Assert.Empty(result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Return_Top_Controls_First()
-        {
-            using (TestApplication())
-            {
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Children =
-                        {
-                            new Border
-                            {
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            },
-                            new Border
-                            {
-                                Width = 50,
-                                Height = 50,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            }
-                        }
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(new[] { container.Children[1], container.Children[0] }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Return_Top_Controls_First_With_ZIndex()
-        {
-            using (TestApplication())
-            {
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Children =
-                        {
-                            new Border
-                            {
-                                Width = 100,
-                                Height = 100,
-                                ZIndex = 1,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            },
-                            new Border
-                            {
-                                Width = 50,
-                                Height = 50,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            },
-                            new Border
-                            {
-                                Width = 75,
-                                Height = 75,
-                                ZIndex = 2,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            }
-                        }
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(new[] { container.Children[2], container.Children[0], container.Children[1] }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Find_Control_Translated_Outside_Parent_Bounds()
-        {
-            using (TestApplication())
-            {
-                Border target;
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Background = Brushes.Red,
-                        ClipToBounds = false,
-                        Children =
-                        {
-                            new Border
-                            {
-                                Width = 100,
-                                Height = 100,
-                                ZIndex = 1,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Left,
-                                VerticalAlignment = VerticalAlignment.Top,
-                                Child = target = new Border
-                                {
-                                    Width = 50,
-                                    Height = 50,
-                                    Background = Brushes.Red,
-                                    HorizontalAlignment = HorizontalAlignment.Left,
-                                    VerticalAlignment = VerticalAlignment.Top,
-                                    RenderTransform = new TranslateTransform(110, 110),
-                                }
-                            },
-                        }
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                container.Measure(Size.Infinity);
-                container.Arrange(new Rect(container.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(120, 120), root, null);
-
-                Assert.Equal(new Visual[] { target, container }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Control_Outside_Parent_Bounds_When_Clipped()
-        {
-            using (TestApplication())
-            {
-                Border target;
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 100,
-                        Height = 200,
-                        Background = Brushes.Red,
-                        Children =
-                        {
-                            new Panel()
-                            {
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                                Margin = new Thickness(0, 100, 0, 0),
-                                ClipToBounds = true,
-                                Children =
-                                {
-                                    (target = new Border()
-                                    {
-                                        Width = 100,
-                                        Height = 100,
-                                        Background = Brushes.Red,
-                                        Margin = new Thickness(0, -100, 0, 0)
-                                    })
-                                }
-                            }
-                        }
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(50, 50), root, null);
-
-                Assert.Equal(new[] { container }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Control_Outside_Scroll_Viewport()
-        {
-            using (TestApplication())
-            {
-                Border target;
-                Border item1;
-                Border item2;
-                ScrollContentPresenter scroll;
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 100,
-                        Height = 200,
-                        Background = Brushes.Red,
-                        Children =
-                        {
-                            (target = new Border()
-                            {
-                                Name = "b1",
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                            }),
-                            new Border()
-                            {
-                                Name = "b2",
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                                Margin = new Thickness(0, 100, 0, 0),
-                                Child = scroll = new ScrollContentPresenter()
-                                {
-                                    CanHorizontallyScroll = true,
-                                    CanVerticallyScroll = true,
-                                    Content = new StackPanel()
-                                    {
-                                        Children =
-                                        {
-                                            (item1 = new Border()
-                                            {
-                                                Name = "b3",
-                                                Width = 100,
-                                                Height = 100,
-                                                Background = Brushes.Red,
-                                            }),
-                                            (item2 = new Border()
-                                            {
-                                                Name = "b4",
-                                                Width = 100,
-                                                Height = 100,
-                                                Background = Brushes.Red,
-                                            }),
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                };
-
-                scroll.UpdateChild();
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-                
-                root.Renderer.Paint(default);
-                var result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
-
-                Assert.Equal(item1, result);
-
-                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
-
-                Assert.Equal(target, result);
-
-                scroll.Offset = new Vector(0, 100);
-
-                // We don't have LayoutManager set up so do the layout pass manually.
-                scroll.Parent.InvalidateArrange();
-                container.InvalidateArrange();
-                container.Arrange(new Rect(container.DesiredSize));
-
-                root.Renderer.Paint(default);
-                result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
-                Assert.Equal(item2, result);
-
-                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
-                Assert.Equal(target, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Path_When_Outside_Fill()
-        {
-            using (TestApplication())
-            {
-                Path path;
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = path = new Path
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Fill = Brushes.Red,
-                        Data = StreamGeometry.Parse("M100,0 L0,100 100,100")
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-                Assert.Equal(new[] { path }, result);
-
-                result = root.Renderer.HitTest(new Point(10, 10), root, null);
-                Assert.Empty(result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Respect_Geometry_Clip()
-        {
-            using (TestApplication())
-            {
-                Border border;
-                Canvas canvas;
-                var root = new TestRoot
-                {
-                    Width = 400,
-                    Height = 400,
-                    Child = border = new Border
-                    {
-                        Background = Brushes.Red,
-                        Clip = StreamGeometry.Parse("M100,0 L0,100 100,100"),
-                        Width = 200,
-                        Height = 200,
-                        Child = canvas = new Canvas
-                        {
-                            Background = Brushes.Yellow,
-                            Margin = new Thickness(10),
-                        }
-                    }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-                Assert.Equal(new Rect(100, 100, 200, 200), border.Bounds);
-
-                var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
-
-                var result = root.Renderer.HitTest(new Point(200, 200), root, null);
-                Assert.Equal(new Visual[] { canvas, border }, result);
-
-                result = root.Renderer.HitTest(new Point(110, 110), root, null);
-                Assert.Empty(result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Accommodate_ICustomHitTest()
-        {
-            using (TestApplication())
-            {
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 300,
-                    Height = 200,
-                    Child = border = new CustomHitTestBorder
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                }; 
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(75, 100), root, null);
-                Assert.Equal(new[] { border }, result);
-
-                result = root.Renderer.HitTest(new Point(125, 100), root, null);
-                Assert.Equal(new[] { border }, result);
-
-                result = root.Renderer.HitTest(new Point(175, 100), root, null);
-                Assert.Empty(result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Hit_Controls_Next_Pixel()
-        {
-            using (TestApplication())
-            {
-                Border targetRectangle;
-
-                var root = new TestRoot
-                {
-                    Width = 50,
-                    Height = 200,
-                    Child = new StackPanel
-                    {
-                        Orientation = Orientation.Vertical,
-                        HorizontalAlignment = HorizontalAlignment.Left,
-                        Children =
-                            {
-                                new Border { Width = 50, Height = 50, Background = Brushes.Red},
-                                { targetRectangle = new Border { Width = 50, Height = 50, Background = Brushes.Green} }
-                            }
-                    }
-                }; 
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = root.Renderer.HitTest(new Point(25, 50), root, null);
-                Assert.Equal(new[] { targetRectangle }, result);
-            }
-        }
-
-        private static IDisposable TestApplication()
-        {
-            return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
-        }
-    }
-}

+ 0 - 273
tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs

@@ -1,273 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering
-{
-    public class ImmediateRendererTests
-    {
-        [Fact]
-        public void AddDirty_Call_RenderRoot_Invalidate()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var child = new Border
-                {
-                    Width = 100,
-                    Height = 100,
-                    Margin = new(10),
-                    HorizontalAlignment = HorizontalAlignment.Left,
-                    VerticalAlignment = VerticalAlignment.Top,
-                };
-
-                var root = new RenderRoot
-                {
-                    Child = child,
-                    Width = 400,
-                    Height = 400,
-                };
-
-                root.LayoutManager.ExecuteInitialLayoutPass();
-
-                var target = new ImmediateRenderer(root, root.CreateRenderTarget);
-
-                target.AddDirty(child);
-
-                Assert.Equal(new[] { new Rect(10, 10, 100, 100) }, root.Invalidations);
-            }
-        }
-
-        [Fact]
-        public void AddDirty_With_RenderTransform_Call_RenderRoot_Invalidate()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var child = new Border
-                {
-                    Width = 100,
-                    Height = 100,
-                    Margin = new(100),
-                    HorizontalAlignment = HorizontalAlignment.Left,
-                    VerticalAlignment = VerticalAlignment.Top,
-                };
-
-                var root = new RenderRoot
-                {
-                    Child = child,
-                    Width = 400,
-                    Height = 400,
-                };
-
-                root.LayoutManager.ExecuteInitialLayoutPass();
-
-                child.RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 };
-
-                var target = new ImmediateRenderer(root, root.CreateRenderTarget);
-
-                target.AddDirty(child);
-
-                Assert.Equal(new[] { new Rect(50, 50, 200, 200) }, root.Invalidations);
-            }
-        }
-
-        [Fact]
-        public void AddDirty_For_Child_Moved_Should_Invalidate_Previous_Bounds()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var child = new Border
-                {
-                    Width = 100,
-                    Height = 100,
-                    HorizontalAlignment = HorizontalAlignment.Left,
-                    VerticalAlignment = VerticalAlignment.Top,
-                };
-
-                var root = new RenderRoot
-                {
-                    Child = child,
-                    Width = 400,
-                    Height = 400,
-                };
-
-                var target = new ImmediateRenderer(root, root.CreateRenderTarget);
-
-                root.LayoutManager.ExecuteInitialLayoutPass();
-                target.AddDirty(child);
-
-                Assert.Equal(new Rect(0, 0, 100, 100), root.Invalidations[0]);
-
-                target.Paint(new Rect(0, 0, 100, 100));
-
-                //move child 100 pixels bottom/right
-                child.Margin = new(100, 100);
-                root.LayoutManager.ExecuteLayoutPass();
-
-                //renderer should invalidate old child bounds with new one
-                //as on old area there can be artifacts
-                target.AddDirty(child);
-
-                //invalidate first old position
-                Assert.Equal(new Rect(0, 0, 100, 100), root.Invalidations[1]);
-
-                //then new position
-                Assert.Equal(new Rect(100, 100, 100, 100), root.Invalidations[2]);
-            }
-        }
-
-        [Fact]
-        public void Should_Not_Clip_Children_With_RenderTransform_When_In_Bounds()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                const int RootWidth = 300;
-                const int RootHeight = 300;
-
-                var rootGrid = new Grid { Width = RootWidth, Height = RootHeight, ClipToBounds = true };
-
-                var stackPanel = new StackPanel
-                {
-                    Orientation = Orientation.Horizontal,
-                    VerticalAlignment = VerticalAlignment.Top,
-                    HorizontalAlignment = HorizontalAlignment.Right,
-                    Margin = new Thickness(0, 10, 0, 0),
-                    RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Relative),
-                    RenderTransform = new TransformGroup
-                    {
-                        Children = { new RotateTransform { Angle = 90 }, new TranslateTransform { X = 240 } }
-                    }
-                };
-
-                rootGrid.Children.Add(stackPanel);
-
-                TestControl CreateControl()
-                    => new TestControl
-                    {
-                        Width = 80,
-                        Height = 40,
-                        Margin = new Thickness(0, 0, 5, 0),
-                        ClipToBounds = true
-                    };
-
-                var control1 = CreateControl();
-                var control2 = CreateControl();
-                var control3 = CreateControl();
-
-                stackPanel.Children.Add(control1);
-                stackPanel.Children.Add(control2);
-                stackPanel.Children.Add(control3);
-
-                var root = new TestRoot(rootGrid);
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.LayoutManager.ExecuteInitialLayoutPass();
-
-                var rootSize = new Size(RootWidth, RootHeight);
-                root.Measure(rootSize);
-                root.Arrange(new Rect(rootSize));
-
-                root.Renderer.Paint(root.Bounds);
-
-                Assert.True(control1.Rendered);
-                Assert.True(control2.Rendered);
-                Assert.True(control3.Rendered);
-            }
-        }
-
-        [Fact]
-        public void Should_Not_Render_Clipped_Child_With_RenderTransform_When_Not_In_Bounds()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                const int RootWidth = 300;
-                const int RootHeight = 300;
-
-                var rootGrid = new Grid { Width = RootWidth, Height = RootHeight, ClipToBounds = true };
-
-                var stackPanel = new StackPanel
-                {
-                    Orientation = Orientation.Horizontal,
-                    VerticalAlignment = VerticalAlignment.Top,
-                    HorizontalAlignment = HorizontalAlignment.Right,
-                    Margin = new Thickness(0, 10, 0, 0),
-                    RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Relative),
-                    RenderTransform = new TransformGroup
-                    {
-                        Children = { new RotateTransform { Angle = 90 }, new TranslateTransform { X = 280 } }
-                    }
-                };
-
-                rootGrid.Children.Add(stackPanel);
-
-                TestControl CreateControl()
-                    => new TestControl
-                    {
-                        Width = 160,
-                        Height = 40,
-                        Margin = new Thickness(0, 0, 5, 0),
-                        ClipToBounds = true
-                    };
-
-                var control1 = CreateControl();
-                var control2 = CreateControl();
-                var control3 = CreateControl();
-
-                stackPanel.Children.Add(control1);
-                stackPanel.Children.Add(control2);
-                stackPanel.Children.Add(control3);
-
-                var root = new TestRoot(rootGrid);
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.LayoutManager.ExecuteInitialLayoutPass();
-
-                var rootSize = new Size(RootWidth, RootHeight);
-                root.Measure(rootSize);
-                root.Arrange(new Rect(rootSize));
-
-                root.Renderer.Paint(root.Bounds);
-
-                Assert.True(control1.Rendered);
-                Assert.True(control2.Rendered);
-                Assert.False(control3.Rendered);
-            }
-        }
-
-        [Fact]
-        public void Static_Render_Method_Does_Not_Update_TransformedBounds()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var target = new Border();
-                var expected = new TransformedBounds(new Rect(1, 2, 3, 4), new Rect(4, 5, 6, 7), Matrix.CreateRotation(0.8));
-
-                target.SetTransformedBounds(expected);
-
-                var renderTarget = Mock.Of<IRenderTarget>(x =>
-                    x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>()) == Mock.Of<IDrawingContextImpl>());
-                ImmediateRenderer.Render(target, renderTarget);
-
-                Assert.Equal(expected, target.TransformedBounds);
-            }
-        }
-
-        private class RenderRoot : TestRoot, IRenderRoot
-        {
-            public List<Rect> Invalidations { get; } = new();
-            void IRenderRoot.Invalidate(Rect rect) => Invalidations.Add(rect);
-        }
-
-        private class TestControl : Control
-        {
-            public bool Rendered { get; private set; }
-
-            public override void Render(DrawingContext context)
-                => Rendered = true;
-        }
-    }
-}

+ 0 - 458
tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs

@@ -1,458 +0,0 @@
-using System;
-using System.Linq;
-using Avalonia.Controls;
-using Avalonia.Controls.Presenters;
-using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Rendering;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering
-{
-    public class ImmediateRendererTests_HitTesting
-    {
-        [Fact]
-        public void HitTest_Should_Find_Controls_At_Point()
-        {
-            using (TestApplication())
-            {
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(new[] { root.Child, root }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Invisible_Controls_At_Point()
-        {
-            using (TestApplication())
-            {
-                Border visible;
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center,
-                        IsVisible = false,
-                        Child = visible = new Border
-                        {
-                            Background = Brushes.Red,
-                            HorizontalAlignment = HorizontalAlignment.Stretch,
-                            VerticalAlignment = VerticalAlignment.Stretch,
-                        }
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(new[] { root }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Control_Outside_Point()
-        {
-            using (TestApplication())
-            {
-                var root = new TestRoot
-                {
-                    Width = 200,
-                    Height = 200,
-                    Child = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(10, 10), root, null);
-
-                Assert.Equal(new[] { root }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Return_Top_Controls_First()
-        {
-            using (TestApplication())
-            {
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Children =
-                        {
-                            new Border
-                            {
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            },
-                            new Border
-                            {
-                                Width = 50,
-                                Height = 50,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            }
-                        }
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(new[] { container.Children[1], container.Children[0], container, root }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Return_Top_Controls_First_With_ZIndex()
-        {
-            using (TestApplication())
-            {
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Children =
-                        {
-                            new Border
-                            {
-                                Width = 100,
-                                Height = 100,
-                                ZIndex = 1,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            },
-                            new Border
-                            {
-                                Width = 50,
-                                Height = 50,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            },
-                            new Border
-                            {
-                                Width = 75,
-                                Height = 75,
-                                ZIndex = 2,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Center,
-                                VerticalAlignment = VerticalAlignment.Center
-                            }
-                        }
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
-
-                Assert.Equal(
-                    new[] 
-                    {
-                        container.Children[2],
-                        container.Children[0],
-                        container.Children[1],
-                        container,
-                        root
-                    }, 
-                    result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Find_Control_Translated_Outside_Parent_Bounds()
-        {
-            using (TestApplication())
-            {
-                Border target;
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 200,
-                        Height = 200,
-                        Background = Brushes.Red,
-                        ClipToBounds = false,
-                        Children =
-                        {
-                            new Border
-                            {
-                                Width = 100,
-                                Height = 100,
-                                ZIndex = 1,
-                                Background = Brushes.Red,
-                                HorizontalAlignment = HorizontalAlignment.Left,
-                                VerticalAlignment = VerticalAlignment.Top,
-                                Child = target = new Border
-                                {
-                                    Width = 50,
-                                    Height = 50,
-                                    Background = Brushes.Red,
-                                    HorizontalAlignment = HorizontalAlignment.Left,
-                                    VerticalAlignment = VerticalAlignment.Top,
-                                    RenderTransform = new TranslateTransform(110, 110),
-                                }
-                            },
-                        }
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                container.Measure(Size.Infinity);
-                container.Arrange(new Rect(container.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(120, 120), root, null);
-
-                Assert.Equal(new Visual[] { target, container }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Control_Outside_Parent_Bounds_When_Clipped()
-        {
-            using (TestApplication())
-            {
-                Border target;
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 100,
-                        Height = 200,
-                        Background = Brushes.Red,
-                        Children =
-                        {
-                            new Panel()
-                            {
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                                Margin = new Thickness(0, 100, 0, 0),
-                                ClipToBounds = true,
-                                Children =
-                                {
-                                    (target = new Border()
-                                    {
-                                        Width = 100,
-                                        Height = 100,
-                                        Background = Brushes.Red,
-                                        Margin = new Thickness(0, -100, 0, 0)
-                                    })
-                                }
-                            }
-                        }
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(50, 50), root, null);
-
-                Assert.Equal(new Visual[] { container, root }, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Not_Find_Control_Outside_Scroll_Viewport()
-        {
-            using (TestApplication())
-            {
-                Border target;
-                Border item1;
-                Border item2;
-                ScrollContentPresenter scroll;
-                Panel container;
-                var root = new TestRoot
-                {
-                    Child = container = new Panel
-                    {
-                        Width = 100,
-                        Height = 200,
-                        Background = Brushes.Red,
-                        Children =
-                        {
-                            (target = new Border()
-                            {
-                                Name = "b1",
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                            }),
-                            new Border()
-                            {
-                                Name = "b2",
-                                Width = 100,
-                                Height = 100,
-                                Background = Brushes.Red,
-                                Margin = new Thickness(0, 100, 0, 0),
-                                Child = scroll = new ScrollContentPresenter()
-                                {
-                                    CanHorizontallyScroll = true,
-                                    CanVerticallyScroll = true,
-                                    Content = new StackPanel()
-                                    {
-                                        Children =
-                                        {
-                                            (item1 = new Border()
-                                            {
-                                                Name = "b3",
-                                                Width = 100,
-                                                Height = 100,
-                                                Background = Brushes.Red,
-                                            }),
-                                            (item2 = new Border()
-                                            {
-                                                Name = "b4",
-                                                Width = 100,
-                                                Height = 100,
-                                                Background = Brushes.Red,
-                                            }),
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                };
-
-                scroll.UpdateChild();
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(container.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
-
-                Assert.Equal(item1, result);
-
-                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
-
-                Assert.Equal(target, result);
-
-                scroll.Offset = new Vector(0, 100);
-
-                // We don't have LayoutManager set up so do the layout pass manually.
-                scroll.Parent.InvalidateArrange();
-                container.InvalidateArrange();
-                container.Arrange(new Rect(container.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
-                Assert.Equal(item2, result);
-
-                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
-                Assert.Equal(target, result);
-            }
-        }
-
-        [Fact]
-        public void HitTest_Should_Accommodate_ICustomHitTest()
-        {
-            using (TestApplication())
-            {
-                Border border;
-
-                var root = new TestRoot
-                {
-                    Width = 300,
-                    Height = 200,
-                    Child = border = new CustomHitTestBorder
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
-                    }
-                };
-
-                root.Renderer = new ImmediateRenderer(root, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-                root.Renderer.Paint(new Rect(root.ClientSize));
-
-                var result = root.Renderer.HitTest(new Point(75, 100), root, null).First();
-                Assert.Equal(border, result);
-
-                result = root.Renderer.HitTest(new Point(125, 100), root, null).First();
-                Assert.Equal(border, result);
-
-                result = root.Renderer.HitTest(new Point(175, 100), root, null).First();
-                Assert.Equal(root, result);
-            }
-        }
-
-        private static IDisposable TestApplication()
-        {
-            return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
-        }
-    }
-}

+ 0 - 225
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs

@@ -1,225 +0,0 @@
-using System.Linq;
-using Avalonia.Media;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.UnitTests;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
-{
-    public class DeferredDrawingContextImplTests
-    {
-        [Fact]
-        public void Should_Add_VisualNode()
-        {
-            var parent = new VisualNode(new TestRoot(), null);
-            var child = new VisualNode(Mock.Of<Visual>(), parent);
-            var layers = new SceneLayers(parent.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            target.BeginUpdate(parent);
-            target.BeginUpdate(child);
-
-            Assert.Equal(1, parent.Children.Count);
-            Assert.Same(child, parent.Children[0]);
-        }
-
-        [Fact]
-        public void Should_Not_Replace_Identical_VisualNode()
-        {
-            var parent = new VisualNode(new TestRoot(), null);
-            var child = new VisualNode(Mock.Of<Visual>(), parent);
-            var layers = new SceneLayers(parent.Visual);
-
-            parent.AddChild(child);
-
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            target.BeginUpdate(parent);
-            target.BeginUpdate(child);
-
-            Assert.Equal(1, parent.Children.Count);
-            Assert.Same(child, parent.Children[0]);
-        }
-
-        [Fact]
-        public void Should_Replace_Different_VisualNode()
-        {
-            var parent = new VisualNode(new TestRoot(), null);
-            var child1 = new VisualNode(Mock.Of<Visual>(), parent);
-            var child2 = new VisualNode(Mock.Of<Visual>(), parent);
-            var layers = new SceneLayers(parent.Visual);
-
-            parent.AddChild(child1);
-
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            target.BeginUpdate(parent);
-            target.BeginUpdate(child2);
-
-            Assert.Equal(1, parent.Children.Count);
-            Assert.Same(child2, parent.Children[0]);
-        }
-
-        [Fact]
-        public void TrimChildren_Should_Trim_Children()
-        {
-            var root = new TestRoot();
-            var node = new VisualNode(root, null) { LayerRoot = root };
-
-            node.AddChild(new VisualNode(Mock.Of<Visual>(), node) { LayerRoot = root });
-            node.AddChild(new VisualNode(Mock.Of<Visual>(), node) { LayerRoot = root });
-            node.AddChild(new VisualNode(Mock.Of<Visual>(), node) { LayerRoot = root });
-            node.AddChild(new VisualNode(Mock.Of<Visual>(), node) { LayerRoot = root });
-
-            var layers = new SceneLayers(root);
-            var target = new DeferredDrawingContextImpl(null, layers);
-            var child1 = new VisualNode(Mock.Of<Visual>(), node) { LayerRoot = root };
-            var child2 = new VisualNode(Mock.Of<Visual>(), node) { LayerRoot = root };
-
-            target.BeginUpdate(node);
-            using (target.BeginUpdate(child1)) { }
-            using (target.BeginUpdate(child2)) { }
-            target.TrimChildren();
-
-            Assert.Equal(2, node.Children.Count);
-        }
-
-        [Fact]
-        public void Should_Add_DrawOperations()
-        {
-            var node = new VisualNode(new TestRoot(), null);
-            var layers = new SceneLayers(node.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            node.LayerRoot = node.Visual;
-
-            using (target.BeginUpdate(node))
-            {
-                target.DrawRectangle(Brushes.Red, new Pen(Brushes.Green, 1), new Rect(0, 0, 100, 100));
-            }
-
-            Assert.Equal(1, node.DrawOperations.Count);
-            Assert.IsType<RectangleNode>(node.DrawOperations[0].Item);
-        }
-
-        [Fact]
-        public void Should_Not_Replace_Identical_DrawOperation()
-        {
-            var node = new VisualNode(new TestRoot(), null);
-            var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), default));
-            var layers = new SceneLayers(node.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            node.LayerRoot = node.Visual;
-            node.AddDrawOperation(operation);
-
-            using (target.BeginUpdate(node))
-            {
-                target.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100));
-            }
-
-            Assert.Equal(1, node.DrawOperations.Count);
-            Assert.Same(operation.Item, node.DrawOperations.Single().Item);
-
-            Assert.IsType<RectangleNode>(node.DrawOperations[0].Item);
-        }
-
-        [Fact]
-        public void Should_Replace_Different_DrawOperation()
-        {
-            var node = new VisualNode(new TestRoot(), null);
-            var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), default));
-            var layers = new SceneLayers(node.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            node.LayerRoot = node.Visual;
-            node.AddDrawOperation(operation);
-
-            using (target.BeginUpdate(node))
-            {
-                target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 100, 100));
-            }
-
-            Assert.Equal(1, node.DrawOperations.Count);
-            Assert.NotSame(operation, node.DrawOperations.Single());
-
-            Assert.IsType<RectangleNode>(node.DrawOperations[0].Item);
-        }
-
-        [Fact]
-        public void Should_Update_DirtyRects()
-        {
-            var node = new VisualNode(new TestRoot(), null);
-            var operation = new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), default);
-            var layers = new SceneLayers(node.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            node.LayerRoot = node.Visual;
-
-            using (target.BeginUpdate(node))
-            {
-                target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 100, 100));
-            }
-
-            Assert.Equal(new Rect(0, 0, 100, 100), layers.Single().Dirty.Single());
-        }
-
-        [Fact]
-        public void Should_Trim_DrawOperations()
-        {
-            var node = new VisualNode(new TestRoot(), null);
-            node.LayerRoot = node.Visual;
-
-            for (var i = 0; i < 4; ++i)
-            {
-                var drawOperation = new Mock<IDrawOperation>();
-                using (var r = RefCountable.Create(drawOperation.Object))
-                {
-                    node.AddDrawOperation(r);
-                }
-            }
-
-            var drawOperations = node.DrawOperations.Select(op => op.Item).ToList();
-            var layers = new SceneLayers(node.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            using (target.BeginUpdate(node))
-            {
-                target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 10, 100));
-                target.DrawRectangle(Brushes.Blue, null, new Rect(0, 0, 20, 100));
-            }
-
-            Assert.Equal(2, node.DrawOperations.Count);
-
-            foreach (var i in drawOperations)
-            {
-                Mock.Get(i).Verify(x => x.Dispose());
-            }
-        }
-
-        [Fact]
-        public void Trimmed_DrawOperations_Releases_Reference()
-        {
-            var node = new VisualNode(new TestRoot(), null);
-            var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), default));
-            var layers = new SceneLayers(node.Visual);
-            var target = new DeferredDrawingContextImpl(null, layers);
-
-            node.LayerRoot = node.Visual;
-            node.AddDrawOperation(operation);
-            Assert.Equal(2, operation.RefCount);
-
-            using (target.BeginUpdate(node))
-            {
-                target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 100, 100));
-            }
-
-            Assert.Equal(1, node.DrawOperations.Count);
-            Assert.NotSame(operation, node.DrawOperations.Single());
-            Assert.Equal(1, operation.RefCount);
-        }
-    }
-}

+ 0 - 1087
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@@ -1,1087 +0,0 @@
-using System.Linq;
-using System.Reactive.Subjects;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Media.Imaging;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.UnitTests;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
-{
-    public partial class SceneBuilderTests
-    {
-        [Fact]
-        public void Should_Build_Initial_Scene()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                TextBlock textBlock;
-                var tree = new TestRoot
-                {
-                    Child = border = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        Child = textBlock = new TextBlock
-                        {
-                            TextWrapping = TextWrapping.NoWrap,
-                            Text = "Hello World",
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var result = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(result);
-
-                Assert.Same(tree, ((VisualNode)result.Root).LayerRoot);
-                Assert.Equal(1, result.Root.Children.Count);
-
-                var borderNode = (VisualNode)result.Root.Children[0];
-                Assert.Same(borderNode, result.FindNode(border));
-                Assert.Same(border, borderNode.Visual);
-                Assert.Equal(1, borderNode.Children.Count);
-                Assert.Equal(1, borderNode.DrawOperations.Count);
-
-                var backgroundNode = (RectangleNode)borderNode.DrawOperations[0].Item;
-                Assert.Equal(Brushes.Red, backgroundNode.Brush);
-
-                var textBlockNode = borderNode.Children[0];
-                Assert.Same(textBlockNode, result.FindNode(textBlock));
-                Assert.Same(textBlock, textBlockNode.Visual);
-                Assert.Equal(1, textBlockNode.DrawOperations.Count);
-
-                var textNode = (GlyphRunNode)textBlockNode.DrawOperations[0].Item;
-                Assert.NotNull(textNode.GlyphRun);
-            }
-        }
-
-        [Fact]
-        public void Should_Respect_Margin_For_ClipBounds()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Width = 200,
-                    Height = 300,
-                    Child = new Border
-                    {
-                        Margin = new Thickness(10, 20, 30, 40),
-                        Child = canvas = new Canvas
-                        {
-                            ClipToBounds = true,
-                            Background = Brushes.AliceBlue,
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var result = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(result);
-
-                var canvasNode = result.FindNode(canvas);
-                Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds);
-
-                // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
-                result = result.CloneScene();
-                Assert.True(sceneBuilder.Update(result, canvas));
-
-                canvasNode = result.FindNode(canvas);
-                Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds);
-            }
-        }
-
-        [Fact]
-        public void ClipBounds_Should_Be_Intersection_With_Parent_ClipBounds()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                var tree = new TestRoot
-                {
-                    Width = 200,
-                    Height = 300,
-                    Child = new Canvas
-                    {
-                        ClipToBounds = true,
-                        Width = 100,
-                        Height = 100,
-                        HorizontalAlignment = HorizontalAlignment.Left,
-                        VerticalAlignment = VerticalAlignment.Top,
-                        Children =
-                        {
-                            (border = new Border
-                            {
-                                Background = Brushes.AliceBlue,
-                                ClipToBounds = true,
-                                Width = 100,
-                                Height = 100,
-                                [Canvas.LeftProperty] = 50,
-                                [Canvas.TopProperty] = 50,
-                            })
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_Descendent_ClipBounds_When_Margin_Changed()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Width = 200,
-                    Height = 300,
-                    Child = canvas = new Canvas
-                    {
-                        ClipToBounds = true,
-                        Width = 100,
-                        Height = 100,
-                        HorizontalAlignment = HorizontalAlignment.Left,
-                        VerticalAlignment = VerticalAlignment.Top,
-                        Children =
-                        {
-                            (border = new Border
-                            {
-                                Background = Brushes.AliceBlue,
-                                ClipToBounds = true,
-                                Width = 100,
-                                Height = 100,
-                                [Canvas.LeftProperty] = 50,
-                                [Canvas.TopProperty] = 50,
-                            })
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
-
-                canvas.Width = canvas.Height = 125;
-                canvas.Measure(Size.Infinity);
-                canvas.Arrange(new Rect(tree.DesiredSize));
-
-                // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
-                scene = scene.CloneScene();
-                Assert.True(sceneBuilder.Update(scene, canvas));
-
-                borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(50, 50, 75, 75), borderNode.ClipBounds);
-            }
-        }
-
-        [Fact]
-        public void Should_Respect_ZIndex()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border front;
-                Border back;
-                var tree = new TestRoot
-                {
-                    Child = new Panel
-                    {
-                        Children =
-                        {
-                            (front = new Border
-                            {
-                                ZIndex = 1,
-                            }),
-                            (back = new Border
-                            {
-                                ZIndex = 0,
-                            }),
-                        }
-                    }
-                };
-
-                var result = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(result);
-
-                var panelNode = result.FindNode(tree.Child);
-                var expected = new Visual[] { back, front };
-                var actual = panelNode.Children.OfType<VisualNode>().Select(x => x.Visual).ToArray();
-                Assert.Equal(expected, actual);
-            }
-        }
-
-        [Fact]
-        public void Should_Respect_Uniform_ZIndex()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Panel panel;
-
-                var tree = new TestRoot
-                {
-                    Child = panel = new Panel()
-                };
-
-                for (var i = 0; i < 128; i++)
-                {
-                    panel.Children.Add(new Border());
-                }
-
-                var result = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(result);
-
-                var panelNode = result.FindNode(tree.Child);
-                var expected = panel.Children.ToArray();
-                var actual = panelNode.Children.OfType<VisualNode>().Select(x => x.Visual).ToArray();
-                Assert.Equal(expected, actual);
-            }
-        }
-
-        [Fact]
-        public void ClipBounds_Should_Be_In_Global_Coordinates()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border target;
-                var tree = new TestRoot
-                {
-                    Child = new Decorator
-                    {
-                        Margin = new Thickness(24, 26),
-                        Child = target = new Border
-                        {
-                            ClipToBounds = true,
-                            Margin = new Thickness(26, 24),
-                            Width = 100,
-                            Height = 100,
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var result = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(result);
-
-                var targetNode = result.FindNode(target);
-
-                Assert.Equal(new Rect(50, 50, 100, 100), targetNode.ClipBounds);
-            }
-        }
-
-        [Fact]
-        public void Transform_For_Control_With_RenderTransform_Should_Be_Correct_After_Update()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                var tree = new TestRoot
-                {
-                    Width = 400,
-                    Height = 200,
-                    Child = new Decorator
-                    {
-                        Width = 200,
-                        Height = 100,
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            HorizontalAlignment = HorizontalAlignment.Right,
-                            Width = 100,
-                            RenderTransform = new ScaleTransform(0.5, 1),
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var expectedTransform = Matrix.CreateScale(0.5, 1) * Matrix.CreateTranslation(225, 50);
-                var borderNode = scene.FindNode(border);
-                Assert.Equal(expectedTransform, borderNode.Transform);
-
-                scene = scene.CloneScene();
-                Assert.True(sceneBuilder.Update(scene, border));
-
-                borderNode = scene.FindNode(border);
-                Assert.Equal(expectedTransform, borderNode.Transform);
-            }
-        }
-
-        [Fact]
-        public void MirrorTransform_For_Control_With_RenderTransform_Should_Be_Correct()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                var tree = new TestRoot
-                {
-                    Width = 400,
-                    Height = 200,
-                    Child = border = new Border
-                    {
-                        HorizontalAlignment = HorizontalAlignment.Left,
-                        Background = Brushes.Red,
-                        Width = 100,
-                        RenderTransform = new ScaleTransform(0.5, 1),
-                        FlowDirection = FlowDirection.RightToLeft
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var expectedTransform = new Matrix(-1, 0, 0, 1, 100, 0) * Matrix.CreateScale(0.5, 1) * Matrix.CreateTranslation(25, 0);
-                var borderNode = scene.FindNode(border);
-                Assert.Equal(expectedTransform, borderNode.Transform);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_Border_Background_Node()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                TextBlock textBlock;
-                var tree = new TestRoot
-                {
-                    Child = border = new Border
-                    {
-                        Width = 100,
-                        Height = 100,
-                        Background = Brushes.Red,
-                        Child = textBlock = new TextBlock
-                        {
-                            Foreground = Brushes.Green,
-                            Text = "Hello World",
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var initial = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(initial);
-
-                var initialBackgroundNode = initial.FindNode(border).Children[0];
-                var initialTextNode = initial.FindNode(textBlock).DrawOperations[0];
-
-                Assert.NotNull(initialBackgroundNode);
-                Assert.NotNull(initialTextNode);
-
-                border.Background = Brushes.Green;
-
-                var result = initial.CloneScene();
-                sceneBuilder.Update(result, border);
-
-                var borderNode = (VisualNode)result.Root.Children[0];
-                Assert.Same(border, borderNode.Visual);
-
-                var backgroundNode = (RectangleNode)borderNode.DrawOperations[0].Item;
-                Assert.NotSame(initialBackgroundNode, backgroundNode);
-                Assert.Equal(Brushes.Green, backgroundNode.Brush);
-
-                var textBlockNode = (VisualNode)borderNode.Children[0];
-                Assert.Same(textBlock, textBlockNode.Visual);
-
-                var textNode = (GlyphRunNode)textBlockNode.DrawOperations[0].Item;
-                Assert.Same(initialTextNode.Item, textNode);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_When_Control_Added()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = border = new Border
-                    {
-                        Background = Brushes.Red,
-                    }
-                };
-
-                Canvas canvas;
-                var decorator = new Decorator
-                {
-                    Child = canvas = new Canvas(),
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var initial = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(initial);
-
-                border.Child = decorator;
-                var result = initial.CloneScene();
-
-                Assert.True(sceneBuilder.Update(result, decorator));
-
-                // Updating canvas should result in no-op as it should have been updated along 
-                // with decorator as part of the add opeation.
-                Assert.False(sceneBuilder.Update(result, canvas));
-
-                var borderNode = (VisualNode)result.Root.Children[0];
-                Assert.Equal(1, borderNode.Children.Count);
-                Assert.Equal(1, borderNode.DrawOperations.Count);
-
-                var decoratorNode = (VisualNode)borderNode.Children[0];
-                Assert.Same(decorator, decoratorNode.Visual);
-                Assert.Same(decoratorNode, result.FindNode(decorator));
-
-                var canvasNode = (VisualNode)decoratorNode.Children[0];
-                Assert.Same(canvas, canvasNode.Visual);
-                Assert.Same(canvasNode, result.FindNode(canvas));
-            }
-        }
-
-        [Fact]
-        public void Should_Update_When_Control_Removed()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Border border;
-                Decorator decorator;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = border = new Border
-                    {
-                        Background = Brushes.Red,
-                        Child = decorator = new Decorator
-                        {
-                            Child = canvas = new Canvas
-                            {
-                                Background = Brushes.AliceBlue,
-                            }
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var initial = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(initial);
-
-                border.Child = null;
-                var result = initial.CloneScene();
-
-                Assert.True(sceneBuilder.Update(result, decorator));
-                Assert.False(sceneBuilder.Update(result, canvas));
-
-                var borderNode = (VisualNode)result.Root.Children[0];
-                Assert.Equal(0, borderNode.Children.Count);
-                Assert.Equal(1, borderNode.DrawOperations.Count);
-
-                Assert.Null(result.FindNode(decorator));
-                Assert.Equal(new Rect(0, 0, 100, 100), result.Layers.Single().Dirty.Single());
-            }
-        }
-
-        [Fact]
-        public void Should_Update_When_Control_Moved()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator moveFrom;
-                Decorator moveTo;
-                Canvas moveMe;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new StackPanel
-                    {
-                        Children =
-                        {
-                            (moveFrom = new Decorator
-                            {
-                                Child = moveMe = new Canvas(),
-                            }),
-                            (moveTo = new Decorator()),
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
-                var moveToNode = (VisualNode)scene.FindNode(moveTo);
-
-                Assert.Equal(1, moveFromNode.Children.Count);
-                Assert.Same(moveMe, moveFromNode.Children[0].Visual);
-                Assert.Empty(moveToNode.Children);
-
-                moveFrom.Child = null;
-                moveTo.Child = moveMe;
-
-                scene = scene.CloneScene();
-                moveFromNode = (VisualNode)scene.FindNode(moveFrom);
-                moveToNode = (VisualNode)scene.FindNode(moveTo);
-
-                moveFromNode.SortChildren(scene);
-                moveToNode.SortChildren(scene);
-                sceneBuilder.Update(scene, moveFrom);
-                sceneBuilder.Update(scene, moveTo);
-                sceneBuilder.Update(scene, moveMe);
-
-                Assert.Empty(moveFromNode.Children);
-                Assert.Equal(1, moveToNode.Children.Count);
-                Assert.Same(moveMe, moveToNode.Children[0].Visual);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_When_Control_Moved_Causing_Layout_Change()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator moveFrom;
-                Decorator moveTo;
-                Canvas moveMe;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = new DockPanel
-                    {
-                        Children =
-                        {
-                            (moveFrom = new Decorator
-                            {
-                                Child = moveMe = new Canvas
-                                {
-                                    Width = 100,
-                                    Height = 100,
-                                },
-                            }),
-                            (moveTo = new Decorator()),
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
-                var moveToNode = (VisualNode)scene.FindNode(moveTo);
-
-                Assert.Equal(1, moveFromNode.Children.Count);
-                Assert.Same(moveMe, moveFromNode.Children[0].Visual);
-                Assert.Empty(moveToNode.Children);
-
-                moveFrom.Child = null;
-                moveTo.Child = moveMe;
-                tree.LayoutManager.ExecuteLayoutPass();
-
-                scene = scene.CloneScene();
-                moveFromNode = (VisualNode)scene.FindNode(moveFrom);
-                moveToNode = (VisualNode)scene.FindNode(moveTo);
-
-                moveFromNode.SortChildren(scene);
-                moveToNode.SortChildren(scene);
-                sceneBuilder.Update(scene, moveFrom);
-                sceneBuilder.Update(scene, moveTo);
-                sceneBuilder.Update(scene, moveMe);
-
-                Assert.Empty(moveFromNode.Children);
-                Assert.Equal(1, moveToNode.Children.Count);
-                Assert.Same(moveMe, moveToNode.Children[0].Visual);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_When_Control_Made_Invisible()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = decorator = new Decorator
-                    {
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Child = canvas = new Canvas(),
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var initial = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(initial);
-
-                border.IsVisible = false;
-                var result = initial.CloneScene();
-
-                Assert.True(sceneBuilder.Update(result, border));
-                Assert.False(sceneBuilder.Update(result, canvas));
-
-                var decoratorNode = (VisualNode)result.Root.Children[0];
-                Assert.Equal(0, decoratorNode.Children.Count);
-
-                Assert.Null(result.FindNode(border));
-                Assert.Null(result.FindNode(canvas));
-                Assert.Equal(new Rect(0, 0, 100, 100), result.Layers.Single().Dirty.Single());
-            }
-        }
-
-        [Fact]
-        public void Should_Not_Dispose_Active_VisualNode_When_Control_Reparented_And_Child_Made_Invisible()
-        {
-            // Issue #3115
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                StackPanel panel;
-                Border border1;
-                Border border2;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = panel = new StackPanel
-                    {
-                        Children =
-                        {
-                            (border1 = new Border
-                            {
-                                Background = Brushes.Red,
-                            }),
-                            (border2 = new Border
-                            {
-                                Background = Brushes.Green,
-                            }),
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var decorator = new Decorator();
-                tree.Child = null;
-                decorator.Child = panel;
-                tree.Child = decorator;
-                border1.IsVisible = false;
-
-                scene = scene.CloneScene();
-                sceneBuilder.Update(scene, decorator);
-
-                var panelNode = (VisualNode)scene.FindNode(panel);
-                Assert.Equal(2, panelNode.Children.Count);
-                Assert.False(panelNode.Children[0].Disposed);
-                Assert.False(panelNode.Children[1].Disposed);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_ClipBounds_For_Negative_Margin()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = decorator = new Decorator
-                    {
-                        Margin = new Thickness(0, 10, 0, 0),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            ClipToBounds = true,
-                            Margin = new Thickness(0, -5, 0, 0),
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(0, 5, 100, 95), borderNode.ClipBounds);
-
-                border.Margin = new Thickness(0, -8, 0, 0);
-                layout.ExecuteLayoutPass();
-
-                scene = scene.CloneScene();
-                sceneBuilder.Update(scene, border);
-
-                borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(0, 2, 100, 98), borderNode.ClipBounds);
-            }
-        }
-
-        [Fact]
-        public void Should_Update_Descendent_Tranform_When_Margin_Changed()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = decorator = new Decorator
-                    {
-                        Margin = new Thickness(0, 10, 0, 0),
-                        Child = border = new Border
-                        {
-                            Child = canvas = new Canvas(),
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var borderNode = scene.FindNode(border);
-                var canvasNode = scene.FindNode(canvas);
-                Assert.Equal(Matrix.CreateTranslation(0, 10), borderNode.Transform);
-                Assert.Equal(Matrix.CreateTranslation(0, 10), canvasNode.Transform);
-
-                decorator.Margin = new Thickness(0, 20, 0, 0);
-                layout.ExecuteLayoutPass();
-
-                scene = scene.CloneScene();
-                sceneBuilder.Update(scene, decorator);
-
-                borderNode = scene.FindNode(border);
-                canvasNode = scene.FindNode(canvas);
-                Assert.Equal(Matrix.CreateTranslation(0, 20), borderNode.Transform);
-                Assert.Equal(Matrix.CreateTranslation(0, 20), canvasNode.Transform);
-            }
-        }
-
-        [Fact]
-        public void DirtyRects_Should_Contain_Old_And_New_Bounds_When_Margin_Changed()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Width = 100,
-                    Height = 100,
-                    Child = decorator = new Decorator
-                    {
-                        Margin = new Thickness(0, 10, 0, 0),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Child = canvas = new Canvas(),
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var borderNode = scene.FindNode(border);
-                var canvasNode = scene.FindNode(canvas);
-                Assert.Equal(Matrix.CreateTranslation(0, 10), borderNode.Transform);
-                Assert.Equal(Matrix.CreateTranslation(0, 10), canvasNode.Transform);
-
-                decorator.Margin = new Thickness(0, 20, 0, 0);
-                layout.ExecuteLayoutPass();
-
-                scene = scene.CloneScene();
-
-                sceneBuilder.Update(scene, decorator);
-
-                var rects = scene.Layers.Single().Dirty.ToArray();
-                Assert.Equal(new[] { new Rect(0, 10, 100, 90) }, rects);
-            }
-        }
-
-        [Fact]
-        public void Resizing_Scene_Should_Add_DirtyRects()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    ClientSize = new Size(100, 100),
-                    Child = decorator = new Decorator
-                    {
-                        Margin = new Thickness(0, 10, 0, 0),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Child = canvas = new Canvas(),
-                        }
-                    }
-                };
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                Assert.Equal(new Size(100, 100), scene.Size);
-
-                tree.ClientSize = new Size(110, 120);
-                scene = scene.CloneScene();
-                sceneBuilder.Update(scene, tree);
-
-                Assert.Equal(new Size(110, 120), scene.Size);
-
-                var expected = new[]
-                {
-                    new Rect(100, 0, 10, 100),
-                    new Rect(0, 100, 110, 20),
-                };
-
-                Assert.Equal(expected, scene.Layers[tree].Dirty.ToArray());
-                
-                // Layers are disabled. See #2244
-                // Assert.Equal(expected, scene.Layers[border].Dirty.ToArray());
-            }
-        }
-
-        [Fact]
-        public void Setting_Opacity_Should_Add_Descendent_Bounds_To_DirtyRects()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                var tree = new TestRoot
-                {
-                    Child = decorator = new Decorator
-                    {
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Width = 100,
-                            Height = 100,
-                        }
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(tree.DesiredSize));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                decorator.Opacity = 0.5;
-                scene = scene.CloneScene();
-                sceneBuilder.Update(scene, decorator);
-
-                Assert.NotEmpty(scene.Layers.Single().Dirty);
-                var dirty = scene.Layers.Single().Dirty.Single();
-                Assert.Equal(new Rect(0, 0, 100, 100), dirty);
-            }
-        }
-
-        [Fact]
-        public void Should_Set_GeometryClip()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var clip = StreamGeometry.Parse("M100,0 L0,100 100,100");
-                Decorator decorator;
-                var tree = new TestRoot
-                {
-                    Child = decorator = new Decorator
-                    {
-                        Clip = clip,
-                    }
-                };
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var decoratorNode = scene.FindNode(decorator);
-                Assert.Same(clip.PlatformImpl, decoratorNode.GeometryClip);
-            }
-        }
-
-        [Fact]
-        public void Disposing_Scene_Releases_DrawOperation_References()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var bitmap = RefCountable.Create(Mock.Of<IBitmapImpl>(
-                    x => x.PixelSize == new PixelSize(100, 100) &&
-                    x.Dpi == new Vector(96, 96)));
-
-                Image img;
-                var tree = new TestRoot
-                {
-                    Child = img = new Image
-                    {
-                        Source = new Bitmap(bitmap),
-                        Height = 100,
-                        Width = 100
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(new Size(100, 100)));
-
-                Assert.Equal(2, bitmap.RefCount);
-                IRef<IDrawOperation> operation;
-
-                using (var scene = new Scene(tree))
-                {
-                    var sceneBuilder = new SceneBuilder();
-                    sceneBuilder.UpdateAll(scene);
-                    operation = scene.FindNode(img).DrawOperations[0];
-                    Assert.Equal(1, operation.RefCount);
-
-                    Assert.Equal(3, bitmap.RefCount);
-                }
-                Assert.Equal(0, operation.RefCount);
-                Assert.Equal(2, bitmap.RefCount);
-            }
-        }
-
-        [Fact]
-        public void Replacing_Control_Releases_DrawOperation_Reference()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var bitmap = RefCountable.Create(Mock.Of<IBitmapImpl>(
-                    x => x.PixelSize == new PixelSize(100, 100) &&
-                    x.Dpi == new Vector(96, 96)));
-
-                Image img;
-                var tree = new TestRoot
-                {
-                    Child = img = new Image
-                    {
-                        Source = new Bitmap(bitmap),
-                        Width = 100,
-                        Height = 100
-                    }
-                };
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(new Size(100, 100)));
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var operation = scene.FindNode(img).DrawOperations[0];
-
-                tree.Child = new Decorator();
-
-                using (var result = scene.CloneScene())
-                {
-                    sceneBuilder.Update(result, img);
-                    scene.Dispose();
-
-                    Assert.Equal(0, operation.RefCount);
-                    Assert.Equal(2, bitmap.RefCount);
-                }
-            }
-        }
-    }
-}

+ 0 - 258
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs

@@ -1,258 +0,0 @@
-using System.Linq;
-using System.Reactive.Subjects;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Media;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
-{
-    public partial class SceneBuilderTests
-    {
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Control_With_Animated_Opacity_And_Children_Should_Start_New_Layer()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Padding = new Thickness(10),
-                    Width = 100,
-                    Height = 120,
-                    Child = decorator = new Decorator
-                    {
-                        Padding = new Thickness(11),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Padding = new Thickness(12),
-                            Child = canvas = new Canvas()
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var rootNode = (VisualNode)scene.Root;
-                var borderNode = (VisualNode)scene.FindNode(border);
-                var canvasNode = (VisualNode)scene.FindNode(canvas);
-
-                Assert.Same(tree, rootNode.LayerRoot);
-                Assert.Same(border, borderNode.LayerRoot);
-                Assert.Same(border, canvasNode.LayerRoot);
-                Assert.Equal(0.5, scene.Layers[border].Opacity);
-
-                Assert.Equal(2, scene.Layers.Count());
-                Assert.Empty(scene.Layers.Select(x => x.LayerRoot).Except(new Visual[] { tree, border }));
-
-                animation.OnCompleted();
-                scene = scene.CloneScene();
-
-                sceneBuilder.Update(scene, border);
-
-                rootNode = (VisualNode)scene.Root;
-                borderNode = (VisualNode)scene.FindNode(border);
-                canvasNode = (VisualNode)scene.FindNode(canvas);
-
-                Assert.Same(tree, rootNode.LayerRoot);
-                Assert.Same(tree, borderNode.LayerRoot);
-                Assert.Same(tree, canvasNode.LayerRoot);
-                Assert.Single(scene.Layers);
-
-                var rootDirty = scene.Layers[tree].Dirty;
-
-                Assert.Single(rootDirty);
-                Assert.Equal(new Rect(21, 21, 58, 78), rootDirty.Single());
-            }
-        }
-
-        [Fact]
-        public void Control_With_Animated_Opacity_And_No_Children_Should_Not_Start_New_Layer()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                var tree = new TestRoot
-                {
-                    Padding = new Thickness(10),
-                    Width = 100,
-                    Height = 120,
-                    Child = decorator = new Decorator
-                    {
-                        Padding = new Thickness(11),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                Assert.Single(scene.Layers);
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Removing_Control_With_Animated_Opacity_Should_Remove_Layers()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Padding = new Thickness(10),
-                    Width = 100,
-                    Height = 120,
-                    Child = decorator = new Decorator
-                    {
-                        Padding = new Thickness(11),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Padding = new Thickness(12),
-                            Child = canvas = new Canvas
-                            {
-                                Children = { new TextBlock() },
-                            }
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-                canvas.Bind(Canvas.OpacityProperty, animation, BindingPriority.Animation);
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                Assert.Equal(3, scene.Layers.Count);
-
-                decorator.Child = null;
-                scene = scene.CloneScene();
-
-                sceneBuilder.Update(scene, border);
-
-                Assert.Equal(1, scene.Layers.Count);
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void Hiding_Transparent_Control_Should_Remove_Layers()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                Decorator decorator;
-                Border border;
-                Canvas canvas;
-                var tree = new TestRoot
-                {
-                    Padding = new Thickness(10),
-                    Width = 100,
-                    Height = 120,
-                    Child = decorator = new Decorator
-                    {
-                        Padding = new Thickness(11),
-                        Child = border = new Border
-                        {
-                            Background = Brushes.Red,
-                            Padding = new Thickness(12),
-                            Child = canvas = new Canvas
-                            {
-                                Children = { new TextBlock() },
-                            }
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-                canvas.Bind(Canvas.OpacityProperty, animation, BindingPriority.Animation);
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                Assert.Equal(3, scene.Layers.Count);
-
-                border.IsVisible = false;
-                scene = scene.CloneScene();
-
-                sceneBuilder.Update(scene, border);
-
-                Assert.Equal(1, scene.Layers.Count);
-            }
-        }
-
-        [Fact(Skip = "Layers are disabled. See #2244")]
-        public void GeometryClip_Should_Affect_Child_Layers()
-        {
-            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
-            {
-                var clip = StreamGeometry.Parse("M100,0 L0,100 100,100");
-                Decorator decorator;
-                Border border;
-                var tree = new TestRoot
-                {
-                    Child = decorator = new Decorator
-                    {
-                        Clip = clip,
-                        Margin = new Thickness(12, 16),
-                        Child = border = new Border
-                        {
-                            Opacity = 0.5,
-                            Child = new Canvas(),
-                        }
-                    }
-                };
-
-                var layout = tree.LayoutManager;
-                layout.ExecuteInitialLayoutPass();
-
-                var animation = new BehaviorSubject<double>(0.5);
-                border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
-
-                var scene = new Scene(tree);
-                var sceneBuilder = new SceneBuilder();
-                sceneBuilder.UpdateAll(scene);
-
-                var borderLayer = scene.Layers[border];
-                Assert.Equal(
-                    Matrix.CreateTranslation(12, 16),
-                    ((MockStreamGeometryImpl)borderLayer.GeometryClip).Transform);
-            }
-        }
-    }
-}

+ 0 - 35
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs

@@ -1,35 +0,0 @@
-using System.Linq;
-using Avalonia.Controls;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
-{
-    public class SceneLayersTests
-    {
-        [Fact]
-        public void Layers_Should_Be_Ordered()
-        {
-            Border border;
-            Decorator decorator;
-            var root = new TestRoot
-            {
-                Child = border = new Border
-                {
-                    Child = decorator = new Decorator(),
-                }
-            };
-
-            var target = new SceneLayers(root);
-            target.Add(root);
-            target.Add(decorator);
-            target.Add(border);
-
-            var result = target.Select(x => x.LayerRoot).ToArray();
-
-            Assert.Equal(new Visual[] { root, border, decorator }, result);
-        }
-    }
-}

+ 0 - 33
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs

@@ -1,33 +0,0 @@
-using System.Linq;
-using Avalonia.Controls;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.UnitTests;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
-{
-    public class SceneTests
-    {
-        [Fact]
-        public void Cloning_Scene_Should_Retain_Layers_But_Not_DirtyRects()
-        {
-            Decorator decorator;
-            var tree = new TestRoot
-            {
-                Child = decorator = new Decorator(),
-            };
-
-            var scene = new Scene(tree);
-            scene.Layers.Add(tree);
-            scene.Layers.Add(decorator);
-
-            scene.Layers[tree].Dirty.Add(new Rect(0, 0, 100, 100));
-            scene.Layers[decorator].Dirty.Add(new Rect(0, 0, 50, 100));
-
-            scene = scene.CloneScene();
-            Assert.Equal(2, scene.Layers.Count());
-            Assert.Empty(scene.Layers[0].Dirty);
-            Assert.Empty(scene.Layers[1].Dirty);
-        }
-    }
-}

+ 0 - 123
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs

@@ -1,123 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Utilities;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
-{
-    public class VisualNodeTests
-    {
-        [Fact]
-        public void Empty_Children_Collections_Should_Be_Shared()
-        {
-            var node1 = new VisualNode(new Control(), null);
-            var node2 = new VisualNode(new Control(), null);
-
-            Assert.Same(node1.Children, node2.Children);
-        }
-
-        [Fact]
-        public void Adding_Child_Should_Create_Collection()
-        {
-            var node = new VisualNode(new Control(), null);
-            var collection = node.Children;
-
-            node.AddChild(new VisualNode(new Border(), node));
-
-            Assert.NotSame(collection, node.Children);
-        }
-
-        [Fact]
-        public void Empty_DrawOperations_Collections_Should_Be_Shared()
-        {
-            var node1 = new VisualNode(new Control(), null);
-            var node2 = new VisualNode(new Control(), null);
-
-            Assert.Same(node1.DrawOperations, node2.DrawOperations);
-        }
-
-        [Fact]
-        public void Adding_DrawOperation_Should_Create_Collection()
-        {
-            var node = new VisualNode(new Control(), null);
-            var collection = node.DrawOperations;
-
-            node.AddDrawOperation(RefCountable.Create(Mock.Of<IDrawOperation>()));
-
-            Assert.NotSame(collection, node.DrawOperations);
-        }
-
-        [Fact]
-        public void Cloned_Nodes_Should_Share_DrawOperations_Collection()
-        {
-            var node1 = new VisualNode(new Control(), null);
-            node1.AddDrawOperation(RefCountable.Create(Mock.Of<IDrawOperation>()));
-
-            var node2 = node1.Clone(null);
-
-            Assert.Same(node1.DrawOperations, node2.DrawOperations);
-        }
-
-        [Fact]
-        public void Adding_DrawOperation_To_Cloned_Node_Should_Create_New_Collection()
-        {
-            var node1 = new VisualNode(new Control(), null);
-            var operation1 = RefCountable.Create(Mock.Of<IDrawOperation>());
-            node1.AddDrawOperation(operation1);
-
-            var node2 = node1.Clone(null);
-            var operation2 = RefCountable.Create(Mock.Of<IDrawOperation>());
-            node2.ReplaceDrawOperation(0, operation2);
-
-            Assert.NotSame(node1.DrawOperations, node2.DrawOperations);
-            Assert.Equal(1, node1.DrawOperations.Count);
-            Assert.Equal(1, node2.DrawOperations.Count);
-            Assert.Same(operation1.Item, node1.DrawOperations[0].Item);
-            Assert.Same(operation2.Item, node2.DrawOperations[0].Item);
-        }
-
-        [Fact]
-        public void DrawOperations_In_Cloned_Node_Are_Cloned()
-        {
-            var node1 = new VisualNode(new Control(), null);
-            var operation1 = RefCountable.Create(Mock.Of<IDrawOperation>());
-            node1.AddDrawOperation(operation1);
-
-            var node2 = node1.Clone(null);
-            var operation2 = RefCountable.Create(Mock.Of<IDrawOperation>());
-            node2.AddDrawOperation(operation2);
-
-            Assert.Same(node1.DrawOperations[0].Item, node2.DrawOperations[0].Item);
-            Assert.NotSame(node1.DrawOperations[0], node2.DrawOperations[0]);
-        }
-
-        [Fact]
-        public void SortChildren_Does_Not_Throw_On_Null_Children()
-        {
-            var node = new VisualNode(new Control(), null);
-            var scene = new Scene(new Control());
-
-            node.SortChildren(scene);
-        }
-
-        [Fact]
-        public void TrimChildren_Should_Work_Correctly()
-        {
-            var parent = new VisualNode(new Control(), null);
-            var child1 = new VisualNode(new Control(), parent);
-            var child2 = new VisualNode(new Control(), parent);
-            var child3 = new VisualNode(new Control(), parent);
-
-            parent.AddChild(child1);
-            parent.AddChild(child2);
-            parent.AddChild(child3);
-            parent.TrimChildren(2);
-
-            Assert.Equal(2, parent.Children.Count);
-            Assert.False(child1.Disposed);
-            Assert.False(child2.Disposed);
-            Assert.True(child3.Disposed);
-        }
-    }
-}

+ 0 - 52
tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs

@@ -1,52 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Controls.Shapes;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Moq;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.VisualTree
-{
-    public class TransformedBoundsTests
-    {
-        [Fact]
-        public void Should_Track_Bounds()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                var control = default(Rectangle);
-                var tree = new Decorator
-                {
-                    Padding = new Thickness(10),
-                    Child = new Decorator
-                    {
-                        Padding = new Thickness(5),
-                        Child = control = new Rectangle
-                        {
-                            Width = 15,
-                            Height = 15,
-                        },
-                    }
-                };
-
-                var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
-
-                tree.Measure(Size.Infinity);
-                tree.Arrange(new Rect(0, 0, 100, 100));
-                ImmediateRenderer.Render(tree, context, true);
-
-                var track = control.GetObservable(Visual.TransformedBoundsProperty);
-                var results = new List<TransformedBounds?>();
-                track.Subscribe(results.Add);
-
-                Assert.Equal(new Rect(0, 0, 15, 15), results[0].Value.Bounds);
-                Assert.Equal(Matrix.CreateTranslation(42, 42), results[0].Value.Transform);
-            }
-        }
-    }
-}

+ 19 - 34
tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs

@@ -15,16 +15,14 @@ namespace Avalonia.Base.UnitTests.VisualTree
         [Fact]
         public void Should_Find_Control()
         {
-            using (TestApplication())
+            Border target;
+            var services = new CompositorTestServices(new Size(200, 200))
             {
-                Border target;
-                var root = new TestRoot
+                TopLevel =
                 {
-                    Width = 200,
-                    Height = 200,
-                    Child = new StackPanel
+                    Content = new StackPanel
                     {
-                        Background = Brushes.White,
+                        Background = null,
                         Children =
                         {
                             (target = new Border
@@ -42,29 +40,25 @@ namespace Avalonia.Base.UnitTests.VisualTree
                         },
                         Orientation = Orientation.Horizontal,
                     }
-                };
+                }
+            };
 
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
+            services.RunJobs();
+            var result = target.GetVisualsAt(new Point(50, 50));
 
-                var result = target.GetVisualsAt(new Point(50, 50));
+            Assert.Same(target, result.Single());
 
-                Assert.Same(target, result.Single());
-            }
         }
 
         [Fact]
         public void Should_Not_Find_Sibling_Control()
         {
-            using (TestApplication())
+            Border target;
+            var services = new CompositorTestServices(new Size(200, 200))
             {
-                Border target;
-                var root = new TestRoot
+                TopLevel =
                 {
-                    Width = 200,
-                    Height = 200,
-                    Child = new StackPanel
+                    Content = new StackPanel
                     {
                         Background = Brushes.White,
                         Children =
@@ -84,21 +78,12 @@ namespace Avalonia.Base.UnitTests.VisualTree
                         },
                         Orientation = Orientation.Horizontal,
                     }
-                };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var result = target.GetVisualsAt(new Point(150, 50));
+                }
+            };
+            services.RunJobs();
+            var result = target.GetVisualsAt(new Point(150, 50));
 
-                Assert.Empty(result);
-            }
-        }
-
-        private static IDisposable TestApplication()
-        {
-            return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+            Assert.Empty(result);
         }
     }
 }

+ 1 - 1
tests/Avalonia.RenderTests/Controls/AdornerTests.cs

@@ -68,6 +68,6 @@ public class AdornerTests : TestBase
         tree.Arrange(new Rect(size));
         
         await RenderToFile(tree);
-        CompareImages(skipImmediate: true, skipDeferred: true);
+        CompareImages(skipImmediate: true);
     }
 }

+ 1 - 26
tests/Avalonia.RenderTests/TestBase.cs

@@ -89,7 +89,6 @@ namespace Avalonia.Direct2D1.RenderTests
             }
 
             var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
-            var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
             var compositedPath = Path.Combine(OutputPath, testName + ".composited.out.png");
             var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
             var pixelSize = new PixelSize((int)target.Width, (int)target.Height);
@@ -104,22 +103,6 @@ namespace Avalonia.Direct2D1.RenderTests
                 bitmap.Save(immediatePath);
             }
             
-            
-            using (var rtb = factory.CreateRenderTargetBitmap(pixelSize, dpiVector))
-            using (var renderer = new DeferredRenderer(target, rtb))
-            {
-                target.Measure(size);
-                target.Arrange(new Rect(size));
-                renderer.UnitTestUpdateScene();
-
-                // Do the deferred render on a background thread to expose any threading errors in
-                // the deferred rendering path.
-                await Task.Run((Action)renderer.UnitTestRender);
-                threadingInterface.MainThread = Thread.CurrentThread;
-
-                rtb.Save(deferredPath);
-            }
-
             var timer = new ManualRenderTimer();
 
             var compositor = new Compositor(new RenderLoop(timer, Dispatcher.UIThread), null);
@@ -157,20 +140,17 @@ namespace Avalonia.Direct2D1.RenderTests
         }
 
         protected void CompareImages([CallerMemberName] string testName = "",
-            bool skipImmediate = false, bool skipDeferred = false, bool skipCompositor = false)
+            bool skipImmediate = false,  bool skipCompositor = false)
         {
             var expectedPath = Path.Combine(OutputPath, testName + ".expected.png");
             var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
-            var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
             var compositedPath = Path.Combine(OutputPath, testName + ".composited.out.png");
 
             using (var expected = Image.Load<Rgba32>(expectedPath))
             using (var immediate = Image.Load<Rgba32>(immediatePath))
-            using (var deferred = Image.Load<Rgba32>(deferredPath))
             using (var composited = Image.Load<Rgba32>(compositedPath))
             {
                 var immediateError = CompareImages(immediate, expected);
-                var deferredError = CompareImages(deferred, expected);
                 var compositedError = CompareImages(composited, expected);
 
                 if (immediateError > 0.022 && !skipImmediate)
@@ -178,11 +158,6 @@ namespace Avalonia.Direct2D1.RenderTests
                     Assert.True(false, immediatePath + ": Error = " + immediateError);
                 }
 
-                if (deferredError > 0.022 && !skipDeferred)
-                {
-                    Assert.True(false, deferredPath + ": Error = " + deferredError);
-                }
-                
                 if (compositedError > 0.022 && !skipCompositor)
                 {
                     Assert.True(false, compositedPath + ": Error = " + compositedError);

+ 32 - 39
tests/Avalonia.Skia.UnitTests/HitTesting.cs

@@ -1,6 +1,8 @@
-using Avalonia.Controls.Shapes;
+using System;
+using Avalonia.Controls.Shapes;
 using Avalonia.Layout;
 using Avalonia.Media;
+using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.UnitTests;
 using Xunit;
@@ -16,29 +18,24 @@ namespace Avalonia.Skia.UnitTests
             {
                 SkiaPlatform.Initialize();
 
-                var root = new TestRoot
+                using var services = new CompositorTestServices(new Size(100, 100), 
+                    AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>())
                 {
-                    Width = 100,
-                    Height = 100,
-                    Child = new Ellipse
+                    TopLevel =
                     {
-                        Width = 100,
-                        Height = 100,
-                        Fill = Brushes.Red,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
+                        Content = new Ellipse
+                        {
+                            Width = 100,
+                            Height = 100,
+                            Fill = Brushes.Red,
+                            HorizontalAlignment = HorizontalAlignment.Center,
+                            VerticalAlignment = VerticalAlignment.Center
+                        }
                     }
                 };
 
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var outsideResult = root.Renderer.HitTest(new Point(10, 10), root, null);
-                var insideResult = root.Renderer.HitTest(new Point(50, 50), root, null);
-
-                Assert.Empty(outsideResult);
-                Assert.Equal(new[] { root.Child }, insideResult);
+                services.AssertHitTest(10, 10, null, Array.Empty<object>());
+                services.AssertHitTest(50, 50, null, services.TopLevel.Content);
             }
         }
 
@@ -49,30 +46,26 @@ namespace Avalonia.Skia.UnitTests
             {
                 SkiaPlatform.Initialize();
 
-                var root = new TestRoot
+                using var services = new CompositorTestServices(new Size(100, 100),
+                    AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>())
                 {
-                    Width = 100,
-                    Height = 100,
-                    Child = new Ellipse
+                    TopLevel =
                     {
-                        Width = 100,
-                        Height = 100,
-                        Stroke = Brushes.Red,
-                        StrokeThickness = 5,
-                        HorizontalAlignment = HorizontalAlignment.Center,
-                        VerticalAlignment = VerticalAlignment.Center
+                        Content = new Ellipse
+                        {
+                            Width = 100,
+                            Height = 100,
+                            Stroke = Brushes.Red,
+                            StrokeThickness = 5,
+                            HorizontalAlignment = HorizontalAlignment.Center,
+                            VerticalAlignment = VerticalAlignment.Center
+                        }
                     }
                 };
-
-                root.Renderer = new DeferredRenderer((IRenderRoot)root, null, root.CreateRenderTarget);
-                root.Measure(Size.Infinity);
-                root.Arrange(new Rect(root.DesiredSize));
-
-                var outsideResult = root.Renderer.HitTest(new Point(50, 50), root, null);
-                var insideResult = root.Renderer.HitTest(new Point(1, 50), root, null);
-
-                Assert.Empty(outsideResult);
-                Assert.Equal(new[] { root.Child }, insideResult);
+                
+                
+                services.AssertHitTest(50, 50, null, Array.Empty<object>());
+                services.AssertHitTest(1, 50, null, services.TopLevel.Content);
             }
         }
     }

+ 3 - 4
tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj

@@ -1,11 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFrameworks>netstandard2.0</TargetFrameworks>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <OutputType>Library</OutputType>
     <IsPackable>false</IsPackable>
-    <AssemblyOriginatorKeyFile>..\..\build\avalonia.snk</AssemblyOriginatorKeyFile>
-    <SignAssembly>false</SignAssembly>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>
@@ -20,6 +18,7 @@
   </ItemGroup>
   <Import Project="..\..\build\Moq.props" />
   <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\SharedVersion.props" />
   <Import Project="..\..\build\HarfBuzzSharp.props" />
+  <Import Project="..\..\build\XUnit.props" />
+  <Import Project="..\..\build\SharedVersion.props" />
 </Project>

+ 207 - 0
tests/Avalonia.UnitTests/CompositorTestServices.cs

@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Controls.Embedding;
+using Avalonia.Controls.Platform.Surfaces;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Input.Raw;
+using Avalonia.Platform;
+using Avalonia.Rendering;
+using Avalonia.Rendering.Composition;
+using Avalonia.Threading;
+using Xunit;
+
+namespace Avalonia.UnitTests;
+public class CompositorTestServices : IDisposable
+{
+    private readonly IDisposable _app;
+    public Compositor Compositor { get; }
+    public ManualRenderTimer Timer { get; } = new();
+    public EmbeddableControlRoot TopLevel { get; }
+    public CompositingRenderer Renderer { get; } = null!;
+    public DebugEvents Events { get; } = new();
+
+    public void Dispose()
+    {
+        TopLevel.Renderer.Stop();
+        TopLevel.Dispose();
+        _app.Dispose();
+    }
+
+    public CompositorTestServices(Size? size = null, IPlatformRenderInterface renderInterface = null)
+    {
+        var services = TestServices.MockPlatformRenderInterface;
+        if (renderInterface != null)
+            services = services.With(renderInterface: renderInterface);
+        
+        _app = UnitTestApplication.Start(services);
+        try
+        {
+            AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(Timer);
+            AvaloniaLocator.CurrentMutable.Bind<IRenderLoop>()
+                .ToConstant(new RenderLoop(Timer, Dispatcher.UIThread));
+
+            Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null);
+            var impl = new TopLevelImpl(Compositor, size ?? new Size(1000, 1000));
+            TopLevel = new EmbeddableControlRoot(impl)
+            {
+                Template = new FuncControlTemplate((parent, scope) =>
+                {
+                    var presenter = new ContentPresenter
+                    {
+                        [~ContentPresenter.ContentProperty] = new TemplateBinding(ContentControl.ContentProperty)
+                    };
+                    scope.Register("PART_ContentPresenter", presenter);
+                    return presenter;
+                })
+            };
+            Renderer = impl.Renderer;
+            TopLevel.Prepare();
+            TopLevel.Renderer.Start();
+            RunJobs();
+            Renderer.CompositionTarget.Server.DebugEvents = Events;
+        }
+        catch
+        {
+            _app.Dispose();
+            throw;
+        }
+    }
+
+    public void RunJobs()
+    {
+        Dispatcher.UIThread.RunJobs();
+        Timer.TriggerTick();
+        Dispatcher.UIThread.RunJobs();
+    }
+
+    public void AssertRects(params Rect[] rects)
+    {
+        RunJobs();
+        var toAssert = rects.Select(x => x.ToString()).Distinct().OrderBy(x => x);
+        var invalidated = Events.Rects.Select(x => x.ToString()).Distinct().OrderBy(x => x);
+        Assert.Equal(toAssert, invalidated);
+        Events.Rects.Clear();
+    }
+
+    public void AssertHitTest(double x, double y, Func<Visual, bool> filter, params object[] expected)
+        => AssertHitTest(new Point(x, y), filter, expected);
+
+    public void AssertHitTest(Point pt, Func<Visual, bool> filter, params object[] expected)
+    {
+        RunJobs();
+        var tested = Renderer.HitTest(pt, TopLevel, filter);
+        Assert.Equal(expected, tested);
+    }
+
+    public void AssertHitTestFirst(Point pt, Func<Visual, bool> filter, object expected)
+    {
+        RunJobs();
+        var tested = Renderer.HitTest(pt, TopLevel, filter).First();
+        Assert.Equal(expected, tested);
+    }
+
+    public class DebugEvents : ICompositionTargetDebugEvents
+    {
+        public List<Rect> Rects = new();
+
+        public void RectInvalidated(Rect rc)
+        {
+            Rects.Add(rc);
+        }
+
+        public void Reset()
+        {
+            Rects.Clear();
+        }
+    }
+
+    public class ManualRenderTimer : IRenderTimer
+    {
+        public event Action<TimeSpan> Tick;
+        public bool RunsInBackground => false;
+        public void TriggerTick() => Tick?.Invoke(TimeSpan.Zero);
+        public Task TriggerBackgroundTick() => Task.Run(TriggerTick);
+    }
+
+    class TopLevelImpl : ITopLevelImpl
+    {
+        private readonly Compositor _compositor;
+        public CompositingRenderer Renderer { get; private set; }
+
+        public TopLevelImpl(Compositor compositor, Size clientSize)
+        {
+            ClientSize = clientSize;
+            _compositor = compositor;
+        }
+
+        public void Dispose()
+        {
+
+        }
+
+        public Size ClientSize { get; }
+        public Size? FrameSize { get; }
+        public double RenderScaling => 1;
+        public IEnumerable<object> Surfaces { get; } = new[] { new DummyFramebufferSurface() };
+        public Action<RawInputEventArgs> Input { get; set; }
+        public Action<Rect> Paint { get; set; }
+        public Action<Size, PlatformResizeReason> Resized { get; set; }
+        public Action<double> ScalingChanged { get; set; }
+        public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
+
+        class DummyFramebufferSurface : IFramebufferPlatformSurface
+        {
+            public ILockedFramebuffer Lock()
+            {
+                var ptr = Marshal.AllocHGlobal(128);
+                return new LockedFramebuffer(ptr, new PixelSize(1, 1), 4, new Vector(96, 96),
+                    PixelFormat.Rgba8888, () => Marshal.FreeHGlobal(ptr));
+            }
+        }
+
+        public IRenderer CreateRenderer(IRenderRoot root)
+        {
+            return Renderer = new CompositingRenderer(root, _compositor, () => Surfaces);
+        }
+
+        public void Invalidate(Rect rect)
+        {
+        }
+
+        public void SetInputRoot(IInputRoot inputRoot)
+        {
+        }
+
+        public Point PointToClient(PixelPoint point) => default;
+
+        public PixelPoint PointToScreen(Point point) => new();
+
+        public void SetCursor(ICursorImpl cursor)
+        {
+        }
+
+        public Action Closed { get; set; }
+        public Action LostFocus { get; set; }
+        public IMouseDevice MouseDevice { get; } = new MouseDevice();
+        public IPopupImpl CreatePopup() => throw new NotImplementedException();
+
+        public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
+        {
+        }
+
+        public WindowTransparencyLevel TransparencyLevel { get; }
+
+        public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+        {
+        }
+
+        public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
+    }
+}

BIN
tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png


BIN
tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png