diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 9f49d48da0..f7af2e8d1f 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -30,6 +30,7 @@ namespace Avalonia.Rendering private bool _rendering; private int _lastSceneId = -1; private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects(); + private IDrawOperation _currentDraw; public DeferredRenderer( IRenderRoot root, @@ -107,20 +108,16 @@ namespace Avalonia.Rendering Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush) { - if (brush.Visual != null) - { - return _scene.FindNode(brush.Visual)?.ClipBounds.Size ?? Size.Empty; - } - - return Size.Empty; + return (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty; } void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush) { - if (brush.Visual != null) + var childScene = (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual]; + + if (childScene != null) { - var node = (VisualNode)_scene.FindNode(brush.Visual); - Render(context, node, null, node.ClipBounds); + Render(context, (VisualNode)childScene.Root, null, new Rect(childScene.Size)); } } @@ -130,29 +127,37 @@ namespace Avalonia.Rendering private void Render(Scene scene) { - _rendering = true; - _dirtyRectsDisplay.Tick(); - - if (scene.Size != Size.Empty) + if (!_rendering) { - if (scene.Id != _lastSceneId) + try { - _layers.Update(scene); - RenderToLayers(scene); + _rendering = true; + _dirtyRectsDisplay.Tick(); - if (DebugFramesPath != null) + if (scene.Size != Size.Empty) { - SaveDebugFrames(scene.Id); - } + if (scene.Id != _lastSceneId) + { + _layers.Update(scene); + RenderToLayers(scene); - _lastSceneId = scene.Id; - } + if (DebugFramesPath != null) + { + SaveDebugFrames(scene.Id); + } - RenderOverlay(scene); - RenderComposite(scene); - } + _lastSceneId = scene.Id; + } - _rendering = false; + RenderOverlay(scene); + RenderComposite(scene); + } + } + finally + { + _rendering = false; + } + } } private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds) @@ -167,7 +172,9 @@ namespace Avalonia.Rendering foreach (var operation in node.DrawOperations) { + _currentDraw = operation; operation.Render(context); + _currentDraw = null; } foreach (var child in node.Children) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs index bc003563e1..ebff5f9070 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using Avalonia.Media; using Avalonia.Rendering.SceneGraph.Media; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { @@ -8,6 +10,8 @@ namespace Avalonia.Rendering.SceneGraph { public abstract Rect Bounds { get; } public abstract bool HitTest(Point p); + public abstract IDictionary ChildScenes { get; } + public abstract void Render(IDrawingContextImpl context); protected IBrush Convert(IBrush brush) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 196535a32d..e6ca9865a8 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -2,19 +2,23 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { internal class DeferredDrawingContextImpl : IDrawingContextImpl { + private readonly ISceneBuilder _sceneBuilder; private VisualNode _node; private int _childIndex; private int _drawOperationindex; - public DeferredDrawingContextImpl(SceneLayers layers) + public DeferredDrawingContextImpl(ISceneBuilder sceneBuilder, SceneLayers layers) { + _sceneBuilder = sceneBuilder; Layers = layers; } @@ -67,7 +71,7 @@ namespace Avalonia.Rendering.SceneGraph if (next == null || !next.Equals(Transform, brush, pen, geometry)) { - Add(new GeometryNode(Transform, brush, pen, geometry)); + Add(new GeometryNode(Transform, brush, pen, geometry, CreateChildScene(brush))); } else { @@ -109,7 +113,7 @@ namespace Avalonia.Rendering.SceneGraph if (next == null || !next.Equals(Transform, null, pen, rect, cornerRadius)) { - Add(new RectangleNode(Transform, null, pen, rect, cornerRadius)); + Add(new RectangleNode(Transform, null, pen, rect, cornerRadius, CreateChildScene(pen.Brush))); } else { @@ -137,7 +141,7 @@ namespace Avalonia.Rendering.SceneGraph if (next == null || !next.Equals(Transform, brush, null, rect, cornerRadius)) { - Add(new RectangleNode(Transform, brush, null, rect, cornerRadius)); + Add(new RectangleNode(Transform, brush, null, rect, cornerRadius, CreateChildScene(brush))); } else { @@ -239,5 +243,25 @@ namespace Avalonia.Rendering.SceneGraph { return _drawOperationindex < _node.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as T : null; } + + private IDictionary CreateChildScene(IBrush brush) + { + var visualBrush = brush as VisualBrush; + + if (visualBrush != null) + { + var visual = visualBrush.Visual; + + if (visual != null) + { + (visual as IVisualBrushInitialize)?.EnsureInitialized(); + var scene = new Scene(visual); + _sceneBuilder.UpdateAll(scene); + return new Dictionary { { visualBrush.Visual, scene } }; + } + } + + return null; + } } } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs index f4dbdbecf6..238868bc47 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs @@ -2,20 +2,28 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { public class GeometryNode : BrushDrawOperation { - public GeometryNode(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry) + public GeometryNode( + Matrix transform, + IBrush brush, + Pen pen, + IGeometryImpl geometry, + IDictionary childScenes = null) { Bounds = geometry.GetRenderBounds(pen.Thickness).TransformToAABB(transform); Transform = transform; Brush = Convert(brush); Pen = Convert(pen); Geometry = geometry; + ChildScenes = childScenes; } public override Rect Bounds { get; } @@ -23,6 +31,7 @@ namespace Avalonia.Rendering.SceneGraph public IBrush Brush { get; } public Pen Pen { get; } public IGeometryImpl Geometry { get; } + public override IDictionary ChildScenes { get; } public bool Equals(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs index d7d634f1aa..69d8cdc94f 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs index 77cbba4181..893ffae3cf 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs @@ -2,8 +2,10 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { @@ -25,6 +27,7 @@ namespace Avalonia.Rendering.SceneGraph public double Opacity { get; } public Rect SourceRect { get; } public Rect DestRect { get; } + public IDictionary ChildScenes => null; public bool Equals(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs index 16e7c65cad..a9e2560492 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { @@ -22,6 +24,7 @@ namespace Avalonia.Rendering.SceneGraph public Pen Pen { get; } public Point P1 { get; } public Point P2 { get; } + public override IDictionary ChildScenes => null; public bool Equals(Matrix transform, Pen pen, Point p1, Point p2) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs index 88756b34ab..5592474f91 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs @@ -2,13 +2,21 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { public class RectangleNode : BrushDrawOperation { - public RectangleNode(Matrix transform, IBrush brush, Pen pen, Rect rect, float cornerRadius) + public RectangleNode( + Matrix transform, + IBrush brush, + Pen pen, + Rect rect, + float cornerRadius, + IDictionary childScenes = null) { Bounds = rect.TransformToAABB(transform).Inflate(pen?.Thickness ?? 0); Transform = transform; @@ -16,6 +24,7 @@ namespace Avalonia.Rendering.SceneGraph Pen = Convert(pen); Rect = rect; CornerRadius = cornerRadius; + ChildScenes = childScenes; } public override Rect Bounds { get; } @@ -24,6 +33,7 @@ namespace Avalonia.Rendering.SceneGraph public Pen Pen { get; } public Rect Rect { get; } public float CornerRadius { get; } + public override IDictionary ChildScenes { get; } public bool Equals(Matrix transform, IBrush brush, Pen pen, Rect rect, float cornerRadius) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 7071320118..4772528f08 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -19,7 +19,7 @@ namespace Avalonia.Rendering.SceneGraph UpdateSize(scene); scene.Layers.GetOrAdd(scene.Root.Visual); - using (var impl = new DeferredDrawingContextImpl(scene.Layers)) + using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var context = new DrawingContext(impl)) { Update(context, scene, (VisualNode)scene.Root, scene.Root.Visual.Bounds, true); @@ -55,7 +55,7 @@ namespace Avalonia.Rendering.SceneGraph // descendents too. var recurse = node.Visual != visual; - using (var impl = new DeferredDrawingContextImpl(scene.Layers)) + using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var context = new DrawingContext(impl)) { var clip = scene.Root.Visual.Bounds; diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs index 2bddea88bf..a602a37074 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs @@ -2,8 +2,10 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Generic; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { @@ -23,6 +25,7 @@ namespace Avalonia.Rendering.SceneGraph public IBrush Foreground { get; } public Point Origin { get; } public IFormattedTextImpl Text { get; } + public IDictionary ChildScenes => null; public void Render(IDrawingContextImpl context) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index 6775afb178..c4a8738270 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -31,12 +31,6 @@ namespace Avalonia.Rendering.SceneGraph { Contract.Requires(visual != null); - if (parent == null && visual.VisualParent != null) - { - throw new AvaloniaInternalException( - "Attempted to create root VisualNode for parented visual."); - } - Visual = visual; Parent = parent; } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs index 373bd22459..1906775783 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs @@ -17,7 +17,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var parent = new VisualNode(new TestRoot(), null); var child = new VisualNode(Mock.Of(), null); var layers = new SceneLayers(parent.Visual); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); target.BeginUpdate(parent); target.BeginUpdate(child); @@ -35,7 +35,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph parent.AddChild(child); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); target.BeginUpdate(parent); target.BeginUpdate(child); @@ -54,7 +54,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph parent.AddChild(child1); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); target.BeginUpdate(parent); target.BeginUpdate(child2); @@ -75,7 +75,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph node.AddChild(new VisualNode(Mock.Of(), node) { LayerRoot = root }); var layers = new SceneLayers(root); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); var child1 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; var child2 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; @@ -92,7 +92,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph { var node = new VisualNode(new TestRoot(), null); var layers = new SceneLayers(node.Visual); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); node.LayerRoot = node.Visual; @@ -113,7 +113,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(new TestRoot(), null); var operation = new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0); var layers = new SceneLayers(node.Visual); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); node.LayerRoot = node.Visual; node.AddDrawOperation(operation); @@ -135,7 +135,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(new TestRoot(), null); var operation = new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0); var layers = new SceneLayers(node.Visual); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); node.LayerRoot = node.Visual; node.AddDrawOperation(operation); @@ -157,7 +157,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(new TestRoot(), null); var operation = new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0); var layers = new SceneLayers(node.Visual); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); node.LayerRoot = node.Visual; @@ -181,7 +181,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph node.AddDrawOperation(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 40, 100), 0)); var layers = new SceneLayers(node.Visual); - var target = new DeferredDrawingContextImpl(layers); + var target = new DeferredDrawingContextImpl(null, layers); using (target.BeginUpdate(node)) {