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) public IRenderer CreateRenderer(IRenderRoot root)
{ {
if (_deferredRendering) 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); return new ImmediateRenderer(root);
} }

26
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

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

5
src/Windows/Avalonia.Win32/WindowImpl.cs

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

Loading…
Cancel
Save