diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs index f1b5032cd3..45062632c7 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs @@ -11,10 +11,16 @@ namespace Avalonia.Rendering.Composition.Server; internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisual { +#if DEBUG + public readonly Visual UiVisual; +#endif private CompositionDrawList? _renderCommands; - public ServerCompositionDrawListVisual(ServerCompositor compositor) : base(compositor) + public ServerCompositionDrawListVisual(ServerCompositor compositor, Visual v) : base(compositor) { +#if DEBUG + UiVisual = v; +#endif } Rect? _contentBounds; @@ -47,12 +53,19 @@ internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisua base.DeserializeChangesCore(reader, commitedAt); } - protected override void RenderCore(CompositorDrawingContextProxy canvas, Matrix4x4 transform) + protected override void RenderCore(CompositorDrawingContextProxy canvas) { if (_renderCommands != null) { _renderCommands.Render(canvas); } - base.RenderCore(canvas, transform); + base.RenderCore(canvas); } + +#if DEBUG + public override string ToString() + { + return UiVisual.GetType().ToString(); + } +#endif } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index e8c417a98d..fb0d6fce60 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -26,6 +26,7 @@ namespace Avalonia.Rendering.Composition.Server private bool _redrawRequested; private bool _disposed; private HashSet _attachedVisuals = new(); + private Queue _adornerUpdateQueue = new(); public ReadbackIndices Readback { get; } = new(); @@ -80,6 +81,12 @@ namespace Avalonia.Rendering.Composition.Server // Update happens in a separate phase to extend dirty rect if needed Root.Update(this, Matrix4x4.Identity); + + while (_adornerUpdateQueue.Count > 0) + { + var adorner = _adornerUpdateQueue.Dequeue(); + adorner.Update(this, adorner.AdornedVisual?.GlobalTransformMatrix ?? Matrix4x4.Identity); + } Readback.CompleteWrite(Revision); @@ -108,7 +115,7 @@ namespace Avalonia.Rendering.Composition.Server { context.PushClip(_dirtyRect); context.Clear(Colors.Transparent); - Root.Render(new CompositorDrawingContextProxy(context, visualBrushHelper), Root.CombinedTransformMatrix); + Root.Render(new CompositorDrawingContextProxy(context, visualBrushHelper)); context.PopClip(); } } @@ -147,6 +154,7 @@ namespace Avalonia.Rendering.Composition.Server { var snapped = SnapToDevicePixels(rect, Scaling); _dirtyRect = _dirtyRect.Union(snapped); + _redrawRequested = true; } public void Invalidate() @@ -176,6 +184,10 @@ namespace Avalonia.Rendering.Composition.Server { if (_attachedVisuals.Remove(visual) && IsEnabled) visual.Deactivate(); + if(visual.IsVisibleInFrame) + AddDirtyRect(visual.TransformedBounds); } + + public void EnqueueAdornerUpdate(ServerCompositionVisual visual) => _adornerUpdateQueue.Enqueue(visual); } } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerContainerVisual.cs index a277450214..bcfcfbe4f2 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerContainerVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerContainerVisual.cs @@ -7,24 +7,26 @@ namespace Avalonia.Rendering.Composition.Server { public ServerCompositionVisualCollection Children { get; private set; } = null!; - protected override void RenderCore(CompositorDrawingContextProxy canvas, Matrix4x4 transform) + protected override void RenderCore(CompositorDrawingContextProxy canvas) { - base.RenderCore(canvas, transform); + base.RenderCore(canvas); foreach (var ch in Children) { - var t = transform; - - t = ch.CombinedTransformMatrix * t; - ch.Render(canvas, t); + ch.Render(canvas); } } public override void Update(ServerCompositionTarget root, Matrix4x4 transform) { base.Update(root, transform); - foreach (var child in Children) - child.Update(root, GlobalTransformMatrix); + foreach (var child in Children) + { + if (child.AdornedVisual != null) + root.EnqueueAdornerUpdate(child); + else + child.Update(root, GlobalTransformMatrix); + } } partial void Initialize() diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerSolidColorVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerSolidColorVisual.cs index 786779bb2d..5720d80304 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerSolidColorVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerSolidColorVisual.cs @@ -6,11 +6,10 @@ namespace Avalonia.Rendering.Composition.Server { internal partial class ServerCompositionSolidColorVisual { - protected override void RenderCore(CompositorDrawingContextProxy canvas, Matrix4x4 transform) + protected override void RenderCore(CompositorDrawingContextProxy canvas) { - canvas.Transform = MatrixUtils.ToMatrix(transform); canvas.DrawRectangle(new ImmutableSolidColorBrush(Color), null, new RoundedRect(new Rect(new Size(Size)))); - base.RenderCore(canvas, transform); + base.RenderCore(canvas); } } } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerSpriteVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerSpriteVisual.cs index c658dc8ae3..2f4c446cfa 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerSpriteVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerSpriteVisual.cs @@ -6,7 +6,7 @@ namespace Avalonia.Rendering.Composition.Server internal partial class ServerCompositionSpriteVisual { - protected override void RenderCore(CompositorDrawingContextProxy canvas, Matrix4x4 transform) + protected override void RenderCore(CompositorDrawingContextProxy canvas) { if (Brush != null) { @@ -14,7 +14,7 @@ namespace Avalonia.Rendering.Composition.Server //canvas.FillRect((Vector2)Size, (ICbBrush)Brush.Brush!); } - base.RenderCore(canvas, transform); + base.RenderCore(canvas); } } } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerVisual.cs index 5c7067bfb1..0b2ba6922b 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerVisual.cs @@ -8,17 +8,18 @@ namespace Avalonia.Rendering.Composition.Server { private bool _isDirty; private bool _isBackface; - protected virtual void RenderCore(CompositorDrawingContextProxy canvas, Matrix4x4 transform) + protected virtual void RenderCore(CompositorDrawingContextProxy canvas) { } - public void Render(CompositorDrawingContextProxy canvas, Matrix4x4 transform) + public void Render(CompositorDrawingContextProxy canvas) { if(Visible == false) return; if(Opacity == 0) return; + var transform = GlobalTransformMatrix; canvas.PreTransform = MatrixUtils.ToMatrix(transform); canvas.Transform = Matrix.Identity; if (Opacity != 1) @@ -29,8 +30,9 @@ namespace Avalonia.Rendering.Composition.Server canvas.PushGeometryClip(Clip); //TODO: Check clip - RenderCore(canvas, transform); + RenderCore(canvas); + // Hack to force invalidation of SKMatrix canvas.PreTransform = MatrixUtils.ToMatrix(transform); canvas.Transform = Matrix.Identity; @@ -60,7 +62,9 @@ namespace Avalonia.Rendering.Composition.Server public virtual void Update(ServerCompositionTarget root, Matrix4x4 transform) { // Calculate new parent-relative transform - CombinedTransformMatrix = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, TransformMatrix, + CombinedTransformMatrix = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, + // HACK: Ignore RenderTransform set by the adorner layer + AdornedVisual != null ? Matrix4x4.Identity : TransformMatrix, Scale, RotationAngle, Orientation, Offset); var newTransform = CombinedTransformMatrix * transform; diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index c11ea2c40a..2f4fbbd3f0 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -470,7 +470,7 @@ namespace Avalonia { if (CompositionVisual == null || CompositionVisual.Compositor != compositor) CompositionVisual = new CompositionDrawListVisual(compositor, - new ServerCompositionDrawListVisual(compositor.Server), this); + new ServerCompositionDrawListVisual(compositor.Server, this), this); return CompositionVisual; } diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index a7ae341bb3..b86ce35d5f 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -22,6 +22,7 @@ + diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index 5ad4e39baf..57fb7226e8 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -164,6 +164,9 @@ namespace Avalonia.Controls.Primitives private void UpdateAdornedElement(Visual adorner, Visual? adorned) { + if (adorner.CompositionVisual != null) + adorner.CompositionVisual.AdornedVisual = adorned?.CompositionVisual; + var info = adorner.GetValue(s_adornedElementInfoProperty); if (info != null) @@ -184,11 +187,18 @@ namespace Avalonia.Controls.Primitives adorner.SetValue(s_adornedElementInfoProperty, info); } - info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x => - { - info.Bounds = x; - InvalidateMeasure(); - }); + if (adorner.CompositionVisual != null) + info.Subscription = adorned.GetObservable(BoundsProperty).Subscribe(x => + { + info.Bounds = new TransformedBounds(new Rect(adorned.Bounds.Size), new Rect(adorned.Bounds.Size), Matrix.Identity); + InvalidateMeasure(); + }); + else + info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x => + { + info.Bounds = x; + InvalidateMeasure(); + }); } } diff --git a/src/Avalonia.SourceGenerator/CompositionGenerator/Config.cs b/src/Avalonia.SourceGenerator/CompositionGenerator/Config.cs index 096864e52a..d1fc691a8b 100644 --- a/src/Avalonia.SourceGenerator/CompositionGenerator/Config.cs +++ b/src/Avalonia.SourceGenerator/CompositionGenerator/Config.cs @@ -110,6 +110,8 @@ namespace Avalonia.SourceGenerator.CompositionGenerator public bool Animated { get; set; } [XmlAttribute] public bool InternalSet { get; set; } + [XmlAttribute] + public bool Internal { get; set; } } public class GAnimationType diff --git a/src/Avalonia.SourceGenerator/CompositionGenerator/Generator.cs b/src/Avalonia.SourceGenerator/CompositionGenerator/Generator.cs index 3c38c0331e..f8f86265cf 100644 --- a/src/Avalonia.SourceGenerator/CompositionGenerator/Generator.cs +++ b/src/Avalonia.SourceGenerator/CompositionGenerator/Generator.cs @@ -378,7 +378,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator return client .AddMembers(DeclareField(prop.Type, fieldName)) .AddMembers(PropertyDeclaration(propType, prop.Name) - .AddModifiers(SyntaxKind.PublicKeyword) + .AddModifiers(prop.Internal ? SyntaxKind.InternalKeyword : SyntaxKind.PublicKeyword) .AddAccessorListAccessors( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, Block(ReturnStatement(IdentifierName(fieldName)))),