diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index cba09297f3..2bc7121d73 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -25,7 +25,6 @@ namespace Avalonia.Rendering private readonly IRenderLoop _renderLoop; private readonly IVisual _root; private readonly ISceneBuilder _sceneBuilder; - private readonly RenderLayers _layers; private bool _running; private Scene _scene; @@ -56,7 +55,7 @@ namespace Avalonia.Rendering _dispatcher = dispatcher ?? Dispatcher.UIThread; _root = root; _sceneBuilder = sceneBuilder ?? new SceneBuilder(); - _layers = new RenderLayers(); + Layers = new RenderLayers(); _renderLoop = renderLoop; } @@ -80,7 +79,7 @@ namespace Avalonia.Rendering _root = root; _renderTarget = renderTarget; _sceneBuilder = sceneBuilder ?? new SceneBuilder(); - _layers = new RenderLayers(); + Layers = new RenderLayers(); } /// @@ -94,6 +93,11 @@ namespace Avalonia.Rendering /// public string DebugFramesPath { get; set; } + /// + /// Gets the render layers. + /// + internal RenderLayers Layers { get; } + /// public void AddDirty(IVisual visual) { @@ -192,7 +196,7 @@ namespace Avalonia.Rendering if (scene.Generation != _lastSceneId) { context = _renderTarget.CreateDrawingContext(this); - _layers.Update(scene, context); + Layers.Update(scene, context); RenderToLayers(scene); @@ -262,7 +266,7 @@ namespace Avalonia.Rendering { foreach (var layer in scene.Layers) { - var renderTarget = _layers[layer.LayerRoot].Bitmap; + var renderTarget = Layers[layer.LayerRoot].Bitmap; var node = (VisualNode)scene.FindNode(layer.LayerRoot); if (node != null) @@ -322,7 +326,7 @@ namespace Avalonia.Rendering foreach (var layer in scene.Layers) { - var bitmap = _layers[layer.LayerRoot].Bitmap; + var bitmap = Layers[layer.LayerRoot].Bitmap; var sourceRect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight); if (layer.GeometryClip != null) @@ -442,7 +446,7 @@ namespace Avalonia.Rendering { var index = 0; - foreach (var layer in _layers) + foreach (var layer in Layers) { var fileName = Path.Combine(DebugFramesPath, $"frame-{id}-layer-{index++}.png"); layer.Bitmap.Save(fileName); diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs index 9ec053f075..dc137e3533 100644 --- a/tests/Avalonia.UnitTests/TestRoot.cs +++ b/tests/Avalonia.UnitTests/TestRoot.cs @@ -16,8 +16,6 @@ namespace Avalonia.UnitTests public class TestRoot : Decorator, IFocusScope, ILayoutRoot, IInputRoot, INameScope, IRenderRoot, IStyleRoot { private readonly NameScope _nameScope = new NameScope(); - private readonly IRenderTarget _renderTarget = Mock.Of( - x => x.CreateDrawingContext(It.IsAny()) == Mock.Of()); public TestRoot() { @@ -65,7 +63,21 @@ namespace Avalonia.UnitTests IStyleHost IStyleHost.StylingParent => StylingParent; - public IRenderTarget CreateRenderTarget() => _renderTarget; + public IRenderTarget CreateRenderTarget() + { + var dc = new Mock(); + dc.Setup(x => x.CreateLayer(It.IsAny())).Returns(() => + { + var layerDc = new Mock(); + var layer = new Mock(); + layer.Setup(x => x.CreateDrawingContext(It.IsAny())).Returns(layerDc.Object); + return layer.Object; + }); + + var result = new Mock(); + result.Setup(x => x.CreateDrawingContext(It.IsAny())).Returns(dc.Object); + return result.Object; + } public void Invalidate(Rect rect) { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs index afe7b04664..e3b2577e79 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs @@ -21,28 +21,18 @@ namespace Avalonia.Visuals.UnitTests.Rendering [Fact] public void First_Frame_Calls_UpdateScene_On_Dispatcher() { - var loop = new Mock(); var root = new TestRoot(); var dispatcher = new Mock(); dispatcher.Setup(x => x.InvokeAsync(It.IsAny(), DispatcherPriority.Render)) .Callback((a, p) => a()); - var target = new DeferredRenderer( - root, - loop.Object, - sceneBuilder: MockSceneBuilder(root).Object, - dispatcher: dispatcher.Object); + CreateTargetAndRunFrame(root, dispatcher: dispatcher.Object); - target.Start(); - RunFrame(loop); - -#if !NETCOREAPP1_1 // Delegate.Method is not available in netcoreapp1.1 dispatcher.Verify(x => x.InvokeAsync( It.Is(a => a.Method.Name == "UpdateScene"), DispatcherPriority.Render)); -#endif } [Fact] @@ -51,15 +41,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering var loop = new Mock(); var root = new TestRoot(); var sceneBuilder = MockSceneBuilder(root); - var dispatcher = new ImmediateDispatcher(); - var target = new DeferredRenderer( - root, - loop.Object, - sceneBuilder: sceneBuilder.Object, - dispatcher: dispatcher); - target.Start(); - RunFrame(loop); + CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object); sceneBuilder.Verify(x => x.UpdateAll(It.IsAny())); } @@ -70,12 +53,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering var loop = new Mock(); var root = new TestRoot(); var sceneBuilder = MockSceneBuilder(root); - var dispatcher = new ImmediateDispatcher(); var target = new DeferredRenderer( root, loop.Object, - sceneBuilder: sceneBuilder.Object, - dispatcher: dispatcher); + sceneBuilder: sceneBuilder.Object); target.Start(); IgnoreFirstFrame(loop, sceneBuilder); @@ -145,24 +126,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering root.Measure(Size.Infinity); root.Arrange(new Rect(root.DesiredSize)); - var rootLayer = CreateLayer(); - var borderLayer = CreateLayer(); - var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null)); - renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny())) - .Returns(rootLayer) - .Returns(borderLayer); - - var loop = new Mock(); - var target = new DeferredRenderer( - root, - loop.Object, - dispatcher: new ImmediateDispatcher()); - root.Renderer = target; - - target.Start(); - RunFrame(loop); - - var context = Mock.Get(rootLayer.CreateDrawingContext(null)); + var target = CreateTargetAndRunFrame(root); + var context = GetLayerContext(target, root); var animation = new BehaviorSubject(0.5); context.Verify(x => x.PushOpacity(0.5), Times.Once); @@ -191,24 +156,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering root.Measure(Size.Infinity); root.Arrange(new Rect(root.DesiredSize)); - var rootLayer = CreateLayer(); - var borderLayer = CreateLayer(); - var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null)); - renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny())) - .Returns(rootLayer) - .Returns(borderLayer); - - var loop = new Mock(); - var target = new DeferredRenderer( - root, - loop.Object, - dispatcher: new ImmediateDispatcher()); - root.Renderer = target; - - target.Start(); - RunFrame(loop); - - var context = Mock.Get(rootLayer.CreateDrawingContext(null)); + var target = CreateTargetAndRunFrame(root); + var context = GetLayerContext(target, root); var animation = new BehaviorSubject(0.5); context.Verify(x => x.PushOpacity(0.5), Times.Never); @@ -217,12 +166,11 @@ namespace Avalonia.Visuals.UnitTests.Rendering } [Fact] - public void Frame_Should_Create_Layer_For_Root() + public void Should_Create_Layer_For_Root() { var loop = new Mock(); var root = new TestRoot(); var rootLayer = new Mock(); - var dispatcher = new ImmediateDispatcher(); var sceneBuilder = new Mock(); sceneBuilder.Setup(x => x.UpdateAll(It.IsAny())) @@ -233,18 +181,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering }); var renderInterface = new Mock(); + var target = CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object); - var target = new DeferredRenderer( - root, - loop.Object, - sceneBuilder: sceneBuilder.Object, - dispatcher: dispatcher); - - target.Start(); - RunFrame(loop); - - var context = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null)); - context.Verify(x => x.CreateLayer(root.ClientSize)); + Assert.Single(target.Layers); } [Fact] @@ -269,49 +208,44 @@ namespace Avalonia.Visuals.UnitTests.Rendering root.Measure(Size.Infinity); root.Arrange(new Rect(root.DesiredSize)); - var rootLayer = CreateLayer(); - var borderLayer = CreateLayer(); - var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null)); - renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny())) - .Returns(rootLayer) - .Returns(borderLayer); - var loop = new Mock(); - var target = new DeferredRenderer( - root, - loop.Object, - dispatcher: new ImmediateDispatcher()); - root.Renderer = target; + var target = CreateTargetAndRunFrame(root, loop: loop); - target.Start(); - RunFrame(loop); + Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot)); - var rootContext = Mock.Get(rootLayer.CreateDrawingContext(null)); - var borderContext = Mock.Get(borderLayer.CreateDrawingContext(null)); var animation = new BehaviorSubject(0.5); - - rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); - rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once); - borderContext.Verify(x => x.FillRectangle(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - - rootContext.ResetCalls(); - borderContext.ResetCalls(); border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation); RunFrame(loop); - rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); - rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Never); - borderContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once); + Assert.Equal(new IVisual[] { root, border }, target.Layers.Select(x => x.LayerRoot)); - rootContext.ResetCalls(); - borderContext.ResetCalls(); animation.OnCompleted(); RunFrame(loop); - Mock.Get(borderLayer).Verify(x => x.Dispose()); - rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); - rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once); - borderContext.Verify(x => x.FillRectangle(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot)); + } + + private DeferredRenderer CreateTargetAndRunFrame( + TestRoot root, + Mock loop = null, + ISceneBuilder sceneBuilder = null, + IDispatcher dispatcher = null) + { + loop = loop ?? new Mock(); + var target = new DeferredRenderer( + root, + loop.Object, + sceneBuilder: sceneBuilder, + dispatcher: dispatcher ?? new ImmediateDispatcher()); + root.Renderer = target; + target.Start(); + RunFrame(loop); + return target; + } + + private Mock GetLayerContext(DeferredRenderer renderer, IControl layerRoot) + { + return Mock.Get(renderer.Layers[layerRoot].Bitmap.CreateDrawingContext(null)); } private void IgnoreFirstFrame(Mock loop, Mock sceneBuilder)