浏览代码

Enforce DeferredRenderer to produce a frame when requested by OS

Nikita Tsukanov 7 年之前
父节点
当前提交
06dd72e9c8

+ 23 - 0
src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Reactive.Disposables;
+using Avalonia.Native.Interop;
+using Avalonia.Rendering;
+
+namespace Avalonia.Native
+{
+    public class AvaloniaNativeDeferredRendererLock : IDeferredRendererLock
+    {
+        private readonly IAvnWindowBase _window;
+
+        public AvaloniaNativeDeferredRendererLock(IAvnWindowBase window)
+        {
+            _window = window;
+        }
+        public IDisposable TryLock()
+        {
+            if (_window.TryLock())
+                Disposable.Create(() => _window.Unlock());
+            return null;
+        }
+    }
+}

+ 0 - 108
src/Avalonia.Native/DeferredRendererProxy.cs

@@ -1,108 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using Avalonia.Native.Interop;
-using Avalonia.Rendering;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Native
-{
-    public class DeferredRendererProxy : IRenderer, IRenderLoopTask, IRenderLoop
-    {
-        public DeferredRendererProxy(IRenderRoot root, IAvnWindowBase window)
-        {
-            if (window != null)
-            {
-                _useLock = true;
-                window.AddRef();
-_window = new IAvnWindowBase(window.NativePointer);
-            }
-            _renderer = new DeferredRenderer(root, this);
-            _rendererTask = (IRenderLoopTask)_renderer;
-        }
-
-        void IRenderLoop.Add(IRenderLoopTask i)
-        {
-            AvaloniaLocator.Current.GetService<IRenderLoop>().Add(this);
-        }
-
-        void IRenderLoop.Remove(IRenderLoopTask i)
-        {
-            AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this);
-        }
-
-        private DeferredRenderer _renderer;
-        private IRenderLoopTask _rendererTask;
-        private IAvnWindowBase _window;
-        private bool _useLock;
-
-        public bool DrawFps{
-            get => _renderer.DrawFps;
-            set => _renderer.DrawFps = value;
-        }
-        public bool DrawDirtyRects 
-        {
-            get => _renderer.DrawDirtyRects;
-            set => _renderer.DrawDirtyRects = value;
-        }
-
-        public bool NeedsUpdate => _rendererTask.NeedsUpdate;
-
-        public void AddDirty(IVisual visual) => _renderer.AddDirty(visual);
-
-        public void Dispose()
-        {
-            _renderer.Dispose();
-            _window?.Dispose();
-            _window = null;
-        }
-        public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
-        {
-            return _renderer.HitTest(p, root, filter);
-        }
-
-        public void Paint(Rect rect)
-        {
-            if (NeedsUpdate)
-            {
-                Update(TimeSpan.FromMilliseconds(Environment.TickCount));
-            }
-
-            Render();
-        }
-
-        public void Resized(Size size) => _renderer.Resized(size);
-
-        public void Start() => _renderer.Start();
-
-        public void Stop() => _renderer.Stop();
-
-        public void Update(TimeSpan time)
-        {
-            _rendererTask.Update(time);
-        }
-
-        public void Render()
-        {
-            if(_useLock)
-            {
-                _rendererTask.Render();
-                return;
-            }
-            if (_window == null)
-                return;
-            if (!_window.TryLock())
-                return;
-            try
-            {
-                _rendererTask.Render();
-            }
-            finally
-            {
-                _window.Unlock();
-            }
-        }
-    }
-}

+ 3 - 1
src/Avalonia.Native/WindowImplBase.cs

@@ -245,7 +245,9 @@ namespace Avalonia.Native
         public IRenderer CreateRenderer(IRenderRoot root)
         {
             if (_deferredRendering)
-                return new DeferredRendererProxy(root, _gpu ? _native : null);
+                return new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>(),
+                    rendererLock:
+                    _gpu ? new AvaloniaNativeDeferredRendererLock(_native) : null);
             return new ImmediateRenderer(root);
         }
 

+ 20 - 6
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -36,6 +36,7 @@ namespace Avalonia.Rendering
         private int _lastSceneId = -1;
         private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
         private IRef<IDrawOperation> _currentDraw;
+        private readonly IDeferredRendererLock _lock;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
@@ -44,11 +45,13 @@ namespace Avalonia.Rendering
         /// <param name="renderLoop">The render loop.</param>
         /// <param name="sceneBuilder">The scene builder to use. Optional.</param>
         /// <param name="dispatcher">The dispatcher to use. Optional.</param>
+        /// <param name="rendererLock">Lock object used before trying to access render target</param>
         public DeferredRenderer(
             IRenderRoot root,
             IRenderLoop renderLoop,
             ISceneBuilder sceneBuilder = null,
-            IDispatcher dispatcher = null)
+            IDispatcher dispatcher = null,
+            IDeferredRendererLock rendererLock = null)
         {
             Contract.Requires<ArgumentNullException>(root != null);
 
@@ -57,6 +60,7 @@ namespace Avalonia.Rendering
             _sceneBuilder = sceneBuilder ?? new SceneBuilder();
             Layers = new RenderLayers();
             _renderLoop = renderLoop;
+            _lock = rendererLock ?? new ManagedDeferredRendererLock();
         }
 
         /// <summary>
@@ -137,6 +141,10 @@ namespace Avalonia.Rendering
         /// <inheritdoc/>
         public void Paint(Rect rect)
         {
+            var t = (IRenderLoopTask)this;
+            if(t.NeedsUpdate)
+                UpdateScene();
+            t.Render();
         }
 
         /// <inheritdoc/>
@@ -170,10 +178,9 @@ namespace Avalonia.Rendering
 
         void IRenderLoopTask.Render()
         {
-            using (var scene = _scene?.Clone())
-            {
-                Render(scene?.Item);
-            }
+            using (var l = _lock.TryLock())
+                if (l != null)
+                    Render();
         }
 
         /// <inheritdoc/>
@@ -197,6 +204,14 @@ namespace Avalonia.Rendering
 
         internal void UnitTestRender() => Render(_scene.Item);
 
+        private void Render()
+        {
+            using (var scene = _scene?.Clone())
+            {
+                Render(scene?.Item);
+            }
+        }
+        
         private void Render(Scene scene)
         {
             bool renderOverlay = DrawDirtyRects || DrawFps;
@@ -415,7 +430,6 @@ namespace Avalonia.Rendering
                 oldScene?.Dispose();
 
                 _dirty.Clear();
-                (_root as IRenderRoot)?.Invalidate(new Rect(scene.Size));
             }
             else
             {

+ 9 - 0
src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace Avalonia.Rendering
+{
+    public interface IDeferredRendererLock
+    {
+        IDisposable TryLock();
+    }
+}

+ 17 - 0
src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Reactive.Disposables;
+using System.Threading;
+
+namespace Avalonia.Rendering
+{
+    public class ManagedDeferredRendererLock : IDeferredRendererLock
+    {
+        private readonly object _lock = new object();
+        public IDisposable TryLock()
+        {
+            if (Monitor.TryEnter(_lock))
+                return Disposable.Create(() => Monitor.Exit(_lock));
+            return null;
+        }
+    }
+}

+ 2 - 0
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -265,6 +265,8 @@ namespace Avalonia.Gtk3
                 Paint?.Invoke(new Rect(ClientSize));
                 CurrentCairoContext = IntPtr.Zero;
             }
+            else
+                Paint?.Invoke(new Rect(ClientSize));
             return true;
         }
 

+ 3 - 2
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -633,8 +633,9 @@ namespace Avalonia.Win32
 
                 case UnmanagedMethods.WindowsMessage.WM_PAINT:
                     UnmanagedMethods.PAINTSTRUCT ps;
-
-                    if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
+                    if (Win32Platform.UseDeferredRendering)
+                        Paint.Invoke(new Rect(ClientSize));
+                    else if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
                     {
                         var f = Scaling;
                         var r = ps.rcPaint;