Skip to content

Conversation

@rozele
Copy link
Contributor

@rozele rozele commented Nov 22, 2021

Fixes #8419

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Why

Animations on Animated values can be stopped and restarted for any number of reasons. When this occurs, unless the Animated value is removed, it is expected that the next animation on that value will continue from where it left off. Previously, this only worked if the animation ran to completion. These changes allow restarting animations even if the animation over the value is imperatively stopped.

Resolves #8419
Resolves #9206

What

Animated Composition values cannot be queried until the animation completes (generally, 1 full frame callback after the animation is stopped). The value is also ready to be queried after a scoped batch completion event fires.

This change introduces deferred animation starts. If we attempt to start an animation on a ValueAnimatedNode where an active animation was recently (i.e., synchronously) stopped, we wait for the Completed callback on that animations scoped batch to fire before starting the next animation. The Completed callback will fire either after the animation has run to completion or roughly one animation frame after StopAnimation is called.

Doing this ensures that animated value in the composition property set reports the correct value when computing the frames for the next animation.

Testing

Before:

React.Native.Playground.Win32.2021-11-22.14-44-58.mp4

After:

React.Native.Playground.Win32.2021-11-22.14-42-27.mp4

This change also fixes restarts for tracking animations:

React.Native.Playground.Win32.2021-12-15.21-28-46.mp4

Notice that the animations get out of sync with one another because the UI.Composition approach requires waiting for the completion callback to start any new animations on the same value node, so "restarting" the animation, or setting a new tracked to value, causes the animation to fall behind by a frame or two. This is partly the motivation for #9249.

Microsoft Reviewers: Open in CodeFlow

@rozele rozele requested a review from a team as a code owner November 22, 2021 18:56
@ghost ghost added the Area: Animation label Nov 22, 2021
@rozele rozele marked this pull request as draft November 23, 2021 16:44
@rozele
Copy link
Contributor Author

rozele commented Nov 23, 2021

Converting back to draft - there's still a minor issue where the animations do not start correctly when deferred.

@rozele rozele marked this pull request as ready for review November 25, 2021 18:35
Copy link
Contributor

@chiaramooney chiaramooney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Deferring approval until another dev can review.

rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 8, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. #....: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. #....: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. #....: Calling `setValue` on an animated value should stop any active
animations, but currently does not in the UI.Composition implementation.
4. #....: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. #....: Animated.decay animations are complete when the difference
between the current value and the previous value is less than 0.1, ...
6. #....: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 8, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. #....: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. #....: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. #....: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. #....: Animated.decay animations are complete when the difference
between the current value and the previous value is less than 0.1, ...
6. microsoft#9350: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 8, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. #....: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. #....: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. #....: Animated.decay animations are complete when the difference
between the current value and the previous value is less than 0.1, ...
6. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 8, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. #....: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. #....: Animated.decay animations are complete when the difference
between the current value and the previous value is less than 0.1, ...
6. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 8, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 8, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 9, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 13, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 13, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 14, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 14, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
Unblocks microsoft#4311
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Dec 15, 2021
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
…racking animation nodes, as we skipped that step previously
Some separate handling needs to be done to effectively restart tracking
animated nodes. This change ensures that we first stop the tracking
animation (to remove it from the active animations list and put it's
value node as a key for deferring animations), and fixes the completion
callbacks to avoid removing active animations for tracking nodes, since
they use the same animation ID each time the animation is restarted.

Fixes microsoft#9206
@rozele
Copy link
Contributor Author

rozele commented Jan 10, 2022

Thanks @NickGerleman - rebased and also fixed a minor bug where deferred animations leaked / prevented new animations from starting.

@NickGerleman NickGerleman merged commit 73c4b20 into microsoft:main Jan 10, 2022
@rozele rozele deleted the issue8419 branch January 10, 2022 20:54
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 11, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 11, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 18, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 18, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 18, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 24, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jan 24, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jun 17, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Jun 17, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Aug 2, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Oct 23, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Nov 7, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
rozele added a commit to rozele/react-native-windows that referenced this pull request Nov 7, 2022
Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. microsoft#8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like microsoft#9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. microsoft#3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. microsoft#4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (microsoft#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. microsoft#9255: Similar to microsoft#3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. microsoft#3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. microsoft#9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. microsoft#9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. microsoft#9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. microsoft#9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes microsoft#3283
Fixes microsoft#3460
Fixes microsoft#8419
Fixes microsoft#9250
Fixes microsoft#9251
Fixes microsoft#9252
Fixes microsoft#9255
Fixes microsoft#9256
ghost pushed a commit that referenced this pull request Nov 10, 2022
* Adds Rendering driver option to NativeAnimated

Currently, NativeAnimated compiles the animation graph into
CompositionAnimations from UI.Composition. While this approach likely
provides the ideal performance for native animations on Windows, it
suffers a few insurmountable limitations:

1. #8419: Creating a new animation on an animated value after stopping a
previous animation on the same value does not retain the current value.
  a. Even with a fix like #9190, because UI.Composition values cannot be
  queried synchronously, starting a new animation on an animated value
  immediately after stopping a previous animation causes jitter in the
  animation, because it takes 1-2 frames for the completion callback to
  fire, signaling that the animated value has an up-to-date value.
2. #3283: UI.Composition can only animate supported properties, like
opacity and transforms. NativeAnimated provides a prop "hook" (the prop
name is `progress`) to allow arbitrary views to subscribe to animation
value changes synchronously. This is not possible with UI.Composition.
3. #4311: Similar to the limitation for starting new animations
synchronously after stopping a previous animation on the same animated
value (#8419), animated value listeners will always be 1-2 frames out of sync
while waiting for an up-to-date composition value. This feature is
currently not implemented for the UI.Composition approach, but I suspect
it would require a frame callback via CompositionTarget::Rendering, so
not only would the values be out of sync, but the approach would have a
similar performance profile to the CompositionTarget::Rendering driven
animations.
4. #9255: Similar to #3283, it's actually possible to animate arbitrary
props with Animated (e.g., `borderRadius`). It's unlikely that it would
be possible to support such an animation with UI.Composition.

There are also a few bugs that are likely possible to workaround for
UI.Composition, but would be fixed immediately by this
CompositionTarget::Rendering approach:
1. #3460: Animated values persist even after the animation is unmounted
with UI.Composition. The CompositionTarget::Rendering approach uses the
view's shadow node as the source of truth for the prop value, and resets
the prop to null when the animation is unmounted.
2. #9256: The UI.Composition implementation for NativeAnimated
commandeers the `Offset` value for animated nodes, using it to drive
progress on an animation. Animated value offsets are not intended for this
purpose and can cause bugs as demonstrated in the linked issue.
3. #9251: Calling `setValue` on an animated value should stop any active
animations and update the animation graph to reflect the value that was
set, but in the UI.Composition implementation, the animation is only
stopped in place.
4. #9252: Animated.diffClamp nodes are intended to clamp the difference
between the last value and the current value, but the UI.Composition
`clamp` expression only has the capability to clamp the current value.
5. #9250: Calling `getValue` on an animated value does not return the
current value for the UI.Composition approach (because values are not
available until the animation has been stopped and the completion
callback fires).
6. A more minor issue that I haven't filed an issue for, is that
Animated.decay animations work slightly differently in NativeAnimated
vs. JS-driven animations. The NativeAnimated approach is a bit more
"accurate", in that it stops the animation when its value is within 0.1
of the final value if the decay ran infinitely. The JS-driven approach
stops the animation more eagerly when the value differential (the
difference between the current value and the previous value) is 0.1.

This change allows each animation node and driver to be used in either
composition mode or frame callback / CompositionTarget::Rendering mode.
The latter approach is largely derived from the Android implementation
of NativeAnimated (and the C# implementation in react-native-windows
v0.59 and earlier).

This change will leverage WIP changes to the Animated APIs in RN core
that pass a property bag to each AnimationDriver and AnimatedNode
signaling which mode to use. The API surface will look something like
the following:

```js
const value = Animated.value(0);

Animated.timing(value, {
  ...,
  useNativeDriver: true,
  platformConfig: {
    useComposition: false,
  });
```

We will also likely complement this API change with a way to set default
`platformConfig` values for all Animated APIs using `useNativeDriver:
true`:
```js
Animated.setDefaultPlatformConfig({useComposition: false});
```

For now, in order to not regress performance on existing
react-native-windows applications, using the
CompositionTarget::Rendering approach will be strictly opt-in. As of
this commit, these two approaches cannot be blended together. I.e., you
cannot connect a node using UI.Composition to a node using
CompositionTarget::Rendering, and it's unlikely these two approaches
could be combined until the UI.Composition approach supports synchronous
queries of values (at which point, quite a few of the justifications for the
CompositionTarget::Rendering approach will be resolved).

Fixes #3283
Fixes #3460
Fixes #8419
Fixes #9250
Fixes #9251
Fixes #9252
Fixes #9255
Fixes #9256

* Change files

* Adds RNTester example demonstrating bugs in UI Composition Animated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tracking animation nodes do not restart correctly Native animation gets "stuck" when restarting animation before previous animation completes

3 participants