Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Disable Animations processing when Visual's not visible

Open jmacato opened this issue 1 year ago • 15 comments

This disables the processing of animation values whenever the target visual is not effectively visible.

cc @grokys

jmacato avatar Feb 21 '24 12:02 jmacato

You can test this PR using the following package version. 11.1.999-cibuild0045199-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

avaloniaui-bot avatar Feb 21 '24 12:02 avaloniaui-bot

You can test this PR using the following package version. 11.1.999-cibuild0045220-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

avaloniaui-bot avatar Feb 22 '24 07:02 avaloniaui-bot

I'm not sure about this. The control's properties won't be updated at all while it's not visible, which I would find surprising. What if I am reading the output of some animated value of a control, and that control then becomes invisible?

The animated value in this scenario could very reasonably be affecting another still visible control in the same window. It's also possible (though I wouldn't do this) to bind the animated value to a viewmodel property and expect it to be continually updated.

These are edge cases and I think it's reasonable to disable animation of hidden control by default. But I would recommend also adding a way to configure a control to still animate while hidden.

TomEdwardsEnscape avatar Mar 09 '24 08:03 TomEdwardsEnscape

I think this feature is very valuable but I also agree with @TomEdwardsEnscape that if properties are not animated while invisible it may have hard to workaround side effects. e.g. there are transitions (Crossfade, PageSlide, etc. I wonder if these work if these set the visibility to be false). I think the proper implementation would be to disable just the Invalidation. Properties would be updated but changes would not call Invalidate - or an empty Invalidate would not propagate further - or empty dirty rects would not force rendering (swapping buffers, etc.) This may have benefits even outside of the Animations (e.g. user changing a bound value of a hidden label will trigger rendering but will change no pixels - it still uses GPU though for swapping buffers).

Please see this PR if it makes sense https://github.com/AvaloniaUI/Avalonia/pull/14904

ltetak avatar Mar 10 '24 12:03 ltetak

Hi folks!

I think it's an edge case that doesn't really makes sense for most applications. For one, nothing should depend directly on animation's output values at all. All animations should be "storyboarded" with keyframes and what not.

Another thing is that IIRC (please correct me if i'm wrong), WPF/UWP also does the same behavior. Having the properties change from the hidden visual's animation will still waste CPU cycles which is what we wanted to fix on this PR in the first place.

jmacato avatar Mar 11 '24 09:03 jmacato

If the initial intend is to reduce CPU usage of ProgressBar indeterminate animation, I would suggest just change the control theme, animate it when it is visible. But globally I think animation should still take effect. I used FillMode=Forward a lot in code to animate control positioning in Ursa.Avalonia project. (think about Drawer Sliding in while its width differs each time).

rabbitism avatar Mar 12 '24 10:03 rabbitism

What prevents you from animating opacity instead of visibility?

Gillibald avatar Mar 12 '24 11:03 Gillibald

I was going to say that "WPF also disables animations when a control is hidden" which is what I thought happened, but I went and checked it:

<Window.Resources>
  <BooleanToVisibilityConverter x:Key="c"/>
</Window.Resources>
<DockPanel>
  <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
    <CheckBox x:Name="Visible" IsChecked="True">Is Visible</CheckBox>
    <TextBlock Margin="8 0 0 0" Text="{Binding Opacity, ElementName=MyRectangle}"/>
  </StackPanel>
  <Canvas>
    <Rectangle Name="MyRectangle"
               Width="100" 
               Height="100"
               Fill="Blue"
               Visibility="{Binding IsChecked, ElementName=Visible, Converter={StaticResource c}}">
      <Rectangle.Triggers>
        <!-- Animates the rectangle's opacity. -->
        <EventTrigger RoutedEvent="Rectangle.Loaded">
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation Storyboard.TargetName="MyRectangle" 
                               Storyboard.TargetProperty="Opacity"
                               From="1.0" To="0.0" Duration="0:0:1" 
                               AutoReverse="True" RepeatBehavior="Forever" />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle>
  </Canvas>
</DockPanel>

And it seems that animations run on invisible controls in WPF...

So I'm tempted to agree with the commenters that we probably shouldn't merge this PR, though I think we should also check CSS as our animation system is more like that than WPF.

grokys avatar May 09 '24 13:05 grokys

You can test this PR using the following package version. 11.2.999-cibuild0048311-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

avaloniaui-bot avatar May 10 '24 10:05 avaloniaui-bot

Hi folks, upon discussing with @grokys we have found out the following:

https://github.com/AvaloniaUI/Avalonia/assets/16554748/483c29af-c8e7-46eb-9ae7-a6f23e3bc14c

On WPF at least, the main indication of activity on the ProgressBar's Indeterminate animation seems to stop and reset when the visibility changes

jmacato avatar May 10 '24 11:05 jmacato

Other findings:

WPF:

image

Multiple Indeterminate ProgressBars clocking at around 11% cpu usage

image

Total silence once the Parent Visibility is set to Collapsed and/or Hidden.

jmacato avatar May 10 '24 12:05 jmacato

@TomEdwardsEnscape was about to post as well, thanks!

We just found it out now

image

jmacato avatar May 10 '24 12:05 jmacato

Another finding:

https://github.com/AvaloniaUI/Avalonia/assets/16554748/918cdcbf-7e9f-4e5e-b346-1a6ffb73d579

CSS does stop animations on hide.

jmacato avatar May 10 '24 12:05 jmacato

I think the problem is not that the animation is running (that's fairly cheap to recalculate some numbers, although can be optimized in specific cases, e.g. ProgressBar). The problem is that changing a property of invisible control triggers invalidation + redraw. I still believe the best solution would be to improve this part - if the control is invisible then don't add its dirty rect and thus don't force redrawing. I tried it here https://github.com/AvaloniaUI/Avalonia/pull/14904 but it does not cover the case when the control was visible and then goes invisible.

Try this. Enable showing dirty rects. Create a hidden indeterminate ProgressBar. Its dirty rect is still visible and applied to the position where the ProgressBar was before. So it is redrawing some random position over and over with no visual result.

ltetak avatar May 13 '24 10:05 ltetak