Skip to content

Commit

Permalink
Fix visual snapshot on headless (#17565)
Browse files Browse the repository at this point in the history
* Check for HasRenderContextAffinity before calling CreateNonAffinedSnapshot

* Add ServerVisualRenderContext.RenderChildren context parameter

* Add Should_Render_To_A_Compositor_Snapshot_Capture test

* Fix xunit tests
  • Loading branch information
maxkatz6 committed Nov 26, 2024
1 parent f63b60c commit fe28a3f
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Rendering/Composition/Compositor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ public async Task<Bitmap> CreateCompositionVisualSnapshot(CompositionVisual visu
throw new InvalidOperationException();
if (visual.Root == null)
throw new InvalidOperationException();
var impl = await InvokeServerJobAsync(() => _server.CreateCompositionVisualSnapshot(visual.Server, scaling), true);
var impl = await InvokeServerJobAsync(() => _server.CreateCompositionVisualSnapshot(visual.Server, scaling, true), true);
return new Bitmap(RefCountable.Create(impl));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ protected override void RenderCore(ServerVisualRenderContext context, LtrbRect c
{
base.RenderCore(context, currentTransformedClip);

foreach (var ch in Children)
if (context.RenderChildren)
{
ch.Render(context, currentTransformedClip);
foreach (var ch in Children)
{
ch.Render(context, currentTransformedClip);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ void RenderRootToContextWithClip(IDrawingContextImpl context, ServerCompositionV

using (var proxy = new CompositorDrawingContextProxy(context))
{
var ctx = new ServerVisualRenderContext(proxy, DirtyRects, false);
var ctx = new ServerVisualRenderContext(proxy, DirtyRects, false, true);
root.Render(ctx, null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public IReadOnlyDictionary<Type, object> RT_GetRenderInterfaceFeatures()
}

public IBitmapImpl CreateCompositionVisualSnapshot(ServerCompositionVisual visual,
double scaling)
double scaling, bool renderChildren)
{
using (RenderInterface.EnsureCurrent())
{
Expand All @@ -69,11 +69,12 @@ public IBitmapImpl CreateCompositionVisualSnapshot(ServerCompositionVisual visua
PostTransform = invertRootTransform * scaleTransform,
Transform = Matrix.Identity
};
var ctx = new ServerVisualRenderContext(proxy, null, true);
var ctx = new ServerVisualRenderContext(proxy, null, true, renderChildren);
visual.Render(ctx, null);
}

if (target is IDrawingContextLayerWithRenderContextAffinityImpl affined)
if (target is IDrawingContextLayerWithRenderContextAffinityImpl affined
&& affined.HasRenderContextAffinity)
return affined.CreateNonAffinedSnapshot();

// We are returning the original target, so prevent it from being disposed
Expand All @@ -87,4 +88,4 @@ public IBitmapImpl CreateCompositionVisualSnapshot(ServerCompositionVisual visua
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ internal class ServerVisualRenderContext
{
public IDirtyRectTracker? DirtyRects { get; }
public bool DetachedRendering { get; }
public bool RenderChildren { get; }
public CompositorDrawingContextProxy Canvas { get; }
private readonly Stack<Matrix>? _transformStack;

public ServerVisualRenderContext(CompositorDrawingContextProxy canvas, IDirtyRectTracker? dirtyRects,
bool detachedRendering)
bool detachedRendering, bool renderChildren)
{
Canvas = canvas;
DirtyRects = dirtyRects;
DetachedRendering = detachedRendering;
RenderChildren = renderChildren;
if (detachedRendering)
{
_transformStack = new();
Expand Down Expand Up @@ -72,4 +74,4 @@ public void Dispose()
}
}

}
}
36 changes: 35 additions & 1 deletion tests/Avalonia.Headless.UnitTests/RenderingTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;

namespace Avalonia.Headless.UnitTests;
Expand Down Expand Up @@ -37,7 +39,7 @@ public void Should_Render_Last_Frame_To_Bitmap()

Assert.NotNull(frame);
}

#if NUNIT
[AvaloniaTest, Timeout(10000)]
#elif XUNIT
Expand Down Expand Up @@ -136,4 +138,36 @@ public void Should_Not_Hang_With_Non_Trivial_Layout()
var frame = window.CaptureRenderedFrame();
Assert.NotNull(frame);
}

#if NUNIT
[AvaloniaTest, Timeout(10000)]
#elif XUNIT
[AvaloniaFact(Timeout = 10000)]
#endif
public async Task Should_Render_To_A_Compositor_Snapshot_Capture()
{
var window = new Window
{
Content = new ContentControl
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Width = 100,
Height = 100,
Background = Brushes.Green
},
SizeToContent = SizeToContent.WidthAndHeight
};

window.Show();

var compositionVisual = ElementComposition.GetElementVisual(window)!;
var snapshot = await compositionVisual.Compositor.CreateCompositionVisualSnapshot(compositionVisual, 1);

Assert.NotNull(snapshot);
// ReSharper disable CompareOfFloatsByEqualityOperator
Assert.True(100 == snapshot.Size.Width);
Assert.True(100 == snapshot.Size.Height);
// ReSharper restore CompareOfFloatsByEqualityOperator
}
}

0 comments on commit fe28a3f

Please sign in to comment.