From 55d882cf5f36edeafccfb97ed539a4c13e85fbfd Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Wed, 22 Jul 2020 16:59:05 -0700 Subject: [PATCH] WindowStateAnimator/ViewRoot: Move surface resizing to client The bounds on the client surface are controlled by higher levels of the hierarchy and so the client can use whatever Surface size it wants on its node. We move the SurfaceControl setSize call to the client, following 3 sort of ideas: 1. Project to make relayout window async, but currently it is used as a sync point for resizing, resizing on client, one problem solved! 2. WM Shouldn't do what it doesnt have to for clients 3. Totally move management of lowest level Surface to client clean up lots of WM code (see WindowStateAnimator slimming project) In the future maybe we don't need to set the SurfaceControl size at all and the client can just set its buffer size, but it may produce some differences in geometry handling on the server so I want to maintain the old semantics for this CL. Bug: 161937501 Test: Existing tests pass Change-Id: Icebd94f8443fdbe9f0e6968bc35bbb0504a1520c --- core/java/android/view/ViewRootImpl.java | 43 +++++++++++--- .../server/wm/WindowManagerService.java | 4 +- .../com/android/server/wm/WindowState.java | 33 +++++++++++ .../server/wm/WindowStateAnimator.java | 56 +++---------------- 4 files changed, 76 insertions(+), 60 deletions(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4176e887c0e7c..b1b77edad07f8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1790,7 +1790,7 @@ public final class ViewRootImpl implements ViewParent, .setParent(getRenderSurfaceControl()) .setCallsite("ViewRootImpl.getBoundsLayer") .build(); - setBoundsLayerCrop(); + setBoundsLayerCrop(mTransaction); mTransaction.show(mBoundsLayer).apply(); } return mBoundsLayer; @@ -1818,25 +1818,41 @@ public final class ViewRootImpl implements ViewParent, return ret; } - private void setBoundsLayerCrop() { + private void setBoundsLayerCrop(Transaction t) { // mWinFrame is already adjusted for surface insets. So offset it and use it as // the cropping bounds. mTempBoundsRect.set(mWinFrame); mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left, mWindowAttributes.surfaceInsets.top); - mTransaction.setWindowCrop(mBoundsLayer, mTempBoundsRect); + t.setWindowCrop(mBoundsLayer, mTempBoundsRect); } /** * Called after window layout to update the bounds surface. If the surface insets have changed * or the surface has resized, update the bounds surface. */ - private void updateBoundsLayer() { + private boolean updateBoundsLayer(SurfaceControl.Transaction t) { if (mBoundsLayer != null) { - setBoundsLayerCrop(); - mTransaction.deferTransactionUntil(mBoundsLayer, - getRenderSurfaceControl(), mSurface.getNextFrameNumber()) - .apply(); + setBoundsLayerCrop(t); + t.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), + mSurface.getNextFrameNumber()); + return true; + } + return false; + } + + private void prepareSurfaces(boolean sizeChanged) { + final SurfaceControl.Transaction t = mTransaction; + final SurfaceControl sc = getRenderSurfaceControl(); + if (!sc.isValid()) return; + + boolean applyTransaction = updateBoundsLayer(t); + if (sizeChanged) { + applyTransaction = true; + t.setBufferSize(sc, mSurfaceSize.x, mSurfaceSize.y); + } + if (applyTransaction) { + t.apply(); } } @@ -2927,7 +2943,16 @@ public final class ViewRootImpl implements ViewParent, } if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { - updateBoundsLayer(); + // If the surface has been replaced, there's a chance the bounds layer is not parented + // to the new layer. When updating bounds layer, also reparent to the main VRI + // SurfaceControl to ensure it's correctly placed in the hierarchy. + // + // This needs to be done on the client side since WMS won't reparent the children to the + // new surface if it thinks the app is closing. WMS gets the signal that the app is + // stopping, but on the client side it doesn't get stopped since it's restarted quick + // enough. WMS doesn't want to keep around old children since they will leak when the + // client creates new children. + prepareSurfaces(surfaceSizeChanged); } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d9c574ccc64c3..a7f5eca9af947 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2427,8 +2427,8 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } if (winAnimator.mSurfaceController != null) { - outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(), - winAnimator.mSurfaceController.getHeight()); + win.calculateSurfaceBounds(win.getAttrs(), mTmpRect); + outSurfaceSize.set(mTmpRect.width(), mTmpRect.height()); } getInsetsSourceControls(win, outActiveControls); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8cdcb9b6af07b..0a1c184d84853 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5940,4 +5940,37 @@ class WindowState extends WindowContainer implements WindowManagerP void requestRedrawForSync() { mRedrawForSyncReported = false; } + + void calculateSurfaceBounds(WindowManager.LayoutParams attrs, Rect outSize) { + outSize.setEmpty(); + if ((attrs.flags & FLAG_SCALED) != 0) { + // For a scaled surface, we always want the requested size. + outSize.right = mRequestedWidth; + outSize.bottom = mRequestedHeight; + } else { + // When we're doing a drag-resizing, request a surface that's fullscreen size, + // so that we don't need to reallocate during the process. This also prevents + // buffer drops due to size mismatch. + if (isDragResizing()) { + final DisplayInfo displayInfo = getDisplayInfo(); + outSize.right = displayInfo.logicalWidth; + outSize.bottom = displayInfo.logicalHeight; + } else { + getCompatFrameSize(outSize); + } + } + + // This doesn't necessarily mean that there is an error in the system. The sizes might be + // incorrect, because it is before the first layout or draw. + if (outSize.width() < 1) { + outSize.right = 1; + } + if (outSize.height() < 1) { + outSize.bottom = 1; + } + + // Adjust for surface insets. + outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top, + -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom); + } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 7bb79555c52c9..a31c5fef29b80 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -456,7 +456,8 @@ class WindowStateAnimator { flags |= SurfaceControl.SKIP_SCREENSHOT; } - calculateSurfaceBounds(w, attrs, mTmpSize); + w.calculateSurfaceBounds(attrs, mTmpSize); + final int width = mTmpSize.width(); final int height = mTmpSize.height(); @@ -532,40 +533,6 @@ class WindowStateAnimator { return mSurfaceController; } - private void calculateSurfaceBounds(WindowState w, LayoutParams attrs, Rect outSize) { - outSize.setEmpty(); - if ((attrs.flags & FLAG_SCALED) != 0) { - // For a scaled surface, we always want the requested size. - outSize.right = w.mRequestedWidth; - outSize.bottom = w.mRequestedHeight; - } else { - // When we're doing a drag-resizing, request a surface that's fullscreen size, - // so that we don't need to reallocate during the process. This also prevents - // buffer drops due to size mismatch. - if (w.isDragResizing()) { - final DisplayInfo displayInfo = w.getDisplayInfo(); - outSize.right = displayInfo.logicalWidth; - outSize.bottom = displayInfo.logicalHeight; - } else { - w.getCompatFrameSize(outSize); - } - } - - // Something is wrong and SurfaceFlinger will not like this, try to revert to reasonable - // values. This doesn't necessarily mean that there is an error in the system. The sizes - // might be incorrect, because it is before the first layout or draw. - if (outSize.width() < 1) { - outSize.right = 1; - } - if (outSize.height() < 1) { - outSize.bottom = 1; - } - - // Adjust for surface insets. - outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top, - -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom); - } - boolean hasSurface() { return mSurfaceController != null && mSurfaceController.hasSurface(); } @@ -771,20 +738,6 @@ class WindowStateAnimator { final LayoutParams attrs = mWin.getAttrs(); final Task task = w.getTask(); - calculateSurfaceBounds(w, attrs, mTmpSize); - - // Once relayout has been called at least once, we need to make sure - // we only resize the client surface during calls to relayout. For - // clients which use indeterminate measure specs (MATCH_PARENT), - // we may try and change their window size without a call to relayout. - // However, this would be unsafe, as the client may be in the middle - // of producing a frame at the old size, having just completed layout - // to find the surface size changed underneath it. - final boolean relayout = !w.mRelayoutCalled || w.mInRelayout; - if (relayout) { - mSurfaceController.setBufferSizeInTransaction( - mTmpSize.width(), mTmpSize.height(), recoveringMemory); - } // If we are undergoing seamless rotation, the surface has already // been set up to persist at it's old location. We need to freeze // updates until a resize occurs. @@ -808,6 +761,11 @@ class WindowStateAnimator { final Rect insets = attrs.surfaceInsets; + // getFrameNumber is only valid in the call-stack of relayoutWindow + // as this is the only-time we know the client renderer + // is paused. + final boolean relayout = !w.mRelayoutCalled || w.mInRelayout; + if (!w.mSeamlesslyRotated) { // Used to offset the WSA when stack position changes before a resize. int xOffset = mXOffset;