Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Optimize gesture by allowing the original work in progress tree to be… · react/react@4cf9063 · GitHub
Skip to content

Commit 4cf9063

Browse files
authored
Optimize gesture by allowing the original work in progress tree to be a suspended commit (#35510)
Stacked on #35487. This is slightly different because the first suspended commit is on blockers that prevent us from committing which still needs to be resolved first. If a gesture lane has to be rerendered while the gesture is happening then it reenters this state with a new tree. (Currently this doesn't happen for a ping I think which is not really how it usually works but better in this case.)
1 parent eac3c95 commit 4cf9063

4 files changed

Lines changed: 125 additions & 27 deletions

File tree

‎fixtures/view-transition/src/components/SwipeRecognizer.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ export default function SwipeRecognizer({
114114
);
115115
}
116116
functiononGestureEnd(changed){
117-
// Reset scroll
118-
if(changed){
119-
// Trigger side-effects
120-
startTransition(action);
121-
}
117+
// We cancel the gesture before invoking side-effects to allow the gesture lane to fully commit
118+
// before scheduling new updates.
122119
if(activeGesture.current!==null){
123120
constcancelGesture=activeGesture.current;
124121
activeGesture.current=null;
125122
cancelGesture();
126123
}
124+
if(changed){
125+
// Trigger side-effects
126+
startTransition(action);
127+
}
127128
}
128129
functiononScrollEnd(){
129130
if(touchTimeline.current){

‎packages/react-reconciler/src/ReactFiberGestureScheduler.js‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ScheduledGesture = {
3535
rangeEnd: number,// The percentage along the timeline where the "destination" state is reached.
3636
types: null|TransitionTypes,// Any addTransitionType call made during startGestureTransition.
3737
running: null|RunningViewTransition,// Used to cancel the running transition after we're done.
38+
commit: null|(()=>void),// Callback to run to commit if there's a pending commit.
3839
committing: boolean,// If the gesture was released in a committed state and should actually commit.
3940
revertLane: Lane,// The Lane that we'll use to schedule the revert.
4041
prev: null|ScheduledGesture,// The previous scheduled gesture in the queue for this root.
@@ -64,6 +65,7 @@ export function scheduleGesture(
6465
rangeEnd: 100,// Uninitialized
6566
types: null,
6667
running: null,
68+
commit: null,
6769
committing: false,
6870
revertLane: NoLane,// Starts uninitialized.
6971
prev: prev,
@@ -164,9 +166,17 @@ export function cancelScheduledGesture(
164166
// lane to actually commit it.
165167
gesture.committing=true;
166168
if(root.pendingGestures===gesture){
167-
// Ping the root given the new state. This is similar to pingSuspendedRoot.
168-
// This will either schedule the gesture lane to be committed possibly from its current state.
169-
pingGestureRoot(root);
169+
constcommitCallback=gesture.commit;
170+
if(commitCallback!==null){
171+
gesture.commit=null;
172+
// If we already have a commit prepared we can immediately commit the tree
173+
// without rerendering.
174+
// TODO: Consider scheduling this in a task instead of synchronously inside the last cancellation.s
175+
commitCallback();
176+
}else{
177+
// Ping the root given the new state. This is similar to pingSuspendedRoot.
178+
pingGestureRoot(root);
179+
}
170180
}
171181
}else{
172182
// If we're not going to commit this gesture we can stop the View Transition
@@ -235,3 +245,13 @@ export function stopCommittedGesture(root: FiberRoot) {
235245
}
236246
}
237247
}
248+
249+
exportfunctionscheduleGestureCommit(
250+
gesture: ScheduledGesture,
251+
callback: ()=>void,
252+
): ()=>void{
253+
gesture.commit=callback;
254+
returnfunction(){
255+
gesture.commit=null;
256+
};
257+
}

‎packages/react-reconciler/src/ReactFiberPerformanceTrack.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,41 @@ export function logPaintYieldPhase(
15691569
}
15701570
}
15711571

1572+
exportfunctionlogApplyGesturePhase(
1573+
startTime: number,
1574+
endTime: number,
1575+
debugTask: null|ConsoleTask,
1576+
): void{
1577+
if(supportsUserTiming){
1578+
if(endTime<=startTime){
1579+
return;
1580+
}
1581+
if(__DEV__&&debugTask){
1582+
debugTask.run(
1583+
// $FlowFixMe[method-unbinding]
1584+
console.timeStamp.bind(
1585+
console,
1586+
'CreateGhostTree',
1587+
startTime,
1588+
endTime,
1589+
currentTrack,
1590+
LANES_TRACK_GROUP,
1591+
'secondary-dark',
1592+
),
1593+
);
1594+
}else{
1595+
console.timeStamp(
1596+
'CreateGhostTree',
1597+
startTime,
1598+
endTime,
1599+
currentTrack,
1600+
LANES_TRACK_GROUP,
1601+
'secondary-dark',
1602+
);
1603+
}
1604+
}
1605+
}
1606+
15721607
exportfunctionlogStartViewTransitionYieldPhase(
15731608
startTime: number,
15741609
endTime: number,

‎packages/react-reconciler/src/ReactFiberWorkLoop.js‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import {
9292
logSuspendedYieldTime,
9393
setCurrentTrackFromLanes,
9494
markAllLanesInOrder,
95+
logApplyGesturePhase,
9596
}from'./ReactFiberPerformanceTrack';
9697

9798
import{
@@ -398,7 +399,10 @@ import {
398399
}from'./ReactFiberRootScheduler';
399400
import{getMaskedContext,getUnmaskedContext}from'./ReactFiberLegacyContext';
400401
import{logUncaughtError}from'./ReactFiberErrorLogger';
401-
import{stopCommittedGesture}from'./ReactFiberGestureScheduler';
402+
import{
403+
scheduleGestureCommit,
404+
stopCommittedGesture,
405+
}from'./ReactFiberGestureScheduler';
402406
import{claimQueuedTransitionTypes}from'./ReactFiberTransitionTypes';
403407

404408
constPossiblyWeakMap=typeofWeakMap==='function' ? WeakMap : Map;
@@ -1542,11 +1546,10 @@ function completeRootWhenReady(
15421546
isViewTransitionEligible||
15431547
(isGestureTransition&&
15441548
root.pendingGestures!==null&&
1545-
// If we're committing this gesture and it already has a View Transition
1546-
// running, then we don't have to wait for that gesture. We'll stop it
1547-
// when we commit.
1548-
(root.pendingGestures.running===null||
1549-
!root.pendingGestures.committing))
1549+
// If this gesture already has a View Transition running then we don't
1550+
// have to wait on that one before proceeding. We may hold the commit
1551+
// on the gesture committing later on in completeRoot.
1552+
root.pendingGestures.running===null)
15501553
){
15511554
// Wait for any pending View Transition (including gestures) to finish.
15521555
suspendOnActiveViewTransition(suspendedState,root.containerInfo);
@@ -3463,6 +3466,16 @@ function completeRoot(
34633466
if(enableProfilerTimer&&enableComponentPerformanceTrack){
34643467
// Log the previous render phase once we commit. I.e. we weren't interrupted.
34653468
setCurrentTrackFromLanes(lanes);
3469+
if(isGestureRender(lanes)){
3470+
// Clamp the render start time in case if something else on this lane was committed
3471+
// (such as this same tree before).
3472+
if(completedRenderStartTime<gestureClampTime){
3473+
completedRenderStartTime=gestureClampTime;
3474+
}
3475+
if(completedRenderEndTime<gestureClampTime){
3476+
completedRenderEndTime=gestureClampTime;
3477+
}
3478+
}
34663479
if(exitStatus===RootErrored){
34673480
logErroredRenderPhase(
34683481
completedRenderStartTime,
@@ -3580,7 +3593,38 @@ function completeRoot(
35803593
}else{
35813594
// If we already have a gesture running, we don't update it in place
35823595
// even if we have a new tree. Instead we wait until we can commit.
3596+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
3597+
// Clamp at the render time since we're not going to finish the rest
3598+
// of this commit or apply yet.
3599+
finalizeRender(lanes,completedRenderEndTime);
3600+
}
3601+
// We are no longer committing.
3602+
pendingEffectsRoot=(null: any);// Clear for GC purposes.
3603+
pendingFinishedWork=(null: any);// Clear for GC purposes.
3604+
pendingEffectsLanes=NoLanes;
35833605
}
3606+
// Schedule the root to be committed when the gesture completes.
3607+
root.cancelPendingCommit=scheduleGestureCommit(
3608+
committingGesture,
3609+
completeRoot.bind(
3610+
null,
3611+
root,
3612+
finishedWork,
3613+
lanes,
3614+
recoverableErrors,
3615+
transitions,
3616+
didIncludeRenderPhaseUpdate,
3617+
spawnedLane,
3618+
updatedLanes,
3619+
suspendedRetryLanes,
3620+
didSkipSuspendedSiblings,
3621+
exitStatus,
3622+
suspendedState,
3623+
'Waiting for the Gesture to finish'/* suspendedCommitReason */,
3624+
completedRenderStartTime,
3625+
completedRenderEndTime,
3626+
),
3627+
);
35843628
return;
35853629
}
35863630
}
@@ -4368,6 +4412,15 @@ function flushGestureMutations(): void {
43684412
ReactSharedInternals.T=prevTransition;
43694413
}
43704414

4415+
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4416+
recordCommitEndTime();
4417+
logApplyGesturePhase(
4418+
pendingEffectsRenderEndTime,
4419+
commitEndTime,
4420+
animatingTask,
4421+
);
4422+
}
4423+
43714424
pendingEffectsStatus=PENDING_GESTURE_ANIMATION_PHASE;
43724425
}
43734426

@@ -4385,10 +4438,11 @@ function flushGestureAnimations(): void {
43854438
constlanes=pendingEffectsLanes;
43864439

43874440
if(enableProfilerTimer&&enableComponentPerformanceTrack){
4441+
conststartViewTransitionStartTime=commitEndTime;
43884442
// Update the new commitEndTime to when we started the animation.
43894443
recordCommitEndTime();
43904444
logStartViewTransitionYieldPhase(
4391-
pendingEffectsRenderEndTime,
4445+
startViewTransitionStartTime,
43924446
commitEndTime,
43934447
pendingDelayedCommitReason===ABORTED_VIEW_TRANSITION_COMMIT,
43944448
animatingTask,
@@ -4904,18 +4958,6 @@ export function pingGestureRoot(root: FiberRoot): void {
49044958
if(gesture===null){
49054959
return;
49064960
}
4907-
if(
4908-
root.cancelPendingCommit!==null&&
4909-
isGestureRender(pendingEffectsLanes)
4910-
){
4911-
// We have a suspended commit which we'll discard and rerender.
4912-
// TODO: Just use this commit since it's ready to go.
4913-
constcancelPendingCommit=root.cancelPendingCommit;
4914-
if(cancelPendingCommit!==null){
4915-
root.cancelPendingCommit=null;
4916-
cancelPendingCommit();
4917-
}
4918-
}
49194961
// Ping it for rerender and commit.
49204962
markRootPinged(root,GestureLane);
49214963
ensureRootIsScheduled(root);

0 commit comments

Comments
 (0)