From a7e33a41e54e25761704dc0217ba05d8db3f57d3 Mon Sep 17 00:00:00 2001 From: Tiger Huang Date: Fri, 19 Mar 2021 03:42:44 +0800 Subject: [PATCH] Re-land: Make window stable while resizing When the frame size and the frame position of the window are changed at the same time, setPosition to the surface will be applied first, and the client will draw on the new-size buffer later, which makes the window flicker. This CL uses applyWithNextDraw to make the new surface position can be applied while the new frame is drawn. applyWithNextDraw is applied only when - the window doesn't have a move animation. So if a window needs to make resizing stable, it may need PRIVATE_FLAG_NO_MOVE_ANIMATION. - it is visible, e.g., the surface is shown, and the window is OK to display -- for better performance. If applyWithNextDraw is used while the window frame is changed to an empty rectangle, e.g., Rect(10, 10 - 1000, 10), mNextDrawUseBlastSync will stay true forever, because we don't draw while the dirty area is empty, and ViewRootImpl would lose the only chance to clear the flag. performTraversals will never be executed. This CL makes mNextDrawUseBlastSync can be cleared in that case. Bug: 182729646 Fix: 176874720 Test: steps in the bug Change-Id: I81b0574ee8db7e4d9053639f56e04858d9feae90 --- core/java/android/view/ViewRootImpl.java | 2 +- .../android/internal/policy/PhoneWindow.java | 2 + .../server/wm/InsetsSourceProvider.java | 43 ++++++--------- .../com/android/server/wm/WindowFrames.java | 9 +++- .../server/wm/WindowManagerService.java | 7 --- .../com/android/server/wm/WindowState.java | 52 ++++++++++++------- 6 files changed, 59 insertions(+), 56 deletions(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index dbccf10822cd2..fdbec8b803610 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4293,7 +4293,7 @@ public final class ViewRootImpl implements ViewParent, mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS; boolean useAsyncReport = false; - if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { + if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) { if (isHardwareEnabled()) { // If accessibility focus moved, always invalidate the root. boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 39bde742e8287..611fe29f4fcf6 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import android.annotation.NonNull; import android.annotation.Nullable; @@ -2508,6 +2509,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mDecor.mForceWindowDrawsBarBackgrounds) { params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; } + params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; } if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { decor.setSystemUiVisibility( diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 38ad4f08b35d5..7b0fb0146bc2b 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -30,7 +30,6 @@ import static com.android.server.wm.InsetsSourceProviderProto.CONTROLLABLE; import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET; -import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER; import static com.android.server.wm.InsetsSourceProviderProto.FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING; @@ -60,6 +59,7 @@ import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import java.io.PrintWriter; +import java.util.function.Consumer; /** * Controller for a specific inset source on the server. It's called provider as it provides the @@ -85,6 +85,16 @@ class InsetsSourceProvider { private final Rect mImeOverrideFrame = new Rect(); private boolean mIsLeashReadyForDispatching; + private final Consumer mSetLeashPositionConsumer = t -> { + if (mControl != null) { + final SurfaceControl leash = mControl.getLeash(); + if (leash != null) { + final Point position = mControl.getSurfacePosition(); + t.setPosition(leash, position.x, position.y); + } + } + }; + /** The visibility override from the current controlling window. */ private boolean mClientVisible; @@ -151,7 +161,6 @@ class InsetsSourceProvider { // TODO: Ideally, we should wait for the animation to finish so previous window can // animate-out as new one animates-in. mWin.cancelAnimation(); - mWin.mPendingPositionChanged = null; mWin.mProvidedInsetsSources.remove(mSource.getType()); } ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win); @@ -252,12 +261,11 @@ class InsetsSourceProvider { final Point position = getWindowFrameSurfacePosition(); if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) { changed = true; - if (!mWin.getWindowFrames().didFrameSizeChange()) { - updateLeashPosition(-1 /* frameNumber */); - } else if (mWin.mInRelayout) { - updateLeashPosition(mWin.getFrameNumber()); + if (mWin.getWindowFrames().didFrameSizeChange() && mWin.mWinAnimator.getShown() + && mWin.okToDisplay()) { + mWin.applyWithNextDraw(mSetLeashPositionConsumer); } else { - mWin.mPendingPositionChanged = this; + mSetLeashPositionConsumer.accept(mWin.getPendingTransaction()); } } final Insets insetsHint = mSource.calculateInsets( @@ -272,19 +280,6 @@ class InsetsSourceProvider { } } - void updateLeashPosition(long frameNumber) { - if (mControl == null) { - return; - } - final SurfaceControl leash = mControl.getLeash(); - if (leash != null) { - final Transaction t = mDisplayContent.getPendingTransaction(); - final Point position = mControl.getSurfacePosition(); - t.setPosition(leash, position.x, position.y); - deferTransactionUntil(t, leash, frameNumber); - } - } - private Point getWindowFrameSurfacePosition() { final Rect frame = mWin.getFrame(); final Point position = new Point(); @@ -292,14 +287,6 @@ class InsetsSourceProvider { return position; } - private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) { - if (frameNumber >= 0) { - final SurfaceControl barrier = mWin.getClientViewRootSurface(); - t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); - t.deferTransactionUntil(leash, barrier, frameNumber); - } - } - /** * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget) */ diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index 9245f8c3efe5e..ffd6d21c1026b 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -113,7 +113,7 @@ public class WindowFrames { } /** - * @return true if the width or height has changed since last reported to the client. + * @return true if the width or height has changed since last updating resizing window. */ boolean didFrameSizeChange() { return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height()); @@ -134,6 +134,13 @@ public class WindowFrames { return mLastForceReportingResized || mFrameSizeChanged; } + /** + * @return true if the width or height has changed since last reported to the client. + */ + boolean isFrameSizeChangeReported() { + return mFrameSizeChanged || didFrameSizeChange(); + } + /** * Resets the size changed flags so they're all set to false again. This should be called * after the frames are reported to client. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c99b01f1d2b58..0b78a7338e026 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2228,13 +2228,6 @@ public class WindowManagerService extends IWindowManager.Stub win.setFrameNumber(frameNumber); - final DisplayContent dc = win.getDisplayContent(); - - if (win.mPendingPositionChanged != null) { - win.mPendingPositionChanged.updateLeashPosition(frameNumber); - win.mPendingPositionChanged = null; - } - int attrChanges = 0; int flagChanges = 0; int privateFlagChanges = 0; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 48d4fc5464b00..eb8315248db2a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -726,8 +726,6 @@ class WindowState extends WindowContainer implements WindowManagerP */ private InsetsState mFrozenInsetsState; - @Nullable InsetsSourceProvider mPendingPositionChanged; - private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; private KeyInterceptionInfo mKeyInterceptionInfo; @@ -824,6 +822,12 @@ class WindowState extends WindowContainer implements WindowManagerP updateSurfacePosition(t); }; + private final Consumer mSetSurfacePositionConsumer = t -> { + if (mSurfaceControl != null && mSurfaceControl.isValid()) { + t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); + } + }; + /** * @see #setSurfaceTranslationY(int) */ @@ -2180,18 +2184,7 @@ class WindowState extends WindowContainer implements WindowManagerP final int left = mWindowFrames.mFrame.left; final int top = mWindowFrames.mFrame.top; - // During the transition from pip to fullscreen, the activity windowing mode is set to - // fullscreen at the beginning while the task is kept in pinned mode. Skip the move - // animation in such case since the transition is handled in SysUI. - final boolean hasMovementAnimation = getTask() == null - ? getWindowConfiguration().hasMovementAnimations() - : getTask().getWindowConfiguration().hasMovementAnimations(); - if (mToken.okToAnimate() - && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 - && !isDragResizing() - && hasMovementAnimation - && !mWinAnimator.mLastHidden - && !mSeamlesslyRotated) { + if (canPlayMoveAnimation()) { startMoveAnimation(left, top); } @@ -2207,6 +2200,22 @@ class WindowState extends WindowContainer implements WindowManagerP mMovedByResize = false; } + private boolean canPlayMoveAnimation() { + + // During the transition from pip to fullscreen, the activity windowing mode is set to + // fullscreen at the beginning while the task is kept in pinned mode. Skip the move + // animation in such case since the transition is handled in SysUI. + final boolean hasMovementAnimation = getTask() == null + ? getWindowConfiguration().hasMovementAnimations() + : getTask().getWindowConfiguration().hasMovementAnimations(); + return mToken.okToAnimate() + && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 + && !isDragResizing() + && hasMovementAnimation + && !mWinAnimator.mLastHidden + && !mSeamlesslyRotated; + } + /** * Return whether this window has moved. (Only makes * sense to call from performLayoutAndPlaceSurfacesLockedInner().) @@ -5377,13 +5386,18 @@ class WindowState extends WindowContainer implements WindowManagerP // prior to the rotation. if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null && !mLastSurfacePosition.equals(mSurfacePosition)) { - t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); + final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported(); + final boolean surfaceInsetsChanged = surfaceInsetsChanging(); + final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged; mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); - if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) { + if (surfaceInsetsChanged) { mLastSurfaceInsets.set(mAttrs.surfaceInsets); - t.deferTransactionUntil(mSurfaceControl, - mWinAnimator.mSurfaceController.mSurfaceControl, - getFrameNumber()); + } + if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation() + && okToDisplay()) { + applyWithNextDraw(mSetSurfacePositionConsumer); + } else { + mSetSurfacePositionConsumer.accept(t); } } }