From c334ee17c8c2fc8ef69b433c907505125ad1beb5 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Fri, 19 Sep 2025 22:13:21 -0400 Subject: [PATCH] Track the Task of the first ViewTransition that we detected as animating Use this as the Task as "Starting Animation", "Animating" etc. --- .../src/ReactFiberCommitViewTransitions.js | 31 ++++++++++++------ .../src/ReactFiberWorkLoop.js | 32 +++++++------------ .../src/ReactProfilerTimer.js | 9 ++++++ 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 9a75195915d..c8e59afb88e 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -39,6 +39,11 @@ import { getViewTransitionName, getViewTransitionClassName, } from './ReactFiberViewTransitionComponent'; +import {trackAnimatingTask} from './ReactProfilerTimer'; +import { + enableComponentPerformanceTrack, + enableProfilerTimer, +} from 'shared/ReactFeatureFlags'; export let shouldStartViewTransition: boolean = false; @@ -101,21 +106,27 @@ export function popViewTransitionCancelableScope( let viewTransitionHostInstanceIdx = 0; -export function applyViewTransitionToHostInstances( - child: null | Fiber, +function applyViewTransitionToHostInstances( + fiber: Fiber, name: string, className: ?string, collectMeasurements: null | Array, stopAtNestedViewTransitions: boolean, ): boolean { viewTransitionHostInstanceIdx = 0; - return applyViewTransitionToHostInstancesRecursive( - child, + const inViewport = applyViewTransitionToHostInstancesRecursive( + fiber.child, name, className, collectMeasurements, stopAtNestedViewTransitions, ); + if (enableProfilerTimer && enableComponentPerformanceTrack && inViewport) { + if (fiber._debugTask != null) { + trackAnimatingTask(fiber._debugTask); + } + } + return inViewport; } function applyViewTransitionToHostInstancesRecursive( @@ -247,7 +258,7 @@ function commitAppearingPairViewTransitions(placement: Fiber): void { // We found a new appearing view transition with the same name as this deletion. // We'll transition between them. const inViewport = applyViewTransitionToHostInstances( - child.child, + child, name, className, null, @@ -284,7 +295,7 @@ export function commitEnterViewTransitions( ); if (className !== 'none') { const inViewport = applyViewTransitionToHostInstances( - placement.child, + placement, name, className, null, @@ -355,7 +366,7 @@ function commitDeletedPairViewTransitions(deletion: Fiber): void { if (className !== 'none') { // We found a new appearing view transition with the same name as this deletion. const inViewport = applyViewTransitionToHostInstances( - child.child, + child, name, className, null, @@ -406,7 +417,7 @@ export function commitExitViewTransitions(deletion: Fiber): void { ); if (className !== 'none') { const inViewport = applyViewTransitionToHostInstances( - deletion.child, + deletion, name, className, null, @@ -490,7 +501,7 @@ export function commitBeforeUpdateViewTransition( return; } applyViewTransitionToHostInstances( - current.child, + current, oldName, className, (current.memoizedState = []), @@ -518,7 +529,7 @@ export function commitNestedViewTransitions(changedParent: Fiber): void { child.flags &= ~Update; if (className !== 'none') { applyViewTransitionToHostInstances( - child.child, + child, name, className, (child.memoizedState = []), diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 7003d25aff6..7c899f6ff89 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -324,6 +324,7 @@ import { animatingLanes, retryClampTime, idleClampTime, + animatingTask, } from './ReactProfilerTimer'; // DEV stuff @@ -1995,7 +1996,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { logAnimatingPhase( blockingClampTime, clampedRenderStartTime, - previousUpdateTask, + animatingTask, ); } logBlockingStart( @@ -2048,7 +2049,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { logAnimatingPhase( transitionClampTime, clampedRenderStartTime, - previousUpdateTask, + animatingTask, ); } logTransitionStart( @@ -2069,14 +2070,14 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { if (includesRetryLane(animatingLanes)) { // If this lane is still animating, log the time from previous render finishing to now as animating. setCurrentTrackFromLanes(SomeRetryLane); - logAnimatingPhase(retryClampTime, renderStartTime, previousUpdateTask); + logAnimatingPhase(retryClampTime, renderStartTime, animatingTask); } } if (includesIdleGroupLanes(lanes)) { if (includesIdleGroupLanes(animatingLanes)) { // If this lane is still animating, log the time from previous render finishing to now as animating. setCurrentTrackFromLanes(IdleLane); - logAnimatingPhase(idleClampTime, renderStartTime, previousUpdateTask); + logAnimatingPhase(idleClampTime, renderStartTime, animatingTask); } } } @@ -3667,12 +3668,7 @@ function commitRoot( enableProfilerTimer ? suspendedViewTransition : (null: any), enableProfilerTimer ? // This callback fires after "pendingEffects" so we need to snapshot the arguments. - finishedViewTransition.bind( - null, - lanes, - // TODO: Use a ViewTransition Task - __DEV__ ? workInProgressUpdateTask : null, - ) + finishedViewTransition.bind(null, lanes) : (null: any), ); } else { @@ -3712,15 +3708,13 @@ function suspendedViewTransition(reason: string): void { } } -function finishedViewTransition( - lanes: Lanes, - task: null | ConsoleTask, // DEV-only -): void { +function finishedViewTransition(lanes: Lanes): void { if (enableProfilerTimer && enableComponentPerformanceTrack) { if ((animatingLanes & lanes) === NoLanes) { // Was already stopped by some other action or maybe other root. return; } + const task = animatingTask; stopAnimating(lanes); // If an affected track isn't in the middle of rendering or committing, log from the previous // finished render until the end of the animation. @@ -3835,7 +3829,7 @@ function flushLayoutEffects(): void { commitEndTime, // The start is the end of the first commit part. commitStartTime, // The end is the start of the second commit part. suspendedViewTransitionReason, - workInProgressUpdateTask, // TODO: Use a ViewTransition Task and this is not safe to read in this phase. + animatingTask, ); } } @@ -3938,7 +3932,7 @@ function flushSpawnedWork(): void { startViewTransitionStartTime, commitEndTime, pendingDelayedCommitReason === ABORTED_VIEW_TRANSITION_COMMIT, - workInProgressUpdateTask, // TODO: Use a ViewTransition Task. + animatingTask, ); if (pendingDelayedCommitReason !== ABORTED_VIEW_TRANSITION_COMMIT) { pendingDelayedCommitReason = ANIMATION_STARTED_COMMIT; @@ -4440,11 +4434,7 @@ function flushPassiveEffectsImpl() { passiveEffectStartTime = now(); if (pendingDelayedCommitReason === ANIMATION_STARTED_COMMIT) { // The animation was started, so we've been animating since that happened. - logAnimatingPhase( - commitEndTime, - passiveEffectStartTime, - workInProgressUpdateTask, // TODO: Use a ViewTransition Task - ); + logAnimatingPhase(commitEndTime, passiveEffectStartTime, animatingTask); } else { logPaintYieldPhase( commitEndTime, diff --git a/packages/react-reconciler/src/ReactProfilerTimer.js b/packages/react-reconciler/src/ReactProfilerTimer.js index 0891a4f5f5e..f2cf8efdce5 100644 --- a/packages/react-reconciler/src/ReactProfilerTimer.js +++ b/packages/react-reconciler/src/ReactProfilerTimer.js @@ -93,6 +93,7 @@ export let retryClampTime: number = -0; export let idleClampTime: number = -0; export let animatingLanes: Lanes = NoLanes; +export let animatingTask: null | ConsoleTask = null; // First ViewTransition applying an Animation. export let yieldReason: SuspendedReason = (0: any); export let yieldStartTime: number = -1.1; // The time when we yielded to the event loop @@ -601,8 +602,16 @@ export function transferActualDuration(fiber: Fiber): void { export function startAnimating(lanes: Lanes): void { animatingLanes |= lanes; + animatingTask = null; } export function stopAnimating(lanes: Lanes): void { animatingLanes &= ~lanes; + animatingTask = null; +} + +export function trackAnimatingTask(task: ConsoleTask): void { + if (animatingTask === null) { + animatingTask = task; + } }