Skip to content

Commit

Permalink
Bind Compositor/MediaContext to a fixed UI thread
Browse files Browse the repository at this point in the history
  • Loading branch information
MrJul committed Sep 22, 2023
1 parent 751d43f commit 98dfc26
Show file tree
Hide file tree
Showing 9 changed files with 28 additions and 31 deletions.
8 changes: 3 additions & 5 deletions src/Avalonia.Base/Media/MediaContext.Clock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using System.Diagnostics;
using Avalonia.Animation;
using Avalonia.Reactive;
using Avalonia.Threading;
using Avalonia.Utilities;

namespace Avalonia.Media;

Expand Down Expand Up @@ -33,12 +31,12 @@ public MediaContextClock(MediaContext parent)
public IDisposable Subscribe(IObserver<TimeSpan> observer)
{
_parent.ScheduleRender(false);
Dispatcher.UIThread.VerifyAccess();
_parent._uiThreadDispatcher.VerifyAccess();
_observers.Add(observer);
_newObservers.Add(observer);
return Disposable.Create(() =>
{
Dispatcher.UIThread.VerifyAccess();
_parent._uiThreadDispatcher.VerifyAccess();
_observers.Remove(observer);
});
}
Expand Down Expand Up @@ -79,4 +77,4 @@ public PlayState PlayState
}

public void RequestAnimationFrame(Action<TimeSpan> action) => _clock.RequestAnimationFrame(action);
}
}
6 changes: 2 additions & 4 deletions src/Avalonia.Base/Media/MediaContext.Compositor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Transport;
Expand All @@ -22,7 +20,7 @@ private CompositionBatch CommitCompositor(Compositor compositor)
_requestedCommits.Remove(compositor);
_pendingCompositionBatches[compositor] = commit;
commit.Processed.ContinueWith(_ =>
Dispatcher.UIThread.Post(() => CompositionBatchFinished(compositor, commit), DispatcherPriority.Send));
_uiThreadDispatcher.Post(() => CompositionBatchFinished(compositor, commit), DispatcherPriority.Send));
return commit;
}

Expand Down Expand Up @@ -147,4 +145,4 @@ void ICompositorScheduler.CommitRequested(Compositor compositor)
// TODO: maybe skip the full render here?
ScheduleRender(true);
}
}
}
13 changes: 6 additions & 7 deletions src/Avalonia.Base/Media/MediaContext.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Animation;
using Avalonia.Layout;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
Expand All @@ -23,6 +20,7 @@ internal partial class MediaContext : ICompositorScheduler
private readonly Action _inputMarkerHandler;
private readonly HashSet<Compositor> _requestedCommits = new();
private readonly Dictionary<Compositor, CompositionBatch> _pendingCompositionBatches = new();
private readonly Dispatcher _uiThreadDispatcher;
private record TopLevelInfo(Compositor Compositor, CompositingRenderer Renderer, ILayoutManager LayoutManager);

private List<Action>? _invokeOnRenderCallbacks;
Expand All @@ -38,11 +36,12 @@ private record TopLevelInfo(Compositor Compositor, CompositingRenderer Renderer

private Dictionary<object, TopLevelInfo> _topLevels = new();

private MediaContext()
private MediaContext(Dispatcher uiThreadDispatcher)
{
_render = Render;
_inputMarkerHandler = InputMarkerHandler;
_clock = new(this);
_uiThreadDispatcher = uiThreadDispatcher;
_animationsTimer.Tick += (_, _) =>
{
_animationsTimer.Stop();
Expand All @@ -58,7 +57,7 @@ public static MediaContext Instance
// and need to do a full reset for unit tests
var context = AvaloniaLocator.Current.GetService<MediaContext>();
if (context == null)
AvaloniaLocator.CurrentMutable.Bind<MediaContext>().ToConstant(context = new());
AvaloniaLocator.CurrentMutable.Bind<MediaContext>().ToConstant(context = new(Dispatcher.UIThread));
return context;
}
}
Expand All @@ -84,7 +83,7 @@ private void ScheduleRender(bool now)

if (_inputMarkerOp == null)
{
_inputMarkerOp = Dispatcher.UIThread.InvokeAsync(_inputMarkerHandler, DispatcherPriority.Input);
_inputMarkerOp = _uiThreadDispatcher.InvokeAsync(_inputMarkerHandler, DispatcherPriority.Input);
_inputMarkerAddedAt = _time.Elapsed;
}
else if (!now && (_time.Elapsed - _inputMarkerAddedAt).TotalSeconds > MaxSecondsWithoutInput)
Expand All @@ -93,7 +92,7 @@ private void ScheduleRender(bool now)
}


_nextRenderOp = Dispatcher.UIThread.InvokeAsync(_render, priority);
_nextRenderOp = _uiThreadDispatcher.InvokeAsync(_render, priority);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public Task UpdateAsync(ICompositionImportedGpuImage image)

~CompositionDrawingSurface()
{
Dispatcher.UIThread.Post(Dispose);
Compositor.UIThreadDispatcher.Post(Dispose);
}

public new void Dispose() => base.Dispose();
Expand Down
23 changes: 12 additions & 11 deletions src/Avalonia.Base/Rendering/Composition/Compositor.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Threading.Tasks;
using Avalonia.Animation.Easings;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.Rendering.Composition.Animations;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport;
using Avalonia.Threading;
Expand Down Expand Up @@ -40,6 +37,8 @@ public partial class Compositor

internal IEasing DefaultEasing { get; }

internal Dispatcher UIThreadDispatcher { get; }

private DiagnosticTextRenderer? DiagnosticTextRenderer
{
get
Expand Down Expand Up @@ -69,16 +68,18 @@ public Compositor(IPlatformGraphics? gpu, bool useUiThreadForSynchronousCommits
}

internal Compositor(IRenderLoop loop, IPlatformGraphics? gpu, bool useUiThreadForSynchronousCommits = false)
: this(loop, gpu, useUiThreadForSynchronousCommits, MediaContext.Instance, false)
: this(loop, gpu, useUiThreadForSynchronousCommits, MediaContext.Instance, false, Dispatcher.UIThread)
{
}

internal Compositor(IRenderLoop loop, IPlatformGraphics? gpu,
bool useUiThreadForSynchronousCommits,
ICompositorScheduler scheduler, bool reclaimBuffersImmediately)
ICompositorScheduler scheduler, bool reclaimBuffersImmediately,
Dispatcher uiThreadDispatcher)
{
Loop = loop;
UseUiThreadForSynchronousCommits = useUiThreadForSynchronousCommits;
UIThreadDispatcher = uiThreadDispatcher;
_batchMemoryPool = new(reclaimBuffersImmediately);
_batchObjectPool = new(reclaimBuffersImmediately);
_server = new ServerCompositor(loop, gpu, _batchObjectPool, _batchMemoryPool);
Expand All @@ -99,14 +100,14 @@ internal Compositor(IRenderLoop loop, IPlatformGraphics? gpu,
/// <returns>A CompositionBatch object that provides batch lifetime information</returns>
public CompositionBatch RequestCompositionBatchCommitAsync()
{
Dispatcher.UIThread.VerifyAccess();
UIThreadDispatcher.VerifyAccess();
if (_nextCommit == null)
{
_nextCommit = new ();
var pending = _pendingBatch;
if (pending != null)
pending.Processed.ContinueWith(
_ => Dispatcher.UIThread.Post(_triggerCommitRequested, DispatcherPriority.Send));
_ => UIThreadDispatcher.Post(_triggerCommitRequested, DispatcherPriority.Send));
else
_triggerCommitRequested();
}
Expand All @@ -130,7 +131,7 @@ internal CompositionBatch Commit()

CompositionBatch CommitCore()
{
Dispatcher.UIThread.VerifyAccess();
UIThreadDispatcher.VerifyAccess();
using var noPump = NonPumpingLockHelper.Use();

var commit = _nextCommit ??= new();
Expand Down Expand Up @@ -197,7 +198,7 @@ CompositionBatch CommitCore()

internal void RegisterForSerialization(ICompositorSerializable compositionObject)
{
Dispatcher.UIThread.VerifyAccess();
UIThreadDispatcher.VerifyAccess();
if(_objectSerializationHashSet.Add(compositionObject))
_objectSerializationQueue.Enqueue(compositionObject);
RequestCommitAsync();
Expand All @@ -217,14 +218,14 @@ internal void DisposeOnNextBatch(SimpleServerObject obj)
/// </summary>
public void RequestCompositionUpdate(Action action)
{
Dispatcher.UIThread.VerifyAccess();
UIThreadDispatcher.VerifyAccess();
_invokeBeforeCommitWrite.Enqueue(action);
RequestCommitAsync();
}

internal void PostServerJob(Action job)
{
Dispatcher.UIThread.VerifyAccess();
UIThreadDispatcher.VerifyAccess();
_pendingServerCompositorJobs.Add(job);
RequestCommitAsync();
}
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Base/Threading/Dispatcher.Queue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ internal static void ResetForUnitTests()
job = s_uiThread._queue.Peek();
if (job == null || job.Priority <= DispatcherPriority.Inactive)
{
s_uiThread.ShutdownImpl();
s_uiThread = null;
return;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Avalonia.RenderTests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ protected async Task RenderToFile(Control target, [CallerMemberName] string test
var timer = new ManualRenderTimer();

var compositor = new Compositor(new RenderLoop(timer), null, true,
new DispatcherCompositorScheduler(), true);
new DispatcherCompositorScheduler(), true, Dispatcher.UIThread);
using (var writableBitmap = factory.CreateWriteableBitmap(pixelSize, dpiVector, factory.DefaultPixelFormat, factory.DefaultAlphaFormat))
{
var root = new TestRenderRoot(dpiVector.X / 96, null!);
Expand Down
2 changes: 1 addition & 1 deletion tests/Avalonia.UnitTests/CompositorTestServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public CompositorTestServices(Size? size = null, IPlatformRenderInterface render
AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(Timer);

Compositor = new Compositor(new RenderLoop(Timer), null,
true, new DispatcherCompositorScheduler(), true);
true, new DispatcherCompositorScheduler(), true, Dispatcher.UIThread);
var impl = new TopLevelImpl(Compositor, size ?? new Size(1000, 1000));
TopLevel = new EmbeddableControlRoot(impl)
{
Expand Down
2 changes: 1 addition & 1 deletion tests/Avalonia.UnitTests/RendererMocks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal static Mock<IRenderer> CreateRenderer()

public static Compositor CreateDummyCompositor() =>
new(new RenderLoop(new CompositorTestServices.ManualRenderTimer()), null, false,
new CompositionCommitScheduler(), true);
new CompositionCommitScheduler(), true, Dispatcher.UIThread);

class CompositionCommitScheduler : ICompositorScheduler
{
Expand Down

0 comments on commit 98dfc26

Please sign in to comment.