Browse Source

Enforce DeferredRenderer to produce a frame when requested by OS

pull/2092/head
Nikita Tsukanov 8 years ago
parent
commit
06dd72e9c8
  1. 23
      src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs
  2. 108
      src/Avalonia.Native/DeferredRendererProxy.cs
  3. 4
      src/Avalonia.Native/WindowImplBase.cs
  4. 26
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  5. 9
      src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs
  6. 17
      src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs
  7. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  8. 5
      src/Windows/Avalonia.Win32/WindowImpl.cs

23
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;
}
}
}

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();
}
}
}
}

4
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);
}

26
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
src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs

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

17
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
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;
}

5
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;

Loading…
Cancel
Save