DeferredRenderer.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using Avalonia.Media;
  5. using Avalonia.Platform;
  6. using Avalonia.Rendering.SceneGraph;
  7. using Avalonia.Threading;
  8. using Avalonia.VisualTree;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using Avalonia.Media.Immutable;
  12. using System.Threading;
  13. using System.Linq;
  14. using Avalonia.Utilities;
  15. namespace Avalonia.Rendering
  16. {
  17. /// <summary>
  18. /// A renderer which renders the state of the visual tree to an intermediate scene graph
  19. /// representation which is then rendered on a rendering thread.
  20. /// </summary>
  21. public class DeferredRenderer : RendererBase, IRenderer, IVisualBrushRenderer
  22. {
  23. private readonly IDispatcher _dispatcher;
  24. private readonly IRenderLoop _renderLoop;
  25. private readonly IVisual _root;
  26. private readonly ISceneBuilder _sceneBuilder;
  27. private bool _running;
  28. private volatile IRef<Scene> _scene;
  29. private DirtyVisuals _dirty;
  30. private IRef<IRenderTargetBitmapImpl> _overlay;
  31. private bool _updateQueued;
  32. private object _rendering = new object();
  33. private int _lastSceneId = -1;
  34. private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
  35. private IRef<IDrawOperation> _currentDraw;
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
  38. /// </summary>
  39. /// <param name="root">The control to render.</param>
  40. /// <param name="renderLoop">The render loop.</param>
  41. /// <param name="sceneBuilder">The scene builder to use. Optional.</param>
  42. /// <param name="dispatcher">The dispatcher to use. Optional.</param>
  43. public DeferredRenderer(
  44. IRenderRoot root,
  45. IRenderLoop renderLoop,
  46. ISceneBuilder sceneBuilder = null,
  47. IDispatcher dispatcher = null)
  48. {
  49. Contract.Requires<ArgumentNullException>(root != null);
  50. _dispatcher = dispatcher ?? Dispatcher.UIThread;
  51. _root = root;
  52. _sceneBuilder = sceneBuilder ?? new SceneBuilder();
  53. Layers = new RenderLayers();
  54. _renderLoop = renderLoop;
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
  58. /// </summary>
  59. /// <param name="root">The control to render.</param>
  60. /// <param name="renderTarget">The render target.</param>
  61. /// <param name="sceneBuilder">The scene builder to use. Optional.</param>
  62. /// <remarks>
  63. /// This constructor is intended to be used for unit testing.
  64. /// </remarks>
  65. public DeferredRenderer(
  66. IVisual root,
  67. IRenderTarget renderTarget,
  68. ISceneBuilder sceneBuilder = null)
  69. {
  70. Contract.Requires<ArgumentNullException>(root != null);
  71. Contract.Requires<ArgumentNullException>(renderTarget != null);
  72. _root = root;
  73. RenderTarget = renderTarget;
  74. _sceneBuilder = sceneBuilder ?? new SceneBuilder();
  75. Layers = new RenderLayers();
  76. }
  77. /// <inheritdoc/>
  78. public bool DrawFps { get; set; }
  79. /// <inheritdoc/>
  80. public bool DrawDirtyRects { get; set; }
  81. /// <summary>
  82. /// Gets or sets a path to which rendered frame should be rendered for debugging.
  83. /// </summary>
  84. public string DebugFramesPath { get; set; }
  85. /// <summary>
  86. /// Gets the render layers.
  87. /// </summary>
  88. internal RenderLayers Layers { get; }
  89. /// <summary>
  90. /// Gets the current render target.
  91. /// </summary>
  92. internal IRenderTarget RenderTarget { get; private set; }
  93. /// <inheritdoc/>
  94. public void AddDirty(IVisual visual)
  95. {
  96. _dirty?.Add(visual);
  97. }
  98. /// <summary>
  99. /// Disposes of the renderer and detaches from the render loop.
  100. /// </summary>
  101. public void Dispose()
  102. {
  103. var scene = Interlocked.Exchange(ref _scene, null);
  104. scene?.Dispose();
  105. Stop();
  106. Layers.Clear();
  107. RenderTarget?.Dispose();
  108. }
  109. /// <inheritdoc/>
  110. public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
  111. {
  112. if (_renderLoop == null && (_dirty == null || _dirty.Count > 0))
  113. {
  114. // When unit testing the renderLoop may be null, so update the scene manually.
  115. UpdateScene();
  116. }
  117. return _scene?.Item.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>();
  118. }
  119. /// <inheritdoc/>
  120. public void Paint(Rect rect)
  121. {
  122. }
  123. /// <inheritdoc/>
  124. public void Resized(Size size)
  125. {
  126. }
  127. /// <inheritdoc/>
  128. public void Start()
  129. {
  130. if (!_running && _renderLoop != null)
  131. {
  132. _renderLoop.Tick += OnRenderLoopTick;
  133. _running = true;
  134. }
  135. }
  136. /// <inheritdoc/>
  137. public void Stop()
  138. {
  139. if (_running && _renderLoop != null)
  140. {
  141. _renderLoop.Tick -= OnRenderLoopTick;
  142. _running = false;
  143. }
  144. }
  145. /// <inheritdoc/>
  146. Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
  147. {
  148. return (_currentDraw.Item as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty;
  149. }
  150. /// <inheritdoc/>
  151. void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
  152. {
  153. var childScene = (_currentDraw.Item as BrushDrawOperation)?.ChildScenes?[brush.Visual];
  154. if (childScene != null)
  155. {
  156. Render(context, (VisualNode)childScene.Root, null, new Rect(childScene.Size));
  157. }
  158. }
  159. internal void UnitTestUpdateScene() => UpdateScene();
  160. internal void UnitTestRender() => Render(_scene.Item);
  161. private void Render(Scene scene)
  162. {
  163. bool renderOverlay = DrawDirtyRects || DrawFps;
  164. bool composite = false;
  165. if (RenderTarget == null)
  166. {
  167. RenderTarget = ((IRenderRoot)_root).CreateRenderTarget();
  168. }
  169. if (renderOverlay)
  170. {
  171. _dirtyRectsDisplay.Tick();
  172. }
  173. try
  174. {
  175. if (scene != null && scene.Size != Size.Empty)
  176. {
  177. IDrawingContextImpl context = null;
  178. if (scene.Generation != _lastSceneId)
  179. {
  180. context = RenderTarget.CreateDrawingContext(this);
  181. Layers.Update(scene, context);
  182. RenderToLayers(scene);
  183. if (DebugFramesPath != null)
  184. {
  185. SaveDebugFrames(scene.Generation);
  186. }
  187. _lastSceneId = scene.Generation;
  188. composite = true;
  189. }
  190. if (renderOverlay)
  191. {
  192. context = context ?? RenderTarget.CreateDrawingContext(this);
  193. RenderOverlay(scene, context);
  194. RenderComposite(scene, context);
  195. }
  196. else if (composite)
  197. {
  198. context = context ?? RenderTarget.CreateDrawingContext(this);
  199. RenderComposite(scene, context);
  200. }
  201. context?.Dispose();
  202. }
  203. }
  204. catch (RenderTargetCorruptedException ex)
  205. {
  206. Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
  207. RenderTarget?.Dispose();
  208. RenderTarget = null;
  209. }
  210. }
  211. private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds)
  212. {
  213. if (layer == null || node.LayerRoot == layer)
  214. {
  215. clipBounds = node.ClipBounds.Intersect(clipBounds);
  216. if (!clipBounds.IsEmpty && node.Opacity > 0)
  217. {
  218. var isLayerRoot = node.Visual == layer;
  219. node.BeginRender(context, isLayerRoot);
  220. foreach (var operation in node.DrawOperations)
  221. {
  222. _currentDraw = operation;
  223. operation.Item.Render(context);
  224. _currentDraw = null;
  225. }
  226. foreach (var child in node.Children)
  227. {
  228. Render(context, (VisualNode)child, layer, clipBounds);
  229. }
  230. node.EndRender(context, isLayerRoot);
  231. }
  232. }
  233. }
  234. private void RenderToLayers(Scene scene)
  235. {
  236. if (scene.Layers.HasDirty)
  237. {
  238. foreach (var layer in scene.Layers)
  239. {
  240. var renderTarget = Layers[layer.LayerRoot].Bitmap;
  241. var node = (VisualNode)scene.FindNode(layer.LayerRoot);
  242. if (node != null)
  243. {
  244. using (var context = renderTarget.Item.CreateDrawingContext(this))
  245. {
  246. foreach (var rect in layer.Dirty)
  247. {
  248. context.Transform = Matrix.Identity;
  249. context.PushClip(rect);
  250. context.Clear(Colors.Transparent);
  251. Render(context, node, layer.LayerRoot, rect);
  252. context.PopClip();
  253. if (DrawDirtyRects)
  254. {
  255. _dirtyRectsDisplay.Add(rect);
  256. }
  257. }
  258. }
  259. }
  260. }
  261. }
  262. }
  263. private void RenderOverlay(Scene scene, IDrawingContextImpl parentContent)
  264. {
  265. if (DrawDirtyRects)
  266. {
  267. var overlay = GetOverlay(parentContent, scene.Size, scene.Scaling);
  268. using (var context = overlay.Item.CreateDrawingContext(this))
  269. {
  270. context.Clear(Colors.Transparent);
  271. RenderDirtyRects(context);
  272. }
  273. }
  274. else
  275. {
  276. _overlay?.Dispose();
  277. _overlay = null;
  278. }
  279. }
  280. private void RenderDirtyRects(IDrawingContextImpl context)
  281. {
  282. foreach (var r in _dirtyRectsDisplay)
  283. {
  284. var brush = new ImmutableSolidColorBrush(Colors.Magenta, r.Opacity);
  285. context.FillRectangle(brush, r.Rect);
  286. }
  287. }
  288. private void RenderComposite(Scene scene, IDrawingContextImpl context)
  289. {
  290. var clientRect = new Rect(scene.Size);
  291. foreach (var layer in scene.Layers)
  292. {
  293. var bitmap = Layers[layer.LayerRoot].Bitmap;
  294. var sourceRect = new Rect(0, 0, bitmap.Item.PixelWidth, bitmap.Item.PixelHeight);
  295. if (layer.GeometryClip != null)
  296. {
  297. context.PushGeometryClip(layer.GeometryClip);
  298. }
  299. if (layer.OpacityMask == null)
  300. {
  301. context.DrawImage(bitmap, layer.Opacity, sourceRect, clientRect);
  302. }
  303. else
  304. {
  305. context.DrawImage(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
  306. }
  307. if (layer.GeometryClip != null)
  308. {
  309. context.PopGeometryClip();
  310. }
  311. }
  312. if (_overlay != null)
  313. {
  314. var sourceRect = new Rect(0, 0, _overlay.Item.PixelWidth, _overlay.Item.PixelHeight);
  315. context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
  316. }
  317. if (DrawFps)
  318. {
  319. RenderFps(context, clientRect, scene.Layers.Count);
  320. }
  321. }
  322. private void UpdateScene()
  323. {
  324. Dispatcher.UIThread.VerifyAccess();
  325. try
  326. {
  327. if (_root.IsVisible)
  328. {
  329. var sceneRef = RefCountable.Create(_scene?.Item.CloneScene() ?? new Scene(_root));
  330. var scene = sceneRef.Item;
  331. if (_dirty == null)
  332. {
  333. _dirty = new DirtyVisuals();
  334. _sceneBuilder.UpdateAll(scene);
  335. }
  336. else if (_dirty.Count > 0)
  337. {
  338. foreach (var visual in _dirty)
  339. {
  340. _sceneBuilder.Update(scene, visual);
  341. }
  342. }
  343. var oldScene = Interlocked.Exchange(ref _scene, sceneRef);
  344. oldScene?.Dispose();
  345. _dirty.Clear();
  346. (_root as IRenderRoot)?.Invalidate(new Rect(scene.Size));
  347. }
  348. else
  349. {
  350. var oldScene = Interlocked.Exchange(ref _scene, null);
  351. oldScene?.Dispose();
  352. }
  353. }
  354. finally
  355. {
  356. _updateQueued = false;
  357. }
  358. }
  359. private void OnRenderLoopTick(object sender, EventArgs e)
  360. {
  361. if (Monitor.TryEnter(_rendering))
  362. {
  363. try
  364. {
  365. if (!_updateQueued && (_dirty == null || _dirty.Count > 0))
  366. {
  367. _updateQueued = true;
  368. _dispatcher.Post(UpdateScene, DispatcherPriority.Render);
  369. }
  370. using (var scene = _scene?.Clone())
  371. {
  372. Render(scene?.Item);
  373. }
  374. }
  375. catch { }
  376. finally
  377. {
  378. Monitor.Exit(_rendering);
  379. }
  380. }
  381. }
  382. private IRef<IRenderTargetBitmapImpl> GetOverlay(
  383. IDrawingContextImpl parentContext,
  384. Size size,
  385. double scaling)
  386. {
  387. var pixelSize = size * scaling;
  388. if (_overlay == null ||
  389. _overlay.Item.PixelWidth != pixelSize.Width ||
  390. _overlay.Item.PixelHeight != pixelSize.Height)
  391. {
  392. _overlay?.Dispose();
  393. _overlay = RefCountable.Create(parentContext.CreateLayer(size));
  394. }
  395. return _overlay;
  396. }
  397. private void SaveDebugFrames(int id)
  398. {
  399. var index = 0;
  400. foreach (var layer in Layers)
  401. {
  402. var fileName = Path.Combine(DebugFramesPath, $"frame-{id}-layer-{index++}.png");
  403. layer.Bitmap.Item.Save(fileName);
  404. }
  405. }
  406. }
  407. }