Browse Source

Ported FireInvokeOnRenderCallbacks

pull/11557/head
Nikita Tsukanov 3 years ago
parent
commit
1b2af003c6
  1. 4
      src/Avalonia.Base/Layout/LayoutManager.cs
  2. 64
      src/Avalonia.Base/Media/MediaContext.cs

4
src/Avalonia.Base/Layout/LayoutManager.cs

@ -28,10 +28,12 @@ namespace Avalonia.Layout
private bool _queued;
private bool _running;
private int _totalPassCount;
private Action _invokeOnRender;
public LayoutManager(ILayoutRoot owner)
{
_owner = owner as Layoutable ?? throw new ArgumentNullException(nameof(owner));
_invokeOnRender = ExecuteQueuedLayoutPass;
}
public virtual event EventHandler? LayoutUpdated;
@ -345,7 +347,7 @@ namespace Avalonia.Layout
if (!_queued && !_running)
{
_queued = true;
MediaContext.Instance.QueueLayoutPass(this);
MediaContext.Instance.BeginInvokeOnRender(_invokeOnRender);
}
}

64
src/Avalonia.Base/Media/MediaContext.cs

@ -16,6 +16,7 @@ internal partial class MediaContext : ICompositorScheduler
private DispatcherOperation? _nextRenderOp;
private DispatcherOperation? _inputMarkerOp;
private TimeSpan _inputMarkerAddedAt;
private bool _isRendering;
private bool _animationsAreWaitingForComposition;
private const double MaxSecondsWithoutInput = 1;
private readonly Action _render;
@ -23,7 +24,9 @@ internal partial class MediaContext : ICompositorScheduler
private readonly HashSet<Compositor> _requestedCommits = new();
private readonly Dictionary<Compositor, Batch> _pendingCompositionBatches = new();
private record TopLevelInfo(Compositor Compositor, CompositingRenderer Renderer, ILayoutManager LayoutManager);
private readonly HashSet<LayoutManager> _queuedLayoutManagers = new();
private List<Action>? _invokeOnRenderCallbacks;
private readonly Stack<List<Action>> _invokeOnRenderCallbackListPool = new();
private Dictionary<object, TopLevelInfo> _topLevels = new();
@ -95,11 +98,13 @@ internal partial class MediaContext : ICompositorScheduler
{
try
{
_isRendering = true;
RenderCore();
}
finally
{
_nextRenderOp = null;
_isRendering = false;
}
}
@ -114,10 +119,7 @@ internal partial class MediaContext : ICompositorScheduler
for (var c = 0; c < 10; c++)
{
_clock.HasNewSubscriptions = false;
//TODO: Integrate LayoutManager's attempt limit here
foreach (var layout in _queuedLayoutManagers.ToArray())
layout.ExecuteQueuedLayoutPass();
_queuedLayoutManagers.Clear();
FireInvokeOnRenderCallbacks();
if (_clock.HasNewSubscriptions)
{
@ -160,9 +162,55 @@ internal partial class MediaContext : ICompositorScheduler
}
}
public void QueueLayoutPass(LayoutManager layoutManager)
/// <summary>
/// Calls all _invokeOnRenderCallbacks until no more are added
/// </summary>
private void FireInvokeOnRenderCallbacks()
{
_queuedLayoutManagers.Add(layoutManager);
ScheduleRender(true);
int callbackLoopCount = 0;
int count = _invokeOnRenderCallbacks?.Count ?? 0;
// This outer loop is to re-run layout in case the app causes a layout to get enqueued in response
// to a Loaded event. In this case we would like to re-run layout before we allow render.
do
{
while (count > 0)
{
callbackLoopCount++;
if (callbackLoopCount > 153)
throw new InvalidOperationException("Infinite layout loop detected");
var callbacks = _invokeOnRenderCallbacks!;
_invokeOnRenderCallbacks = null;
for (int i = 0; i < count; i++)
callbacks[i].Invoke();
callbacks.Clear();
_invokeOnRenderCallbackListPool.Push(callbacks);
count = _invokeOnRenderCallbacks?.Count ?? 0;
}
// TODO: port the rest of the Loaded logic later
// Fire all the pending Loaded events before Render happens
// but after the layout storm has subsided
// FireLoadedPendingCallbacks();
count = _invokeOnRenderCallbacks?.Count ?? 0;
}
while (count > 0);
}
public void BeginInvokeOnRender(Action callback)
{
if (_invokeOnRenderCallbacks == null)
_invokeOnRenderCallbacks =
_invokeOnRenderCallbackListPool.Count > 0 ? _invokeOnRenderCallbackListPool.Pop() : new();
_invokeOnRenderCallbacks.Add(callback);
if (!_isRendering)
ScheduleRender(true);
}
}
Loading…
Cancel
Save