Browse Source

[DRAFT] Send dispose command for CompositionTarget as an OOB batch (#18119)

* Send dispose command for CompositionTarget as an OOB batch

* Make Close_Should_Remove_PointerOver to provide some render interface stub

* why ins't reactive stuff using our headless testing?

* fix?

---------

Co-authored-by: Dan Walmsley <dan@walms.co.uk>
pull/18331/head
Nikita Tsukanov 12 months ago
committed by GitHub
parent
commit
abd7a88608
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 20
      src/Avalonia.Base/Media/MediaContext.Compositor.cs
  2. 22
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  3. 4
      tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
  4. 4
      tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs

20
src/Avalonia.Base/Media/MediaContext.Compositor.cs

@ -98,6 +98,13 @@ partial class MediaContext
if (AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() == null) if (AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() == null)
return; 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(); using var _ = NonPumpingLockHelper.Use();
if (compositor is if (compositor is
{ {
@ -105,12 +112,10 @@ partial class MediaContext
Loop.RunsInBackground: true Loop.RunsInBackground: true
}) })
{ {
var batch = CommitCompositor(compositor);
(waitFullRender ? batch.Rendered : batch.Processed).Wait(); (waitFullRender ? batch.Rendered : batch.Processed).Wait();
} }
else else
{ {
CommitCompositor(compositor);
compositor.Server.Render(catchExceptions); compositor.Server.Render(catchExceptions);
} }
} }
@ -132,10 +137,15 @@ partial class MediaContext
/// </summary> /// </summary>
public void SyncDisposeCompositionTarget(CompositionTarget compositionTarget) 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? var oobBatch = compositionTarget.Compositor.OobDispose(compositionTarget);
SyncCommit(compositionTarget.Compositor, false, true); SyncWaitCompositorBatch(compositionTarget.Compositor, oobBatch, false, true);
} }
/// <summary> /// <summary>

22
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@ -211,6 +211,28 @@ namespace Avalonia.Rendering.Composition
return commit; return commit;
} }
} }
/// <summary>
/// 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
/// </summary>
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) internal void RegisterForSerialization(ICompositorSerializable compositionObject)
{ {

4
tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Headless;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Rendering; using Avalonia.Rendering;
@ -22,7 +23,8 @@ namespace Avalonia.Base.UnitTests.Input
{ {
using var app = UnitTestApplication.Start(new TestServices( using var app = UnitTestApplication.Start(new TestServices(
inputManager: new InputManager(), inputManager: new InputManager(),
focusManager: new FocusManager())); focusManager: new FocusManager(),
renderInterface: new HeadlessPlatformRenderInterface()));
var renderer = new Mock<IHitTester>(); var renderer = new Mock<IHitTester>();
var device = CreatePointerDeviceMock().Object; var device = CreatePointerDeviceMock().Object;

4
tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs

@ -153,7 +153,7 @@ namespace Avalonia.ReactiveUI.UnitTests
[Fact] [Fact]
public void Activation_For_View_Fetcher_Should_Support_Windows() public void Activation_For_View_Fetcher_Should_Support_Windows()
{ {
using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var window = new TestWindowWithWhenActivated(); var window = new TestWindowWithWhenActivated();
Assert.False(window.Active); Assert.False(window.Active);
@ -171,7 +171,7 @@ namespace Avalonia.ReactiveUI.UnitTests
[Fact] [Fact]
public void Activatable_Window_View_Model_Is_Activated_And_Deactivated() 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 viewModel = new ActivatableViewModel();
var window = new ActivatableWindow { ViewModel = viewModel }; var window = new ActivatableWindow { ViewModel = viewModel };

Loading…
Cancel
Save