Browse Source

Merge branch 'feature/custom-visual-sync'

pull/12336/head
Nikita Tsukanov 3 years ago
parent
commit
688f444c34
  1. 122
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  2. 165
      src/Avalonia.Base/Visual.Composition.cs
  3. 28
      src/Avalonia.Base/Visual.cs

122
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -142,93 +142,6 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
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<Visual>)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 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
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 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
_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;

165
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<Visual>)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;
}
}

28
src/Avalonia.Base/Visual.cs

@ -29,7 +29,7 @@ namespace Avalonia
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
[UsableDuringInitialization]
public class Visual : StyledElement
public partial class Visual : StyledElement
{
/// <summary>
/// Defines the <see cref="Bounds"/> property.
@ -316,9 +316,6 @@ namespace Avalonia
/// </summary>
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 @@ namespace Avalonia
}
}
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;
}
/// <summary>
/// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendants.
@ -547,14 +530,7 @@ namespace Avalonia
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);

Loading…
Cancel
Save