DeferredRendererTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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 root = new TestRoot();
  24. var dispatcher = new Mock<IDispatcher>();
  25. dispatcher.Setup(x => x.InvokeAsync(It.IsAny<Action>(), DispatcherPriority.Render))
  26. .Callback<Action, DispatcherPriority>((a, p) => a());
  27. CreateTargetAndRunFrame(root, dispatcher: dispatcher.Object);
  28. dispatcher.Verify(x =>
  29. x.InvokeAsync(
  30. It.Is<Action>(a => a.Method.Name == "UpdateScene"),
  31. DispatcherPriority.Render));
  32. }
  33. [Fact]
  34. public void First_Frame_Calls_SceneBuilder_UpdateAll()
  35. {
  36. var loop = new Mock<IRenderLoop>();
  37. var root = new TestRoot();
  38. var sceneBuilder = MockSceneBuilder(root);
  39. CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object);
  40. sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()));
  41. }
  42. [Fact]
  43. public void Frame_Does_Not_Call_SceneBuilder_If_No_Dirty_Controls()
  44. {
  45. var loop = new Mock<IRenderLoop>();
  46. var root = new TestRoot();
  47. var sceneBuilder = MockSceneBuilder(root);
  48. var target = new DeferredRenderer(
  49. root,
  50. loop.Object,
  51. sceneBuilder: sceneBuilder.Object);
  52. target.Start();
  53. IgnoreFirstFrame(loop, sceneBuilder);
  54. RunFrame(loop);
  55. sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()), Times.Never);
  56. sceneBuilder.Verify(x => x.Update(It.IsAny<Scene>(), It.IsAny<Visual>()), Times.Never);
  57. }
  58. [Fact]
  59. public void Should_Update_Dirty_Controls_In_Order()
  60. {
  61. var loop = new Mock<IRenderLoop>();
  62. var dispatcher = new ImmediateDispatcher();
  63. Border border;
  64. Decorator decorator;
  65. Canvas canvas;
  66. var root = new TestRoot
  67. {
  68. Child = decorator = new Decorator
  69. {
  70. Child = border = new Border
  71. {
  72. Child = canvas = new Canvas()
  73. }
  74. }
  75. };
  76. var sceneBuilder = MockSceneBuilder(root);
  77. var target = new DeferredRenderer(
  78. root,
  79. loop.Object,
  80. sceneBuilder: sceneBuilder.Object,
  81. dispatcher: dispatcher);
  82. target.Start();
  83. IgnoreFirstFrame(loop, sceneBuilder);
  84. target.AddDirty(border);
  85. target.AddDirty(canvas);
  86. target.AddDirty(root);
  87. target.AddDirty(decorator);
  88. var result = new List<IVisual>();
  89. sceneBuilder.Setup(x => x.Update(It.IsAny<Scene>(), It.IsAny<IVisual>()))
  90. .Callback<Scene, IVisual>((_, v) => result.Add(v));
  91. RunFrame(loop);
  92. Assert.Equal(new List<IVisual> { root, decorator, border, canvas }, result);
  93. }
  94. [Fact]
  95. public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity()
  96. {
  97. var root = new TestRoot
  98. {
  99. Width = 100,
  100. Height = 100,
  101. Child = new Border
  102. {
  103. Background = Brushes.Red,
  104. Opacity = 0.5,
  105. }
  106. };
  107. root.Measure(Size.Infinity);
  108. root.Arrange(new Rect(root.DesiredSize));
  109. var target = CreateTargetAndRunFrame(root);
  110. var context = GetLayerContext(target, root);
  111. var animation = new BehaviorSubject<double>(0.5);
  112. context.Verify(x => x.PushOpacity(0.5), Times.Once);
  113. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  114. context.Verify(x => x.PopOpacity(), Times.Once);
  115. }
  116. [Fact]
  117. public void Should_Not_Draw_Controls_With_0_Opacity()
  118. {
  119. var root = new TestRoot
  120. {
  121. Width = 100,
  122. Height = 100,
  123. Child = new Border
  124. {
  125. Background = Brushes.Red,
  126. Opacity = 0,
  127. Child = new Border
  128. {
  129. Background = Brushes.Green,
  130. }
  131. }
  132. };
  133. root.Measure(Size.Infinity);
  134. root.Arrange(new Rect(root.DesiredSize));
  135. var target = CreateTargetAndRunFrame(root);
  136. var context = GetLayerContext(target, root);
  137. var animation = new BehaviorSubject<double>(0.5);
  138. context.Verify(x => x.PushOpacity(0.5), Times.Never);
  139. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Never);
  140. context.Verify(x => x.PopOpacity(), Times.Never);
  141. }
  142. [Fact]
  143. public void Should_Push_Opacity_Mask()
  144. {
  145. var root = new TestRoot
  146. {
  147. Width = 100,
  148. Height = 100,
  149. Child = new Border
  150. {
  151. Background = Brushes.Red,
  152. OpacityMask = Brushes.Green,
  153. }
  154. };
  155. root.Measure(Size.Infinity);
  156. root.Arrange(new Rect(root.DesiredSize));
  157. var target = CreateTargetAndRunFrame(root);
  158. var context = GetLayerContext(target, root);
  159. var animation = new BehaviorSubject<double>(0.5);
  160. context.Verify(x => x.PushOpacityMask(Brushes.Green, new Rect(0, 0, 100, 100)), Times.Once);
  161. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  162. context.Verify(x => x.PopOpacityMask(), Times.Once);
  163. }
  164. [Fact]
  165. public void Should_Create_Layer_For_Root()
  166. {
  167. var loop = new Mock<IRenderLoop>();
  168. var root = new TestRoot();
  169. var rootLayer = new Mock<IRenderTargetBitmapImpl>();
  170. var sceneBuilder = new Mock<ISceneBuilder>();
  171. sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
  172. .Callback<Scene>(scene =>
  173. {
  174. scene.Size = root.ClientSize;
  175. scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
  176. });
  177. var renderInterface = new Mock<IPlatformRenderInterface>();
  178. var target = CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object);
  179. Assert.Single(target.Layers);
  180. }
  181. [Fact]
  182. public void Should_Create_And_Delete_Layers_For_Controls_With_Animated_Opacity()
  183. {
  184. Border border;
  185. var root = new TestRoot
  186. {
  187. Width = 100,
  188. Height = 100,
  189. Child = new Border
  190. {
  191. Background = Brushes.Red,
  192. Child = border = new Border
  193. {
  194. Background = Brushes.Green,
  195. Child = new Canvas(),
  196. Opacity = 0.9,
  197. }
  198. }
  199. };
  200. root.Measure(Size.Infinity);
  201. root.Arrange(new Rect(root.DesiredSize));
  202. var loop = new Mock<IRenderLoop>();
  203. var target = CreateTargetAndRunFrame(root, loop: loop);
  204. Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
  205. var animation = new BehaviorSubject<double>(0.5);
  206. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  207. RunFrame(loop);
  208. Assert.Equal(new IVisual[] { root, border }, target.Layers.Select(x => x.LayerRoot));
  209. animation.OnCompleted();
  210. RunFrame(loop);
  211. Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
  212. }
  213. [Fact]
  214. public void Should_Not_Create_Layer_For_Childless_Control_With_Animated_Opacity()
  215. {
  216. Border border;
  217. var root = new TestRoot
  218. {
  219. Width = 100,
  220. Height = 100,
  221. Child = new Border
  222. {
  223. Background = Brushes.Red,
  224. Child = border = new Border
  225. {
  226. Background = Brushes.Green,
  227. }
  228. }
  229. };
  230. var animation = new BehaviorSubject<double>(0.5);
  231. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  232. root.Measure(Size.Infinity);
  233. root.Arrange(new Rect(root.DesiredSize));
  234. var loop = new Mock<IRenderLoop>();
  235. var target = CreateTargetAndRunFrame(root, loop: loop);
  236. Assert.Single(target.Layers);
  237. }
  238. [Fact]
  239. public void Should_Not_Push_Opacity_For_Transparent_Layer_Root_Control()
  240. {
  241. Border border;
  242. var root = new TestRoot
  243. {
  244. Width = 100,
  245. Height = 100,
  246. Child = border = new Border
  247. {
  248. Background = Brushes.Red,
  249. Child = new Canvas(),
  250. }
  251. };
  252. var animation = new BehaviorSubject<double>(0.5);
  253. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  254. root.Measure(Size.Infinity);
  255. root.Arrange(new Rect(root.DesiredSize));
  256. var target = CreateTargetAndRunFrame(root);
  257. var context = GetLayerContext(target, border);
  258. context.Verify(x => x.PushOpacity(0.5), Times.Never);
  259. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  260. context.Verify(x => x.PopOpacity(), Times.Never);
  261. }
  262. [Fact]
  263. public void Should_Draw_Transparent_Layer_With_Correct_Opacity()
  264. {
  265. Border border;
  266. var root = new TestRoot
  267. {
  268. Width = 100,
  269. Height = 100,
  270. Child = border = new Border
  271. {
  272. Background = Brushes.Red,
  273. Child = new Canvas(),
  274. }
  275. };
  276. var animation = new BehaviorSubject<double>(0.5);
  277. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  278. root.Measure(Size.Infinity);
  279. root.Arrange(new Rect(root.DesiredSize));
  280. var target = CreateTargetAndRunFrame(root);
  281. var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null));
  282. var borderLayer = target.Layers[border].Bitmap;
  283. context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>()));
  284. }
  285. private DeferredRenderer CreateTargetAndRunFrame(
  286. TestRoot root,
  287. Mock<IRenderLoop> loop = null,
  288. ISceneBuilder sceneBuilder = null,
  289. IDispatcher dispatcher = null)
  290. {
  291. loop = loop ?? new Mock<IRenderLoop>();
  292. var target = new DeferredRenderer(
  293. root,
  294. loop.Object,
  295. sceneBuilder: sceneBuilder,
  296. dispatcher: dispatcher ?? new ImmediateDispatcher());
  297. root.Renderer = target;
  298. target.Start();
  299. RunFrame(loop);
  300. return target;
  301. }
  302. private Mock<IDrawingContextImpl> GetLayerContext(DeferredRenderer renderer, IControl layerRoot)
  303. {
  304. return Mock.Get(renderer.Layers[layerRoot].Bitmap.CreateDrawingContext(null));
  305. }
  306. private void IgnoreFirstFrame(Mock<IRenderLoop> loop, Mock<ISceneBuilder> sceneBuilder)
  307. {
  308. RunFrame(loop);
  309. sceneBuilder.ResetCalls();
  310. }
  311. private void RunFrame(Mock<IRenderLoop> loop)
  312. {
  313. loop.Raise(x => x.Tick += null, EventArgs.Empty);
  314. }
  315. private IRenderTargetBitmapImpl CreateLayer()
  316. {
  317. return Mock.Of<IRenderTargetBitmapImpl>(x =>
  318. x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>()) == Mock.Of<IDrawingContextImpl>());
  319. }
  320. private Mock<ISceneBuilder> MockSceneBuilder(IRenderRoot root)
  321. {
  322. var result = new Mock<ISceneBuilder>();
  323. result.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
  324. .Callback<Scene>(x => x.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize)));
  325. return result;
  326. }
  327. }
  328. }