|
@@ -2,7 +2,10 @@ using System;
|
|
|
using System.Numerics;
|
|
|
using System.Threading;
|
|
|
using Avalonia.Media;
|
|
|
+using Avalonia.Media.Immutable;
|
|
|
using Avalonia.Platform;
|
|
|
+using Avalonia.Rendering.Composition.Transport;
|
|
|
+using Avalonia.Utilities;
|
|
|
|
|
|
namespace Avalonia.Rendering.Composition.Server
|
|
|
{
|
|
@@ -14,6 +17,13 @@ namespace Avalonia.Rendering.Composition.Server
|
|
|
public long Id { get; }
|
|
|
private ulong _frame = 1;
|
|
|
private IRenderTarget? _renderTarget;
|
|
|
+ private FpsCounter _fpsCounter = new FpsCounter(Typeface.Default.GlyphTypeface);
|
|
|
+ private Rect _dirtyRect;
|
|
|
+ private Random _random = new();
|
|
|
+ private Size _layerSize;
|
|
|
+ private IDrawingContextLayerImpl? _layer;
|
|
|
+ private bool _redrawRequested;
|
|
|
+
|
|
|
|
|
|
public ReadbackIndices Readback { get; } = new();
|
|
|
|
|
@@ -33,6 +43,11 @@ namespace Avalonia.Rendering.Composition.Server
|
|
|
_compositor.RemoveCompositionTarget(this);
|
|
|
}
|
|
|
|
|
|
+ partial void ApplyChangesExtra(CompositionTargetChanges c)
|
|
|
+ {
|
|
|
+ _redrawRequested = true;
|
|
|
+ }
|
|
|
+
|
|
|
public void Render()
|
|
|
{
|
|
|
if (Root == null)
|
|
@@ -40,14 +55,76 @@ namespace Avalonia.Rendering.Composition.Server
|
|
|
_renderTarget ??= _renderTargetFactory();
|
|
|
|
|
|
Compositor.UpdateServerTime();
|
|
|
- using (var context = _renderTarget.CreateDrawingContext(null))
|
|
|
+
|
|
|
+ Root.Update(this, Matrix4x4.Identity);
|
|
|
+
|
|
|
+ if(_dirtyRect.IsEmpty && !_redrawRequested)
|
|
|
+ return;
|
|
|
+ _redrawRequested = false;
|
|
|
+ using (var targetContext = _renderTarget.CreateDrawingContext(null))
|
|
|
{
|
|
|
- context.Clear(Colors.Transparent);
|
|
|
- Root.Render(new CompositorDrawingContextProxy(context), Root.CombinedTransformMatrix);
|
|
|
+ var layerSize = Size * Scaling;
|
|
|
+ if (layerSize != _layerSize || _layer == null)
|
|
|
+ {
|
|
|
+ _layer?.Dispose();
|
|
|
+ _layer = null;
|
|
|
+ _layer = targetContext.CreateLayer(layerSize);
|
|
|
+ _layerSize = layerSize;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!_dirtyRect.IsEmpty)
|
|
|
+ {
|
|
|
+ using (var context = _layer.CreateDrawingContext(null))
|
|
|
+ {
|
|
|
+ context.PushClip(_dirtyRect);
|
|
|
+ context.Clear(Colors.Transparent);
|
|
|
+ Root.Render(new CompositorDrawingContextProxy(context), Root.CombinedTransformMatrix);
|
|
|
+ context.PopClip();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ targetContext.DrawBitmap(RefCountable.CreateUnownedNotClonable(_layer), 1, new Rect(_layerSize),
|
|
|
+ new Rect(_layerSize));
|
|
|
+
|
|
|
+
|
|
|
+ if (DrawDirtyRects)
|
|
|
+ {
|
|
|
+ targetContext.DrawRectangle(new ImmutableSolidColorBrush(
|
|
|
+ new Color(30, (byte)_random.Next(255), (byte)_random.Next(255),
|
|
|
+ (byte)_random.Next(255)))
|
|
|
+ , null, _dirtyRect);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(DrawFps)
|
|
|
+ _fpsCounter.RenderFps(targetContext);
|
|
|
+ _dirtyRect = Rect.Empty;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
Readback.NextWrite(_frame);
|
|
|
_frame++;
|
|
|
}
|
|
|
+
|
|
|
+ private static Rect SnapToDevicePixels(Rect rect, double scale)
|
|
|
+ {
|
|
|
+ return new Rect(
|
|
|
+ new Point(
|
|
|
+ Math.Floor(rect.X * scale) / scale,
|
|
|
+ Math.Floor(rect.Y * scale) / scale),
|
|
|
+ new Point(
|
|
|
+ Math.Ceiling(rect.Right * scale) / scale,
|
|
|
+ Math.Ceiling(rect.Bottom * scale) / scale));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void AddDirtyRect(Rect rect)
|
|
|
+ {
|
|
|
+ var snapped = SnapToDevicePixels(rect, Scaling);
|
|
|
+ _dirtyRect = _dirtyRect.Union(snapped);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Invalidate()
|
|
|
+ {
|
|
|
+ _redrawRequested = true;
|
|
|
+ }
|
|
|
}
|
|
|
}
|