Summary:
One of the struggles with the LA engine is to make sure that LA doesn't break the StubViewTree. In particular, we have strict requirements that the "oldShadowView" with every mutation matches exactly what is in the shadow tree, so it appears that there is a linear progression of ShadowNodes for every mutation instruction.
This is a struggle to do with the current setup, requires some wacky code, and doesn't work properly. Instead of spreading REMOVE/DELETE (especially) or INSERT/UPDATE instructions across multiple keyframes, we now allow keyframes to have multiple "final" instructions to execute,
which makes it much easier to keep state consistent.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D27407769
fbshipit-source-id: 3a709503dc30be69efc345690cb1920eb9591e9a
Summary:
On Android we have the notion of "virtual views", which are defined consistently but the logic is scattered and duplicated throughout the codebase.
The logic exists to mark nodes that exist in the ShadowTree, but not the View tree. We want to CREATE, UPDATE, and DELETE them on the platform, but not INSERT or REMOVE
them. They basically exist as EventEmitter objects.
The only issue with this is (1) duplicated code, which opened the possibility for inconsistent definition (2) StubViewTree did not account for virtualized views, which caused
assert crashes in debug mode for certain LayoutAnimations on Android.
By moving the definition to ShadowViewMutation and accounting for it in StubViewTree, asserts are correct and consistent on all platforms.
This was not caught until recently, because, until recently, no asserts actually ran on Android.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D27001199
fbshipit-source-id: eb29085317037ba8a286d7813bdd57095ad4746f
Summary:
Use react_native_assert in LayoutAnimations to enable asserts to fire on Android.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D26517096
fbshipit-source-id: f000c4848f29c8779170625d357f547f2e9e6365
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/30988
We have a bunch of flags scattered throughout the codebase with poor hygiene and commenting. Consolidate.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D26392518
fbshipit-source-id: 2823de123a5009d6b8c358e8a3f451b9fa0e05b7
Summary:
remove dead code; this flag is always defaulted to false, and not used anywhere
Changelog: [Internal]
Reviewed By: sammy-SC, mdvacca
Differential Revision: D26271507
fbshipit-source-id: e2277cc24f164c53f2e8a0aa72456ac400834d70
Summary:
Add LA_ASSERT macro, this just makes debugging easier on Android since these asserts are compiled out for us even in debug.
Changelog: [internal]
Reviewed By: mdvacca
Differential Revision: D26271508
fbshipit-source-id: 9be8c71e273d762a4f31ff1fcc629ce48218b98d
Summary:
These asserts don't run in prod; only set starting/final view props if the result props object is non-null.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D26271512
fbshipit-source-id: b495c014a062cf255fd4b5cb8609582f23edcec8
Summary:
In this case where there's an ongoing UPDATE animation and an INSERT of the same node, make sure the ongoing animation type is what we expect, and that the `newChildShadowView` is valid.
We were already guarding against these, but we should (1) crash more in debug and (2) fail more elegantly in prod when the asserts don't run.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D26271514
fbshipit-source-id: 48e7d37a2493241f16099d9fe5ecb0d247707ca7
Summary:
LayoutAnimations: assert that tag >0 instead of != 0. It's possible that a corrupt tag value would be below zero which is not valid.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D26271515
fbshipit-source-id: a62445ad29d60e5180e62ec4c6d5b08784655808
Summary:
We should always be able to reference a ComponentDescriptor. If there are any valid cases where the ComponentDescriptor doesn't exist (?) we need to document those and investigate more.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D26271511
fbshipit-source-id: 659d160f82f1e78025d3dbe16efa0fa2d072d15f
Summary:
Make sure there's no corruption happening to these ShadowViews.
Changelog: [internal]
Reviewed By: mdvacca
Differential Revision: D26271509
fbshipit-source-id: 01bfa1bfce56e72b48304fe15b6f6426d3b5247e
Summary:
To ensure that we're not sending old eventEmitters or State objects to the mounting layer, or potentially out-of-date Props objects, base
animated interpolations on the final ShadowNode instead of the initial.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D25727481
fbshipit-source-id: 560ae8d25c7cec4c2137e70b4571b762f461edff
Summary:
The `index` parameter for UpdateMutation is optional, and is normally just -1. It's not useful, so remove it. `parentShadowView` is also not relevant and is not used; in some existing use-cases the actual parent view of the updated view is available, and in some contexts the parent view is not set.
The function now will always set the index to -1 for UpdateMutations, and `{}` for ParentShadowView.
This should have no impact on iOS or Android, as this parameter is not used. It could theoretically have an impact on lifetimes of objects retained (now not retained) by not passing parentShadowView into the mutation. For example, any shared props or state associated with the parent will not be retained in the Update mutation now.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D25342943
fbshipit-source-id: 0ddbef76a6e2eefc2629c9729f721d8674d7737e
Summary:
If a ShadowNode is being animated from opacity 0 to 1, and that animation is interrupted by another update to the same ShadowNode, we stop the animation and send the original "final" ShadowView
to the mounting layer. On iOS, this is enough to make the Mounting layer consistent with the Shadow layer. However, on Android, since only prop deltas are passed to the mounting layer and not the
entire props bag, this will NOT be enough because the opacity will not be updated in that final step.
Therefore, in those cases where we've detected a conflict and we're cleaning up after an interrupted animation, we must send two updates to the mounting layer: one to force the opacity to 1,
and another to make the ShadowTree consistent with the Mounting layer.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D25324942
fbshipit-source-id: 5d9666128feaae87d7530c394ef05db580aa5a75
Summary:
In very marginal cases, it was possible to set up an animation of the following diffing instructions:
```
REMOVE X from parent Y
DELETE X
```
If your LayoutAnimation configuration had no "delete" config, the DELETE would be executed immediately; the REMOVE was erroneously being categorized as an "update" (now fixed)
which caused the REMOVE to be delayed, but then executed very shortly thereafter. So the order of instructions would become:
```
DELETE X
REMOVE X from parent Y
```
which would crash (or at least fail an assertion) when the REMOVE instruction was processed.
This fixes the issue by ensuring that REMOVEs have a corresponding "delete" config, or they are also executed immediately; unless followed by an INSERT (and any combination of `REMOVE, DELETE, INSERT` in the same frame is not possible).
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D25292560
fbshipit-source-id: 7ffdd6cbcb43126de07a70c197dfaf1ebff83555
Summary:
Changelog: [internal]
Return value from `LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations` was a tuple with 3 elements. Two of them are not being used so let's get rid of them.
Reviewed By: JoshuaGross
Differential Revision: D25220601
fbshipit-source-id: 35781e735b6a2e518337fdeaf956c18bb370993b
Summary:
These configs are never actually empty, so they shouldn't be optionals.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D25129254
fbshipit-source-id: 626119fefad0440732541c680286ebbbfab6aeba
Summary:
Changelog: [internal]
`currentAnimation_` is accessed on multiple threads and has dedicated mutex, but it was not acquiring the mutex in `LayoutAnimationKeyFrameManager::shouldAnimateFrame`
Reviewed By: JoshuaGross
Differential Revision: D25121654
fbshipit-source-id: 38b1c82eaabab283beab18dc210ea21379edbe93
Summary:
Imagine the scenario in which there's an ongoing UPDATE animation; a REMOVE+INSERT (move) is queued up for the same tag, but there's no
new corresponding UPDATE - so maybe the indices of the view have changed, but the layout stays the same. Under the old model, the previous animation would be canceled and the node would jump to the final position. In theory, if there's no new UPDATE, we should continue animating the node to its final position.
I'm much happier with this - conflicting animations transition into each other super seamlessly now, and I think the logic is more straightforward as well.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D25071664
fbshipit-source-id: fcefc4619dc34cdafdc4d8e8e730b935e5528290
Summary:
This is noisy when enabled, and not very useful.
Changelog: [Internal]
Reviewed By: PeteTheHeat
Differential Revision: D25071584
fbshipit-source-id: 7205b5fa39622feccaf315ccebb181dbdac4281d
Summary:
See comments, hopefully they explain this situation. This fixes the last remaining case that I have repro'd where StubViewTree asserts fire.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D25062135
fbshipit-source-id: a28afba21f4094200aa0502b1e085dcbc10f9835
Summary:
The current implementation of LayoutAnimations assumed that the "previous/old" ShadowView passed into the diff mutation didn't matter except for purposes of diffing.
As it turns out, iOS components could possibly use the "old" version of props, state, etc - so we should try to keep track of the current value in the tree as much as possible.
This diff accomplishes that by keeping track of the "previous" view, which the AnimationDriver will update over time. This also allows us to simplify logic around conflicting animations.
I'm also adding a few additional asserts to assist in debugging.
This doesn't totally eliminate all asserts hit on iOS, yet, but it does reduce the number of times the asserts are hit in StubViewTree.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D25048644
fbshipit-source-id: d00aeece5af04624d8193063be453c7ce4a6e565
Summary:
Changelog: [internal]
Reference here is incorrect, we need a container for `ShadowViewMutation`.
Reviewed By: JoshuaGross
Differential Revision: D25024080
fbshipit-source-id: f59a18d859ad391bc168c8990d40b25d18003f74
Summary:
For some interrupted animations we will execute a "final" mutation associated with the animation, if it exists. For example, "UPDATE" animations always have a final Update animation associated with them.
Some, however, do not. For example: INSERT animations do not have a final mutation associated by default. In these cases (before this diff) if the animation from opacity 0 to 1 was interrupted, the View will
appear "stuck" at some intermediate opacity. To mitigate that, we generate a synthetic "final" mutation at 100% progress through the animation if it is interrupted.
Changelog: [Internal]
Reviewed By: fred2028
Differential Revision: D24691151
fbshipit-source-id: d9730b8a3493a5eeac4de325e7e0a7a64f73c8a0
Summary:
Index adjustment is tricky. Seems more reliable to adjust each immediate mutation, and then immediately adjust delayed mutations based on it, rinse and repeat.
Previously it was possible to construct examples where the UI would get into a weird state because index adjustment caused items to be inserted in the wrong location.
Changelog: [Internal]
Reviewed By: kacieb
Differential Revision: D24232926
fbshipit-source-id: f8c445213528c2d2aedacf3e0c73c5bbeb62bc3d
Summary:
When an animation is completed or a conflicting animation is detected, force props, state, layout, etc to update.
Currently, when a final animation mutation is queued, some attributes can be updated but not others, causing unexpected visual glitches at least on Android.
Some of these are arguably component bugs, but it's easier to just flush all attributes by tricking the platforms into updating all attributes. This will also prevent us from having to track down more of these bugs, potentially.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D23886519
fbshipit-source-id: 8e5081bbe3b7843c16c0f283fa07fdec0e211aa8
Summary:
In this diff I simplify index adjustment and add comments to rigorously describe what we're doing at each step of index adjustment.
I've also made unflattening detection more correct, robust, and slightly more efficient.
Bugs that existed before:
1) The reparenting detection that existed in the animations layer had some subtle bugs. I don't have proof that it results in any correctness issues or crashes, but I suspect it.
2) Correctness of index adjustment: there were cases where the Android mounting layer would crash because LayoutAnimations would try to remove a View at an index, but the index was wrong. This is why I sat down and diagrammed the relationships between all the bits of data we have for index readjustment - I believe this to be correct now.
3) Correctness of INSERT index adjustment: I had the insight that when we have batches of INSERTs to consecutive indices, we essentially want them to be treated as a single INSERT for adjustment purposes, so that they're all placed consecutively in the view layer. I added `ConsecutiveAdjustmentMetadata` to deal with this, and there are more comments in the code.
Changelog: [Internal]
Reviewed By: yungsters
Differential Revision: D23806163
fbshipit-source-id: cd9e94945034db8b840f2a806c6377034a91af61
Summary:
iOS needs this function to be marked as static.
Changelog: [internal]
Reviewed By: shergin
Differential Revision: D23749613
fbshipit-source-id: a8c160646853450fc7d849448bdbb45e02beb964
Summary:
`adjustDelayedMutationIndicesForMutation` asserts that the mutation is either Remove or Insert. At one callsite, we weren't checking the mutation type before calling `adjustDelayedMutationIndicesForMutation`.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D23746617
fbshipit-source-id: 6046fec2eb4821094937b1b16f40347bbc55c20e
Summary:
Just renaming, nothing more.
The idea of MountingTelemetry already grown to something bigger than just mounting telemetry, so we are renaming it.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: mdvacca
Differential Revision: D23374947
fbshipit-source-id: f60ce38b75d1ce77498b84688e59598314c69a78
Summary:
My goal in this diff is to make LayoutAnimations more stable, and more resilient to challenging situations. Namely, LayoutAnimations already works fine with:
1) Reorders
2) Flattening/unflattening
3) Deletion and recreation of the same hierarchy
4) Updates conflicting with an existing animation
However, what if /all/ of those things are combined? Handling update conflicts with multiple ongoing animations, repeatedly flattening/unflattening the same layer of hierarchy and reordering both parents and children, etc .
This diff does not make LayoutAnimations perfect, but it does make LayoutAnimations much more resilient to situations it was not able to handle before.
My primary method of testing was to use two Playground examples: one just repeatedly queues up mutations (some animated, some not) that create, update, and delete in the same hierarchy layer. The second, more complex one, mutates between random view hierarchies that involve a lot of flattening and unflattening as well as reordering, over a depth of 5. It also exercises animations over TextInlineViews, which is more challenging.
LayoutAnimations works best with the new "Flattening Differ" for now, because the Flattening Differ produces a much smaller, nearly minimal set of instructions in cases of flattening-unflattening. I would like that to not be a hard requirement for using LayoutAnimations, but it's a good starting-point for now.
As part of this work, I also developed a lot of debugging and logging mechanisms that are handy for detecting inconsistencies and debugging crashes. Some are included in this diff behind `#define` statements that are disabled by default, and the rest will be published separately and likely cannot be landed permanently, as they're more invasive changes that are only helpful in debugging.
# Followups:
- Automate testing: write a suite of C++ tests that mutates between random diffs and guarantees that all mutations in a StubViewTree are sensible
- Construct a set of minimal repros that catalogues all remaining crashes and inconsistency issues (these seem to be extremely marginal cases and are very hard to repro - so I think it's fine to run this in prod for now, but I will follow-up as soon as I'm back to catalogue and fix all remaining issues)
- This diff focuses on *not crashing*, but it is still possible to construct a sequence of complex mutations that results in (for example) views having some opacity between 0 and 1 if animations are interrupted repeatedly. Although this is easy enough to prevent in product code - the types of scenarios I'm running in tests are very unlikely to ever happen in production - it would be nice to be *sure* that LayoutAnimations will always converge to a sensible View hierarchy with up-to-date props.
- In general, the index adjustment logic is complicated. I don't know if there's a great way around it, so I need to at least catalogue and test all edge-cases as mentioned above.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D23382975
fbshipit-source-id: f379d9aa2a4b9c33fa2ba8fa07870c9e31fad5e7
Summary:
I've used this while debugging LayoutAnimations a few times. It compiles out to nothing when the define is not set.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D22962113
fbshipit-source-id: 88b7bb1c20a07a7d804e5c81d31cf871d58bee92
Summary:
Consolidated a few places where index adjustment was happening with nearly identical code. There was also one remaining case where index adjustment should be happening, but was not previously.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D22962115
fbshipit-source-id: 732c026b5a3c60bb0eadb8d49826ffd6367f7f62
Summary:
Hook up onSuccess and onFailure callbacks to LayoutAnimations.
Note that in non-Fabric RN, onSuccess is not known to work in Android. I could not find any uses of onFailure and it's not documented, so for now, it is only called if the setup of the animation fails.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D22889352
fbshipit-source-id: 4306debb350388dd2b7a2cbfe295eb99723988e2
Summary:
This diff moves fabric C++ code from ReactCommon/fabric to ReactCommon/react/renderer
As part of this diff I also refactored components, codegen and callsites on CatalystApp, FB4A and venice
Script: P137350694
changelog: [internal] internal refactor
Reviewed By: fkgozali
Differential Revision: D22852139
fbshipit-source-id: f85310ba858b6afd81abfd9cbe6d70b28eca7415