diff options
author | 2024-10-12 00:57:34 +0000 | |
---|---|---|
committer | 2024-10-12 00:59:20 +0000 | |
commit | 89f28270932c1c42c42706285e99bb30e28ae4b6 (patch) | |
tree | d4dbcb548f51729f8aeacb9dd9b15368095ae98f | |
parent | 0c06c085d06ed70234015edfe395a82150c74147 (diff) |
Fix ViewRootImpl traversal issue after SurfaceSyncGroup timeout
ViewRootImpl traversal is blocked from the time a sync is requested
to when the sync is successfully added to a SurfaceSyncGroup.
If the ViewRootImpl draw timeouts, the ViewRootImpl's
SurfaceSyncGroup is marked as ready. If there is another
sync request on the ViewRootImpl before the traversal path,
we can get in a state where we will never draw again.
Fix this by updating cleaning up the ViewRootImpl state
when the SurfaceSyncGroup is marked as ready.
Flag: EXEMPT bug fix
Bug: 362513091
Test: presubmit (coming in future cl)
Change-Id: I6c0ac8f96914d231cc9dc695f6fc8975ba2c5864
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 41 | ||||
-rw-r--r-- | core/java/android/window/SurfaceSyncGroup.java | 10 |
2 files changed, 43 insertions, 8 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d46e1f29597e..4602e90dc01e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6971,9 +6971,7 @@ public final class ViewRootImpl implements ViewParent, handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj); break; case MSG_PAUSED_FOR_SYNC_TIMEOUT: - Log.e(mTag, "Timedout waiting to unpause for sync"); - mNumPausedForSync = 0; - scheduleTraversals(); + resumeAfterSyncTimeout(); break; case MSG_CHECK_INVALIDATION_IDLE: { long delta; @@ -12766,6 +12764,15 @@ public final class ViewRootImpl implements ViewParent, activeSurfaceSyncGroup.addTransaction(t); } + /** + * Resume rendering after being paused for sync due to a timeout. + */ + private void resumeAfterSyncTimeout() { + Log.e(mTag, "Timedout waiting to unpause for sync mNumPausedForSync=" + mNumPausedForSync); + mNumPausedForSync = 0; + scheduleTraversals(); + } + @Override public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() { boolean newSyncGroup = false; @@ -12793,6 +12800,16 @@ public final class ViewRootImpl implements ViewParent, } }); newSyncGroup = true; + + // If the sync group is marked ready by a timeout, check if rendering is paused and + // if it is, resume rendering and trigger a traversal. + mActiveSurfaceSyncGroup.addSyncCompleteCallback(mExecutor, () -> { + if (mActiveSurfaceSyncGroup != null + && mActiveSurfaceSyncGroup.isComplete() && mNumPausedForSync > 0) { + mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); + resumeAfterSyncTimeout(); + } + }); } Trace.instant(Trace.TRACE_TAG_VIEW, @@ -12807,12 +12824,20 @@ public final class ViewRootImpl implements ViewParent, } } - mNumPausedForSync++; - mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); - mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, - 1000 * Build.HW_TIMEOUT_MULTIPLIER); + // The sync group can be marked ready by a timeout. This makes incrementing + // mNumPausedForSync racy. Here we check if the sync group is complete and + // if it is then we don't pause for syncing. + if (!mActiveSurfaceSyncGroup.isComplete()) { + mNumPausedForSync++; + mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, + 1000 * Build.HW_TIMEOUT_MULTIPLIER); + } else { + Log.d(mTag, "Active sync group is already completed " + + mActiveSurfaceSyncGroup.getName()); + } return mActiveSurfaceSyncGroup; - }; + } private final Executor mSimpleExecutor = Runnable::run; diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java index 5d14698c82b3..a68bdc05e20e 100644 --- a/core/java/android/window/SurfaceSyncGroup.java +++ b/core/java/android/window/SurfaceSyncGroup.java @@ -839,6 +839,16 @@ public final class SurfaceSyncGroup { } /** + * Returns true if the SurfaceSyncGroup has completed its sync. + * @hide + */ + public boolean isComplete() { + synchronized (mLock) { + return mFinished; + } + } + + /** * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must * implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync * knows when the frame is ready to add to the sync. |