Skip to content

Commit

Permalink
Merge branch 'feature/custom-visual-sync'
Browse files Browse the repository at this point in the history
  • Loading branch information
kekekeks committed Jul 26, 2023
2 parents 40afe1b + 1fe2c29 commit 688f444
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 145 deletions.
122 changes: 3 additions & 119 deletions src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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;
Expand All @@ -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
{
Expand All @@ -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;
Expand Down
165 changes: 165 additions & 0 deletions src/Avalonia.Base/Visual.Composition.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 26 deletions src/Avalonia.Base/Visual.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -316,9 +316,6 @@ protected internal IAvaloniaList<Visual> VisualChildren
/// </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; }
Expand Down Expand Up @@ -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;
}

/// <summary>
/// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendants.
Expand All @@ -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);
Expand Down

0 comments on commit 688f444

Please sign in to comment.