diff --git a/src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs b/src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs new file mode 100644 index 0000000000..6dd5337b27 --- /dev/null +++ b/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()) + return Disposable.Create(() => _window.Unlock()); + return null; + } + } +} diff --git a/src/Avalonia.Native/DeferredRendererProxy.cs b/src/Avalonia.Native/DeferredRendererProxy.cs deleted file mode 100644 index 126a395f73..0000000000 --- a/src/Avalonia.Native/DeferredRendererProxy.cs +++ /dev/null @@ -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().Add(this); - } - - void IRenderLoop.Remove(IRenderLoopTask i) - { - AvaloniaLocator.Current.GetService().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 HitTest(Point p, IVisual root, Func 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(); - } - } - } -} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index aaaba44fff..629c91a2e8 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -186,10 +186,6 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.RunRenderPriorityJobs() { - if (_parent._deferredRendering - && _parent._lastRenderedLogicalSize != _parent.ClientSize) - // Hack to trigger Paint event on the renderer - _parent.Paint?.Invoke(new Rect()); Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); } } @@ -245,7 +241,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(), + rendererLock: + _gpu ? new AvaloniaNativeDeferredRendererLock(_native) : null); return new ImmediateRenderer(root); } diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 5a6f086f7e..40be0055d3 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -36,6 +36,7 @@ namespace Avalonia.Rendering private int _lastSceneId = -1; private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects(); private IRef _currentDraw; + private readonly IDeferredRendererLock _lock; /// /// Initializes a new instance of the class. @@ -44,11 +45,13 @@ namespace Avalonia.Rendering /// The render loop. /// The scene builder to use. Optional. /// The dispatcher to use. Optional. + /// Lock object used before trying to access render target public DeferredRenderer( IRenderRoot root, IRenderLoop renderLoop, ISceneBuilder sceneBuilder = null, - IDispatcher dispatcher = null) + IDispatcher dispatcher = null, + IDeferredRendererLock rendererLock = null) { Contract.Requires(root != null); @@ -57,6 +60,7 @@ namespace Avalonia.Rendering _sceneBuilder = sceneBuilder ?? new SceneBuilder(); Layers = new RenderLayers(); _renderLoop = renderLoop; + _lock = rendererLock ?? new ManagedDeferredRendererLock(); } /// @@ -137,6 +141,10 @@ namespace Avalonia.Rendering /// public void Paint(Rect rect) { + var t = (IRenderLoopTask)this; + if(t.NeedsUpdate) + UpdateScene(); + t.Render(); } /// @@ -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(); } /// @@ -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 { diff --git a/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs new file mode 100644 index 0000000000..b2ed3afe6a --- /dev/null +++ b/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs @@ -0,0 +1,9 @@ +using System; + +namespace Avalonia.Rendering +{ + public interface IDeferredRendererLock + { + IDisposable TryLock(); + } +} diff --git a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs b/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs new file mode 100644 index 0000000000..75d8f036d6 --- /dev/null +++ b/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; + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs index 304d17b33e..95352fbed8 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ b/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; } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 103ca57cdb..cf7ebeed92 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -27,11 +27,11 @@ namespace Avalonia { public static T UseWin32( this T builder, - bool deferredRendering = true) + bool deferredRendering = true, bool allowEgl = false) where T : AppBuilderBase, new() { return builder.UseWindowingSubsystem( - () => Win32.Win32Platform.Initialize(deferredRendering), + () => Win32.Win32Platform.Initialize(deferredRendering, allowEgl), "Win32"); } } @@ -66,7 +66,7 @@ namespace Avalonia.Win32 Initialize(true); } - public static void Initialize(bool deferredRendering = true) + public static void Initialize(bool deferredRendering = true, bool allowEgl = false) { AvaloniaLocator.CurrentMutable .Bind().ToSingleton() @@ -80,7 +80,8 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToSingleton() .Bind().ToConstant(s_instance); - Win32GlManager.Initialize(); + if (allowEgl) + Win32GlManager.Initialize(); UseDeferredRendering = deferredRendering; _uiThread = Thread.CurrentThread; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 21986bd2ba..f6100c6492 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -633,7 +633,6 @@ namespace Avalonia.Win32 case UnmanagedMethods.WindowsMessage.WM_PAINT: UnmanagedMethods.PAINTSTRUCT ps; - if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero) { var f = Scaling;