From 1602deb6741aeaac37ba4fba5c6efe5949580782 Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 1 Nov 2021 18:29:29 -0500 Subject: [PATCH] Synchronize SV buffers with main window when config changes When the main window gets a configuration change, the new buffer for the SV will get applied in the same transaction as the main window. This will allow apps to implement seamless rotation without a frame or more of black where the SV was resized, but the buffer was not yet updated. This also includes an optimization where if only SV change occurs, but there was no request from WMS to report drawing, VRI can apply the transaction immediately without reporting to WMS. Test: SV with seamless rotation enabled Bug: 200284684 Change-Id: I5569d815eef92acf3bf9d7f753bd5ab94a332cfe --- core/java/android/view/SurfaceView.java | 25 ++++++++--- core/java/android/view/ViewRootImpl.java | 44 +++++++++++++++++-- .../jni/android_graphics_BLASTBufferQueue.cpp | 7 +-- .../android/graphics/BLASTBufferQueue.java | 24 +++++++--- 4 files changed, 82 insertions(+), 18 deletions(-) diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 1ddb043153abc..ba436e16288e7 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -227,6 +227,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall */ private final SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction(); + /** + * Used on the main thread to set the transaction that will be synced with the main window. + */ + private final Transaction mSyncTransaction = new Transaction(); + /** * Transaction that should be used whe * {@link HardwareRenderer.FrameDrawingCallback#onFrameDraw} is invoked. All @@ -496,7 +501,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } - private void performDrawFinished() { + private void performDrawFinished(Transaction t) { + mSyncTransaction.merge(t); if (mDeferredDestroySurfaceControl != null) { synchronized (mSurfaceControlLock) { mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply(); @@ -521,7 +527,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall void notifyDrawFinished() { ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null) { - viewRoot.pendingDrawFinished(); + viewRoot.pendingDrawFinished(mSyncTransaction); } mPendingReportDraws--; } @@ -1202,10 +1208,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall callbacks = getSurfaceCallbacks(); } + final Transaction t = new Transaction(); + if (viewRoot.wasRelayoutRequested()) { + mBlastBufferQueue.setSyncTransaction(t, + false /* acquireSingleBuffer */); + } mPendingReportDraws++; viewRoot.drawPending(); - SurfaceCallbackHelper sch = - new SurfaceCallbackHelper(this::onDrawFinished); + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> { + mBlastBufferQueue.setSyncTransaction(null); + onDrawFinished(t); + }); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } } @@ -1367,13 +1380,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceHeight, mFormat); } - private void onDrawFinished() { + private void onDrawFinished(Transaction t) { if (DEBUG) { Log.i(TAG, System.identityHashCode(this) + " " + "finishedDrawing"); } - runOnUiThread(this::performDrawFinished); + runOnUiThread(() -> performDrawFinished(t)); } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0c30cbb3e1494..3fc7ee3800553 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -532,6 +532,11 @@ public final class ViewRootImpl implements ViewParent, boolean mPerformContentCapture; boolean mReportNextDraw; + /** + * Set if the reportDraw was requested from WM. If just a local report draw was invoked, there's + * no need to report back to system server and can just apply immediately on the client. + */ + boolean mReportDrawToWm; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; boolean mForceNextWindowRelayout; @@ -754,6 +759,8 @@ public final class ViewRootImpl implements ViewParent, */ private int mSurfaceSequenceId = 0; + private boolean mRelayoutRequested; + private String mTag = TAG; public ViewRootImpl(Context context, Display display) { @@ -3311,6 +3318,7 @@ public final class ViewRootImpl implements ViewParent, } mIsInTraversal = false; + mRelayoutRequested = false; } private void notifyContentCatpureEvents() { @@ -3941,10 +3949,18 @@ public final class ViewRootImpl implements ViewParent, mDrawsNeededToReport++; } - void pendingDrawFinished() { + void pendingDrawFinished(Transaction t) { if (mDrawsNeededToReport == 0) { throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls"); } + + if (t != null) { + if (DEBUG_BLAST) { + Log.d(mTag, "Merging transaction into main window transaction"); + } + mSurfaceChangedTransaction.merge(t); + } + mDrawsNeededToReport--; if (mDrawsNeededToReport == 0) { reportDrawFinished(); @@ -3954,17 +3970,31 @@ public final class ViewRootImpl implements ViewParent, } } + void pendingDrawFinished() { + pendingDrawFinished(null); + } + private void postDrawFinished() { mHandler.sendEmptyMessage(MSG_DRAW_FINISHED); } private void reportDrawFinished() { - try { + if (DEBUG_BLAST) { + Log.d(mTag, "reportDrawFinished"); + } + mDrawsNeededToReport = 0; + + if (!mReportDrawToWm) { if (DEBUG_BLAST) { - Log.d(mTag, "reportDrawFinished"); + Log.d(mTag, "No need to report finishDrawing. Apply immediately"); } - mDrawsNeededToReport = 0; + mSurfaceChangedTransaction.apply(); + return; + } + + try { mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction); + mReportDrawToWm = false; } catch (RemoteException e) { Log.e(mTag, "Unable to report draw finished", e); mSurfaceChangedTransaction.apply(); @@ -7756,6 +7786,7 @@ public final class ViewRootImpl implements ViewParent, private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { + mRelayoutRequested = true; float appScale = mAttachInfo.mApplicationScale; boolean restore = false; if (params != null && mTranslator != null) { @@ -9573,6 +9604,7 @@ public final class ViewRootImpl implements ViewParent, if (mReportNextDraw == false) { drawPending(); } + mReportDrawToWm = true; mReportNextDraw = true; } @@ -10501,4 +10533,8 @@ public final class ViewRootImpl implements ViewParent, mBLASTDrawConsumer = consume; return true; } + + boolean wasRelayoutRequested() { + return mRelayoutRequested; + } } diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp index e7b3fba961e6e..a059dd6e52e2e 100644 --- a/core/jni/android_graphics_BLASTBufferQueue.cpp +++ b/core/jni/android_graphics_BLASTBufferQueue.cpp @@ -61,10 +61,11 @@ static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr, queue->getSurface(includeSurfaceControlHandle)); } -static void nativeSetSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) { +static void nativeSetSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr, + jboolean acquireSingleBuffer) { sp queue = reinterpret_cast(ptr); auto transaction = reinterpret_cast(transactionPtr); - queue->setSyncTransaction(transaction); + queue->setSyncTransaction(transaction, acquireSingleBuffer); } static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, @@ -98,7 +99,7 @@ static const JNINativeMethod gMethods[] = { {"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate}, {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface}, {"nativeDestroy", "(J)V", (void*)nativeDestroy}, - {"nativeSetSyncTransaction", "(JJ)V", (void*)nativeSetSyncTransaction}, + {"nativeSetSyncTransaction", "(JJZ)V", (void*)nativeSetSyncTransaction}, {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate}, {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction}, {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum}, diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 405f66dd5d561..8844370f9b4fb 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -31,7 +31,8 @@ public final class BLASTBufferQueue { long height, int format); private static native void nativeDestroy(long ptr); private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle); - private static native void nativeSetSyncTransaction(long ptr, long transactionPtr); + private static native void nativeSetSyncTransaction(long ptr, long transactionPtr, + boolean acquireSingleBuffer); private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height, int format, long transactionPtr); private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr, @@ -66,12 +67,25 @@ public final class BLASTBufferQueue { } /** - * Send the transaction to BBQ so the next frame can be added and not applied immediately. - * This gives the caller a chance to apply the transaction when it's ready. - * @param t The transaction to add the frame to. This can be null to clear the transaction. + * Send the transaction to BBQ so the next frame can be added and not applied immediately. This + * gives the caller a chance to apply the transaction when it's ready. + * + * @param t The transaction to add the frame to. This can be null to clear the + * transaction. + * @param acquireSingleBuffer If true, only acquire a single buffer when processing frames. The + * transaction will be cleared once a single buffer has been + * acquired. If false, continue to acquire all buffers into the + * transaction until setSyncTransaction is called again with a null + * transaction. */ + public void setSyncTransaction(@Nullable SurfaceControl.Transaction t, + boolean acquireSingleBuffer) { + nativeSetSyncTransaction(mNativeObject, t == null ? 0 : t.mNativeObject, + acquireSingleBuffer); + } + public void setSyncTransaction(@Nullable SurfaceControl.Transaction t) { - nativeSetSyncTransaction(mNativeObject, t == null ? 0 : t.mNativeObject); + setSyncTransaction(t, true /* acquireSingleBuffer */); } /**