Skip first frame for app transitions when possible
In most of our standard app transitions, the first frame of a transition results in the same contents on the screen. This is inefficient, as we can directly skip to the second frame of the transition, introduce no jank, but execute the transition 16ms faster. Change-Id: If58337eae5558eae3acced691ae01c769f0ec2b9
This commit is contained in:
@@ -950,6 +950,16 @@ public class AppTransition implements Dump {
|
|||||||
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
|
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if and only if the first frame of the transition can be skipped, i.e. the first
|
||||||
|
* frame of the transition doesn't change the visuals on screen, so we can start
|
||||||
|
* directly with the second one
|
||||||
|
*/
|
||||||
|
boolean canSkipFirstFrame() {
|
||||||
|
return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM
|
||||||
|
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
|
||||||
|
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
|
||||||
|
}
|
||||||
|
|
||||||
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
|
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
|
||||||
int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
|
int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
|
|||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
|
import android.view.Choreographer;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.view.WindowManagerPolicy;
|
import android.view.WindowManagerPolicy;
|
||||||
@@ -89,6 +90,8 @@ public class AppWindowAnimator {
|
|||||||
* See {@link #transferCurrentAnimation}*/
|
* See {@link #transferCurrentAnimation}*/
|
||||||
boolean usingTransferredAnimation = false;
|
boolean usingTransferredAnimation = false;
|
||||||
|
|
||||||
|
private boolean mSkipFirstFrame = false;
|
||||||
|
|
||||||
static final Animation sDummyAnimation = new DummyAnimation();
|
static final Animation sDummyAnimation = new DummyAnimation();
|
||||||
|
|
||||||
public AppWindowAnimator(final AppWindowToken atoken) {
|
public AppWindowAnimator(final AppWindowToken atoken) {
|
||||||
@@ -97,7 +100,7 @@ public class AppWindowAnimator {
|
|||||||
mAnimator = atoken.mAnimator;
|
mAnimator = atoken.mAnimator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAnimation(Animation anim, int width, int height) {
|
public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) {
|
||||||
if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
|
if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
|
||||||
+ ": " + anim + " wxh=" + width + "x" + height
|
+ ": " + anim + " wxh=" + width + "x" + height
|
||||||
+ " isVisible=" + mAppToken.isVisible());
|
+ " isVisible=" + mAppToken.isVisible());
|
||||||
@@ -125,6 +128,8 @@ public class AppWindowAnimator {
|
|||||||
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
|
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
|
||||||
hasTransformation = true;
|
hasTransformation = true;
|
||||||
|
|
||||||
|
this.mSkipFirstFrame = skipFirstFrame;
|
||||||
|
|
||||||
if (!mAppToken.appFullscreen) {
|
if (!mAppToken.appFullscreen) {
|
||||||
anim.setBackgroundColor(0);
|
anim.setBackgroundColor(0);
|
||||||
}
|
}
|
||||||
@@ -271,6 +276,18 @@ public class AppWindowAnimator {
|
|||||||
return hasMoreFrames;
|
return hasMoreFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getStartTimeCorrection() {
|
||||||
|
if (mSkipFirstFrame) {
|
||||||
|
|
||||||
|
// If the transition is an animation in which the first frame doesn't change the screen
|
||||||
|
// contents at all, we can just skip it and start at the second frame. So we shift the
|
||||||
|
// start time of the animation forward by minus the frame duration.
|
||||||
|
return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This must be called while inside a transaction.
|
// This must be called while inside a transaction.
|
||||||
boolean stepAnimationLocked(long currentTime, final int displayId) {
|
boolean stepAnimationLocked(long currentTime, final int displayId) {
|
||||||
if (mService.okToDisplay()) {
|
if (mService.okToDisplay()) {
|
||||||
@@ -292,12 +309,14 @@ public class AppWindowAnimator {
|
|||||||
" @ " + currentTime + " scale="
|
" @ " + currentTime + " scale="
|
||||||
+ mService.getTransitionAnimationScaleLocked()
|
+ mService.getTransitionAnimationScaleLocked()
|
||||||
+ " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
|
+ " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
|
||||||
animation.setStartTime(currentTime);
|
long correction = getStartTimeCorrection();
|
||||||
|
animation.setStartTime(currentTime + correction);
|
||||||
animating = true;
|
animating = true;
|
||||||
if (thumbnail != null) {
|
if (thumbnail != null) {
|
||||||
thumbnail.show();
|
thumbnail.show();
|
||||||
thumbnailAnimation.setStartTime(currentTime);
|
thumbnailAnimation.setStartTime(currentTime + correction);
|
||||||
}
|
}
|
||||||
|
mSkipFirstFrame = false;
|
||||||
}
|
}
|
||||||
if (stepAnimation(currentTime)) {
|
if (stepAnimation(currentTime)) {
|
||||||
// animation isn't over, step any thumbnail and that's
|
// animation isn't over, step any thumbnail and that's
|
||||||
|
|||||||
@@ -3489,7 +3489,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
|||||||
}
|
}
|
||||||
Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
|
Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
|
||||||
}
|
}
|
||||||
atoken.mAppAnimator.setAnimation(a, width, height);
|
atoken.mAppAnimator.setAnimation(a, width, height,
|
||||||
|
mAppTransition.canSkipFirstFrame());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
atoken.mAppAnimator.clearAnimation();
|
atoken.mAppAnimator.clearAnimation();
|
||||||
|
|||||||
Reference in New Issue
Block a user