Repository: AvaloniaUI/Avalonia

Issue:
VirtualizingStackPanel with animated ScaleTransform starting from 0 crashes the renderer
**Describe the bug**
When we add ListBox with VirtualizingStackPanel and animation for ScaleTransform (which starting from 0) to visual tree, it will crash the renderer.  
But with the ListBox'es, which initially in the content (e.g. in the Window markup) - no issue.

**To Reproduce**
Repro solution: https://github.com/SKProCH/AvaloniaBugsRepros
Steps to reproduce the behavior:

1. Add listbox animation:
    ```xaml
    <Style Selector="ListBox">
      <Style.Animations>
        <Animation Duration="0:0:3" Easing="SineEaseInOut">
          <KeyFrame Cue="0%">
            <Setter Property="ScaleTransform.ScaleY" Value="0" />
            <Setter Property="ScaleTransform.ScaleX" Value="0" />
          </KeyFrame>
          <KeyFrame Cue="100%">
            <Setter Property="ScaleTransform.ScaleY" Value="1" />
            <Setter Property="ScaleTransform.ScaleX" Value="1" />
          </KeyFrame>
        </Animation>
      </Style.Animations>
    </Style>
    ```
2. Add an ListBox after window load:
    ```csharp
    ContentPresenter1.Content = new ListBox() {
        ItemsSource = new List<object>() {
            new ListBoxItem() { Content = 1 }
        }
    };
    ```
3. See error

**Expected behavior**
ListBox should be rendered properly.

**Screenshots**

https://github.com/AvaloniaUI/Avalonia/assets/29896317/276dab9b-a487-4ed1-adc9-a32fc2114916

In some cases it will render ListBox after resize:  

https://github.com/AvaloniaUI/Avalonia/assets/29896317/e30cf3f2-5b86-4cb5-a230-8339115f3e12


**Desktop (please complete the following information):**

- OS: Windows (but i think any)
- Version: 11.0.2

**Additional context**
If ScaleTranform starting from non-zero value (such as 0.1) - everything works fine.  
Also, i think what ListBoxes which loaded with Window (inside Window markup) renders properly because it actually skips the frame with ScaleTransform 0.

This is related to DialogHost.Avalonia issue: https://github.com/AvaloniaUtils/DialogHost.Avalonia/issues/35
Probably this is `System.InvalidOperationException` is thrown with the message "Transform is not invertible":
![image](https://github.com/AvaloniaUI/Avalonia/assets/29896317/4b6a52cb-4048-4a69-a047-c36a4a621ff5)

And probably related to [Exception in OnApplyTemplate after initial app doesn't rethrown and makes all app logiс hangs](https://github.com/AvaloniaUI/Avalonia/issues/12388) Avalonia issue (since no exception re-throws when renderer crashes).



Hints:

Patch:
diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs
index dee16191d69..cffda3e84c7 100644
--- a/src/Avalonia.Base/Layout/LayoutManager.cs
+++ b/src/Avalonia.Base/Layout/LayoutManager.cs
@@ -423,12 +423,16 @@ private void CalculateEffectiveViewport(Visual target, Visual control, ref Rect
             // Translate the viewport into this control's coordinate space.
             viewport = viewport.Translate(-control.Bounds.Position);
 
-            if (control != target && control.RenderTransform is object)
+            if (control != target && control.RenderTransform is { } transform)
             {
-                var origin = control.RenderTransformOrigin.ToPixels(control.Bounds.Size);
-                var offset = Matrix.CreateTranslation(origin);
-                var renderTransform = (-offset) * control.RenderTransform.Value.Invert() * (offset);
-                viewport = viewport.TransformToAABB(renderTransform);
+                if (transform.Value.TryInvert(out var invertedMatrix))
+                {
+                    var origin = control.RenderTransformOrigin.ToPixels(control.Bounds.Size);
+                    var offset = Matrix.CreateTranslation(origin);
+                    viewport = viewport.TransformToAABB(-offset * invertedMatrix * offset);
+                }
+                else
+                    viewport = default;
             }
         }
 


Test patch:
diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs
index 5a493cb741a..63eb6589ca1 100644
--- a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs
+++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs
@@ -341,6 +341,32 @@ void OnTargetOnEffectiveViewportChanged(object s, EffectiveViewportChangedEventA
             });
         }
 
+        // https://github.com/AvaloniaUI/Avalonia/issues/12452
+        [Fact]
+        public async Task Zero_ScaleTransform_Sets_Empty_EffectiveViewport()
+        {
+            await RunOnUIThread.Execute(async () =>
+            {
+                var effectiveViewport = new Rect(Size.Infinity);
+
+                var root = CreateRoot();
+                var target = new Canvas { Width = 100, Height = 100 };
+                var parent = new Border { Width = 100, Height = 100, Child = target };
+
+                target.EffectiveViewportChanged += (_, e) => effectiveViewport = e.EffectiveViewport;
+
+                root.Child = parent;
+
+                await ExecuteInitialLayoutPass(root);
+
+                parent.RenderTransform = new ScaleTransform(0, 0);
+
+                await ExecuteLayoutPass(root);
+
+                Assert.Equal(new Rect(0, 0, 0, 0), effectiveViewport);
+            });
+        }
+
         private static TestRoot CreateRoot() => new TestRoot { Width = 1200, Height = 900 };
 
         private static Task ExecuteInitialLayoutPass(TestRoot root)