diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index f2a09b815e..c6cdf474bb 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -258,14 +258,25 @@ namespace Avalonia.Rendering.SceneGraph } else if (visualChildren.Count > 1) { - var sortedChildren = new IVisual[visualChildren.Count]; - visualChildren.CopyTo(sortedChildren, 0); + var count = visualChildren.Count; + var sortedChildren = new (IVisual visual, int index)[count]; - Array.Sort(sortedChildren, ZIndexComparer.ComparisonInstance); + for (var i = 0; i < count; i++) + { + sortedChildren[i] = (visualChildren[i], i); + } + + // Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements. + Array.Sort(sortedChildren, (lhs, rhs) => + { + var result = ZIndexComparer.Instance.Compare(lhs.visual, rhs.visual); + + return result == 0 ? lhs.index.CompareTo(rhs.index) : result; + }); foreach (var child in sortedChildren) { - var childNode = GetOrCreateChildNode(scene, child, node); + var childNode = GetOrCreateChildNode(scene, child.Item1, node); Update(context, scene, (VisualNode)childNode, clip, forceRecurse); } } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index 42e573c8a5..e317b43efc 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -245,6 +245,34 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph } } + [Fact] + public void Should_Respect_Uniform_ZIndex() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + Panel panel; + + var tree = new TestRoot + { + Child = panel = new Panel() + }; + + for (var i = 0; i < 128; i++) + { + panel.Children.Add(new Border()); + } + + var result = new Scene(tree); + var sceneBuilder = new SceneBuilder(); + sceneBuilder.UpdateAll(result); + + var panelNode = result.FindNode(tree.Child); + var expected = panel.Children.ToArray(); + var actual = panelNode.Children.OfType().Select(x => x.Visual).ToArray(); + Assert.Equal(expected, actual); + } + } + [Fact] public void ClipBounds_Should_Be_In_Global_Coordinates() {