diff --git a/src/Avalonia.Base/Media/MediaContext.Compositor.cs b/src/Avalonia.Base/Media/MediaContext.Compositor.cs index ffe73c795c..32ee1ab932 100644 --- a/src/Avalonia.Base/Media/MediaContext.Compositor.cs +++ b/src/Avalonia.Base/Media/MediaContext.Compositor.cs @@ -98,6 +98,13 @@ partial class MediaContext if (AvaloniaLocator.Current.GetService() == null) return; + using var _ = NonPumpingLockHelper.Use(); + SyncWaitCompositorBatch(compositor, CommitCompositor(compositor), waitFullRender, catchExceptions); + } + + private void SyncWaitCompositorBatch(Compositor compositor, CompositionBatch batch, + bool waitFullRender, bool catchExceptions) + { using var _ = NonPumpingLockHelper.Use(); if (compositor is { @@ -105,12 +112,10 @@ partial class MediaContext Loop.RunsInBackground: true }) { - var batch = CommitCompositor(compositor); (waitFullRender ? batch.Rendered : batch.Processed).Wait(); } else { - CommitCompositor(compositor); compositor.Server.Render(catchExceptions); } } @@ -132,10 +137,15 @@ partial class MediaContext /// public void SyncDisposeCompositionTarget(CompositionTarget compositionTarget) { - compositionTarget.Dispose(); + using var _ = NonPumpingLockHelper.Use(); + + // TODO: We are sending a dispose command outside of the normal commit cycle and we might + // want to ask the compositor to skip any actual rendering and return the control ASAP + // Not sure if we should do that for background thread rendering since it might affect the animation + // smoothness of other windows - // TODO: introduce a way to skip any actual rendering for other targets and only do a dispose? - SyncCommit(compositionTarget.Compositor, false, true); + var oobBatch = compositionTarget.Compositor.OobDispose(compositionTarget); + SyncWaitCompositorBatch(compositionTarget.Compositor, oobBatch, false, true); } /// diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.cs index 4190991b25..257e41f2d6 100644 --- a/src/Avalonia.Base/Rendering/Composition/Compositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Compositor.cs @@ -211,6 +211,28 @@ namespace Avalonia.Rendering.Composition return commit; } } + + /// + /// This method submits a composition with a single dispose command outside the normal + /// commit cycle. This is currently used for disposing CompositionTargets since we need to do that ASAP + /// and without affecting the not yet completed composition batch + /// + internal CompositionBatch OobDispose(CompositionObject obj) + { + using var _ = NonPumpingLockHelper.Use(); + obj.Dispose(); + var batch = new CompositionBatch(); + using (var writer = new BatchStreamWriter(batch.Changes, _batchMemoryPool, _batchObjectPool)) + { + writer.WriteObject(ServerCompositor.RenderThreadDisposeStartMarker); + writer.Write(1); + writer.WriteObject(obj.Server); + } + + batch.CommittedAt = Server.Clock.Elapsed; + _server.EnqueueBatch(batch); + return batch; + } internal void RegisterForSerialization(ICompositorSerializable compositionObject) { diff --git a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs index 62c99a553b..45277ca75f 100644 --- a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Headless; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Rendering; @@ -22,7 +23,8 @@ namespace Avalonia.Base.UnitTests.Input { using var app = UnitTestApplication.Start(new TestServices( inputManager: new InputManager(), - focusManager: new FocusManager())); + focusManager: new FocusManager(), + renderInterface: new HeadlessPlatformRenderInterface())); var renderer = new Mock(); var device = CreatePointerDeviceMock().Object; diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index a4e669cb34..c25816040a 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -153,7 +153,7 @@ namespace Avalonia.ReactiveUI.UnitTests [Fact] public void Activation_For_View_Fetcher_Should_Support_Windows() { - using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (UnitTestApplication.Start(TestServices.StyledWindow)) { var window = new TestWindowWithWhenActivated(); Assert.False(window.Active); @@ -171,7 +171,7 @@ namespace Avalonia.ReactiveUI.UnitTests [Fact] public void Activatable_Window_View_Model_Is_Activated_And_Deactivated() { - using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (UnitTestApplication.Start(TestServices.StyledWindow)) { var viewModel = new ActivatableViewModel(); var window = new ActivatableWindow { ViewModel = viewModel };