Browse Source

Add option to draw dirty rects.

scenegraph-after-breakage
Steven Kirk 10 years ago
parent
commit
e222e92340
  1. 2
      samples/RenderTest/MainWindow.xaml.cs
  2. 2
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  3. 7
      src/Avalonia.Visuals/Media/IDrawingContextImpl.cs
  4. 186
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  5. 27
      src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs
  6. 43
      src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs
  7. 1
      src/Avalonia.Visuals/Rendering/IRenderer.cs
  8. 3
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  9. 1
      src/Avalonia.Visuals/Rendering/Renderer.cs
  10. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  11. 5
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  12. 5
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  13. 6
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

2
samples/RenderTest/MainWindow.xaml.cs

@ -25,7 +25,7 @@ namespace RenderTest
this.InitializeComponent();
this.CreateAnimations();
this.AttachDevTools();
Renderer.DrawFps = true;
Renderer.DrawFps = Renderer.DrawDirtyRects = true;
}
private void InitializeComponent()

2
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -108,6 +108,8 @@
<Compile Include="RelativeRect.cs" />
<Compile Include="Rendering\DefaultRenderLayerFactory.cs" />
<Compile Include="Rendering\DeferredRenderer.cs" />
<Compile Include="Rendering\DisplayDirtyRect.cs" />
<Compile Include="Rendering\DisplayDirtyRects.cs" />
<Compile Include="Rendering\IRenderer.cs" />
<Compile Include="Rendering\IRendererFactory.cs" />
<Compile Include="Rendering\IRenderLoop.cs" />

7
src/Avalonia.Visuals/Media/IDrawingContextImpl.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace Avalonia.Media
@ -17,6 +16,12 @@ namespace Avalonia.Media
/// </summary>
Matrix Transform { get; set; }
/// <summary>
/// Clears the render target to the specified color.
/// </summary>
/// <param name="color">The color.</param>
void Clear(Color color);
/// <summary>
/// Draws a bitmap image.
/// </summary>

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

@ -16,10 +16,13 @@ namespace Avalonia.Rendering
private readonly IRenderRoot _root;
private readonly ISceneBuilder _sceneBuilder;
private readonly RenderLayers _layers;
private readonly IRenderLayerFactory _layerFactory;
private Scene _scene;
private IRenderTarget _renderTarget;
private List<IVisual> _dirty;
private LayerDirtyRects _dirtyRects;
private IRenderTargetBitmapImpl _overlay;
private bool _updateQueued;
private bool _rendering;
@ -28,6 +31,7 @@ namespace Avalonia.Rendering
private int _framesThisSecond;
private int _fps;
private TimeSpan _lastFpsUpdate;
private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
public DeferredRenderer(
IRenderRoot root,
@ -42,7 +46,8 @@ namespace Avalonia.Rendering
_root = root;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
_scene = new Scene(root);
_layers = new RenderLayers(layerFactory ?? new DefaultRenderLayerFactory());
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
if (renderLoop != null)
{
@ -52,6 +57,7 @@ namespace Avalonia.Rendering
}
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
public void AddDirty(IVisual visual)
{
@ -106,7 +112,64 @@ namespace Avalonia.Rendering
}
}
private void RenderFps(IDrawingContextImpl context, int count)
private void RenderToLayers(Scene scene, LayerDirtyRects dirtyRects)
{
if (dirtyRects != null)
{
foreach (var layer in dirtyRects)
{
var renderTarget = GetRenderTargetForLayer(layer.Key);
var node = (VisualNode)scene.FindNode(layer.Key);
using (var context = renderTarget.CreateDrawingContext())
{
foreach (var rect in layer.Value)
{
context.PushClip(rect);
Render(context, node, layer.Key, rect);
context.PopClip();
if (DrawDirtyRects)
{
_dirtyRectsDisplay.Add(rect);
}
}
}
}
_layers.RemoveUnused(scene);
}
}
private void RenderOverlay()
{
if (DrawFps || DrawDirtyRects)
{
var overlay = GetOverlay(_root.ClientSize);
using (var context = overlay.CreateDrawingContext())
{
context.Clear(Colors.Transparent);
if (DrawFps)
{
RenderFps(context);
}
if (DrawDirtyRects)
{
RenderDirtyRects(context);
}
}
}
else
{
_overlay?.Dispose();
_overlay = null;
}
}
private void RenderFps(IDrawingContextImpl context)
{
var now = _stopwatch.Elapsed;
var elapsed = now - _lastFpsUpdate;
@ -121,7 +184,7 @@ namespace Avalonia.Rendering
}
var pt = new Point(40, 40);
var txt = new FormattedText($"Frame #{_totalFrames} FPS: {_fps} Updates: {count}", "Arial", 18,
var txt = new FormattedText($"Frame #{_totalFrames} FPS: {_fps}", "Arial", 18,
Size.Infinity,
FontStyle.Normal,
TextAlignment.Left,
@ -132,6 +195,57 @@ namespace Avalonia.Rendering
context.DrawText(Brushes.Black, pt, txt.PlatformImpl);
}
private void RenderDirtyRects(IDrawingContextImpl context)
{
foreach (var r in _dirtyRectsDisplay)
{
var brush = new SolidColorBrush(Colors.Magenta, r.Opacity);
context.FillRectangle(brush, r.Rect);
}
}
//private void SaveLayers()
//{
// int i = 0;
// foreach (var layer in _layers)
// {
// layer.Bitmap.Save($"C:\\Users\\Grokys\\Desktop\\layer{i}.png");
// ++i;
// }
//}
private void RenderComposite(Scene scene, LayerDirtyRects dirtyRects)
{
try
{
if (_renderTarget == null)
{
_renderTarget = _root.CreateRenderTarget();
}
using (var context = _renderTarget.CreateDrawingContext())
{
var clientRect = new Rect(_root.ClientSize);
foreach (var layer in _layers)
{
context.DrawImage(layer.Bitmap, layer.LayerRoot.Opacity, clientRect, clientRect);
}
if (_overlay != null)
{
context.DrawImage(_overlay, 0.5, clientRect, clientRect);
}
}
}
catch (RenderTargetCorruptedException ex)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget.Dispose();
_renderTarget = null;
}
}
private void UpdateScene()
{
Dispatcher.UIThread.VerifyAccess();
@ -186,10 +300,10 @@ namespace Avalonia.Rendering
_rendering = true;
_totalFrames++;
_dirtyRectsDisplay.Tick();
Scene scene;
LayerDirtyRects dirtyRects;
int updateCount = 0;
lock (_scene)
{
@ -197,63 +311,25 @@ namespace Avalonia.Rendering
dirtyRects = _dirtyRects;
}
var toRemove = new List<IVisual>();
if (dirtyRects != null)
{
foreach (var layer in dirtyRects)
{
var renderTarget = GetRenderTargetForLayer(layer.Key);
var node = (VisualNode)scene.FindNode(layer.Key);
using (var context = renderTarget.CreateDrawingContext())
{
foreach (var rect in layer.Value)
{
context.PushClip(rect);
Render(context, node, layer.Key, rect);
context.PopClip();
++updateCount;
}
}
}
_layers.RemoveUnused(scene);
}
try
{
if (_renderTarget == null)
{
_renderTarget = _root.CreateRenderTarget();
}
RenderToLayers(scene, dirtyRects);
RenderOverlay();
RenderComposite(scene, dirtyRects);
using (var context = _renderTarget.CreateDrawingContext())
{
if (dirtyRects != null)
{
var rect = new Rect(_root.ClientSize);
_rendering = false;
}
foreach (var layer in _layers)
{
context.DrawImage(layer.Bitmap, layer.LayerRoot.Opacity, rect, rect);
}
}
private IRenderTargetBitmapImpl GetOverlay(Size size)
{
int width = (int)Math.Ceiling(size.Width);
int height = (int)Math.Ceiling(size.Height);
if (DrawFps)
{
RenderFps(context, updateCount);
}
}
}
catch (RenderTargetCorruptedException ex)
if (_overlay == null || _overlay.PixelWidth != width || _overlay.PixelHeight != height)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget.Dispose();
_renderTarget = null;
_overlay?.Dispose();
_overlay = _layerFactory.CreateLayer(null, size);
}
_rendering = false;
return _overlay;
}
private IRenderTargetBitmapImpl GetRenderTargetForLayer(IVisual layerRoot)

27
src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs

@ -0,0 +1,27 @@
using System;
namespace Avalonia.Rendering
{
public class DisplayDirtyRect
{
public static readonly TimeSpan TimeToLive = TimeSpan.FromMilliseconds(500);
public DisplayDirtyRect(Rect rect)
{
Rect = rect;
ResetAge();
}
public Rect Rect { get; }
public DateTimeOffset Born { get; private set; }
public DateTimeOffset Dies { get; private set; }
public double Opacity => (Dies - DateTimeOffset.UtcNow).TotalMilliseconds / TimeToLive.TotalMilliseconds;
public void ResetAge()
{
Born = DateTimeOffset.UtcNow;
Dies = Born + TimeToLive;
}
}
}

43
src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs

@ -0,0 +1,43 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Avalonia.Rendering
{
public class DisplayDirtyRects : IEnumerable<DisplayDirtyRect>
{
private List<DisplayDirtyRect> _inner = new List<DisplayDirtyRect>();
public void Add(Rect rect)
{
foreach (var r in _inner)
{
if (r.Rect == rect)
{
r.ResetAge();
return;
}
}
_inner.Add(new DisplayDirtyRect(rect));
}
public void Tick()
{
var now = DateTimeOffset.UtcNow;
for (var i = _inner.Count - 1; i >= 0; --i)
{
var r = _inner[i];
if (now > r.Dies)
{
_inner.RemoveAt(i);
}
}
}
public IEnumerator<DisplayDirtyRect> GetEnumerator() => _inner.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

1
src/Avalonia.Visuals/Rendering/IRenderer.cs

@ -10,6 +10,7 @@ namespace Avalonia.Rendering
public interface IRenderer : IDisposable
{
bool DrawFps { get; set; }
bool DrawDirtyRects { get; set; }
void AddDirty(IVisual visual);
IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter);

3
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;
@ -56,6 +57,6 @@ namespace Avalonia.Rendering
}
public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

1
src/Avalonia.Visuals/Rendering/Renderer.cs

@ -25,6 +25,7 @@ namespace Avalonia.Rendering
}
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
public void AddDirty(IVisual visual)
{

5
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -51,6 +51,11 @@ namespace Avalonia.Rendering.SceneGraph
return state;
}
public void Clear(Color color)
{
// Cannot clear a deferred scene.
}
public void Dispose()
{
// Nothing to do here as we allocate no unmanaged resources.

5
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@ -61,6 +61,11 @@ namespace Avalonia.Cairo.Media
}
}
public void Clear(Color color)
{
throw new NotImplementedException();
}
/// <summary>
/// Ends a draw operation.
/// </summary>

5
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -21,6 +21,11 @@ namespace Avalonia.Skia
Canvas.Clear();
}
public void Clear(Color color)
{
Canvas.Clear(color.ToSKColor());
}
public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
var impl = (BitmapImpl)source;

6
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -43,6 +43,12 @@ namespace Avalonia.Direct2D1.Media
get { return _renderTarget.Transform.ToAvalonia(); }
set { _renderTarget.Transform = value.ToDirect2D(); }
}
/// <inheritdoc/>
public void Clear(Color color)
{
_renderTarget.Clear(color.ToDirect2D());
}
/// <summary>
/// Ends a draw operation.

Loading…
Cancel
Save