|
@@ -16,10 +16,13 @@ namespace Avalonia.Rendering
|
|
|
private readonly IRenderRoot _root;
|
|
private readonly IRenderRoot _root;
|
|
|
private readonly ISceneBuilder _sceneBuilder;
|
|
private readonly ISceneBuilder _sceneBuilder;
|
|
|
private readonly RenderLayers _layers;
|
|
private readonly RenderLayers _layers;
|
|
|
|
|
+ private readonly IRenderLayerFactory _layerFactory;
|
|
|
|
|
+
|
|
|
private Scene _scene;
|
|
private Scene _scene;
|
|
|
private IRenderTarget _renderTarget;
|
|
private IRenderTarget _renderTarget;
|
|
|
private List<IVisual> _dirty;
|
|
private List<IVisual> _dirty;
|
|
|
private LayerDirtyRects _dirtyRects;
|
|
private LayerDirtyRects _dirtyRects;
|
|
|
|
|
+ private IRenderTargetBitmapImpl _overlay;
|
|
|
private bool _updateQueued;
|
|
private bool _updateQueued;
|
|
|
private bool _rendering;
|
|
private bool _rendering;
|
|
|
|
|
|
|
@@ -28,6 +31,7 @@ namespace Avalonia.Rendering
|
|
|
private int _framesThisSecond;
|
|
private int _framesThisSecond;
|
|
|
private int _fps;
|
|
private int _fps;
|
|
|
private TimeSpan _lastFpsUpdate;
|
|
private TimeSpan _lastFpsUpdate;
|
|
|
|
|
+ private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
|
|
|
|
|
|
|
|
public DeferredRenderer(
|
|
public DeferredRenderer(
|
|
|
IRenderRoot root,
|
|
IRenderRoot root,
|
|
@@ -42,7 +46,8 @@ namespace Avalonia.Rendering
|
|
|
_root = root;
|
|
_root = root;
|
|
|
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
|
|
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
|
|
|
_scene = new Scene(root);
|
|
_scene = new Scene(root);
|
|
|
- _layers = new RenderLayers(layerFactory ?? new DefaultRenderLayerFactory());
|
|
|
|
|
|
|
+ _layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
|
|
|
|
|
+ _layers = new RenderLayers(_layerFactory);
|
|
|
|
|
|
|
|
if (renderLoop != null)
|
|
if (renderLoop != null)
|
|
|
{
|
|
{
|
|
@@ -52,6 +57,7 @@ namespace Avalonia.Rendering
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public bool DrawFps { get; set; }
|
|
public bool DrawFps { get; set; }
|
|
|
|
|
+ public bool DrawDirtyRects { get; set; }
|
|
|
|
|
|
|
|
public void AddDirty(IVisual visual)
|
|
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 now = _stopwatch.Elapsed;
|
|
|
var elapsed = now - _lastFpsUpdate;
|
|
var elapsed = now - _lastFpsUpdate;
|
|
@@ -121,7 +184,7 @@ namespace Avalonia.Rendering
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var pt = new Point(40, 40);
|
|
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,
|
|
Size.Infinity,
|
|
|
FontStyle.Normal,
|
|
FontStyle.Normal,
|
|
|
TextAlignment.Left,
|
|
TextAlignment.Left,
|
|
@@ -132,6 +195,57 @@ namespace Avalonia.Rendering
|
|
|
context.DrawText(Brushes.Black, pt, txt.PlatformImpl);
|
|
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()
|
|
private void UpdateScene()
|
|
|
{
|
|
{
|
|
|
Dispatcher.UIThread.VerifyAccess();
|
|
Dispatcher.UIThread.VerifyAccess();
|
|
@@ -186,10 +300,10 @@ namespace Avalonia.Rendering
|
|
|
|
|
|
|
|
_rendering = true;
|
|
_rendering = true;
|
|
|
_totalFrames++;
|
|
_totalFrames++;
|
|
|
|
|
+ _dirtyRectsDisplay.Tick();
|
|
|
|
|
|
|
|
Scene scene;
|
|
Scene scene;
|
|
|
LayerDirtyRects dirtyRects;
|
|
LayerDirtyRects dirtyRects;
|
|
|
- int updateCount = 0;
|
|
|
|
|
|
|
|
|
|
lock (_scene)
|
|
lock (_scene)
|
|
|
{
|
|
{
|
|
@@ -197,63 +311,25 @@ namespace Avalonia.Rendering
|
|
|
dirtyRects = _dirtyRects;
|
|
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)
|
|
private IRenderTargetBitmapImpl GetRenderTargetForLayer(IVisual layerRoot)
|