diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 7c5f5d75d5a..4db14ba183e 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -142,93 +142,6 @@ public void RecalculateChildren(Visual visual) QueueUpdate(); } - private static void SyncChildren(Visual v) - { - //TODO: Optimize by moving that logic to Visual itself - if(v.CompositionVisual == null) - return; - var compositionChildren = v.CompositionVisual.Children; - var visualChildren = (AvaloniaList)v.GetVisualChildren(); - - PooledList<(Visual visual, int index)>? sortedChildren = null; - if (v.HasNonUniformZIndexChildren && visualChildren.Count > 1) - { - sortedChildren = new (visualChildren.Count); - for (var c = 0; c < visualChildren.Count; c++) - sortedChildren.Add((visualChildren[c], c)); - - // Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements. - sortedChildren.Sort(static (lhs, rhs) => - { - var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex); - return result == 0 ? lhs.index.CompareTo(rhs.index) : result; - }); - } - - var childVisual = v.ChildCompositionVisual; - - // Check if the current visual somehow got migrated to another compositor - if (childVisual != null && childVisual.Compositor != v.CompositionVisual.Compositor) - childVisual = null; - - var expectedCount = visualChildren.Count; - if (childVisual != null) - expectedCount++; - - if (compositionChildren.Count == expectedCount) - { - bool mismatch = false; - if (sortedChildren != null) - for (var c = 0; c < visualChildren.Count; c++) - { - if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual)) - { - mismatch = true; - break; - } - } - else - for (var c = 0; c < visualChildren.Count; c++) - if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual)) - { - mismatch = true; - break; - } - - if (childVisual != null && - !ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual)) - mismatch = true; - - if (!mismatch) - { - sortedChildren?.Dispose(); - return; - } - } - - compositionChildren.Clear(); - if (sortedChildren != null) - { - foreach (var ch in sortedChildren) - { - var compositionChild = ch.visual.CompositionVisual; - if (compositionChild != null) - compositionChildren.Add(compositionChild); - } - sortedChildren.Dispose(); - } - else - foreach (var ch in visualChildren) - { - var compositionChild = ch.CompositionVisual; - if (compositionChild != null) - compositionChildren.Add(compositionChild); - } - - if (childVisual != null) - compositionChildren.Add(childVisual); - } - private void UpdateCore() { _queuedUpdate = false; @@ -238,36 +151,7 @@ private void UpdateCore() if(comp == null) continue; - // TODO: Optimize all of that by moving to the Visual itself, so we won't have to recalculate every time - comp.Offset = new (visual.Bounds.Left, visual.Bounds.Top, 0); - comp.Size = new (visual.Bounds.Width, visual.Bounds.Height); - comp.Visible = visual.IsVisible; - comp.Opacity = (float)visual.Opacity; - comp.ClipToBounds = visual.ClipToBounds; - comp.Clip = visual.Clip?.PlatformImpl; - - - if (!Equals(comp.OpacityMask, visual.OpacityMask)) - comp.OpacityMask = visual.OpacityMask?.ToImmutable(); - - if (!comp.Effect.EffectEquals(visual.Effect)) - comp.Effect = visual.Effect?.ToImmutable(); - - comp.RenderOptions = visual.RenderOptions; - - var renderTransform = Matrix.Identity; - - if (visual.HasMirrorTransform) - renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); - - if (visual.RenderTransform != null) - { - var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); - var offset = Matrix.CreateTranslation(origin); - renderTransform *= (-offset) * visual.RenderTransform.Value * (offset); - } - - comp.TransformMatrix = renderTransform; + visual.SynchronizeCompositionProperties(); try { @@ -279,11 +163,11 @@ private void UpdateCore() _recorder.Reset(); } - SyncChildren(visual); + visual.SynchronizeCompositionChildVisuals(); } foreach(var v in _recalculateChildren) if (!_dirty.Contains(v)) - SyncChildren(v); + v.SynchronizeCompositionChildVisuals(); _dirty.Clear(); _recalculateChildren.Clear(); CompositionTarget.Size = _root.ClientSize; diff --git a/src/Avalonia.Base/Visual.Composition.cs b/src/Avalonia.Base/Visual.Composition.cs new file mode 100644 index 00000000000..d6a4e3e9be0 --- /dev/null +++ b/src/Avalonia.Base/Visual.Composition.cs @@ -0,0 +1,165 @@ +using Avalonia.Collections; +using Avalonia.Collections.Pooled; +using Avalonia.Media; +using Avalonia.Rendering.Composition; +using Avalonia.Rendering.Composition.Server; +using Avalonia.VisualTree; + +namespace Avalonia; + +public partial class Visual +{ + internal CompositionDrawListVisual? CompositionVisual { get; private set; } + internal CompositionVisual? ChildCompositionVisual { get; set; } + + + private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor) + => new CompositionDrawListVisual(compositor, + new ServerCompositionDrawListVisual(compositor.Server, this), this); + + internal CompositionVisual AttachToCompositor(Compositor compositor) + { + if (CompositionVisual == null || CompositionVisual.Compositor != compositor) + { + CompositionVisual = CreateCompositionVisual(compositor); + } + + return CompositionVisual; + } + + internal virtual void DetachFromCompositor() + { + if (CompositionVisual != null) + { + if (ChildCompositionVisual != null) + CompositionVisual.Children.Remove(ChildCompositionVisual); + + CompositionVisual.DrawList = null; + CompositionVisual = null; + } + } + + internal virtual void SynchronizeCompositionChildVisuals() + { + if(CompositionVisual == null) + return; + var compositionChildren = CompositionVisual.Children; + var visualChildren = (AvaloniaList)VisualChildren; + + PooledList<(Visual visual, int index)>? sortedChildren = null; + if (HasNonUniformZIndexChildren && visualChildren.Count > 1) + { + sortedChildren = new (visualChildren.Count); + for (var c = 0; c < visualChildren.Count; c++) + sortedChildren.Add((visualChildren[c], c)); + + // Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements. + sortedChildren.Sort(static (lhs, rhs) => + { + var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex); + return result == 0 ? lhs.index.CompareTo(rhs.index) : result; + }); + } + + var childVisual = ChildCompositionVisual; + + // Check if the current visual somehow got migrated to another compositor + if (childVisual != null && childVisual.Compositor != CompositionVisual.Compositor) + childVisual = null; + + var expectedCount = visualChildren.Count; + if (childVisual != null) + expectedCount++; + + if (compositionChildren.Count == expectedCount) + { + bool mismatch = false; + if (sortedChildren != null) + for (var c = 0; c < visualChildren.Count; c++) + { + if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual)) + { + mismatch = true; + break; + } + } + else + for (var c = 0; c < visualChildren.Count; c++) + if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual)) + { + mismatch = true; + break; + } + + if (childVisual != null && + !ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual)) + mismatch = true; + + if (!mismatch) + { + sortedChildren?.Dispose(); + return; + } + } + + compositionChildren.Clear(); + if (sortedChildren != null) + { + foreach (var ch in sortedChildren) + { + var compositionChild = ch.visual.CompositionVisual; + if (compositionChild != null) + compositionChildren.Add(compositionChild); + } + sortedChildren.Dispose(); + } + else + foreach (var ch in visualChildren) + { + var compositionChild = ch.CompositionVisual; + if (compositionChild != null) + compositionChildren.Add(compositionChild); + } + + if (childVisual != null) + compositionChildren.Add(childVisual); + } + + internal virtual void SynchronizeCompositionProperties() + { + if(CompositionVisual == null) + return; + var comp = CompositionVisual; + + // TODO: Introduce a dirty mask like WPF has, so we don't overwrite properties every time + + comp.Offset = new (Bounds.Left, Bounds.Top, 0); + comp.Size = new (Bounds.Width, Bounds.Height); + comp.Visible = IsVisible; + comp.Opacity = (float)Opacity; + comp.ClipToBounds = ClipToBounds; + comp.Clip = Clip?.PlatformImpl; + + if (!Equals(comp.OpacityMask, OpacityMask)) + comp.OpacityMask = OpacityMask?.ToImmutable(); + + if (!comp.Effect.EffectEquals(Effect)) + comp.Effect = Effect?.ToImmutable(); + + comp.RenderOptions = RenderOptions; + + var renderTransform = Matrix.Identity; + + if (HasMirrorTransform) + renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, Bounds.Width, 0); + + if (RenderTransform != null) + { + var origin = RenderTransformOrigin.ToPixels(new Size(Bounds.Width, Bounds.Height)); + var offset = Matrix.CreateTranslation(origin); + renderTransform *= (-offset) * RenderTransform.Value * (offset); + } + + comp.TransformMatrix = renderTransform; + } +} \ No newline at end of file diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 1b7975c1543..9774468e163 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -29,7 +29,7 @@ namespace Avalonia /// extension methods defined in . /// [UsableDuringInitialization] - public class Visual : StyledElement + public partial class Visual : StyledElement { /// /// Defines the property. @@ -316,9 +316,6 @@ protected internal IAvaloniaList VisualChildren /// protected internal IRenderRoot? VisualRoot => _visualRoot ?? (this as IRenderRoot); - internal CompositionDrawListVisual? CompositionVisual { get; private set; } - internal CompositionVisual? ChildCompositionVisual { get; set; } - internal RenderOptions RenderOptions { get; set; } internal bool HasNonUniformZIndexChildren { get; private set; } @@ -515,20 +512,6 @@ protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs } } - private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor) - => new CompositionDrawListVisual(compositor, - new ServerCompositionDrawListVisual(compositor.Server, this), this); - - internal CompositionVisual AttachToCompositor(Compositor compositor) - { - if (CompositionVisual == null || CompositionVisual.Compositor != compositor) - { - CompositionVisual = CreateCompositionVisual(compositor); - } - - return CompositionVisual; - } - /// /// Calls the method /// for this control and all of its visual descendants. @@ -547,14 +530,7 @@ protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArg DisableTransitions(); OnDetachedFromVisualTree(e); - if (CompositionVisual != null) - { - if (ChildCompositionVisual != null) - CompositionVisual.Children.Remove(ChildCompositionVisual); - - CompositionVisual.DrawList = null; - CompositionVisual = null; - } + DetachFromCompositor(); DetachedFromVisualTree?.Invoke(this, e); e.Root.Renderer.AddDirty(this);