summaryrefslogtreecommitdiff
path: root/core/java/android/view/ViewRootImpl.java
diff options
context:
space:
mode:
authorchaviw <chaviw@google.com>2021-12-13 16:06:21 -0600
committerchaviw <chaviw@google.com>2022-02-25 13:44:46 -0600
commitd32068af7de83b2a43d40ffa16452b2e342c1826 (patch)
tree9987c0f1ae265b747c8a5ca47652d5aa6b4458a9 /core/java/android/view/ViewRootImpl.java
parente2a47a2c3a45d43af7596d2fc4309e7fb752940d (diff)
Add SurfaceSyncer class
Add Syncer class that allows callers to add desired syncs into a set and wait for them to all complete before getting a callback. The purpose of the Syncer is to be an accounting mechanism so each sync implementation doesn't need to handle it themselves. The Syncer class is used the following way. 1. SurfaceSyncer#setupSync is called 2. addToSync is called for every SyncTarget object that wants to be included in the sync. If the addSync is called for a View or SurfaceView it needs to be called on the UI thread. When addToSync is called, it's guaranteed that any UI updates that were requested before addToSync but after the last frame drew, will be included in the sync. 3. SurfaceSyncer#markSyncReady should be called when all the SyncTargets have been added to the SyncSet. Now the SyncSet is closed and no more SyncTargets can be added to it. 4. When all SyncTargets are complete, the final merged Transaction will either be applied or sent back to the caller. The following is what happens within the SyncSet 1. Each SyncableTarget will get an onReadyToSync callback that contains a SyncBufferCallback. 2. Each SyncableTarget needs to invoke SyncBufferCallback#onBufferReady. This makes sure the SyncSet knows when the SyncTarget is complete, allowing the SyncSet to get the Transaction that contains the buffer. 3. When the final FrameCallback finishes for the SyncSet, the syncRequestComplete Consumer will be invoked with the transaction that contains all information requested in the sync. This could include buffers and geometry changes. The buffer update will include the UI changes that were requested for the View. Test: SurfaceSyncerTest Test: SurfaceSyncerContinuousTest Bug: 200284684 Change-Id: Iab87bff8a0483581e57803724eae88f0a3d96c8e
Diffstat (limited to 'core/java/android/view/ViewRootImpl.java')
-rw-r--r--core/java/android/view/ViewRootImpl.java68
1 files changed, 68 insertions, 0 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 844403298cc9..66b109df5d74 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -195,6 +195,7 @@ import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.window.ClientWindowFrames;
+import android.window.SurfaceSyncer;
import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
@@ -10880,4 +10881,71 @@ public final class ViewRootImpl implements ViewParent,
IWindowSession getWindowSession() {
return mWindowSession;
}
+
+ private void registerCallbacksForSync(
+ final SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
+ if (!isHardwareEnabled()) {
+ // TODO: correctly handle when hardware disabled
+ syncBufferCallback.onBufferReady(null);
+ return;
+ }
+
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag,
+ "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ + frame + ".");
+ }
+
+ final Transaction t = new Transaction();
+
+ // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
+ // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
+ // any blast sync or commit callback, and the code should directly call
+ // pendingDrawFinished.
+ if ((syncResult
+ & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
+ t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
+ syncBufferCallback.onBufferReady(t);
+ return null;
+ }
+
+ mBlastBufferQueue.setSyncTransaction(t);
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting up sync and frameCommitCallback");
+ }
+
+ return didProduceBuffer -> {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCommittedCallback"
+ + " lastAttemptedDrawFrameNum=" + frame
+ + " didProduceBuffer=" + didProduceBuffer);
+ }
+
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect
+ // the next draw attempt. The next transaction and transaction complete callback
+ // were only set for the current draw attempt.
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.setSyncTransaction(null);
+ // Gather the transactions that were sent to mergeWithNextTransaction
+ // since the frame didn't draw on this vsync. It's possible the frame will
+ // draw later, but it's better to not be sync than to block on a frame that
+ // may never come.
+ t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
+ }
+
+ syncBufferCallback.onBufferReady(t);
+ };
+ }
+ });
+ }
+
+ public final SurfaceSyncer.SyncTarget mSyncTarget =
+ syncBufferCallback -> registerCallbacksForSync(syncBufferCallback);
}