瀏覽代碼

Add option to draw dirty rects.

Steven Kirk 9 年之前
父節點
當前提交
e222e92340

+ 1 - 1
samples/RenderTest/MainWindow.xaml.cs

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

+ 2 - 0
src/Avalonia.Visuals/Avalonia.Visuals.csproj

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

+ 6 - 1
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.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
-using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Avalonia.Platform;
 
 
 namespace Avalonia.Media
 namespace Avalonia.Media
@@ -17,6 +16,12 @@ namespace Avalonia.Media
         /// </summary>
         /// </summary>
         Matrix Transform { get; set; }
         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>
         /// <summary>
         /// Draws a bitmap image.
         /// Draws a bitmap image.
         /// </summary>
         /// </summary>

+ 131 - 55
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -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)

+ 27 - 0
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 - 0
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 - 0
src/Avalonia.Visuals/Rendering/IRenderer.cs

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

+ 2 - 1
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using Avalonia.Platform;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
 
 
@@ -56,6 +57,6 @@ namespace Avalonia.Rendering
         }
         }
 
 
         public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
         public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
-        IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
     }
     }
 }
 }

+ 1 - 0
src/Avalonia.Visuals/Rendering/Renderer.cs

@@ -25,6 +25,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)
         {
         {

+ 5 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

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

+ 5 - 0
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

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

+ 5 - 0
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

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

+ 6 - 0
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

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