From e2c336e7d98c67516d0f17bb4f59acb10b4221a7 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Wed, 13 Oct 2021 13:09:34 -0700 Subject: [PATCH] ViewRootImpl: Add P2P sync primitive Expose consumeNextDraw from ViewRootImpl to enable one-off sync between two ViewRoots. Bug: 201046726 Test: Existing tests pass Change-Id: Iaf30830dc529eac83276a76d1908a06d12175b00 --- core/java/android/view/ViewRootImpl.java | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 065cc32f2c2e1..e58d98ab42a6d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -211,6 +211,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.function.Consumer; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -742,6 +743,8 @@ public final class ViewRootImpl implements ViewParent, */ private long mRtLastAttemptedDrawFrameNum = 0; + private Consumer mBLASTDrawConsumer; + private HashSet mRootScrollCaptureCallbacks; private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; @@ -3252,6 +3255,9 @@ public final class ViewRootImpl implements ViewParent, } boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; + if (mBLASTDrawConsumer != null) { + useBlastSync = true; + } if (!cancelDraw) { if (mPendingTransitions != null && mPendingTransitions.size() > 0) { @@ -3966,6 +3972,9 @@ public final class ViewRootImpl implements ViewParent, Log.d(mTag, "Creating frameCompleteCallback"); } + final Consumer blastSyncConsumer = mBLASTDrawConsumer; + mBLASTDrawConsumer = null; + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> { long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum(); if (DEBUG_BLAST) { @@ -3991,6 +4000,9 @@ public final class ViewRootImpl implements ViewParent, mHandler.postAtFrontOfQueue(() -> { if (useBlastSync) { mSurfaceChangedTransaction.merge(tmpTransaction); + if (blastSyncConsumer != null) { + blastSyncConsumer.accept(mSurfaceChangedTransaction); + } } if (reportNextDraw) { @@ -10417,4 +10429,35 @@ public final class ViewRootImpl implements ViewParent, listener.onBufferTransformHintChanged(hint); } } + + /** + * Redirect the next draw of this ViewRoot (from the UI thread perspective) + * to the passed in consumer. This can be used to create P2P synchronization + * between ViewRoot's however it comes with many caveats. + * + * 1. You MUST consume the transaction, by either applying it immediately or + * merging it in to another transaction. The threading model doesn't + * allow you to hold in the passed transaction. + * 2. If you merge it in to another transaction, this ViewRootImpl will be + * paused until you finally apply that transaction and it receives + * the callback from SF. If you lose track of the transaction you will + * ANR the app. + * 3. Only one person can consume the transaction at a time, if you already + * have a pending consumer for this frame, the function will return false + * 4. Someone else may have requested to consume the next frame, in which case + * this function will return false and you will not receive a callback. + * 5. This function does not trigger drawing so even if it returns true you + * may not receive a callback unless there is some other UI thread work + * to trigger drawing. If it returns true, and a draw occurs, the callback + * will be called (Though again watch out for the null transaction case!) + * 6. This function must be called on the UI thread. The consumer will likewise + * be called on the UI thread. + */ + public boolean consumeNextDraw(Consumer consume) { + if (mBLASTDrawConsumer != null) { + return false; + } + mBLASTDrawConsumer = consume; + return true; + } }