microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.3k stars 675 forks source link

Visual Translation is "visually" taking effect, but retrieving the value is not correct #9364

Open Youssef1313 opened 7 months ago

Youssef1313 commented 7 months ago

Describe the bug

In a blank new WinUI app:

    <StackPanel>
        <Button Content="Click me" Click="Button_Click" />
        <Rectangle x:Name="SUT" Width="100" Height="100" Fill="Red" />
    </StackPanel>
    public sealed partial class MainWindow : Window
    {
        private Visual _visual;

        public MainWindow()
        {
            this.InitializeComponent();
            _visual = ElementCompositionPreview.GetElementVisual(SUT);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var result = _visual.Properties.TryGetVector3("Translation", out var value);
            ElementCompositionPreview.SetIsTranslationEnabled(SUT, true);
            _visual.StartAnimation("Translation.Y", _visual.Compositor.CreateExpressionAnimation("500"));
        }
    }
  1. On the first button click, result is NotFound, which is expected.
  2. Then, the Border is moved in Y direction 500px, which is expected.
  3. On the second button click, result is Succeeded, which is expected.
  4. Then, value is (0, 0, 0), which doesn't make any sense.

Steps to reproduce the bug

Detailed above.

Expected behavior

The value should correctly be (0, 500, 0)

Screenshots

No response

NuGet package version

None

Windows version

No response

Additional context

No response

JohnnyWestlake commented 6 months ago

This is by design due to using an expression animation - which don't typically update their source properties whilst running. If you actually set the translation rather that using an expression animation, it will return the proper value to you.

Youssef1313 commented 6 months ago

@JohnnyWestlake Why is this the design?

JohnnyWestlake commented 6 months ago

The compositor exists outside the realm of the base XAML framework - setting an expression animation is not setting a proper value to the property being animated, but setting an instruction that will be compiled to run on the GPU and used by the compositor at render time to adjust the property - the compositor will not set any value back to the object because it doesn't care, it's a compiled GPU instruction (which is where the performance benefits come from).

So essentially you've not "set" the translation to 500, you're created a compiled compositor instruction run on the GPU to adjust the translation to 500 at the point of render. The value not being updated on your WinRT object is by-design behaviour and not a bug (and setting a value back could actually interfere with the animation itself depending on your expression). Given composition is a lower-level layer, more performance focused layer than XAML these quirks are things to be lived with for performance reasons.

Now, being a able to coerce what value the compositor "last-used" to render could be a new feature request, however it will always be at least one frame out-of-date as the value is not calculated until the point of actual composition on-screen which could lead to wildly incorrect values (and potentially harm the performance benefits of using expression animations as now the GPU-only render adjustment must call back to the CPU to write object values)