Browse Source

Removed IRenderLayerFactory

And use `IDrawingContextImpl.CreateLayer` instead.
pull/1184/head
Steven Kirk 9 years ago
parent
commit
bb5a7f0cd3
  1. 34
      src/Avalonia.Visuals/Rendering/DefaultRenderLayerFactory.cs
  2. 153
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  3. 11
      src/Avalonia.Visuals/Rendering/IRenderLayerFactory.cs
  4. 10
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  5. 9
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  6. 220
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

34
src/Avalonia.Visuals/Rendering/DefaultRenderLayerFactory.cs

@ -1,34 +0,0 @@
using System;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class DefaultRenderLayerFactory : IRenderLayerFactory
{
private IPlatformRenderInterface _renderInterface;
public DefaultRenderLayerFactory()
: this(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>())
{
}
public DefaultRenderLayerFactory(IPlatformRenderInterface renderInterface)
{
_renderInterface = renderInterface;
}
public IRenderTargetBitmapImpl CreateLayer(
IVisual layerRoot,
Size size,
double dpiX,
double dpiY)
{
return _renderInterface.CreateRenderTargetBitmap(
(int)Math.Ceiling(size.Width),
(int)Math.Ceiling(size.Height),
dpiX,
dpiY);
}
}
}

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

@ -26,7 +26,6 @@ namespace Avalonia.Rendering
private readonly IVisual _root;
private readonly ISceneBuilder _sceneBuilder;
private readonly RenderLayers _layers;
private readonly IRenderLayerFactory _layerFactory;
private bool _running;
private Scene _scene;
@ -45,13 +44,11 @@ namespace Avalonia.Rendering
/// <param name="root">The control to render.</param>
/// <param name="renderLoop">The render loop.</param>
/// <param name="sceneBuilder">The scene builder to use. Optional.</param>
/// <param name="layerFactory">The layer factory to use. Optional.</param>
/// <param name="dispatcher">The dispatcher to use. Optional.</param>
public DeferredRenderer(
IRenderRoot root,
IRenderLoop renderLoop,
ISceneBuilder sceneBuilder = null,
IRenderLayerFactory layerFactory = null,
IDispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(root != null);
@ -59,8 +56,7 @@ namespace Avalonia.Rendering
_dispatcher = dispatcher ?? Dispatcher.UIThread;
_root = root;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
_layers = new RenderLayers();
_renderLoop = renderLoop;
}
@ -70,15 +66,13 @@ namespace Avalonia.Rendering
/// <param name="root">The control to render.</param>
/// <param name="renderTarget">The render target.</param>
/// <param name="sceneBuilder">The scene builder to use. Optional.</param>
/// <param name="layerFactory">The layer factory to use. Optional.</param>
/// <remarks>
/// This constructor is intended to be used for unit testing.
/// </remarks>
public DeferredRenderer(
IVisual root,
IRenderTarget renderTarget,
ISceneBuilder sceneBuilder = null,
IRenderLayerFactory layerFactory = null)
ISceneBuilder sceneBuilder = null)
{
Contract.Requires<ArgumentNullException>(root != null);
Contract.Requires<ArgumentNullException>(renderTarget != null);
@ -86,8 +80,7 @@ namespace Avalonia.Rendering
_root = root;
_renderTarget = renderTarget;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
_layers = new RenderLayers();
}
/// <inheritdoc/>
@ -180,38 +173,56 @@ namespace Avalonia.Rendering
bool renderOverlay = DrawDirtyRects || DrawFps;
bool composite = false;
if (_renderTarget == null)
{
_renderTarget = ((IRenderRoot)_root).CreateRenderTarget();
}
if (renderOverlay)
{
_dirtyRectsDisplay.Tick();
}
if (scene != null && scene.Size != Size.Empty)
try
{
if (scene.Generation != _lastSceneId)
using (var context = _renderTarget.CreateDrawingContext(this))
{
_layers.Update(scene);
RenderToLayers(scene);
if (DebugFramesPath != null)
if (scene != null && scene.Size != Size.Empty)
{
SaveDebugFrames(scene.Generation);
}
if (scene.Generation != _lastSceneId)
{
_layers.Update(scene, context);
_lastSceneId = scene.Generation;
RenderToLayers(scene);
composite = true;
}
if (DebugFramesPath != null)
{
SaveDebugFrames(scene.Generation);
}
if (renderOverlay)
{
RenderOverlay(scene);
RenderComposite(scene);
}
else if(composite)
{
RenderComposite(scene);
_lastSceneId = scene.Generation;
composite = true;
}
if (renderOverlay)
{
RenderOverlay(scene, context);
RenderComposite(scene, context);
}
else if (composite)
{
RenderComposite(scene, context);
}
}
}
}
catch (RenderTargetCorruptedException ex)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget?.Dispose();
_renderTarget = null;
}
}
private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds)
@ -273,11 +284,11 @@ namespace Avalonia.Rendering
}
}
private void RenderOverlay(Scene scene)
private void RenderOverlay(Scene scene, IDrawingContextImpl parentContent)
{
if (DrawDirtyRects)
{
var overlay = GetOverlay(scene.Size, scene.Scaling);
var overlay = GetOverlay(parentContent, scene.Size, scene.Scaling);
using (var context = overlay.CreateDrawingContext(this))
{
@ -301,61 +312,44 @@ namespace Avalonia.Rendering
}
}
private void RenderComposite(Scene scene)
private void RenderComposite(Scene scene, IDrawingContextImpl context)
{
try
var clientRect = new Rect(scene.Size);
foreach (var layer in scene.Layers)
{
if (_renderTarget == null)
var bitmap = _layers[layer.LayerRoot].Bitmap;
var sourceRect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
if (layer.GeometryClip != null)
{
_renderTarget = ((IRenderRoot)_root).CreateRenderTarget();
context.PushGeometryClip(layer.GeometryClip);
}
using (var context = _renderTarget.CreateDrawingContext(this))
if (layer.OpacityMask == null)
{
var clientRect = new Rect(scene.Size);
foreach (var layer in scene.Layers)
{
var bitmap = _layers[layer.LayerRoot].Bitmap;
var sourceRect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
if (layer.GeometryClip != null)
{
context.PushGeometryClip(layer.GeometryClip);
}
if (layer.OpacityMask == null)
{
context.DrawImage(bitmap, layer.Opacity, sourceRect, clientRect);
}
else
{
context.DrawImage(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
}
if (layer.GeometryClip != null)
{
context.PopGeometryClip();
}
}
if (_overlay != null)
{
var sourceRect = new Rect(0, 0, _overlay.PixelWidth, _overlay.PixelHeight);
context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
}
context.DrawImage(bitmap, layer.Opacity, sourceRect, clientRect);
}
else
{
context.DrawImage(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
}
if (DrawFps)
{
RenderFps(context, clientRect, true);
}
if (layer.GeometryClip != null)
{
context.PopGeometryClip();
}
}
catch (RenderTargetCorruptedException ex)
if (_overlay != null)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget?.Dispose();
_renderTarget = null;
var sourceRect = new Rect(0, 0, _overlay.PixelWidth, _overlay.PixelHeight);
context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
}
if (DrawFps)
{
RenderFps(context, clientRect, true);
}
}
@ -422,7 +416,10 @@ namespace Avalonia.Rendering
}
}
private IRenderTargetBitmapImpl GetOverlay(Size size, double scaling)
private IRenderTargetBitmapImpl GetOverlay(
IDrawingContextImpl parentContext,
Size size,
double scaling)
{
size = new Size(size.Width * scaling, size.Height * scaling);
@ -431,7 +428,7 @@ namespace Avalonia.Rendering
_overlay.PixelHeight != size.Height)
{
_overlay?.Dispose();
_overlay = _layerFactory.CreateLayer(null, size, 96 * scaling, 96 * scaling);
_overlay = parentContext.CreateLayer(size);
}
return _overlay;

11
src/Avalonia.Visuals/Rendering/IRenderLayerFactory.cs

@ -1,11 +0,0 @@
using System;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public interface IRenderLayerFactory
{
IRenderTargetBitmapImpl CreateLayer(IVisual layerRoot, Size size, double dpiX, double dpiY);
}
}

10
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@ -7,16 +7,16 @@ namespace Avalonia.Rendering
{
public class RenderLayer
{
private readonly IRenderLayerFactory _factory;
private readonly IDrawingContextImpl _drawingContext;
public RenderLayer(
IRenderLayerFactory factory,
IDrawingContextImpl drawingContext,
Size size,
double scaling,
IVisual layerRoot)
{
_factory = factory;
Bitmap = factory.CreateLayer(layerRoot, size * scaling, 96 * scaling, 96 * scaling);
_drawingContext = drawingContext;
Bitmap = drawingContext.CreateLayer(size);
Size = size;
Scaling = scaling;
LayerRoot = layerRoot;
@ -31,7 +31,7 @@ namespace Avalonia.Rendering
{
if (Size != size || Scaling != scaling)
{
var resized = _factory.CreateLayer(LayerRoot, size * scaling, 96 * scaling, 96 * scaling);
var resized = _drawingContext.CreateLayer(size);
using (var context = resized.CreateDrawingContext(null))
{

9
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.VisualTree;
@ -8,19 +9,17 @@ namespace Avalonia.Rendering
{
public class RenderLayers : IEnumerable<RenderLayer>
{
private readonly IRenderLayerFactory _factory;
private List<RenderLayer> _inner = new List<RenderLayer>();
private Dictionary<IVisual, RenderLayer> _index = new Dictionary<IVisual, RenderLayer>();
public RenderLayers(IRenderLayerFactory factory)
public RenderLayers()
{
_factory = factory;
}
public int Count => _inner.Count;
public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
public void Update(Scene scene)
public void Update(Scene scene, IDrawingContextImpl context)
{
for (var i = scene.Layers.Count - 1; i >= 0; --i)
{
@ -29,7 +28,7 @@ namespace Avalonia.Rendering
if (!_index.TryGetValue(src.LayerRoot, out layer))
{
layer = new RenderLayer(_factory, scene.Size, scene.Scaling, src.LayerRoot);
layer = new RenderLayer(context, scene.Size, scene.Scaling, src.LayerRoot);
_inner.Add(layer);
_index.Add(src.LayerRoot, layer);
}

220
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -30,7 +30,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering
root,
loop.Object,
sceneBuilder: MockSceneBuilder(root).Object,
layerFactory: MockLayerFactory(root).Object,
dispatcher: dispatcher.Object);
target.Start();
@ -55,7 +54,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering
root,
loop.Object,
sceneBuilder: sceneBuilder.Object,
layerFactory: MockLayerFactory(root).Object,
dispatcher: dispatcher);
target.Start();
@ -75,7 +73,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering
root,
loop.Object,
sceneBuilder: sceneBuilder.Object,
layerFactory: MockLayerFactory(root).Object,
dispatcher: dispatcher);
target.Start();
@ -111,7 +108,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering
root,
loop.Object,
sceneBuilder: sceneBuilder.Object,
layerFactory: MockLayerFactory(root).Object,
dispatcher: dispatcher);
target.Start();
@ -133,100 +129,102 @@ namespace Avalonia.Visuals.UnitTests.Rendering
[Fact]
public void Frame_Should_Create_Layer_For_Root()
{
var loop = new Mock<IRenderLoop>();
var root = new TestRoot();
var rootLayer = new Mock<IRenderTargetBitmapImpl>();
var dispatcher = new ImmediateDispatcher();
var sceneBuilder = new Mock<ISceneBuilder>();
sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
.Callback<Scene>(scene =>
{
scene.Size = root.ClientSize;
scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
});
var layers = new Mock<IRenderLayerFactory>();
layers.Setup(x => x.CreateLayer(root, root.ClientSize, 96, 96)).Returns(CreateLayer());
var renderInterface = new Mock<IPlatformRenderInterface>();
var target = new DeferredRenderer(
root,
loop.Object,
sceneBuilder: sceneBuilder.Object,
layerFactory: layers.Object,
dispatcher: dispatcher);
target.Start();
RunFrame(loop);
layers.Verify(x => x.CreateLayer(root, root.ClientSize, 96, 96));
throw new NotImplementedException();
//var loop = new Mock<IRenderLoop>();
//var root = new TestRoot();
//var rootLayer = new Mock<IRenderTargetBitmapImpl>();
//var dispatcher = new ImmediateDispatcher();
//var sceneBuilder = new Mock<ISceneBuilder>();
//sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
// .Callback<Scene>(scene =>
// {
// scene.Size = root.ClientSize;
// scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
// });
//var layers = new Mock<IRenderLayerFactory>();
//layers.Setup(x => x.CreateLayer(root, root.ClientSize, 96, 96)).Returns(CreateLayer());
//var renderInterface = new Mock<IPlatformRenderInterface>();
//var target = new DeferredRenderer(
// root,
// loop.Object,
// sceneBuilder: sceneBuilder.Object,
// layerFactory: layers.Object,
// dispatcher: dispatcher);
//target.Start();
//RunFrame(loop);
//layers.Verify(x => x.CreateLayer(root, root.ClientSize, 96, 96));
}
[Fact]
public void Should_Create_And_Delete_Layers_For_Transparent_Controls()
{
Border border;
var root = new TestRoot
{
Width = 100,
Height = 100,
Child = new Border
{
Background = Brushes.Red,
Child = border = new Border
{
Background = Brushes.Green,
}
}
};
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
var loop = new Mock<IRenderLoop>();
var layerFactory = new MockRenderLayerFactory(new Dictionary<IVisual, IRenderTargetBitmapImpl>
{
{ root, CreateLayer() },
{ border, CreateLayer() },
});
var target = new DeferredRenderer(
root,
loop.Object,
layerFactory: layerFactory,
dispatcher: new ImmediateDispatcher());
root.Renderer = target;
target.Start();
RunFrame(loop);
var rootContext = layerFactory.GetMockDrawingContext(root);
var borderContext = layerFactory.GetMockDrawingContext(border);
rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never);
rootContext.ResetCalls();
borderContext.ResetCalls();
border.Opacity = 0.5;
RunFrame(loop);
rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Never);
borderContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
rootContext.ResetCalls();
borderContext.ResetCalls();
border.Opacity = 1;
RunFrame(loop);
layerFactory.GetMockBitmap(border).Verify(x => x.Dispose());
rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never);
throw new NotImplementedException();
//Border border;
//var root = new TestRoot
//{
// Width = 100,
// Height = 100,
// Child = new Border
// {
// Background = Brushes.Red,
// Child = border = new Border
// {
// Background = Brushes.Green,
// }
// }
//};
//root.Measure(Size.Infinity);
//root.Arrange(new Rect(root.DesiredSize));
//var loop = new Mock<IRenderLoop>();
//var layerFactory = new MockRenderLayerFactory(new Dictionary<IVisual, IRenderTargetBitmapImpl>
//{
// { root, CreateLayer() },
// { border, CreateLayer() },
//});
//var target = new DeferredRenderer(
// root,
// loop.Object,
// layerFactory: layerFactory,
// dispatcher: new ImmediateDispatcher());
//root.Renderer = target;
//target.Start();
//RunFrame(loop);
//var rootContext = layerFactory.GetMockDrawingContext(root);
//var borderContext = layerFactory.GetMockDrawingContext(border);
//rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
//rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
//borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never);
//rootContext.ResetCalls();
//borderContext.ResetCalls();
//border.Opacity = 0.5;
//RunFrame(loop);
//rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
//rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Never);
//borderContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
//rootContext.ResetCalls();
//borderContext.ResetCalls();
//border.Opacity = 1;
//RunFrame(loop);
//layerFactory.GetMockBitmap(border).Verify(x => x.Dispose());
//rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
//rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once);
//borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never);
}
private void IgnoreFirstFrame(Mock<IRenderLoop> loop, Mock<ISceneBuilder> sceneBuilder)
@ -246,13 +244,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering
x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>()) == Mock.Of<IDrawingContextImpl>());
}
private Mock<IRenderLayerFactory> MockLayerFactory(IRenderRoot root)
{
var result = new Mock<IRenderLayerFactory>();
result.Setup(x => x.CreateLayer(root, root.ClientSize, 96, 96)).Returns(CreateLayer());
return result;
}
private Mock<ISceneBuilder> MockSceneBuilder(IRenderRoot root)
{
var result = new Mock<ISceneBuilder>();
@ -260,34 +251,5 @@ namespace Avalonia.Visuals.UnitTests.Rendering
.Callback<Scene>(x => x.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize)));
return result;
}
private class MockRenderLayerFactory : IRenderLayerFactory
{
private IDictionary<IVisual, IRenderTargetBitmapImpl> _layers;
public MockRenderLayerFactory(IDictionary<IVisual, IRenderTargetBitmapImpl> layers)
{
_layers = layers;
}
public IRenderTargetBitmapImpl CreateLayer(
IVisual layerRoot,
Size size,
double dpiX,
double dpiY)
{
return _layers[layerRoot];
}
public Mock<IRenderTargetBitmapImpl> GetMockBitmap(IVisual layerRoot)
{
return Mock.Get(_layers[layerRoot]);
}
public Mock<IDrawingContextImpl> GetMockDrawingContext(IVisual layerRoot)
{
return Mock.Get(_layers[layerRoot].CreateDrawingContext(null));
}
}
}
}

Loading…
Cancel
Save