Loading core/java/android/view/ViewRootImpl.java +9 −55 Original line number Diff line number Diff line Loading @@ -228,7 +228,6 @@ import java.util.List; import java.util.Objects; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; /** * The top of a view hierarchy, implementing the needed protocol between View Loading Loading @@ -827,8 +826,6 @@ public final class ViewRootImpl implements ViewParent, */ private long mRtLastAttemptedDrawFrameNum = 0; private Consumer<SurfaceControl.Transaction> mBLASTDrawConsumer; private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks; private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; Loading Loading @@ -3492,36 +3489,24 @@ public final class ViewRootImpl implements ViewParent, } private void createSyncIfNeeded() { // Started a sync already. if (mLastSyncId != -1) { // Started a sync already or there's nothing needing to sync if (mLastSyncId != -1 || !mReportNextDraw) { return; } Consumer<Transaction> syncConsumer = null; final int seqId = mSyncSeqId; if (mBLASTDrawConsumer != null) { syncConsumer = mBLASTDrawConsumer; mBLASTDrawConsumer = null; } else if (mReportNextDraw) { syncConsumer = transaction -> { mSurfaceChangedTransaction.merge(transaction); reportDrawFinished(seqId); }; } if (syncConsumer != null) { final Consumer<Transaction> capturedSyncConsumer = syncConsumer; mLastSyncId = mSurfaceSyncer.setupSync(transaction -> { // Callback will be invoked on executor thread so post to main thread. mHandler.postAtFrontOfQueue(() -> capturedSyncConsumer.accept(transaction)); mHandler.postAtFrontOfQueue(() -> { mSurfaceChangedTransaction.merge(transaction); reportDrawFinished(seqId); }); }); if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync id=" + mLastSyncId); } mSurfaceSyncer.addToSync(mLastSyncId, mSyncTarget); } } private void notifyContentCatpureEvents() { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); Loading Loading @@ -10774,37 +10759,6 @@ public final class ViewRootImpl implements ViewParent, showControl, transformationApplied, callback); } /** * 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<SurfaceControl.Transaction> consume) { if (mBLASTDrawConsumer != null) { return false; } mBLASTDrawConsumer = consume; return true; } boolean wasRelayoutRequested() { return mRelayoutRequested; } Loading packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt +7 −31 Original line number Diff line number Diff line package com.android.systemui.animation import android.app.ActivityManager import android.view.SurfaceControl import android.view.View import android.view.ViewRootImpl import android.window.SurfaceSyncer /** A util class to synchronize 2 view roots. */ // TODO(b/200284684): Remove this class. object ViewRootSync { // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed private val forceDisableSynchronization = ActivityManager.isLowRamDeviceStatic() private var surfaceSyncer: SurfaceSyncer? = null /** * Synchronize the next draw between the view roots of [view] and [otherView], then run [then]. Loading @@ -33,35 +33,11 @@ object ViewRootSync { return } // Consume the next frames of both view roots to make sure the ghost view is drawn at // exactly the same time as when the touch surface is made invisible. var remainingTransactions = 0 val mergedTransactions = SurfaceControl.Transaction() fun onTransaction(transaction: SurfaceControl.Transaction?) { remainingTransactions-- transaction?.let { mergedTransactions.merge(it) } if (remainingTransactions == 0) { mergedTransactions.apply() then() } } fun consumeNextDraw(viewRootImpl: ViewRootImpl) { if (viewRootImpl.consumeNextDraw(::onTransaction)) { remainingTransactions++ // Make sure we trigger a traversal. viewRootImpl.view.invalidate() } } consumeNextDraw(view.viewRootImpl) consumeNextDraw(otherView.viewRootImpl) if (remainingTransactions == 0) { then() surfaceSyncer = SurfaceSyncer().apply { val syncId = setupSync(Runnable { then() }) addToSync(syncId, view) addToSync(syncId, otherView) markSyncReady(syncId) } } Loading Loading
core/java/android/view/ViewRootImpl.java +9 −55 Original line number Diff line number Diff line Loading @@ -228,7 +228,6 @@ import java.util.List; import java.util.Objects; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; /** * The top of a view hierarchy, implementing the needed protocol between View Loading Loading @@ -827,8 +826,6 @@ public final class ViewRootImpl implements ViewParent, */ private long mRtLastAttemptedDrawFrameNum = 0; private Consumer<SurfaceControl.Transaction> mBLASTDrawConsumer; private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks; private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; Loading Loading @@ -3492,36 +3489,24 @@ public final class ViewRootImpl implements ViewParent, } private void createSyncIfNeeded() { // Started a sync already. if (mLastSyncId != -1) { // Started a sync already or there's nothing needing to sync if (mLastSyncId != -1 || !mReportNextDraw) { return; } Consumer<Transaction> syncConsumer = null; final int seqId = mSyncSeqId; if (mBLASTDrawConsumer != null) { syncConsumer = mBLASTDrawConsumer; mBLASTDrawConsumer = null; } else if (mReportNextDraw) { syncConsumer = transaction -> { mSurfaceChangedTransaction.merge(transaction); reportDrawFinished(seqId); }; } if (syncConsumer != null) { final Consumer<Transaction> capturedSyncConsumer = syncConsumer; mLastSyncId = mSurfaceSyncer.setupSync(transaction -> { // Callback will be invoked on executor thread so post to main thread. mHandler.postAtFrontOfQueue(() -> capturedSyncConsumer.accept(transaction)); mHandler.postAtFrontOfQueue(() -> { mSurfaceChangedTransaction.merge(transaction); reportDrawFinished(seqId); }); }); if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync id=" + mLastSyncId); } mSurfaceSyncer.addToSync(mLastSyncId, mSyncTarget); } } private void notifyContentCatpureEvents() { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); Loading Loading @@ -10774,37 +10759,6 @@ public final class ViewRootImpl implements ViewParent, showControl, transformationApplied, callback); } /** * 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<SurfaceControl.Transaction> consume) { if (mBLASTDrawConsumer != null) { return false; } mBLASTDrawConsumer = consume; return true; } boolean wasRelayoutRequested() { return mRelayoutRequested; } Loading
packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt +7 −31 Original line number Diff line number Diff line package com.android.systemui.animation import android.app.ActivityManager import android.view.SurfaceControl import android.view.View import android.view.ViewRootImpl import android.window.SurfaceSyncer /** A util class to synchronize 2 view roots. */ // TODO(b/200284684): Remove this class. object ViewRootSync { // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed private val forceDisableSynchronization = ActivityManager.isLowRamDeviceStatic() private var surfaceSyncer: SurfaceSyncer? = null /** * Synchronize the next draw between the view roots of [view] and [otherView], then run [then]. Loading @@ -33,35 +33,11 @@ object ViewRootSync { return } // Consume the next frames of both view roots to make sure the ghost view is drawn at // exactly the same time as when the touch surface is made invisible. var remainingTransactions = 0 val mergedTransactions = SurfaceControl.Transaction() fun onTransaction(transaction: SurfaceControl.Transaction?) { remainingTransactions-- transaction?.let { mergedTransactions.merge(it) } if (remainingTransactions == 0) { mergedTransactions.apply() then() } } fun consumeNextDraw(viewRootImpl: ViewRootImpl) { if (viewRootImpl.consumeNextDraw(::onTransaction)) { remainingTransactions++ // Make sure we trigger a traversal. viewRootImpl.view.invalidate() } } consumeNextDraw(view.viewRootImpl) consumeNextDraw(otherView.viewRootImpl) if (remainingTransactions == 0) { then() surfaceSyncer = SurfaceSyncer().apply { val syncId = setupSync(Runnable { then() }) addToSync(syncId, view) addToSync(syncId, otherView) markSyncReady(syncId) } } Loading