DeferredRendererTests.cs 13 KB

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