DeferredRendererTests.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reactive.Subjects;
  5. using System.Threading.Tasks;
  6. using Avalonia.Controls;
  7. using Avalonia.Data;
  8. using Avalonia.Media;
  9. using Avalonia.Platform;
  10. using Avalonia.Rendering;
  11. using Avalonia.Rendering.SceneGraph;
  12. using Avalonia.Threading;
  13. using Avalonia.UnitTests;
  14. using Avalonia.Visuals.Media.Imaging;
  15. using Avalonia.VisualTree;
  16. using Moq;
  17. using Xunit;
  18. namespace Avalonia.Visuals.UnitTests.Rendering
  19. {
  20. public class DeferredRendererTests
  21. {
  22. [Fact]
  23. public void First_Frame_Calls_SceneBuilder_UpdateAll()
  24. {
  25. var root = new TestRoot();
  26. var sceneBuilder = MockSceneBuilder(root);
  27. CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object);
  28. sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()));
  29. }
  30. [Fact]
  31. public void Frame_Does_Not_Call_SceneBuilder_If_No_Dirty_Controls()
  32. {
  33. var dispatcher = new ImmediateDispatcher();
  34. var loop = new Mock<IRenderLoop>();
  35. var root = new TestRoot();
  36. var sceneBuilder = MockSceneBuilder(root);
  37. var target = new DeferredRenderer(
  38. root,
  39. loop.Object,
  40. sceneBuilder: sceneBuilder.Object);
  41. target.Start();
  42. IgnoreFirstFrame(target, sceneBuilder);
  43. RunFrame(target);
  44. sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>()), Times.Never);
  45. sceneBuilder.Verify(x => x.Update(It.IsAny<Scene>(), It.IsAny<Visual>()), Times.Never);
  46. }
  47. [Fact]
  48. public void Should_Update_Dirty_Controls_In_Order()
  49. {
  50. var dispatcher = new ImmediateDispatcher();
  51. var loop = new Mock<IRenderLoop>();
  52. Border border;
  53. Decorator decorator;
  54. Canvas canvas;
  55. var root = new TestRoot
  56. {
  57. Child = decorator = new Decorator
  58. {
  59. Child = border = new Border
  60. {
  61. Child = canvas = new Canvas()
  62. }
  63. }
  64. };
  65. var sceneBuilder = MockSceneBuilder(root);
  66. var target = new DeferredRenderer(
  67. root,
  68. loop.Object,
  69. sceneBuilder: sceneBuilder.Object,
  70. dispatcher: dispatcher);
  71. target.Start();
  72. IgnoreFirstFrame(target, sceneBuilder);
  73. target.AddDirty(border);
  74. target.AddDirty(canvas);
  75. target.AddDirty(root);
  76. target.AddDirty(decorator);
  77. var result = new List<IVisual>();
  78. sceneBuilder.Setup(x => x.Update(It.IsAny<Scene>(), It.IsAny<IVisual>()))
  79. .Callback<Scene, IVisual>((_, v) => result.Add(v));
  80. RunFrame(target);
  81. Assert.Equal(new List<IVisual> { root, decorator, border, canvas }, result);
  82. }
  83. [Fact]
  84. public void Should_Update_VisualNode_Order_On_Child_Remove_Insert()
  85. {
  86. var dispatcher = new ImmediateDispatcher();
  87. var loop = new Mock<IRenderLoop>();
  88. StackPanel stack;
  89. Canvas canvas1;
  90. Canvas canvas2;
  91. var root = new TestRoot
  92. {
  93. Child = stack = new StackPanel
  94. {
  95. Children=
  96. {
  97. (canvas1 = new Canvas()),
  98. (canvas2 = new Canvas()),
  99. }
  100. }
  101. };
  102. var sceneBuilder = new SceneBuilder();
  103. var target = new DeferredRenderer(
  104. root,
  105. loop.Object,
  106. sceneBuilder: sceneBuilder,
  107. dispatcher: dispatcher);
  108. root.Renderer = target;
  109. target.Start();
  110. RunFrame(target);
  111. stack.Children.Remove(canvas2);
  112. stack.Children.Insert(0, canvas2);
  113. RunFrame(target);
  114. var scene = target.UnitTestScene();
  115. var stackNode = scene.FindNode(stack);
  116. Assert.Same(stackNode.Children[0].Visual, canvas2);
  117. Assert.Same(stackNode.Children[1].Visual, canvas1);
  118. }
  119. [Fact]
  120. public void Should_Update_VisualNode_Order_On_Child_Move()
  121. {
  122. var dispatcher = new ImmediateDispatcher();
  123. var loop = new Mock<IRenderLoop>();
  124. StackPanel stack;
  125. Canvas canvas1;
  126. Canvas canvas2;
  127. var root = new TestRoot
  128. {
  129. Child = stack = new StackPanel
  130. {
  131. Children =
  132. {
  133. (canvas1 = new Canvas()),
  134. (canvas2 = new Canvas()),
  135. }
  136. }
  137. };
  138. var sceneBuilder = new SceneBuilder();
  139. var target = new DeferredRenderer(
  140. root,
  141. loop.Object,
  142. sceneBuilder: sceneBuilder,
  143. dispatcher: dispatcher);
  144. root.Renderer = target;
  145. target.Start();
  146. RunFrame(target);
  147. stack.Children.Move(1, 0);
  148. RunFrame(target);
  149. var scene = target.UnitTestScene();
  150. var stackNode = scene.FindNode(stack);
  151. Assert.Same(stackNode.Children[0].Visual, canvas2);
  152. Assert.Same(stackNode.Children[1].Visual, canvas1);
  153. }
  154. [Fact]
  155. public void Should_Update_VisualNode_Order_On_ZIndex_Change()
  156. {
  157. var dispatcher = new ImmediateDispatcher();
  158. var loop = new Mock<IRenderLoop>();
  159. StackPanel stack;
  160. Canvas canvas1;
  161. Canvas canvas2;
  162. var root = new TestRoot
  163. {
  164. Child = stack = new StackPanel
  165. {
  166. Children =
  167. {
  168. (canvas1 = new Canvas { ZIndex = 1 }),
  169. (canvas2 = new Canvas { ZIndex = 2 }),
  170. }
  171. }
  172. };
  173. var sceneBuilder = new SceneBuilder();
  174. var target = new DeferredRenderer(
  175. root,
  176. loop.Object,
  177. sceneBuilder: sceneBuilder,
  178. dispatcher: dispatcher);
  179. root.Renderer = target;
  180. target.Start();
  181. RunFrame(target);
  182. canvas1.ZIndex = 3;
  183. RunFrame(target);
  184. var scene = target.UnitTestScene();
  185. var stackNode = scene.FindNode(stack);
  186. Assert.Same(stackNode.Children[0].Visual, canvas2);
  187. Assert.Same(stackNode.Children[1].Visual, canvas1);
  188. }
  189. [Fact]
  190. public void Should_Update_VisualNode_Order_On_ZIndex_Change_With_Dirty_Ancestor()
  191. {
  192. var dispatcher = new ImmediateDispatcher();
  193. var loop = new Mock<IRenderLoop>();
  194. StackPanel stack;
  195. Canvas canvas1;
  196. Canvas canvas2;
  197. var root = new TestRoot
  198. {
  199. Child = stack = new StackPanel
  200. {
  201. Children =
  202. {
  203. (canvas1 = new Canvas { ZIndex = 1 }),
  204. (canvas2 = new Canvas { ZIndex = 2 }),
  205. }
  206. }
  207. };
  208. var sceneBuilder = new SceneBuilder();
  209. var target = new DeferredRenderer(
  210. root,
  211. loop.Object,
  212. sceneBuilder: sceneBuilder,
  213. dispatcher: dispatcher);
  214. root.Renderer = target;
  215. target.Start();
  216. RunFrame(target);
  217. root.InvalidateVisual();
  218. canvas1.ZIndex = 3;
  219. RunFrame(target);
  220. var scene = target.UnitTestScene();
  221. var stackNode = scene.FindNode(stack);
  222. Assert.Same(stackNode.Children[0].Visual, canvas2);
  223. Assert.Same(stackNode.Children[1].Visual, canvas1);
  224. }
  225. [Fact]
  226. public void Should_Update_VisualNodes_When_Child_Moved_To_New_Parent()
  227. {
  228. var dispatcher = new ImmediateDispatcher();
  229. var loop = new Mock<IRenderLoop>();
  230. Decorator moveFrom;
  231. Decorator moveTo;
  232. Canvas moveMe;
  233. var root = new TestRoot
  234. {
  235. Child = new StackPanel
  236. {
  237. Children =
  238. {
  239. (moveFrom = new Decorator
  240. {
  241. Child = moveMe = new Canvas(),
  242. }),
  243. (moveTo = new Decorator()),
  244. }
  245. }
  246. };
  247. var sceneBuilder = new SceneBuilder();
  248. var target = new DeferredRenderer(
  249. root,
  250. loop.Object,
  251. sceneBuilder: sceneBuilder,
  252. dispatcher: dispatcher);
  253. root.Renderer = target;
  254. target.Start();
  255. RunFrame(target);
  256. moveFrom.Child = null;
  257. moveTo.Child = moveMe;
  258. RunFrame(target);
  259. var scene = target.UnitTestScene();
  260. var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
  261. var moveToNode = (VisualNode)scene.FindNode(moveTo);
  262. Assert.Empty(moveFromNode.Children);
  263. Assert.Equal(1, moveToNode.Children.Count);
  264. Assert.Same(moveMe, moveToNode.Children[0].Visual);
  265. }
  266. [Fact]
  267. public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity()
  268. {
  269. var root = new TestRoot
  270. {
  271. Width = 100,
  272. Height = 100,
  273. Child = new Border
  274. {
  275. Background = Brushes.Red,
  276. Opacity = 0.5,
  277. }
  278. };
  279. root.Measure(Size.Infinity);
  280. root.Arrange(new Rect(root.DesiredSize));
  281. var target = CreateTargetAndRunFrame(root);
  282. var context = GetLayerContext(target, root);
  283. var animation = new BehaviorSubject<double>(0.5);
  284. context.Verify(x => x.PushOpacity(0.5), Times.Once);
  285. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  286. context.Verify(x => x.PopOpacity(), Times.Once);
  287. }
  288. [Fact]
  289. public void Should_Not_Draw_Controls_With_0_Opacity()
  290. {
  291. var root = new TestRoot
  292. {
  293. Width = 100,
  294. Height = 100,
  295. Child = new Border
  296. {
  297. Background = Brushes.Red,
  298. Opacity = 0,
  299. Child = new Border
  300. {
  301. Background = Brushes.Green,
  302. }
  303. }
  304. };
  305. root.Measure(Size.Infinity);
  306. root.Arrange(new Rect(root.DesiredSize));
  307. var target = CreateTargetAndRunFrame(root);
  308. var context = GetLayerContext(target, root);
  309. var animation = new BehaviorSubject<double>(0.5);
  310. context.Verify(x => x.PushOpacity(0.5), Times.Never);
  311. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Never);
  312. context.Verify(x => x.PopOpacity(), Times.Never);
  313. }
  314. [Fact]
  315. public void Should_Push_Opacity_Mask()
  316. {
  317. var root = new TestRoot
  318. {
  319. Width = 100,
  320. Height = 100,
  321. Child = new Border
  322. {
  323. Background = Brushes.Red,
  324. OpacityMask = Brushes.Green,
  325. }
  326. };
  327. root.Measure(Size.Infinity);
  328. root.Arrange(new Rect(root.DesiredSize));
  329. var target = CreateTargetAndRunFrame(root);
  330. var context = GetLayerContext(target, root);
  331. var animation = new BehaviorSubject<double>(0.5);
  332. context.Verify(x => x.PushOpacityMask(Brushes.Green, new Rect(0, 0, 100, 100)), Times.Once);
  333. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  334. context.Verify(x => x.PopOpacityMask(), Times.Once);
  335. }
  336. [Fact]
  337. public void Should_Create_Layer_For_Root()
  338. {
  339. var root = new TestRoot();
  340. var rootLayer = new Mock<IRenderTargetBitmapImpl>();
  341. var sceneBuilder = new Mock<ISceneBuilder>();
  342. sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
  343. .Callback<Scene>(scene =>
  344. {
  345. scene.Size = root.ClientSize;
  346. scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
  347. });
  348. var renderInterface = new Mock<IPlatformRenderInterface>();
  349. var target = CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object);
  350. Assert.Single(target.Layers);
  351. }
  352. [Fact]
  353. public void Should_Create_And_Delete_Layers_For_Controls_With_Animated_Opacity()
  354. {
  355. Border border;
  356. var root = new TestRoot
  357. {
  358. Width = 100,
  359. Height = 100,
  360. Child = new Border
  361. {
  362. Background = Brushes.Red,
  363. Child = border = new Border
  364. {
  365. Background = Brushes.Green,
  366. Child = new Canvas(),
  367. Opacity = 0.9,
  368. }
  369. }
  370. };
  371. root.Measure(Size.Infinity);
  372. root.Arrange(new Rect(root.DesiredSize));
  373. var timer = new Mock<IRenderTimer>();
  374. var target = CreateTargetAndRunFrame(root, timer);
  375. Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
  376. var animation = new BehaviorSubject<double>(0.5);
  377. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  378. RunFrame(target);
  379. Assert.Equal(new IVisual[] { root, border }, target.Layers.Select(x => x.LayerRoot));
  380. animation.OnCompleted();
  381. RunFrame(target);
  382. Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
  383. }
  384. [Fact]
  385. public void Should_Not_Create_Layer_For_Childless_Control_With_Animated_Opacity()
  386. {
  387. Border border;
  388. var root = new TestRoot
  389. {
  390. Width = 100,
  391. Height = 100,
  392. Child = new Border
  393. {
  394. Background = Brushes.Red,
  395. Child = border = new Border
  396. {
  397. Background = Brushes.Green,
  398. }
  399. }
  400. };
  401. var animation = new BehaviorSubject<double>(0.5);
  402. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  403. root.Measure(Size.Infinity);
  404. root.Arrange(new Rect(root.DesiredSize));
  405. var timer = new Mock<IRenderTimer>();
  406. var target = CreateTargetAndRunFrame(root, timer);
  407. Assert.Single(target.Layers);
  408. }
  409. [Fact]
  410. public void Should_Not_Push_Opacity_For_Transparent_Layer_Root_Control()
  411. {
  412. Border border;
  413. var root = new TestRoot
  414. {
  415. Width = 100,
  416. Height = 100,
  417. Child = border = new Border
  418. {
  419. Background = Brushes.Red,
  420. Child = new Canvas(),
  421. }
  422. };
  423. var animation = new BehaviorSubject<double>(0.5);
  424. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  425. root.Measure(Size.Infinity);
  426. root.Arrange(new Rect(root.DesiredSize));
  427. var target = CreateTargetAndRunFrame(root);
  428. var context = GetLayerContext(target, border);
  429. context.Verify(x => x.PushOpacity(0.5), Times.Never);
  430. context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
  431. context.Verify(x => x.PopOpacity(), Times.Never);
  432. }
  433. [Fact]
  434. public void Should_Draw_Transparent_Layer_With_Correct_Opacity()
  435. {
  436. Border border;
  437. var root = new TestRoot
  438. {
  439. Width = 100,
  440. Height = 100,
  441. Child = border = new Border
  442. {
  443. Background = Brushes.Red,
  444. Child = new Canvas(),
  445. }
  446. };
  447. var animation = new BehaviorSubject<double>(0.5);
  448. border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
  449. root.Measure(Size.Infinity);
  450. root.Arrange(new Rect(root.DesiredSize));
  451. var target = CreateTargetAndRunFrame(root);
  452. var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null));
  453. var borderLayer = target.Layers[border].Bitmap;
  454. context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>(), BitmapInterpolationMode.Default));
  455. }
  456. [Fact]
  457. public void Can_Dirty_Control_In_SceneInvalidated()
  458. {
  459. Border border1;
  460. Border border2;
  461. var root = new TestRoot
  462. {
  463. Width = 100,
  464. Height = 100,
  465. Child = new StackPanel
  466. {
  467. Children =
  468. {
  469. (border1 = new Border
  470. {
  471. Background = Brushes.Red,
  472. Child = new Canvas(),
  473. }),
  474. (border2 = new Border
  475. {
  476. Background = Brushes.Red,
  477. Child = new Canvas(),
  478. }),
  479. }
  480. }
  481. };
  482. root.Measure(Size.Infinity);
  483. root.Arrange(new Rect(root.DesiredSize));
  484. var target = CreateTargetAndRunFrame(root);
  485. var invalidated = false;
  486. target.SceneInvalidated += (s, e) =>
  487. {
  488. invalidated = true;
  489. target.AddDirty(border2);
  490. };
  491. target.AddDirty(border1);
  492. target.Paint(new Rect(root.DesiredSize));
  493. Assert.True(invalidated);
  494. Assert.True(((IRenderLoopTask)target).NeedsUpdate);
  495. }
  496. private DeferredRenderer CreateTargetAndRunFrame(
  497. TestRoot root,
  498. Mock<IRenderTimer> timer = null,
  499. ISceneBuilder sceneBuilder = null,
  500. IDispatcher dispatcher = null)
  501. {
  502. timer = timer ?? new Mock<IRenderTimer>();
  503. dispatcher = dispatcher ?? new ImmediateDispatcher();
  504. var target = new DeferredRenderer(
  505. root,
  506. new RenderLoop(timer.Object, dispatcher),
  507. sceneBuilder: sceneBuilder,
  508. dispatcher: dispatcher);
  509. root.Renderer = target;
  510. target.Start();
  511. RunFrame(target);
  512. return target;
  513. }
  514. private Mock<IDrawingContextImpl> GetLayerContext(DeferredRenderer renderer, IControl layerRoot)
  515. {
  516. return Mock.Get(renderer.Layers[layerRoot].Bitmap.Item.CreateDrawingContext(null));
  517. }
  518. private void IgnoreFirstFrame(IRenderLoopTask task, Mock<ISceneBuilder> sceneBuilder)
  519. {
  520. RunFrame(task);
  521. sceneBuilder.ResetCalls();
  522. }
  523. private void RunFrame(IRenderLoopTask task)
  524. {
  525. task.Update(TimeSpan.Zero);
  526. task.Render();
  527. }
  528. private IRenderTargetBitmapImpl CreateLayer()
  529. {
  530. return Mock.Of<IRenderTargetBitmapImpl>(x =>
  531. x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>()) == Mock.Of<IDrawingContextImpl>());
  532. }
  533. private Mock<ISceneBuilder> MockSceneBuilder(IRenderRoot root)
  534. {
  535. var result = new Mock<ISceneBuilder>();
  536. result.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
  537. .Callback<Scene>(x => x.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize)));
  538. return result;
  539. }
  540. }
  541. }