Auto offscreen-composite elevated descendants under reduced opacity on Android (flagged) - #58493
Open
crazeface wants to merge 1 commit into
Open
Auto offscreen-composite elevated descendants under reduced opacity on Android (flagged)#58493crazeface wants to merge 1 commit into
crazeface wants to merge 1 commit into
Conversation
…n Android (flagged) Summary: On Android, a view drawn with reduced `opacity` that contains a descendant with `elevation` composites per drawing primitive, so the elevation shadow renders as concentric bands with an over-opaque center instead of fading uniformly (see react#23090). Behind the new default-off feature flag `enableAndroidAutoOffscreenCompositingForElevation`, `ReactViewGroup` reports overlapping rendering (`hasOverlappingRendering()` returns true) whenever it contains a descendant with `elevation`, so the framework bakes the offscreen decision into the RenderNode at record time. Any alpha applied afterwards -- static `opacity`, a JS-driven animation, or a native-driver opacity animation that sets alpha directly on the RenderNode without re-recording -- then composites the subtree through that layer and the shadow fades uniformly. A layer is only actually allocated when `alpha < 1`, so this costs nothing at full opacity. This is the automatic equivalent of the community `needsOffscreenAlphaCompositing` workaround. The descendant-elevation check is cached and lazily invalidated: it is marked stale from the logical mount/unmount and elevation hooks -- via a new `uimanager` interface `ElevatedDescendantCache`, never from the subview-clipping/scroll path -- and rescanned once on the next query. So the per-draw callback stays O(1), mounting N children stays O(N) rather than O(N^2), and scrolling a `removeClippedSubviews` list does no per-frame work. The cache is self-correcting and cannot drift like a maintained counter. Behavior is unchanged unless the flag is enabled. Adds ReactViewGroupTest coverage: flag on/off, alpha == 1 (layer readied ahead of a later alpha change), no / nested elevated descendant, an elevated view inside a non-ReactViewGroup container, invalidation bubbling through a non-ReactViewGroup ancestor, an elevated child added after a prior query, removal, and needsOffscreenAlphaCompositing. Changelog: [Internal] Reviewed By: javache Differential Revision: D118581751
|
@crazeface has exported this pull request. If you are a Meta employee, you can view the originating Diff in D118581751. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
On Android, a view drawn with reduced
opacitythat contains a descendant withelevationcomposites per drawing primitive, so the elevation shadow renders asconcentric bands with an over-opaque center instead of fading uniformly (see
#23090).
Behind the new default-off feature flag
enableAndroidAutoOffscreenCompositingForElevation,ReactViewGroupreportsoverlapping rendering (
hasOverlappingRendering()returns true) whenever itcontains a descendant with
elevation, so the framework bakes the offscreendecision into the RenderNode at record time. Any alpha applied afterwards --
static
opacity, a JS-driven animation, or a native-driver opacity animationthat sets alpha directly on the RenderNode without re-recording -- then
composites the subtree through that layer and the shadow fades uniformly. A layer
is only actually allocated when
alpha < 1, so this costs nothing at fullopacity. This is the automatic equivalent of the community
needsOffscreenAlphaCompositingworkaround.The descendant-elevation check is cached and lazily invalidated: it is marked
stale from the logical mount/unmount and elevation hooks -- via a new
uimanagerinterface
ElevatedDescendantCache, never from the subview-clipping/scroll path-- and rescanned once on the next query. So the per-draw callback stays O(1),
mounting N children stays O(N) rather than O(N^2), and scrolling a
removeClippedSubviewslist does no per-frame work. The cache is self-correctingand cannot drift like a maintained counter. Behavior is unchanged unless the flag
is enabled.
Adds ReactViewGroupTest coverage: flag on/off, alpha == 1 (layer readied ahead of
a later alpha change), no / nested elevated descendant, an elevated view inside a
non-ReactViewGroup container, invalidation bubbling through a non-ReactViewGroup
ancestor, an elevated child added after a prior query, removal, and
needsOffscreenAlphaCompositing.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D118581751