diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index d091b9072d..1cb29e4e37 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -35,8 +35,10 @@
+
+
diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs
index e79e9341a9..1a6372d346 100644
--- a/src/Avalonia.Base/Input/KeyGesture.cs
+++ b/src/Avalonia.Base/Input/KeyGesture.cs
@@ -96,7 +96,7 @@ namespace Avalonia.Input
public override string ToString()
{
- var s = new StringBuilder();
+ var s = StringBuilderCache.Acquire();
static void Plus(StringBuilder s)
{
@@ -132,7 +132,7 @@ namespace Avalonia.Input
Plus(s);
s.Append(Key);
- return s.ToString();
+ return StringBuilderCache.GetStringAndRelease(s);
}
public bool Matches(KeyEventArgs keyEvent) =>
diff --git a/src/Avalonia.Base/Logging/TraceLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs
index 05e4b8bc5a..fc3897fade 100644
--- a/src/Avalonia.Base/Logging/TraceLogSink.cs
+++ b/src/Avalonia.Base/Logging/TraceLogSink.cs
@@ -46,7 +46,7 @@ namespace Avalonia.Logging
object? source,
object?[]? values)
{
- var result = new StringBuilder(template.Length);
+ var result = StringBuilderCache.Acquire(template.Length);
var r = new CharacterReader(template.AsSpan());
var i = 0;
@@ -89,7 +89,7 @@ namespace Avalonia.Logging
result.Append(')');
}
- return result.ToString();
+ return StringBuilderCache.GetStringAndRelease(result);
}
private static string Format(
@@ -98,7 +98,7 @@ namespace Avalonia.Logging
object? source,
object?[] v)
{
- var result = new StringBuilder(template.Length);
+ var result = StringBuilderCache.Acquire(template.Length);
var r = new CharacterReader(template.AsSpan());
var i = 0;
diff --git a/src/Avalonia.Base/Media/BoxShadow.cs b/src/Avalonia.Base/Media/BoxShadow.cs
index b01f59f5f8..cc97d89cfc 100644
--- a/src/Avalonia.Base/Media/BoxShadow.cs
+++ b/src/Avalonia.Base/Media/BoxShadow.cs
@@ -80,7 +80,7 @@ namespace Avalonia.Media
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
if (IsEmpty)
{
@@ -114,7 +114,7 @@ namespace Avalonia.Media
sb.AppendFormat(" {0}", Color.ToString());
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
public static unsafe BoxShadow Parse(string s)
diff --git a/src/Avalonia.Base/Media/BoxShadows.cs b/src/Avalonia.Base/Media/BoxShadows.cs
index 4614ea4e3c..ab2694389f 100644
--- a/src/Avalonia.Base/Media/BoxShadows.cs
+++ b/src/Avalonia.Base/Media/BoxShadows.cs
@@ -1,7 +1,7 @@
using System;
using System.ComponentModel;
-using System.Text;
using Avalonia.Animation.Animators;
+using Avalonia.Utilities;
namespace Avalonia.Media
{
@@ -45,7 +45,7 @@ namespace Avalonia.Media
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
if (Count == 0)
{
@@ -57,7 +57,7 @@ namespace Avalonia.Media
sb.AppendFormat("{0} ", boxShadow.ToString());
}
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
diff --git a/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs
index 99daaf2143..eb42f6443b 100644
--- a/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs
+++ b/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs
@@ -77,7 +77,7 @@ namespace Avalonia.Media.Fonts
///
public override string ToString()
{
- var builder = new StringBuilder();
+ var builder = StringBuilderCache.Acquire();
for (var index = 0; index < Names.Count; index++)
{
@@ -91,7 +91,7 @@ namespace Avalonia.Media.Fonts
builder.Append(", ");
}
- return builder.ToString();
+ return StringBuilderCache.GetStringAndRelease(builder);
}
///
diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs
index e8a4d6f94f..485bb1db16 100644
--- a/src/Avalonia.Base/Media/HslColor.cs
+++ b/src/Avalonia.Base/Media/HslColor.cs
@@ -202,7 +202,7 @@ namespace Avalonia.Media
///
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
// Use a format similar to CSS. However:
// - To ensure precision is never lost, allow decimal places.
@@ -225,7 +225,7 @@ namespace Avalonia.Media
sb.Append(A.ToString(CultureInfo.InvariantCulture));
sb.Append(')');
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
///
diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs
index 924ef4778b..512e57ae07 100644
--- a/src/Avalonia.Base/Media/HsvColor.cs
+++ b/src/Avalonia.Base/Media/HsvColor.cs
@@ -202,7 +202,7 @@ namespace Avalonia.Media
///
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
// Use a format similar to CSS. However:
// - To ensure precision is never lost, allow decimal places.
@@ -225,7 +225,7 @@ namespace Avalonia.Media
sb.Append(A.ToString(CultureInfo.InvariantCulture));
sb.Append(')');
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
///
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
index e47c8e7e31..98a6a3600e 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
+using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Collections.Pooled;
using Avalonia.Media;
@@ -27,8 +28,7 @@ public class CompositingRenderer : IRendererWithCompositor
private HashSet _dirty = new();
private HashSet _recalculateChildren = new();
private bool _queuedUpdate;
- private Action _update;
- private Action _invalidateScene;
+ private Action _update;
private bool _updating;
internal CompositionTarget CompositionTarget;
@@ -47,7 +47,6 @@ public class CompositingRenderer : IRendererWithCompositor
CompositionTarget = compositor.CreateCompositionTarget(root.CreateRenderTarget);
CompositionTarget.Root = ((Visual)root!.VisualRoot!).AttachToCompositor(compositor);
_update = Update;
- _invalidateScene = InvalidateScene;
}
///
@@ -72,7 +71,7 @@ public class CompositingRenderer : IRendererWithCompositor
if(_queuedUpdate)
return;
_queuedUpdate = true;
- _compositor.InvokeWhenReadyForNextCommit(_update);
+ _compositor.InvokeBeforeNextCommit(_update);
}
///
@@ -151,12 +150,6 @@ public class CompositingRenderer : IRendererWithCompositor
if (compositionChildren.Count == visualChildren.Count)
{
bool mismatch = false;
- if (v.HasNonUniformZIndexChildren)
- {
-
-
- }
-
if (sortedChildren != null)
for (var c = 0; c < visualChildren.Count; c++)
{
@@ -202,9 +195,6 @@ public class CompositingRenderer : IRendererWithCompositor
}
}
- private void InvalidateScene() =>
- SceneInvalidated?.Invoke(this, new SceneInvalidatedEventArgs(_root, new Rect(_root.ClientSize)));
-
private void UpdateCore()
{
_queuedUpdate = false;
@@ -252,10 +242,15 @@ public class CompositingRenderer : IRendererWithCompositor
_recalculateChildren.Clear();
CompositionTarget.Size = _root.ClientSize;
CompositionTarget.Scaling = _root.RenderScaling;
- Compositor.InvokeOnNextCommit(_invalidateScene);
}
- private void Update()
+ private async void TriggerSceneInvalidatedOnBatchCompletion(Task batchCompletion)
+ {
+ await batchCompletion;
+ SceneInvalidated?.Invoke(this, new SceneInvalidatedEventArgs(_root, new Rect(_root.ClientSize)));
+ }
+
+ private void Update(Task batchCompletion)
{
if(_updating)
return;
@@ -276,10 +271,10 @@ public class CompositingRenderer : IRendererWithCompositor
public void Paint(Rect rect)
{
- Update();
+ QueueUpdate();
CompositionTarget.RequestRedraw();
if(RenderOnlyOnRenderThread && Compositor.Loop.RunsInBackground)
- Compositor.RequestCommitAsync().Wait();
+ Compositor.Commit().Wait();
else
CompositionTarget.ImmediateUIThreadRender();
}
@@ -299,7 +294,7 @@ public class CompositingRenderer : IRendererWithCompositor
// Wait for the composition batch to be applied and rendered to guarantee that
// render target is not used anymore and can be safely disposed
if (Compositor.Loop.RunsInBackground)
- _compositor.RequestCommitAsync().Wait();
+ _compositor.Commit().Wait();
}
///
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
index eb499604e0..d8a608651b 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
@@ -129,7 +129,7 @@ namespace Avalonia.Rendering.Composition
///
internal void ImmediateUIThreadRender()
{
- Compositor.RequestCommitAsync();
+ Compositor.Commit();
Compositor.Server.Render();
}
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
new file mode 100644
index 0000000000..1a13d23acd
--- /dev/null
+++ b/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
@@ -0,0 +1,32 @@
+using System;
+using Avalonia.Platform;
+using Avalonia.Rendering.Composition.Animations;
+using Avalonia.Rendering.Composition.Server;
+
+namespace Avalonia.Rendering.Composition;
+
+public partial class Compositor
+{
+ ///
+ /// Creates a new CompositionTarget
+ ///
+ /// A factory method to create IRenderTarget to be called from the render thread
+ ///
+ public CompositionTarget CreateCompositionTarget(Func renderTargetFactory)
+ {
+ return new CompositionTarget(this, new ServerCompositionTarget(_server, renderTargetFactory));
+ }
+
+ public CompositionContainerVisual CreateContainerVisual() => new(this, new ServerCompositionContainerVisual(_server));
+
+ public ExpressionAnimation CreateExpressionAnimation() => new ExpressionAnimation(this);
+
+ public ExpressionAnimation CreateExpressionAnimation(string expression) => new ExpressionAnimation(this)
+ {
+ Expression = expression
+ };
+
+ public ImplicitAnimationCollection CreateImplicitAnimationCollection() => new ImplicitAnimationCollection(this);
+
+ public CompositionAnimationGroup CreateAnimationGroup() => new CompositionAnimationGroup(this);
+}
\ No newline at end of file
diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.cs
index 10360f7874..21c7dd6bf9 100644
--- a/src/Avalonia.Base/Rendering/Composition/Compositor.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Compositor.cs
@@ -10,6 +10,7 @@ using Avalonia.Rendering.Composition.Animations;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport;
using Avalonia.Threading;
+using Avalonia.Utilities;
// Special license applies License.md
@@ -24,16 +25,18 @@ namespace Avalonia.Rendering.Composition
{
internal IRenderLoop Loop { get; }
private ServerCompositor _server;
- private bool _implicitBatchCommitQueued;
- private Action _implicitBatchCommit;
+ private TaskCompletionSource? _nextCommit;
+ private Action _commit;
private BatchStreamObjectPool