diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index 77aedb6ae3c7a..ca2745beea5cc 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -621,6 +621,10 @@ export function waitForCommitToBeReady(timeoutOffset) { return null; } +export function getSuspendedCommitReason(state, rootContainer) { + return null; +} + export const NotPendingTransition = null; export const HostTransitionContext: ReactContext = { $$typeof: REACT_CONTEXT_TYPE, diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 8f67e12db1e1f..d7ca1951e00aa 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -2100,6 +2100,7 @@ export function startViewTransition( passiveCallback: () => mixed, errorCallback: mixed => void, blockedCallback: string => void, // Profiling-only + finishedAnimation: () => void, // Profiling-only ): null | RunningViewTransition { const ownerDocument: Document = rootContainer.nodeType === DOCUMENT_NODE @@ -2302,6 +2303,9 @@ export function startViewTransition( // $FlowFixMe[prop-missing] ownerDocument.__reactViewTransition = null; } + if (enableProfilerTimer) { + finishedAnimation(); + } passiveCallback(); }); return transition; @@ -5965,6 +5969,7 @@ export opaque type SuspendedState = { imgBytes: number, // number of bytes we estimate needing to download suspenseyImages: Array, // instances of suspensey images (whether loaded or not) waitingForImages: boolean, // false when we're no longer blocking on images + waitingForViewTransition: boolean, unsuspend: null | (() => void), }; @@ -5976,6 +5981,7 @@ export function startSuspendingCommit(): SuspendedState { imgBytes: 0, suspenseyImages: [], waitingForImages: true, + waitingForViewTransition: false, // We use a noop function when we begin suspending because if possible we want the // waitfor step to finish synchronously. If it doesn't we'll return a function to // provide the actual unsuspend function and that will get completed when the count @@ -6123,6 +6129,7 @@ export function suspendOnActiveViewTransition( return; } state.count++; + state.waitingForViewTransition = true; const ping = onUnsuspend.bind(state); activeViewTransition.finished.then(ping, ping); } @@ -6206,6 +6213,28 @@ export function waitForCommitToBeReady( return null; } +export function getSuspendedCommitReason( + state: SuspendedState, + rootContainer: Container, +): null | string { + if (state.waitingForViewTransition) { + return 'Waiting for the previous Animation'; + } + if (state.count > 0) { + if (state.imgCount > 0) { + return 'Suspended on CSS and Images'; + } + return 'Suspended on CSS'; + } + if (state.imgCount === 1) { + return 'Suspended on an Image'; + } + if (state.imgCount > 0) { + return 'Suspended on Images'; + } + return null; +} + function checkIfFullyUnsuspended(state: SuspendedState) { if (state.count === 0 && (state.imgCount === 0 || !state.waitingForImages)) { if (state.stylesheets) { diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index 233c267a1d6a3..c030b8528b62c 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -627,6 +627,13 @@ export function waitForCommitToBeReady( return null; } +export function getSuspendedCommitReason( + state: SuspendedState, + rootContainer: Container, +): null | string { + return null; +} + export type FragmentInstanceType = { _fragmentFiber: Fiber, _observers: null | Set, diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 8271a62327aea..12b256e016fe2 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -674,6 +674,7 @@ export function startViewTransition( passiveCallback: () => mixed, errorCallback: mixed => void, blockedCallback: string => void, // Profiling-only + finishedAnimation: () => void, // Profiling-only ): null | RunningViewTransition { mutationCallback(); layoutCallback(); @@ -806,6 +807,13 @@ export function waitForCommitToBeReady( return null; } +export function getSuspendedCommitReason( + state: SuspendedState, + rootContainer: Container, +): null | string { + return null; +} + export const NotPendingTransition: TransitionStatus = null; export const HostTransitionContext: ReactContext = { $$typeof: REACT_CONTEXT_TYPE, diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 7235837cd64df..db69232d1295a 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -702,6 +702,13 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { waitForCommitToBeReady, + getSuspendedCommitReason( + state: SuspendedState, + rootContainer: Container, + ): null | string { + return null; + }, + NotPendingTransition: (null: TransitionStatus), resetFormInstance(form: Instance) {}, @@ -853,6 +860,8 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { spawnedWorkCallback: () => void, passiveCallback: () => mixed, errorCallback: mixed => void, + blockedCallback: string => void, // Profiling-only + finishedAnimation: () => void, // Profiling-only ): null | RunningViewTransition { mutationCallback(); layoutCallback(); diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 9a75195915d53..c8e59afb88e20 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/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.js index 3248556fbe077..3e4f22b854104 100644 --- a/packages/react-reconciler/src/ReactFiberLane.js +++ b/packages/react-reconciler/src/ReactFiberLane.js @@ -73,6 +73,8 @@ const TransitionLane12: Lane = /* */ 0b0000000000010000000 const TransitionLane13: Lane = /* */ 0b0000000000100000000000000000000; const TransitionLane14: Lane = /* */ 0b0000000001000000000000000000000; +export const SomeTransitionLane: Lane = TransitionLane1; + const TransitionUpdateLanes = TransitionLane1 | TransitionLane2 | @@ -633,6 +635,22 @@ export function includesTransitionLane(lanes: Lanes): boolean { return (lanes & TransitionLanes) !== NoLanes; } +export function includesRetryLane(lanes: Lanes): boolean { + return (lanes & RetryLanes) !== NoLanes; +} + +export function includesIdleGroupLanes(lanes: Lanes): boolean { + return ( + (lanes & + (SelectiveHydrationLane | + IdleHydrationLane | + IdleLane | + OffscreenLane | + DeferredLane)) !== + NoLanes + ); +} + export function includesOnlyHydrationLanes(lanes: Lanes): boolean { return (lanes & HydrationLanes) === lanes; } diff --git a/packages/react-reconciler/src/ReactFiberPerformanceTrack.js b/packages/react-reconciler/src/ReactFiberPerformanceTrack.js index 67438b7f817e8..c19ef704b20b2 100644 --- a/packages/react-reconciler/src/ReactFiberPerformanceTrack.js +++ b/packages/react-reconciler/src/ReactFiberPerformanceTrack.js @@ -1180,45 +1180,10 @@ export function logInconsistentRender( } } -export function logSuspenseThrottlePhase( - startTime: number, - endTime: number, - debugTask: null | ConsoleTask, -): void { - // This was inside a throttled Suspense boundary commit. - if (supportsUserTiming) { - if (endTime <= startTime) { - return; - } - if (__DEV__ && debugTask) { - debugTask.run( - // $FlowFixMe[method-unbinding] - console.timeStamp.bind( - console, - 'Throttled', - startTime, - endTime, - currentTrack, - LANES_TRACK_GROUP, - 'secondary-light', - ), - ); - } else { - console.timeStamp( - 'Throttled', - startTime, - endTime, - currentTrack, - LANES_TRACK_GROUP, - 'secondary-light', - ); - } - } -} - export function logSuspendedCommitPhase( startTime: number, endTime: number, + reason: string, debugTask: null | ConsoleTask, ): void { // This means the commit was suspended on CSS or images. @@ -1233,7 +1198,7 @@ export function logSuspendedCommitPhase( // $FlowFixMe[method-unbinding] console.timeStamp.bind( console, - 'Suspended on CSS or Images', + reason, startTime, endTime, currentTrack, @@ -1243,7 +1208,7 @@ export function logSuspendedCommitPhase( ); } else { console.timeStamp( - 'Suspended on CSS or Images', + reason, startTime, endTime, currentTrack, @@ -1493,7 +1458,7 @@ export function logAnimatingPhase( endTime, currentTrack, LANES_TRACK_GROUP, - 'secondary', + 'secondary-dark', ), ); } else { @@ -1503,7 +1468,7 @@ export function logAnimatingPhase( endTime, currentTrack, LANES_TRACK_GROUP, - 'secondary', + 'secondary-dark', ); } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index b2b53281b9234..7c899f6ff899a 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -79,7 +79,6 @@ import { logErroredRenderPhase, logInconsistentRender, logSuspendedWithDelayPhase, - logSuspenseThrottlePhase, logSuspendedCommitPhase, logSuspendedViewTransitionPhase, logCommitPhase, @@ -103,6 +102,7 @@ import { startSuspendingCommit, suspendOnActiveViewTransition, waitForCommitToBeReady, + getSuspendedCommitReason, preloadInstance, preloadResource, supportsHydration, @@ -179,6 +179,8 @@ import { includesOnlyTransitions, includesBlockingLane, includesTransitionLane, + includesRetryLane, + includesIdleGroupLanes, includesExpiredLane, getNextLanes, getEntangledLanes, @@ -201,6 +203,9 @@ import { includesOnlyViewTransitionEligibleLanes, isGestureRender, GestureLane, + SomeTransitionLane, + SomeRetryLane, + IdleLane, } from './ReactFiberLane'; import { DiscreteEventPriority, @@ -292,6 +297,8 @@ import { clearTransitionTimers, clampBlockingTimers, clampTransitionTimers, + clampRetryTimers, + clampIdleTimers, markNestedUpdateScheduled, renderStartTime, commitStartTime, @@ -312,6 +319,12 @@ import { resetCommitErrors, PINGED_UPDATE, SPAWNED_UPDATE, + startAnimating, + stopAnimating, + animatingLanes, + retryClampTime, + idleClampTime, + animatingTask, } from './ReactProfilerTimer'; // DEV stuff @@ -672,12 +685,10 @@ export function getRenderTargetTime(): number { let legacyErrorBoundariesThatAlreadyFailed: Set | null = null; -type SuspendedCommitReason = 0 | 1 | 2; -const IMMEDIATE_COMMIT = 0; -const SUSPENDED_COMMIT = 1; -const THROTTLED_COMMIT = 2; +type SuspendedCommitReason = null | string; type DelayedCommitReason = 0 | 1 | 2 | 3; +const IMMEDIATE_COMMIT = 0; const ABORTED_VIEW_TRANSITION_COMMIT = 1; const DELAYED_PASSIVE_COMMIT = 2; const ANIMATION_STARTED_COMMIT = 3; @@ -703,7 +714,7 @@ let pendingViewTransitionEvents: Array<(types: Array) => void> | null = null; let pendingTransitionTypes: null | TransitionTypes = null; let pendingDidIncludeRenderPhaseUpdate: boolean = false; -let pendingSuspendedCommitReason: SuspendedCommitReason = IMMEDIATE_COMMIT; // Profiling-only +let pendingSuspendedCommitReason: SuspendedCommitReason = null; // Profiling-only let pendingDelayedCommitReason: DelayedCommitReason = IMMEDIATE_COMMIT; // Profiling-only let pendingSuspendedViewTransitionReason: null | string = null; // Profiling-only @@ -1391,7 +1402,7 @@ function finishConcurrentRender( workInProgressSuspendedRetryLanes, exitStatus, null, - IMMEDIATE_COMMIT, + null, renderStartTime, renderEndTime, ); @@ -1428,6 +1439,7 @@ function finishConcurrentRender( // immediately, wait for more data to arrive. // TODO: Combine retry throttling with Suspensey commits. Right now they // run one after the other. + pendingEffectsLanes = lanes; root.timeoutHandle = scheduleTimeout( commitRootWhenReady.bind( null, @@ -1442,7 +1454,7 @@ function finishConcurrentRender( workInProgressSuspendedRetryLanes, workInProgressRootDidSkipSuspendedSiblings, exitStatus, - THROTTLED_COMMIT, + 'Throttled', renderStartTime, renderEndTime, ), @@ -1463,7 +1475,7 @@ function finishConcurrentRender( workInProgressSuspendedRetryLanes, workInProgressRootDidSkipSuspendedSiblings, exitStatus, - IMMEDIATE_COMMIT, + null, renderStartTime, renderEndTime, ); @@ -1541,6 +1553,7 @@ function commitRootWhenReady( // Not yet ready to commit. Delay the commit until the renderer notifies // us that it's ready. This will be canceled if we start work on the // root again. + pendingEffectsLanes = lanes; root.cancelPendingCommit = schedulePendingCommit( commitRoot.bind( null, @@ -1555,7 +1568,9 @@ function commitRootWhenReady( suspendedRetryLanes, exitStatus, suspendedState, - SUSPENDED_COMMIT, + enableProfilerTimer + ? getSuspendedCommitReason(suspendedState, root.containerInfo) + : null, completedRenderStartTime, completedRenderEndTime, ), @@ -1889,6 +1904,12 @@ function finalizeRender(lanes: Lanes, finalizationTime: number): void { if (includesTransitionLane(lanes)) { clampTransitionTimers(finalizationTime); } + if (includesRetryLane(lanes)) { + clampRetryTimers(finalizationTime); + } + if (includesIdleGroupLanes(lanes)) { + clampIdleTimers(finalizationTime); + } } } @@ -1939,6 +1960,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { } finalizeRender(workInProgressRootRenderLanes, renderStartTime); } + const previousUpdateTask = workInProgressUpdateTask; workInProgressUpdateTask = null; if (includesSyncLane(lanes) || includesBlockingLane(lanes)) { @@ -1951,18 +1973,30 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { blockingEventTime >= 0 && blockingEventTime < blockingClampTime ? blockingClampTime : blockingEventTime; + const clampedRenderStartTime = // Clamp the suspended time to the first event/update. + clampedEventTime >= 0 + ? clampedEventTime + : clampedUpdateTime >= 0 + ? clampedUpdateTime + : renderStartTime; if (blockingSuspendedTime >= 0) { - setCurrentTrackFromLanes(lanes); + setCurrentTrackFromLanes(SyncLane); logSuspendedWithDelayPhase( blockingSuspendedTime, - // Clamp the suspended time to the first event/update. - clampedEventTime >= 0 - ? clampedEventTime - : clampedUpdateTime >= 0 - ? clampedUpdateTime - : renderStartTime, + clampedRenderStartTime, lanes, - workInProgressUpdateTask, + previousUpdateTask, + ); + } else if ( + includesSyncLane(animatingLanes) || + includesBlockingLane(animatingLanes) + ) { + // If this lane is still animating, log the time from previous render finishing to now as animating. + setCurrentTrackFromLanes(SyncLane); + logAnimatingPhase( + blockingClampTime, + clampedRenderStartTime, + animatingTask, ); } logBlockingStart( @@ -1994,19 +2028,29 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { transitionEventTime >= 0 && transitionEventTime < transitionClampTime ? transitionClampTime : transitionEventTime; + const clampedRenderStartTime = + // Clamp the suspended time to the first event/update. + clampedEventTime >= 0 + ? clampedEventTime + : clampedUpdateTime >= 0 + ? clampedUpdateTime + : renderStartTime; if (transitionSuspendedTime >= 0) { - setCurrentTrackFromLanes(lanes); + setCurrentTrackFromLanes(SomeTransitionLane); logSuspendedWithDelayPhase( transitionSuspendedTime, - // Clamp the suspended time to the first event/update. - clampedEventTime >= 0 - ? clampedEventTime - : clampedUpdateTime >= 0 - ? clampedUpdateTime - : renderStartTime, + clampedRenderStartTime, lanes, workInProgressUpdateTask, ); + } else if (includesTransitionLane(animatingLanes)) { + // If this lane is still animating, log the time from previous render finishing to now as animating. + setCurrentTrackFromLanes(SomeTransitionLane); + logAnimatingPhase( + transitionClampTime, + clampedRenderStartTime, + animatingTask, + ); } logTransitionStart( clampedStartTime, @@ -2022,6 +2066,20 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { ); clearTransitionTimers(); } + if (includesRetryLane(lanes)) { + 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, 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, animatingTask); + } + } } const timeoutHandle = root.timeoutHandle; @@ -2038,6 +2096,8 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { cancelPendingCommit(); } + pendingEffectsLanes = NoLanes; + resetWorkInProgressStack(); workInProgressRoot = root; const rootWorkInProgress = createWorkInProgress(root.current, null); @@ -3458,7 +3518,7 @@ function commitRoot( recoverableErrors, suspendedState, enableProfilerTimer - ? suspendedCommitReason === IMMEDIATE_COMMIT + ? suspendedCommitReason === null ? completedRenderEndTime : commitStartTime : 0, @@ -3530,16 +3590,11 @@ function commitRoot( resetCommitErrors(); recordCommitTime(); if (enableComponentPerformanceTrack) { - if (suspendedCommitReason === SUSPENDED_COMMIT) { + if (suspendedCommitReason !== null) { logSuspendedCommitPhase( completedRenderEndTime, commitStartTime, - workInProgressUpdateTask, - ); - } else if (suspendedCommitReason === THROTTLED_COMMIT) { - logSuspenseThrottlePhase( - completedRenderEndTime, - commitStartTime, + suspendedCommitReason, workInProgressUpdateTask, ); } @@ -3597,6 +3652,9 @@ function commitRoot( pendingEffectsStatus = PENDING_MUTATION_PHASE; if (enableViewTransition && willStartViewTransition) { + if (enableProfilerTimer && enableComponentPerformanceTrack) { + startAnimating(lanes); + } pendingViewTransition = startViewTransition( suspendedState, root.containerInfo, @@ -3608,6 +3666,10 @@ function commitRoot( flushPassiveEffects, reportViewTransitionError, enableProfilerTimer ? suspendedViewTransition : (null: any), + enableProfilerTimer + ? // This callback fires after "pendingEffects" so we need to snapshot the arguments. + finishedViewTransition.bind(null, lanes) + : (null: any), ); } else { // Flush synchronously. @@ -3633,16 +3695,63 @@ function suspendedViewTransition(reason: string): void { // We'll split the commit into two phases, because we're suspended in the middle. recordCommitEndTime(); logCommitPhase( - pendingSuspendedCommitReason === IMMEDIATE_COMMIT + pendingSuspendedCommitReason === null ? pendingEffectsRenderEndTime : commitStartTime, commitEndTime, commitErrors, pendingDelayedCommitReason === ABORTED_VIEW_TRANSITION_COMMIT, - workInProgressUpdateTask, + workInProgressUpdateTask, // TODO: Use a ViewTransition Task and this is not safe to read in this phase. ); pendingSuspendedViewTransitionReason = reason; - pendingSuspendedCommitReason = SUSPENDED_COMMIT; + pendingSuspendedCommitReason = reason; + } +} + +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. + if ( + (includesSyncLane(lanes) || includesBlockingLane(lanes)) && + !includesSyncLane(workInProgressRootRenderLanes) && + !includesBlockingLane(workInProgressRootRenderLanes) && + !includesSyncLane(pendingEffectsLanes) && + !includesBlockingLane(pendingEffectsLanes) + ) { + setCurrentTrackFromLanes(SyncLane); + logAnimatingPhase(blockingClampTime, now(), task); + } + if ( + includesTransitionLane(lanes) && + !includesTransitionLane(workInProgressRootRenderLanes) && + !includesTransitionLane(pendingEffectsLanes) + ) { + setCurrentTrackFromLanes(SomeTransitionLane); + logAnimatingPhase(transitionClampTime, now(), task); + } + if ( + includesRetryLane(lanes) && + !includesRetryLane(workInProgressRootRenderLanes) && + !includesRetryLane(pendingEffectsLanes) + ) { + setCurrentTrackFromLanes(SomeRetryLane); + logAnimatingPhase(retryClampTime, now(), task); + } + if ( + includesIdleGroupLanes(lanes) && + !includesIdleGroupLanes(workInProgressRootRenderLanes) && + !includesIdleGroupLanes(pendingEffectsLanes) + ) { + setCurrentTrackFromLanes(IdleLane); + logAnimatingPhase(idleClampTime, now(), task); + } } } @@ -3720,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, + animatingTask, ); } } @@ -3792,9 +3901,7 @@ function flushLayoutEffects(): void { if (enableProfilerTimer && enableComponentPerformanceTrack) { recordCommitEndTime(); logCommitPhase( - suspendedCommitReason === IMMEDIATE_COMMIT - ? completedRenderEndTime - : commitStartTime, + suspendedCommitReason === null ? completedRenderEndTime : commitStartTime, commitEndTime, commitErrors, pendingDelayedCommitReason === ABORTED_VIEW_TRANSITION_COMMIT, @@ -3825,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; @@ -4327,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 62b76ee6a407a..f2cf8efdce5b2 100644 --- a/packages/react-reconciler/src/ReactProfilerTimer.js +++ b/packages/react-reconciler/src/ReactProfilerTimer.js @@ -22,6 +22,7 @@ import { includesTransitionLane, includesBlockingLane, includesSyncLane, + NoLanes, } from './ReactFiberLane'; import {resolveEventType, resolveEventTimeStamp} from './ReactFiberConfig'; @@ -88,6 +89,12 @@ export let transitionEventType: null | string = null; // Event type of the first export let transitionEventIsRepeat: boolean = false; export let transitionSuspendedTime: number = -1.1; +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 @@ -306,6 +313,20 @@ export function clampTransitionTimers(finalTime: number): void { transitionClampTime = finalTime; } +export function clampRetryTimers(finalTime: number): void { + if (!enableProfilerTimer || !enableComponentPerformanceTrack) { + return; + } + retryClampTime = finalTime; +} + +export function clampIdleTimers(finalTime: number): void { + if (!enableProfilerTimer || !enableComponentPerformanceTrack) { + return; + } + idleClampTime = finalTime; +} + export function pushNestedEffectDurations(): number { if (!enableProfilerTimer || !enableProfilerCommitHooks) { return 0; @@ -578,3 +599,19 @@ export function transferActualDuration(fiber: Fiber): void { child = child.sibling; } } + +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; + } +} diff --git a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js index 33305f80d1750..b0e827259dc48 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js @@ -114,6 +114,9 @@ describe('ReactFiberHostContext', () => { waitForCommitToBeReady(state, timeoutOffset) { return null; }, + getSuspendedCommitReason(state, rootContainer) { + return null; + }, supportsMutation: true, }); diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 7edea606a94c5..a6c6613eb39e9 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -99,6 +99,7 @@ export const suspendInstance = $$$config.suspendInstance; export const suspendOnActiveViewTransition = $$$config.suspendOnActiveViewTransition; export const waitForCommitToBeReady = $$$config.waitForCommitToBeReady; +export const getSuspendedCommitReason = $$$config.getSuspendedCommitReason; export const NotPendingTransition = $$$config.NotPendingTransition; export const HostTransitionContext = $$$config.HostTransitionContext; export const resetFormInstance = $$$config.resetFormInstance; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index 7b1477fa25602..b5bddad8e6156 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -424,6 +424,7 @@ export function startViewTransition( passiveCallback: () => mixed, errorCallback: mixed => void, blockedCallback: string => void, // Profiling-only + finishedAnimation: () => void, // Profiling-only ): null | RunningViewTransition { mutationCallback(); layoutCallback(); @@ -589,6 +590,13 @@ export function waitForCommitToBeReady( return null; } +export function getSuspendedCommitReason( + state: SuspendedState, + rootContainer: Container, +): null | string { + return null; +} + export const NotPendingTransition: TransitionStatus = null; export const HostTransitionContext: ReactContext = { $$typeof: REACT_CONTEXT_TYPE,