Browse Source

WIP: Getting resizing working.

scenegraph-after-breakage
Steven Kirk 9 years ago
parent
commit
6d3ca92a4c
  1. 2
      samples/ControlCatalog/MainWindow.xaml.cs
  2. 68
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  3. 4
      src/Avalonia.Visuals/Rendering/DirtyRects.cs
  4. 36
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  5. 18
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  6. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  7. 37
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  8. 3
      src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
  9. 1
      src/Windows/Avalonia.Direct2D1/RenderTarget.cs
  10. 23
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  11. 2
      tests/Avalonia.UnitTests/TestRoot.cs
  12. 1
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  13. 45
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

2
samples/ControlCatalog/MainWindow.xaml.cs

@ -10,7 +10,7 @@ namespace ControlCatalog
{
this.InitializeComponent();
this.AttachDevTools();
Renderer.DrawDirtyRects = Renderer.DrawFps = true;
//Renderer.DrawDirtyRects = Renderer.DrawFps = true;
}
private void InitializeComponent()

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

@ -87,6 +87,28 @@ namespace Avalonia.Rendering
{
}
private void Render(Scene scene)
{
_rendering = true;
_totalFrames++;
_dirtyRectsDisplay.Tick();
if (scene.Size != Size.Empty)
{
if (scene.Id != _lastSceneId)
{
_layers.RemoveUnused(scene);
RenderToLayers(scene);
_lastSceneId = scene.Id;
}
RenderOverlay(scene);
RenderComposite(scene);
}
_rendering = false;
}
private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds)
{
if (node.LayerRoot == layer)
@ -118,7 +140,7 @@ namespace Avalonia.Rendering
{
foreach (var layer in scene.Layers)
{
var renderTarget = GetRenderTargetForLayer(layer.LayerRoot);
var renderTarget = GetRenderTargetForLayer(scene, layer.LayerRoot);
var node = (VisualNode)scene.FindNode(layer.LayerRoot);
using (var context = renderTarget.CreateDrawingContext())
@ -140,11 +162,11 @@ namespace Avalonia.Rendering
}
}
private void RenderOverlay()
private void RenderOverlay(Scene scene)
{
if (DrawFps || DrawDirtyRects)
{
var overlay = GetOverlay(_root.ClientSize);
var overlay = GetOverlay(scene.Size);
using (var context = overlay.CreateDrawingContext())
{
@ -214,11 +236,11 @@ namespace Avalonia.Rendering
using (var context = _renderTarget.CreateDrawingContext())
{
var clientRect = new Rect(_root.ClientSize);
var clientRect = new Rect(scene.Size);
foreach (var layer in scene.Layers)
{
var renderLayer = _layers.Get(layer.LayerRoot);
var renderLayer = _layers[layer.LayerRoot];
context.DrawImage(renderLayer.Bitmap, layer.Opacity, clientRect, clientRect);
}
@ -263,7 +285,7 @@ namespace Avalonia.Rendering
}
_dirty.Clear();
_root.Invalidate(new Rect(_root.ClientSize));
_root.Invalidate(new Rect(scene.Size));
}
finally
{
@ -284,10 +306,6 @@ namespace Avalonia.Rendering
_dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
}
_rendering = true;
_totalFrames++;
_dirtyRectsDisplay.Tick();
Scene scene;
lock (_scene)
@ -295,17 +313,7 @@ namespace Avalonia.Rendering
scene = _scene;
}
if (scene.Id != _lastSceneId)
{
_layers.RemoveUnused(scene);
RenderToLayers(scene);
_lastSceneId = scene.Id;
}
RenderOverlay();
RenderComposite(scene);
_rendering = false;
Render(scene);
}
private IRenderTargetBitmapImpl GetOverlay(Size size)
@ -322,9 +330,23 @@ namespace Avalonia.Rendering
return _overlay;
}
private IRenderTargetBitmapImpl GetRenderTargetForLayer(IVisual layerRoot)
private IRenderTargetBitmapImpl GetRenderTargetForLayer(Scene scene, IVisual layerRoot)
{
return (_layers.Get(layerRoot) ?? _layers.Add(layerRoot, _root.ClientSize)).Bitmap;
RenderLayer result;
if (_layers.TryGetValue(layerRoot, out result))
{
if (result.Size != scene.Size)
{
result.ResizeBitmap(scene.Size);
}
}
else
{
_layers.Add(layerRoot, scene.Size);
}
return _layers[layerRoot].Bitmap;
}
}
}

4
src/Avalonia.Visuals/Rendering/DirtyRects.cs

@ -21,7 +21,7 @@ namespace Avalonia.Rendering
{
var r = _rects[i];
if (r.Inflate(1).Intersects(rect))
if (r.Intersects(rect))
{
_rects[i] = r.Union(rect);
return;
@ -36,7 +36,7 @@ namespace Avalonia.Rendering
{
for (var i = _rects.Count - 1; i >= 0; --i)
{
var a = _rects[i].Inflate(1);
var a = _rects[i];
for (var j = 0; j < i; ++j)
{

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

@ -1,27 +1,48 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class RenderLayer : IComparable<RenderLayer>
public class RenderLayer
{
private readonly IRenderLayerFactory _factory;
public RenderLayer(
IRenderTargetBitmapImpl bitmap,
IRenderLayerFactory factory,
Size size,
IVisual layerRoot)
{
Bitmap = bitmap;
_factory = factory;
Bitmap = factory.CreateLayer(layerRoot, size);
Size = size;
LayerRoot = layerRoot;
Order = GetDistanceFromRenderRoot(layerRoot);
}
public IRenderTargetBitmapImpl Bitmap { get; }
public Size Size { get; }
public IRenderTargetBitmapImpl Bitmap { get; private set; }
public Size Size { get; private set; }
public IVisual LayerRoot { get; }
public int Order { get; }
public void ResizeBitmap(Size size)
{
if (Size != size)
{
var resized = _factory.CreateLayer(LayerRoot, size);
using (var context = resized.CreateDrawingContext())
{
context.Clear(Colors.Black);
context.DrawImage(Bitmap, 1, new Rect(Size), new Rect(Size));
Bitmap.Dispose();
Bitmap = resized;
Size = size;
}
}
}
private static int GetDistanceFromRenderRoot(IVisual visual)
{
var root = visual as IRenderRoot;
@ -42,10 +63,5 @@ namespace Avalonia.Rendering
return result;
}
public int CompareTo(RenderLayer other)
{
return Order - other.Order;
}
}
}

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

@ -18,14 +18,16 @@ namespace Avalonia.Rendering
_factory = factory;
}
public int Count => _inner.Count;
public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
public RenderLayer Add(IVisual layerRoot, Size size)
{
RenderLayer result;
if (!_index.TryGetValue(layerRoot, out result))
{
var bitmap = _factory.CreateLayer(layerRoot, size);
result = new RenderLayer(bitmap, size, layerRoot);
result = new RenderLayer(_factory, size, layerRoot);
_inner.Add(result);
_index.Add(layerRoot, result);
}
@ -33,12 +35,7 @@ namespace Avalonia.Rendering
return result;
}
public RenderLayer Get(IVisual layerRoot)
{
RenderLayer result;
_index.TryGetValue(layerRoot, out result);
return result;
}
public bool Exists(IVisual layerRoot) => _index.ContainsKey(layerRoot);
public void RemoveUnused(Scene scene)
{
@ -55,6 +52,11 @@ namespace Avalonia.Rendering
}
}
public bool TryGetValue(IVisual layerRoot, out RenderLayer value)
{
return _index.TryGetValue(layerRoot, out value);
}
public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

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

@ -21,6 +21,8 @@ namespace Avalonia.Rendering.SceneGraph
{
Contract.Requires<ArgumentNullException>(root != null);
var renderRoot = root.Visual as IRenderRoot;
_index = index;
Root = root;
Layers = layers;
@ -31,6 +33,7 @@ namespace Avalonia.Rendering.SceneGraph
public int Id { get; }
public SceneLayers Layers { get; }
public IVisualNode Root { get; }
public Size Size { get; set; }
public void Add(IVisualNode node)
{
@ -42,8 +45,13 @@ namespace Avalonia.Rendering.SceneGraph
public Scene Clone()
{
var index = new Dictionary<IVisual, IVisualNode>();
var root = (VisualNode)Clone((VisualNode)Root, null, index);
var result = new Scene(root, index, Layers.Clone(), Id + 1);
var root = Clone((VisualNode)Root, null, index);
var result = new Scene(root, index, Layers.Clone(), Id + 1)
{
Size = Size,
};
return result;
}

37
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -16,6 +16,7 @@ namespace Avalonia.Rendering.SceneGraph
Contract.Requires<ArgumentNullException>(scene != null);
Dispatcher.UIThread.VerifyAccess();
scene.Size = (scene.Root.Visual as IRenderRoot)?.ClientSize ?? scene.Root.Visual.Bounds.Size;
scene.Layers.GetOrAdd(scene.Root.Visual);
using (var impl = new DeferredDrawingContextImpl(scene.Layers))
@ -33,6 +34,11 @@ namespace Avalonia.Rendering.SceneGraph
var node = (VisualNode)scene.FindNode(visual);
if (visual == scene.Root.Visual)
{
UpdateSize(scene);
}
if (visual.VisualRoot != null)
{
if (visual.IsVisible)
@ -195,6 +201,37 @@ namespace Avalonia.Rendering.SceneGraph
}
}
private void UpdateSize(Scene scene)
{
var newSize = (scene.Root.Visual as IRenderRoot)?.ClientSize ?? scene.Root.Visual.Bounds.Size;
if (scene.Size != newSize)
{
var oldSize = scene.Size;
scene.Size = newSize;
Rect horizontalDirtyRect = Rect.Empty;
Rect verticalDirtyRect = Rect.Empty;
if (newSize.Width > oldSize.Width)
{
horizontalDirtyRect = new Rect(oldSize.Width, 0, newSize.Width - oldSize.Width, oldSize.Height);
}
if (newSize.Height > oldSize.Height)
{
verticalDirtyRect = new Rect(0, oldSize.Height, newSize.Width, newSize.Height - oldSize.Height);
}
foreach (var layer in scene.Layers)
{
layer.Dirty.Add(horizontalDirtyRect);
layer.Dirty.Add(verticalDirtyRect);
}
}
}
private static VisualNode CreateNode(Scene scene, IVisual visual, VisualNode parent)
{
var node = new VisualNode(visual, parent);

3
src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs

@ -82,7 +82,8 @@ namespace Avalonia.Direct2D1.Media
/// </summary>
public Bitmap WicImpl
{
get; }
get;
}
/// <summary>
/// Gets a Direct2D bitmap to use on the specified render target.

1
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@ -8,6 +8,7 @@ using Avalonia.Platform;
using Avalonia.Win32.Interop;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using DrawingContext = Avalonia.Media.DrawingContext;
using DwFactory = SharpDX.DirectWrite.Factory;

23
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -163,6 +163,29 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Window_Resize_Notification_Should_Notify_Renderer_Dirty()
{
var renderer = new Mock<IRenderer>();
var services = TestServices.StyledWindow.With(renderer: (_, __) => renderer.Object);
using (UnitTestApplication.Start(services))
{
var impl = new Mock<ITopLevelImpl>();
impl.SetupAllProperties();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
impl.Setup(x => x.Scaling).Returns(1);
var target = new TestTopLevel(impl.Object);
target.Measure(new Size(123, 456));
Assert.True(target.IsMeasureValid);
impl.Object.Resized(new Size(100, 200));
renderer.Verify(x => x.AddDirty(target));
}
}
[Fact]
public void Activate_Should_Call_Impl_Activate()
{

2
tests/Avalonia.UnitTests/TestRoot.cs

@ -42,7 +42,7 @@ namespace Avalonia.UnitTests
public int NameScopeUnregisteredSubscribers { get; private set; }
public Size ClientSize => new Size(100, 100);
public Size ClientSize { get; set;} = new Size(100, 100);
public Size MaxClientSize => Size.Infinity;

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

@ -118,6 +118,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
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));
});

45
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -510,6 +510,51 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
}
[Fact]
public void Resizing_Scene_Should_Add_DirtyRects()
{
using (TestApplication())
{
Decorator decorator;
Border border;
Canvas canvas;
var tree = new TestRoot
{
Child = decorator = new Decorator
{
Margin = new Thickness(0, 10, 0, 0),
Child = border = new Border
{
Opacity = 0.5,
Background = Brushes.Red,
Child = canvas = new Canvas(),
}
}
};
var scene = new Scene(tree);
var sceneBuilder = new SceneBuilder();
sceneBuilder.UpdateAll(scene);
Assert.Equal(new Size(100, 100), scene.Size);
tree.ClientSize = new Size(110, 120);
scene = scene.Clone();
sceneBuilder.Update(scene, tree);
Assert.Equal(new Size(110, 120), scene.Size);
var expected = new[]
{
new Rect(100, 0, 10, 100),
new Rect(0, 100, 110, 20),
};
Assert.Equal(expected, scene.Layers[tree].Dirty.ToArray());
Assert.Equal(expected, scene.Layers[border].Dirty.ToArray());
}
}
private IDisposable TestApplication()
{
return UnitTestApplication.Start(

Loading…
Cancel
Save