123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779 |
- 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,
- 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,
- dispatcher: dispatcher);
- target.Start();
- IgnoreFirstFrame(target, sceneBuilder);
- target.AddDirty(border);
- target.AddDirty(canvas);
- target.AddDirty(root);
- target.AddDirty(decorator);
- var result = new List<IVisual>();
- sceneBuilder.Setup(x => x.Update(It.IsAny<Scene>(), It.IsAny<IVisual>()))
- .Callback<Scene, IVisual>((_, v) => result.Add(v));
- RunFrame(target);
- Assert.Equal(new List<IVisual> { 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- dispatcher: dispatcher);
- var otherSceneBuilder = new SceneBuilder();
- var otherTarget = new DeferredRenderer(
- otherRoot,
- loop.Object,
- sceneBuilder: otherSceneBuilder,
- 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 IVisual[] { 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,
- dispatcher: dispatcher);
- root.Renderer = target;
- target.Start();
- RunFrame(target);
- return target;
- }
- private static Mock<IDrawingContextImpl> GetLayerContext(DeferredRenderer renderer, IControl 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(root).Dirty.Add(new Rect(root.ClientSize)));
- return result;
- }
- }
- }
|