Bläddra i källkod

[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 <[email protected]>
Nikita Tsukanov 7 månader sedan
förälder
incheckning
abd7a88608

+ 15 - 5
src/Avalonia.Base/Media/MediaContext.Compositor.cs

@@ -98,6 +98,13 @@ partial class MediaContext
         if (AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() == 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
     /// </summary>
     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);
     }
     
     /// <summary>

+ 22 - 0
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@@ -211,6 +211,28 @@ namespace Avalonia.Rendering.Composition
                 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)
         {

+ 3 - 1
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<IHitTester>();
             var device = CreatePointerDeviceMock().Object;

+ 2 - 2
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 };