diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs b/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs index d561338a36..baf1bfcddf 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs @@ -25,7 +25,7 @@ namespace Avalonia.Rendering.Composition public void Dispose() { - //Changes.Dispose = true; + RegisterForSerialization(); IsDisposed = true; } @@ -112,7 +112,8 @@ namespace Avalonia.Rendering.Composition private protected virtual void SerializeChangesCore(BatchStreamWriter writer) { - + if (Server is IDisposable) + writer.Write((byte)(IsDisposed ? 1 : 0)); } } } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index 7567eba534..9513fb58fa 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -9,7 +9,7 @@ using Avalonia.Utilities; namespace Avalonia.Rendering.Composition.Server { - internal partial class ServerCompositionTarget + internal partial class ServerCompositionTarget : IDisposable { private readonly ServerCompositor _compositor; private readonly Func _renderTargetFactory; @@ -23,6 +23,7 @@ namespace Avalonia.Rendering.Composition.Server private Size _layerSize; private IDrawingContextLayerImpl? _layer; private bool _redrawRequested; + private bool _disposed; public ReadbackIndices Readback { get; } = new(); @@ -50,6 +51,12 @@ namespace Avalonia.Rendering.Composition.Server public void Render() { + if (_disposed) + { + Compositor.RemoveCompositionTarget(this); + return; + } + if (Root == null) return; _renderTarget ??= _renderTargetFactory(); @@ -69,6 +76,12 @@ namespace Avalonia.Rendering.Composition.Server _redrawRequested = false; using (var targetContext = _renderTarget.CreateDrawingContext(null)) { + // This is a hack to safely dispose layer created by some other render target + // because we can only dispose layers with the corresponding GPU context being + // active on the current thread + while (Compositor.LayersToDispose.Count > 0) + Compositor.LayersToDispose.Dequeue().Dispose(); + var layerSize = Size * Scaling; if (layerSize != _layerSize || _layer == null) { @@ -130,5 +143,17 @@ namespace Avalonia.Rendering.Composition.Server { _redrawRequested = true; } + + public void Dispose() + { + _disposed = true; + if (_layer != null) + { + Compositor.LayersToDispose.Enqueue(_layer); + _layer = null; + } + _renderTarget?.Dispose(); + _renderTarget = null; + } } } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs index f7de704b23..241be479ff 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using Avalonia.Platform; using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Transport; @@ -19,6 +20,7 @@ namespace Avalonia.Rendering.Composition.Server private List _animationsToUpdate = new(); private BatchStreamObjectPool _batchObjectPool; private BatchStreamMemoryPool _batchMemoryPool; + public Queue LayersToDispose { get; } = new(); public ServerCompositor(IRenderLoop renderLoop, BatchStreamObjectPool batchObjectPool, BatchStreamMemoryPool batchMemoryPool) { diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs index 16f57d9059..dde711c3b5 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs @@ -107,7 +107,9 @@ namespace Avalonia.Rendering.Composition.Server protected virtual void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt) { - + if (this is IDisposable disp + && reader.Read() == 1) + disp.Dispose(); } public void DeserializeChanges(BatchStreamReader reader, Batch batch)