diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index d75ff2fc7dc29..5721fa6dd11af 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -422,7 +422,7 @@ public class SurfaceControlViewHost { public void relayout(WindowManager.LayoutParams attrs, WindowlessWindowManager.ResizeCompleteCallback callback) { mViewRoot.setLayoutParams(attrs, false); - mViewRoot.setReportNextDraw(true /* syncBuffer */); + mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout"); mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5324678cab796..5887aa232e468 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -586,8 +586,21 @@ public final class ViewRootImpl implements ViewParent, int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; boolean mPerformContentCapture; - boolean mReportNextDraw; + /** Set only while mReportNextDraw=true, indicating the last reason that was triggered */ + String mLastReportNextDrawReason; + /** The reaason the last call to performDraw() returned false */ + String mLastPerformDrawSkippedReason; + /** The reason the last call to performTraversals() returned without drawing */ + String mLastPerformTraversalsSkipDrawReason; + /** The state of the local sync, if one is in progress. Can be one of the states below. */ + int mLocalSyncState; + + // The possible states of the local sync, see createSyncIfNeeded() + private final int LOCAL_SYNC_NONE = 0; + private final int LOCAL_SYNC_PENDING = 1; + private final int LOCAL_SYNC_RETURNED = 2; + private final int LOCAL_SYNC_MERGED = 3; /** * Set whether the draw should send the buffer to system server. When set to true, VRI will @@ -1813,7 +1826,7 @@ public final class ViewRootImpl implements ViewParent, mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId; if (msg == MSG_RESIZED_REPORT) { - reportNextDraw(); + reportNextDraw("resized"); } if (mView != null && (frameChanged || configChanged)) { @@ -2718,6 +2731,8 @@ public final class ViewRootImpl implements ViewParent, } private void performTraversals() { + mLastPerformTraversalsSkipDrawReason = null; + // cache mView since it is used so much below... final View host = mView; if (DBG) { @@ -2727,12 +2742,14 @@ public final class ViewRootImpl implements ViewParent, } if (host == null || !mAdded) { + mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added"; return; } mIsInTraversal = true; mWillDrawSoon = true; boolean cancelDraw = false; + String cancelReason = null; boolean isSyncRequest = false; boolean windowSizeMayChange = false; @@ -3015,13 +3032,14 @@ public final class ViewRootImpl implements ViewParent, relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW) == RELAYOUT_RES_CANCEL_AND_REDRAW; + cancelReason = "relayout"; final boolean dragResizing = mPendingDragResizing; if (mSyncSeqId > mLastSyncSeqId) { mLastSyncSeqId = mSyncSeqId; if (DEBUG_BLAST) { Log.d(mTag, "Relayout called with blastSync"); } - reportNextDraw(); + reportNextDraw("relayout"); mSyncBuffer = true; isSyncRequest = true; if (!cancelDraw) { @@ -3119,6 +3137,7 @@ public final class ViewRootImpl implements ViewParent, } } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); + mLastPerformTraversalsSkipDrawReason = "oom_initialize_renderer"; return; } } @@ -3156,6 +3175,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.updateSurface(mSurface); } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); + mLastPerformTraversalsSkipDrawReason = "oom_update_surface"; return; } } @@ -3321,6 +3341,7 @@ public final class ViewRootImpl implements ViewParent, if (mCheckIfCanDraw) { try { cancelDraw = mWindowSession.cancelDraw(mWindow); + cancelReason = "wm_sync"; if (DEBUG_BLAST) { Log.d(mTag, "cancelDraw returned " + cancelDraw); } @@ -3543,19 +3564,21 @@ public final class ViewRootImpl implements ViewParent, mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { - reportNextDraw(); + reportNextDraw("first_relayout"); } mCheckIfCanDraw = isSyncRequest || cancelDraw; - boolean cancelAndRedraw = - mAttachInfo.mTreeObserver.dispatchOnPreDraw() || (cancelDraw && mDrewOnceForSync); + boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw(); + boolean cancelAndRedraw = cancelDueToPreDrawListener + || (cancelDraw && mDrewOnceForSync); if (!cancelAndRedraw) { createSyncIfNeeded(); mDrewOnceForSync = true; } if (!isViewVisible) { + mLastPerformTraversalsSkipDrawReason = "view_not_visible"; if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).endChangingAnimations(); @@ -3567,6 +3590,9 @@ public final class ViewRootImpl implements ViewParent, mSyncBufferCallback.onBufferReady(null); } } else if (cancelAndRedraw) { + mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener + ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason() + : "cancel_" + cancelReason; // Try again scheduleTraversals(); } else { @@ -3590,11 +3616,13 @@ public final class ViewRootImpl implements ViewParent, if (!cancelAndRedraw) { mReportNextDraw = false; + mLastReportNextDrawReason = null; mSyncBufferCallback = null; mSyncBuffer = false; if (isInLocalSync()) { mSurfaceSyncer.markSyncReady(mSyncId); mSyncId = UNSET_SYNC_ID; + mLocalSyncState = LOCAL_SYNC_NONE; } } } @@ -3606,9 +3634,12 @@ public final class ViewRootImpl implements ViewParent, } final int seqId = mSyncSeqId; + mLocalSyncState = LOCAL_SYNC_PENDING; mSyncId = mSurfaceSyncer.setupSync(transaction -> { + mLocalSyncState = LOCAL_SYNC_RETURNED; // Callback will be invoked on executor thread so post to main thread. mHandler.postAtFrontOfQueue(() -> { + mLocalSyncState = LOCAL_SYNC_MERGED; mSurfaceChangedTransaction.merge(transaction); reportDrawFinished(seqId); }); @@ -4323,9 +4354,12 @@ public final class ViewRootImpl implements ViewParent, } private boolean performDraw() { + mLastPerformDrawSkippedReason = null; if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { + mLastPerformDrawSkippedReason = "screen_off"; return false; } else if (mView == null) { + mLastPerformDrawSkippedReason = "no_root_view"; return false; } @@ -8392,6 +8426,21 @@ public final class ViewRootImpl implements ViewParent, if (mTraversalScheduled) { writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")"); } + writer.println(innerPrefix + "mReportNextDraw=" + mReportNextDraw); + if (mReportNextDraw) { + writer.println(innerPrefix + " (reason=" + mLastReportNextDrawReason + ")"); + } + if (mLastPerformTraversalsSkipDrawReason != null) { + writer.println(innerPrefix + "mLastPerformTraversalsFailedReason=" + + mLastPerformTraversalsSkipDrawReason); + } + if (mLastPerformDrawSkippedReason != null) { + writer.println(innerPrefix + "mLastPerformDrawFailedReason=" + + mLastPerformDrawSkippedReason); + } + if (mLocalSyncState != LOCAL_SYNC_NONE) { + writer.println(innerPrefix + "mLocalSyncState=" + mLocalSyncState); + } writer.println(innerPrefix + "mIsAmbientMode=" + mIsAmbientMode); writer.println(innerPrefix + "mUnbufferedInputSource=" + Integer.toHexString(mUnbufferedInputSource)); @@ -9892,11 +9941,12 @@ public final class ViewRootImpl implements ViewParent, } } - private void reportNextDraw() { + private void reportNextDraw(String reason) { if (DEBUG_BLAST) { Log.d(mTag, "reportNextDraw " + Debug.getCallers(5)); } mReportNextDraw = true; + mLastReportNextDrawReason = reason; } /** @@ -9909,11 +9959,12 @@ public final class ViewRootImpl implements ViewParent, * @param syncBuffer If true, the transaction that contains the buffer from the draw should be * sent to system to be synced. If false, VRI will not try to sync the buffer, * but only report back that a buffer was drawn. + * @param reason A debug string indicating the reason for reporting the next draw * @hide */ - public void setReportNextDraw(boolean syncBuffer) { + public void setReportNextDraw(boolean syncBuffer, String reason) { mSyncBuffer = syncBuffer; - reportNextDraw(); + reportNextDraw(reason); invalidate(); } diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index ed8350afc109c..fd62ecd11e13d 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -74,6 +74,9 @@ public final class ViewTreeObserver { * that the listener will be immediately called. */ private boolean mWindowShown; + // The reason that the last call to dispatchOnPreDraw() returned true to cancel and redraw + private String mLastDispatchOnPreDrawCanceledReason; + private boolean mAlive = true; /** @@ -1167,6 +1170,7 @@ public final class ViewTreeObserver { */ @SuppressWarnings("unchecked") public final boolean dispatchOnPreDraw() { + mLastDispatchOnPreDrawCanceledReason = null; boolean cancelDraw = false; final CopyOnWriteArray listeners = mOnPreDrawListeners; if (listeners != null && listeners.size() > 0) { @@ -1174,7 +1178,11 @@ public final class ViewTreeObserver { try { int count = access.size(); for (int i = 0; i < count; i++) { - cancelDraw |= !(access.get(i).onPreDraw()); + final OnPreDrawListener preDrawListener = access.get(i); + cancelDraw |= !(preDrawListener.onPreDraw()); + if (cancelDraw) { + mLastDispatchOnPreDrawCanceledReason = preDrawListener.getClass().getName(); + } } } finally { listeners.end(); @@ -1183,6 +1191,15 @@ public final class ViewTreeObserver { return cancelDraw; } + /** + * @return the reason that the last call to dispatchOnPreDraw() returned true to cancel the + * current draw, or null if the last call did not cancel. + * @hide + */ + final String getLastDispatchOnPreDrawCanceledReason() { + return mLastDispatchOnPreDrawCanceledReason; + } + /** * Notifies registered listeners that the window is now shown * @hide