DeferredRendererTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reactive.Subjects;
  5. using Avalonia.Controls;
  6. using Avalonia.Data;
  7. using Avalonia.Media;
  8. using Avalonia.Platform;
  9. using Avalonia.Rendering;
  10. using Avalonia.Rendering.SceneGraph;
  11. using Avalonia.Threading;
  12. using Avalonia.UnitTests;
  13. using Avalonia.VisualTree;
  14. using Moq;
  15. using Xunit;
  16. namespace Avalonia.Visuals.UnitTests.Rendering
  17. {
  18. public class DeferredRendererTests
  19. {
  20. [Fact]
  21. public void First_Frame_Calls_UpdateScene_On_Dispatcher()
  22. {
  23. var loop = new Mock<IRenderLoop>();
  24. var root = new TestRoot();
  25. var dispatcher = new Mock<IDispatcher>();
  26. dispatcher.Setup(x => x.InvokeAsync(It.IsAny<Action>(), DispatcherPriority.Render))
  27. .Callback<Action, DispatcherPriority>((a, p) => a());
  28. var target = new DeferredRenderer(
  29. root,
  30. loop.Object,
  31. sceneBuilder: MockSceneBuilder(root).Object,
  32. dispatcher: dispatcher.Object);
  33. target.Start();
  34. RunFrame(loop);
  35. #if !NETCOREAPP1_1 // Delegate.Method is not available in netcoreapp1.1
  36. dispatcher.Verify(x =>
  37. x.InvokeAsync(
  38. It.Is<Action>(a => a.Method.Name == "UpdateScene"),
  39. DispatcherPriority.Render));
  40. #endif
  41. }
  42. [Fact]
  43. public void First_Frame_Calls_SceneBuilder_UpdateAll()
  44. {
  45. var loop = new Mock<IRenderLoop>();
  46. var root = new TestRoot();
  47. var sceneBuilder = MockSceneBuilder(root);
  48. var dispatcher = new ImmediateDispatcher();
  49. var target = new DeferredRenderer(
  50. root,
  51. loop.Object,
  52. sceneBuilder: sceneBuilder.Object,
  53. dispatcher: dispatcher);
  54. target.Start();
  55. RunFrame(loop);
  56. sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()));
  57. }
  58. [Fact]
  59. public void Frame_Does_Not_Call_SceneBuilder_If_No_Dirty_Controls()
  60. {
  61. var loop = new Mock<IRenderLoop>();
  62. var root = new TestRoot();
  63. var sceneBuilder = MockSceneBuilder(root);
  64. var dispatcher = new ImmediateDispatcher();
  65. var target = new DeferredRenderer(
  66. root,
  67. loop.Object,
  68. sceneBuilder: sceneBuilder.Object,
  69. dispatcher: dispatcher);
  70. target.Start();
  71. IgnoreFirstFrame(loop, sceneBuilder);
  72. RunFrame(loop);
  73. sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()), Times.Never);
  74. sceneBuilder.Verify(x => x.Update(It.IsAny<Scene>(), It.IsAny<Visual>()), Times.Never);
  75. }
  76. [Fact]
  77. public void Should_Update_Dirty_Controls_In_Order()
  78. {
  79. var loop = new Mock<IRenderLoop>();
  80. var dispatcher = new ImmediateDispatcher();
  81. Border border;
  82. Decorator decorator;
  83. Canvas canvas;
  84. var root = new TestRoot
  85. {
  86. Child = decorator = new Decorator
  87. {
  88. Child = border = new Border
  89. {
  90. Child = canvas = new Canvas()
  91. }
  92. }
  93. };
  94. var sceneBuilder = MockSceneBuilder(root);
  95. var target = new DeferredRenderer(
  96. root,
  97. loop.Object,
  98. sceneBuilder: sceneBuilder.Object,
  99. dispatcher: dispatcher);
  100. target.Start();
  101. IgnoreFirstFrame(loop, sceneBuilder);
  102. target.AddDirty(border);
  103. target.AddDirty(canvas);
  104. target.AddDirty(root);
  105. target.AddDirty(decorator);
  106. var result = new List<IVisual>();
  107. sceneBuilder.Setup(x => x.Update(It.IsAny<Scene>(), It.IsAny<IVisual>()))
  108. .Callback<Scene, IVisual>((_, v) => result.Add(v));
  109. RunFrame(loop);
  110. Assert.Equal(new List<IVisual> { root, decorator, border, canvas }, result);
  111. }
  112. [Fact]
  113. public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity()
  114. {
  115. var root = new TestRoot
  116. {
  117. Width = 100,
  118. Height = 100,
  119. Child = new Border
  120. {
  121. Background = Brushes.Red,
  122. Opacity = 0.5,
  123. }
  124. };
  125. root.Measure(Size.Infinity);
  126. root.Arrange(new Rect(root.DesiredSize));
  127. var rootLayer = CreateLayer();
  128. var borderLayer = CreateLayer();
  129. var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null));
  130. renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny<Size>()))
  131. .Returns(rootLayer)
  132. .Returns(borderLayer);
  133. var loop = new Mock<IRenderLoop>();
  134. var target = new DeferredRenderer(
  135. root,
  136. loop.Object,
  137. dispatcher: new ImmediateDispatcher());
  138. root.Renderer = target;
  139. target.Start();
  140. RunFrame(loop);
  141. var context = Mock.Get(rootLayer.CreateDrawingContext(null));
  142. var animation = new BehaviorSubject<double>(0.5);
  143. context.Verify(x => x.PushOpacity(0.5), Times.Once);
  144. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  145. context.Verify(x => x.PopOpacity(), Times.Once);
  146. }
  147. [Fact]
  148. public void Should_Not_Draw_Controls_With_0_Opacity()
  149. {
  150. var root = new TestRoot
  151. {
  152. Width = 100,
  153. Height = 100,
  154. Child = new Border
  155. {
  156. Background = Brushes.Red,
  157. Opacity = 0,
  158. Child = new Border
  159. {
  160. Background = Brushes.Green,
  161. }
  162. }
  163. };
  164. root.Measure(Size.Infinity);
  165. root.Arrange(new Rect(root.DesiredSize));
  166. var rootLayer = CreateLayer();
  167. var borderLayer = CreateLayer();
  168. var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null));
  169. renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny<Size>()))
  170. .Returns(rootLayer)
  171. .Returns(borderLayer);
  172. var loop = new Mock<IRenderLoop>();
  173. var target = new DeferredRenderer(
  174. root,
  175. loop.Object,
  176. dispatcher: new ImmediateDispatcher());
  177. root.Renderer = target;
  178. target.Start();
  179. RunFrame(loop);
  180. var context = Mock.Get(rootLayer.CreateDrawingContext(null));
  181. var animation = new BehaviorSubject<double>(0.5);
  182. context.Verify(x => x.PushOpacity(0.5), Times.Never);
  183. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Never);
  184. context.Verify(x => x.PopOpacity(), Times.Never);
  185. }
  186. [Fact]
  187. public void Frame_Should_Create_Layer_For_Root()
  188. {
  189. var loop = new Mock<IRenderLoop>();
  190. var root = new TestRoot();
  191. var rootLayer = new Mock<IRenderTargetBitmapImpl>();
  192. var dispatcher = new ImmediateDispatcher();
  193. var sceneBuilder = new Mock<ISceneBuilder>();
  194. sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
  195. .Callback<Scene>(scene =>
  196. {
  197. scene.Size = root.ClientSize;
  198. scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
  199. });
  200. var renderInterface = new Mock<IPlatformRenderInterface>();
  201. var target = new DeferredRenderer(
  202. root,
  203. loop.Object,
  204. sceneBuilder: sceneBuilder.Object,
  205. dispatcher: dispatcher);
  206. target.Start();
  207. RunFrame(loop);
  208. var context = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null));
  209. context.Verify(x => x.CreateLayer(root.ClientSize));
  210. }
  211. [Fact]
  212. public void Should_Create_And_Delete_Layers_For_Controls_With_Animated_Opacity()
  213. {
  214. Border border;
  215. var root = new TestRoot
  216. {
  217. Width = 100,
  218. Height = 100,
  219. Child = new Border
  220. {
  221. Background = Brushes.Red,
  222. Child = border = new Border
  223. {
  224. Background = Brushes.Green,
  225. }
  226. }
  227. };
  228. root.Measure(Size.Infinity);
  229. root.Arrange(new Rect(root.DesiredSize));
  230. var rootLayer = CreateLayer();
  231. var borderLayer = CreateLayer();
  232. var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null));
  233. renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny<Size>()))
  234. .Returns(rootLayer)
  235. .Returns(borderLayer);
  236. var loop = new Mock<IRenderLoop>();
  237. var target = new DeferredRenderer(
  238. root,
  239. loop.Object,
  240. dispatcher: new ImmediateDispatcher());
  241. root.Renderer = target;
  242. target.Start();
  243. RunFrame(loop);
  244. var rootContext = Mock.Get(rootLayer.CreateDrawingContext(null));
  245. var borderContext = Mock.Get(borderLayer.CreateDrawingContext(null));
  246. var animation = new BehaviorSubject<double>(0.5);
  247. rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  248. rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
  249. borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never);
  250. rootContext.ResetCalls();
  251. borderContext.ResetCalls();
  252. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  253. RunFrame(loop);
  254. rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  255. rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Never);
  256. borderContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
  257. rootContext.ResetCalls();
  258. borderContext.ResetCalls();
  259. animation.OnCompleted();
  260. RunFrame(loop);
  261. Mock.Get(borderLayer).Verify(x => x.Dispose());
  262. rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  263. rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
  264. borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never);
  265. }
  266. private void IgnoreFirstFrame(Mock<IRenderLoop> loop, Mock<ISceneBuilder> sceneBuilder)
  267. {
  268. RunFrame(loop);
  269. sceneBuilder.ResetCalls();
  270. }
  271. private void RunFrame(Mock<IRenderLoop> loop)
  272. {
  273. loop.Raise(x => x.Tick += null, EventArgs.Empty);
  274. }
  275. private IRenderTargetBitmapImpl CreateLayer()
  276. {
  277. return Mock.Of<IRenderTargetBitmapImpl>(x =>
  278. x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>()) == Mock.Of<IDrawingContextImpl>());
  279. }
  280. private Mock<ISceneBuilder> MockSceneBuilder(IRenderRoot root)
  281. {
  282. var result = new Mock<ISceneBuilder>();
  283. result.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
  284. .Callback<Scene>(x => x.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize)));
  285. return result;
  286. }
  287. }
  288. }