diff --git a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs new file mode 100644 index 0000000000..c6845485dc --- /dev/null +++ b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace Avalonia.Threading +{ + public class ThreadSafeObjectPool where T : class, new() + { + private Stack _stack = new Stack(); + private object _lock = new object(); + public static ThreadSafeObjectPool Default { get; } = new ThreadSafeObjectPool(); + + public T Get() + { + lock (_lock) + { + if(_stack.Count == 0) + return new T(); + return _stack.Pop(); + } + } + + public void Return(T obj) + { + lock (_stack) + { + _stack.Push(obj); + } + } + } +} diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs index 6b19167381..e7d6df5a93 100644 --- a/src/Avalonia.Visuals/Media/DrawingContext.cs +++ b/src/Avalonia.Visuals/Media/DrawingContext.cs @@ -2,23 +2,26 @@ using System; using System.Collections.Generic; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Avalonia.Threading; using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media { public sealed class DrawingContext : IDisposable { + private readonly bool _ownsImpl; private int _currentLevel; - static readonly Stack> StateStackPool = new Stack>(); - static readonly Stack> TransformStackPool = new Stack>(); + private static ThreadSafeObjectPool> StateStackPool { get; } = + ThreadSafeObjectPool>.Default; - private Stack _states = StateStackPool.Count == 0 ? new Stack() : StateStackPool.Pop(); + private static ThreadSafeObjectPool> TransformStackPool { get; } = + ThreadSafeObjectPool>.Default; - private Stack _transformContainers = TransformStackPool.Count == 0 - ? new Stack() - : TransformStackPool.Pop(); + private Stack _states = StateStackPool.Get(); + + private Stack _transformContainers = TransformStackPool.Get(); readonly struct TransformContainer { @@ -35,6 +38,13 @@ namespace Avalonia.Media public DrawingContext(IDrawingContextImpl impl) { PlatformImpl = impl; + _ownsImpl = true; + } + + public DrawingContext(IDrawingContextImpl impl, bool ownsImpl) + { + _ownsImpl = ownsImpl; + PlatformImpl = impl; } public IDrawingContextImpl PlatformImpl { get; } @@ -299,11 +309,14 @@ namespace Avalonia.Media { while (_states.Count != 0) _states.Peek().Dispose(); - StateStackPool.Push(_states); + StateStackPool.Return(_states); _states = null; - TransformStackPool.Push(_transformContainers); + if (_transformContainers.Count != 0) + throw new InvalidOperationException("Transform container stack is non-empty"); + TransformStackPool.Return(_transformContainers); _transformContainers = null; - PlatformImpl.Dispose(); + if (_ownsImpl) + PlatformImpl.Dispose(); } private static bool PenIsVisible(Pen pen) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index 8000d413ea..159c3cd0fa 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -220,6 +220,7 @@ namespace Avalonia.Rendering.SceneGraph Transform = Transform, ClipBounds = ClipBounds, ClipToBounds = ClipToBounds, + LayoutBounds = LayoutBounds, GeometryClip = GeometryClip, _opacity = Opacity, OpacityMask = OpacityMask, diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/GlRenderTarget.cs index 3360255dae..81c70e2be7 100644 --- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/GlRenderTarget.cs @@ -35,6 +35,7 @@ namespace Avalonia.Skia gl.ClearStencil(0); gl.ClearColor(0, 0, 0, 0); gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + _grContext.ResetContext(); GRBackendRenderTarget renderTarget = new GRBackendRenderTarget(size.Width, size.Height, disp.SampleCount, disp.StencilSize,