Browse Source

Run render tests with deferred renderer.

Make render tests run all tests with both the deferred and immediate
renderer.
scenegraph-after-breakage
Steven Kirk 10 years ago
parent
commit
0a36466021
  1. 25
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  2. 2
      src/Avalonia.Visuals/Rendering/DirtyVisuals.cs
  3. 6
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  4. 9
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs
  5. 13
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs
  6. 20
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  7. 40
      tests/Avalonia.RenderTests/TestBase.cs
  8. 18
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs
  9. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs

25
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -14,7 +14,7 @@ namespace Avalonia.Rendering
{
private readonly IDispatcher _dispatcher;
private readonly IRenderLoop _renderLoop;
private readonly IRenderRoot _root;
private readonly IVisual _root;
private readonly ISceneBuilder _sceneBuilder;
private readonly RenderLayers _layers;
private readonly IRenderLayerFactory _layerFactory;
@ -51,6 +51,23 @@ namespace Avalonia.Rendering
}
}
public DeferredRenderer(
IVisual root,
IRenderTarget renderTarget,
ISceneBuilder sceneBuilder = null,
IRenderLayerFactory layerFactory = null)
{
Contract.Requires<ArgumentNullException>(root != null);
Contract.Requires<ArgumentNullException>(renderTarget != null);
_root = root;
_renderTarget = renderTarget;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
_scene = new Scene(root);
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
}
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
public string DebugFramesPath { get; set; }
@ -81,6 +98,8 @@ namespace Avalonia.Rendering
public void Render(Rect rect)
{
UpdateScene();
Render(_scene);
}
private void Render(Scene scene)
@ -198,7 +217,7 @@ namespace Avalonia.Rendering
{
if (_renderTarget == null)
{
_renderTarget = _root.CreateRenderTarget();
_renderTarget = ((IRenderRoot)_root).CreateRenderTarget();
}
using (var context = _renderTarget.CreateDrawingContext())
@ -259,7 +278,7 @@ namespace Avalonia.Rendering
}
_dirty.Clear();
_root.Invalidate(new Rect(scene.Size));
(_root as IRenderRoot)?.Invalidate(new Rect(scene.Size));
}
finally
{

2
src/Avalonia.Visuals/Rendering/DirtyVisuals.cs

@ -14,7 +14,7 @@ namespace Avalonia.Rendering
public void Add(IVisual visual)
{
var distance = visual.IsAttachedToVisualTree ? visual.CalculateDistanceFromVisualRoot() : -1;
var distance = visual.CalculateDistanceFromAncestor(visual.VisualRoot);
int existingDistance;
if (_index.TryGetValue(visual, out existingDistance))

6
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -12,7 +12,11 @@ namespace Avalonia.Rendering.SceneGraph
private Dictionary<IVisual, IVisualNode> _index;
public Scene(IVisual rootVisual)
: this(new VisualNode(rootVisual, null), new Dictionary<IVisual, IVisualNode>(), new SceneLayers(), 0)
: this(
new VisualNode(rootVisual, null),
new Dictionary<IVisual, IVisualNode>(),
new SceneLayers(rootVisual),
0)
{
_index.Add(rootVisual, Root);
}

9
src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs

@ -5,14 +5,7 @@ namespace Avalonia.Rendering.SceneGraph
{
public class SceneLayer
{
public SceneLayer(IVisual layerRoot)
{
LayerRoot = layerRoot;
Dirty = new DirtyRects();
DistanceFromRoot = layerRoot.CalculateDistanceFromVisualRoot();
}
internal SceneLayer(IVisual layerRoot, int distanceFromRoot)
public SceneLayer(IVisual layerRoot, int distanceFromRoot)
{
LayerRoot = layerRoot;
Dirty = new DirtyRects();

13
src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs

@ -7,11 +7,13 @@ namespace Avalonia.Rendering.SceneGraph
{
public class SceneLayers : IEnumerable<SceneLayer>
{
private List<SceneLayer> _inner = new List<SceneLayer>();
private Dictionary<IVisual, SceneLayer> _index = new Dictionary<IVisual, SceneLayer>();
private readonly IVisual _root;
private readonly List<SceneLayer> _inner = new List<SceneLayer>();
private readonly Dictionary<IVisual, SceneLayer> _index = new Dictionary<IVisual, SceneLayer>();
public SceneLayers()
public SceneLayers(IVisual root)
{
_root = root;
}
public int Count => _inner.Count;
@ -39,7 +41,8 @@ namespace Avalonia.Rendering.SceneGraph
{
Contract.Requires<ArgumentNullException>(layerRoot != null);
var layer = new SceneLayer(layerRoot);
var distance = layerRoot.CalculateDistanceFromAncestor(_root);
var layer = new SceneLayer(layerRoot, distance);
var insert = FindInsertIndex(layer);
_index.Add(layerRoot, layer);
_inner.Insert(insert, layer);
@ -48,7 +51,7 @@ namespace Avalonia.Rendering.SceneGraph
public SceneLayers Clone()
{
var result = new SceneLayers();
var result = new SceneLayers(_root);
foreach (var src in _inner)
{

20
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@ -17,28 +17,24 @@ namespace Avalonia.VisualTree
/// Calculates the distance from a visual's <see cref="IRenderRoot"/>.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The number of steps from the visual to the render root.</returns>
/// <exception cref="ArgumentException">The visual is not rooted.</exception>
public static int CalculateDistanceFromVisualRoot(this IVisual visual)
/// <param name="ancestor">The ancestor visual.</param>
/// <returns>
/// The number of steps from the visual to the ancestor or -1 if
/// <paramref name="visual"/> is not a descendent of <paramref name="ancestor"/>.
/// </returns>
public static int CalculateDistanceFromAncestor(this IVisual visual, IVisual ancestor)
{
Contract.Requires<ArgumentNullException>(visual != null);
var root = visual.VisualRoot;
if (root == null)
{
throw new ArgumentException("Visual is not rooted.");
}
var result = 0;
while (visual != root)
while (visual != null && visual != ancestor)
{
++result;
visual = visual.VisualParent;
}
return result;
return visual != null ? result : -1;
}
/// <summary>

40
tests/Avalonia.RenderTests/TestBase.cs

@ -9,6 +9,7 @@ using Avalonia.Media.Imaging;
using Avalonia.Rendering;
using Xunit;
using Avalonia.Platform;
#if AVALONIA_CAIRO
using Avalonia.Cairo;
@ -63,7 +64,9 @@ namespace Avalonia.Direct2D1.RenderTests
Directory.CreateDirectory(OutputPath);
}
string path = Path.Combine(OutputPath, testName + ".out.png");
var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
using (RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)target.Width,
@ -73,22 +76,41 @@ namespace Avalonia.Direct2D1.RenderTests
target.Measure(size);
target.Arrange(new Rect(size));
bitmap.Render(target);
bitmap.Save(path);
bitmap.Save(immediatePath);
}
using (var rtb = factory.CreateRenderTargetBitmap((int)target.Width, (int)target.Height, 96, 96))
using (var renderer = new DeferredRenderer(target, rtb))
{
Size size = new Size(target.Width, target.Height);
target.Measure(size);
target.Arrange(new Rect(size));
renderer.Render(target.Bounds);
rtb.Save(deferredPath);
}
}
protected void CompareImages([CallerMemberName] string testName = "")
{
string expectedPath = Path.Combine(OutputPath, testName + ".expected.png");
string actualPath = Path.Combine(OutputPath, testName + ".out.png");
using (MagickImage expected = new MagickImage(expectedPath))
using (MagickImage actual = new MagickImage(actualPath))
var expectedPath = Path.Combine(OutputPath, testName + ".expected.png");
var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
using (var expected = new MagickImage(expectedPath))
using (var immediate = new MagickImage(immediatePath))
using (var deferred = new MagickImage(deferredPath))
{
double error = expected.Compare(actual, ErrorMetric.RootMeanSquared);
double immediateError = expected.Compare(immediate, ErrorMetric.RootMeanSquared);
double deferredError = expected.Compare(deferred, ErrorMetric.RootMeanSquared);
if (immediateError > 0.022)
{
Assert.True(false, immediatePath + ": Error = " + immediateError);
}
if (error > 0.022)
if (deferredError > 0.022)
{
Assert.True(false, actualPath + ": Error = " + error);
Assert.True(false, deferredPath + ": Error = " + deferredError);
}
}
}

18
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs

@ -16,7 +16,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
{
var parent = new VisualNode(new TestRoot(), null);
var child = new VisualNode(Mock.Of<IVisual>(), null);
var layers = new SceneLayers();
var layers = new SceneLayers(parent.Visual);
var target = new DeferredDrawingContextImpl(layers);
target.BeginUpdate(parent);
@ -31,7 +31,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
{
var parent = new VisualNode(new TestRoot(), null);
var child = new VisualNode(Mock.Of<IVisual>(), null);
var layers = new SceneLayers();
var layers = new SceneLayers(parent.Visual);
parent.AddChild(child);
@ -50,7 +50,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
var parent = new VisualNode(new TestRoot(), null);
var child1 = new VisualNode(Mock.Of<IVisual>(), null);
var child2 = new VisualNode(Mock.Of<IVisual>(), null);
var layers = new SceneLayers();
var layers = new SceneLayers(parent.Visual);
parent.AddChild(child1);
@ -74,7 +74,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
node.AddChild(new VisualNode(Mock.Of<IVisual>(), node) { LayerRoot = root });
node.AddChild(new VisualNode(Mock.Of<IVisual>(), node) { LayerRoot = root });
var layers = new SceneLayers();
var layers = new SceneLayers(root);
var target = new DeferredDrawingContextImpl(layers);
var child1 = new VisualNode(Mock.Of<IVisual>(), null) { LayerRoot = root };
var child2 = new VisualNode(Mock.Of<IVisual>(), null) { LayerRoot = root };
@ -91,7 +91,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Should_Add_DrawOperations()
{
var node = new VisualNode(new TestRoot(), null);
var layers = new SceneLayers();
var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(layers);
node.LayerRoot = node.Visual;
@ -112,7 +112,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();
var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(layers);
node.LayerRoot = node.Visual;
@ -134,7 +134,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();
var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(layers);
node.LayerRoot = node.Visual;
@ -156,7 +156,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();
var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(layers);
node.LayerRoot = node.Visual;
@ -180,7 +180,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
node.AddDrawOperation(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 30, 100), 0));
node.AddDrawOperation(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 40, 100), 0));
var layers = new SceneLayers();
var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(layers);
using (target.BeginUpdate(node))

2
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs

@ -23,7 +23,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var target = new SceneLayers();
var target = new SceneLayers(root);
target.Add(root);
target.Add(decorator);
target.Add(border);

Loading…
Cancel
Save