diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index b644e9efa666f..abda698fdf737 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -420,8 +420,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall final boolean useBLAST = viewRoot.useBLAST(); viewRoot.registerRtFrameCallback(frame -> { try { - final SurfaceControl.Transaction t = useBLAST ? - viewRoot.getBLASTSyncTransaction() : new SurfaceControl.Transaction(); synchronized (mSurfaceControlLock) { if (!parent.isValid()) { if (DEBUG) { @@ -443,16 +441,22 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall Log.d(TAG, System.identityHashCode(this) + " updateSurfaceAlpha RT: set alpha=" + alpha); } - t.setAlpha(mSurfaceControl, alpha); - if (!useBLAST) { + if (useBLAST) { + synchronized (viewRoot.getBlastTransactionLock()) { + viewRoot.getBLASTSyncTransaction() + .setAlpha(mSurfaceControl, alpha); + } + } else { + Transaction t = new SurfaceControl.Transaction(); + t.setAlpha(mSurfaceControl, alpha); t.deferTransactionUntil(mSurfaceControl, viewRoot.getRenderSurfaceControl(), frame); + t.apply(); } } // It's possible that mSurfaceControl is released in the UI thread before // the transaction completes. If that happens, an exception is thrown, which // must be caught immediately. - t.apply(); } catch (Exception e) { Log.e(TAG, System.identityHashCode(this) + "updateSurfaceAlpha RT: Exception during surface transaction", e); @@ -793,23 +797,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall final boolean useBLAST = viewRoot.useBLAST(); viewRoot.registerRtFrameCallback(frame -> { try { - final SurfaceControl.Transaction t = useBLAST - ? viewRoot.getBLASTSyncTransaction() - : new SurfaceControl.Transaction(); synchronized (mSurfaceControlLock) { if (!parent.isValid() || mSurfaceControl == null) { return; } - updateRelativeZ(t); - if (!useBLAST) { + + if (useBLAST) { + synchronized (viewRoot.getBlastTransactionLock()) { + updateRelativeZ(viewRoot.getBLASTSyncTransaction()); + } + } else { + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + updateRelativeZ(t); t.deferTransactionUntil(mSurfaceControl, viewRoot.getRenderSurfaceControl(), frame); + t.apply(); } } // It's possible that mSurfaceControl is released in the UI thread before // the transaction completes. If that happens, an exception is thrown, which // must be caught immediately. - t.apply(); } catch (Exception e) { Log.e(TAG, System.identityHashCode(this) + "setZOrderOnTop RT: Exception during surface transaction", e); @@ -1252,7 +1259,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t, Rect position, long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); - if (frameNumber > 0 && viewRoot != null && !viewRoot.isDrawingToBLASTTransaction()) { + if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) { t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(), frameNumber); } @@ -1277,19 +1284,23 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return mRTLastReportedPosition; } + private void setParentSpaceRectangle(Transaction t, Rect position, long frameNumber) { + final ViewRootImpl viewRoot = getViewRootImpl(); + applySurfaceTransforms(mSurfaceControl, t, position, frameNumber); + applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber); + } + private void setParentSpaceRectangle(Rect position, long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); - final boolean useBLAST = viewRoot.isDrawingToBLASTTransaction(); - final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() : - mRtTransaction; + final boolean useBLAST = viewRoot.useBLAST(); - applySurfaceTransforms(mSurfaceControl, t, position, frameNumber); - - applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, - frameNumber); - - if (!useBLAST) { - t.apply(); + if (useBLAST) { + synchronized (viewRoot.getBlastTransactionLock()) { + setParentSpaceRectangle(viewRoot.getBLASTSyncTransaction(), position, frameNumber); + } + } else { + setParentSpaceRectangle(mRtTransaction, position, frameNumber); + mRtTransaction.apply(); } } @@ -1337,10 +1348,20 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + private void releaseSurfaces(Transaction t) { + if (mRtReleaseSurfaces) { + mRtReleaseSurfaces = false; + t.remove(mSurfaceControl); + t.remove(mBackgroundControl); + mSurfaceControl = null; + mBackgroundControl = null; + } + } + @Override public void positionLost(long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); - boolean useBLAST = viewRoot != null && viewRoot.isDrawingToBLASTTransaction(); + boolean useBLAST = viewRoot != null && viewRoot.useBLAST(); if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); @@ -1351,38 +1372,31 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return; } - final SurfaceControl.Transaction t = useBLAST ? - (viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) : - mRtTransaction; - /** * positionLost can be called while UI thread is un-paused so we * need to hold the lock here. */ synchronized (mSurfaceControlLock) { - if (frameNumber > 0 && viewRoot != null && !useBLAST) { - if (viewRoot.mSurface.isValid()) { + if (useBLAST) { + synchronized (viewRoot.getBlastTransactionLock()) { + viewRoot.getBLASTSyncTransaction().hide(mSurfaceControl); + releaseSurfaces(viewRoot.getBLASTSyncTransaction()); + } + } else { + if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) { mRtTransaction.deferTransactionUntil(mSurfaceControl, viewRoot.getRenderSurfaceControl(), frameNumber); } - } - t.hide(mSurfaceControl); - - if (mRtReleaseSurfaces) { - mRtReleaseSurfaces = false; - mRtTransaction.remove(mSurfaceControl); - mRtTransaction.remove(mBackgroundControl); - mSurfaceControl = null; - mBackgroundControl = null; + mRtTransaction.hide(mSurfaceControl); + releaseSurfaces(mRtTransaction); + // If we aren't using BLAST, we apply the transaction locally, otherwise we let + // the ViewRoot apply it for us. + // If the ViewRoot is null, we behave as if we aren't using BLAST so we need to + // apply the transaction. + mRtTransaction.apply(); } mRtHandlingPositionUpdates = false; } - - // If we aren't using BLAST, we apply the transaction locally, otherise we let the ViewRoot apply it for us. - // If the ViewRoot is null, we behave as if we aren't using BLAST so we need to apply the transaction. - if (!useBLAST || viewRoot == null) { - mRtTransaction.apply(); - } } }; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0bbd8992bc1ac..7ce7a12d77aa1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -654,26 +654,25 @@ public final class ViewRootImpl implements ViewParent, int localChanges; } - // If set, ViewRootImpl will call BLASTBufferQueue::setNextTransaction with - // mRtBLASTSyncTransaction, prior to invoking draw. This provides a way - // to redirect the buffers in to transactions. + // If set, ViewRootImpl will request a callback from HWRender when it's ready to render the next + // frame. This will allow VRI to call BLASTBufferQueue::setNextTransaction with + // mRtBLASTSyncTransaction, so the next frame submitted will be added to the + // mRtBLASTSyncTransaction instead of getting applied. private boolean mNextDrawUseBLASTSyncTransaction; - // Set when calling setNextTransaction, we can't just reuse mNextDrawUseBLASTSyncTransaction - // because, imagine this scenario: - // 1. First draw is using BLAST, mNextDrawUseBLAST = true - // 2. We call perform draw and are waiting on the callback - // 3. After the first perform draw but before the first callback and the - // second perform draw, a second draw sets mNextDrawUseBLAST = true (it already was) - // 4. At this point the callback fires and we set mNextDrawUseBLAST = false; - // 5. We get to performDraw and fail to sync as we intended because mNextDrawUseBLAST - // is now false. - // This is why we use a two-step latch with the two booleans, one consumed from - // performDraw and one consumed from finishBLASTSync() - private boolean mNextReportConsumeBLAST; - // Be very careful with the threading here. This is used from the render thread while - // the UI thread is paused and then applied and cleared from the UI thread right after - // draw returns. - private SurfaceControl.Transaction mRtBLASTSyncTransaction = new SurfaceControl.Transaction(); + + // This is used to signal if the mRtBLASTSyncTransaction should be applied/merged. When true, + // it indicates mRtBLASTSyncTransaction was sent to BLASTBufferQueue::setNextTransaction. + // Therefore, in onFrameComplete, if mRtNextFrameReportConsumeWithBlast is true, that means + // mRtBLASTSyncTransaction now contains the next buffer frame to be applied. + private boolean mRtNextFrameReportedConsumeWithBlast; + + // Be very careful with the threading here. This is used from a thread pool generated by the + // render thread. Therefore, it needs to be locked when updating from the thread pool since + // multiple threads can be accessing it. It does not need to be locked when applied or merged + // since that can only happen from the frame complete callback after the other callbacks have + // been invoked. + private final SurfaceControl.Transaction mRtBLASTSyncTransaction = + new SurfaceControl.Transaction(); // Keeps track of whether the WM requested us to use BLAST Sync when calling relayout. // We use this to make sure we don't send the WM transactions from an internal BLAST sync @@ -3783,26 +3782,29 @@ public final class ViewRootImpl implements ViewParent, commitCallbacks.get(i).run(); } } - });}); + }); + }); } } try { if (mNextDrawUseBLASTSyncTransaction) { - // TODO(b/149747443) - // We aren't prepared to handle overlapping use of mRtBLASTSyncTransaction - // so if we are BLAST syncing we make sure the previous draw has - // totally finished. - if (mAttachInfo.mThreadedRenderer != null) { - mAttachInfo.mThreadedRenderer.pause(); - } - - mNextReportConsumeBLAST = true; + // Frame callbacks will always occur after submitting draw requests and before + // the draw actually occurs. This will ensure that we set the next transaction + // for the frame that's about to get drawn and not on a previous frame that. + // + // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be + // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the + // next frame completed should be reported with the blast sync transaction. + registerRtFrameCallback(frame -> { + mRtNextFrameReportedConsumeWithBlast = true; + if (mBlastBufferQueue != null) { + // We don't need to synchronize mRtBLASTSyncTransaction here since it's not + // being modified and only sent to BlastBufferQueue. + mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction); + } + }); mNextDrawUseBLASTSyncTransaction = false; - - if (mBlastBufferQueue != null) { - mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction); - } } boolean canUseAsync = draw(fullRedrawNeeded); if (usingAsyncReport && !canUseAsync) { @@ -9757,9 +9759,12 @@ public final class ViewRootImpl implements ViewParent, private void finishBLASTSync(boolean apply) { mSendNextFrameToWm = false; - if (mNextReportConsumeBLAST) { - mNextReportConsumeBLAST = false; + if (mRtNextFrameReportedConsumeWithBlast) { + mRtNextFrameReportedConsumeWithBlast = false; + // We don't need to synchronize mRtBLASTSyncTransaction here we're guaranteed that this + // is called after all onFrameDraw and after callbacks to PositionUpdateListener. + // Therefore, no one should be modifying this object until the next vsync. if (apply) { mRtBLASTSyncTransaction.apply(); } else { @@ -9772,6 +9777,10 @@ public final class ViewRootImpl implements ViewParent, return mRtBLASTSyncTransaction; } + Object getBlastTransactionLock() { + return mRtBLASTSyncTransaction; + } + /** * @hide */ @@ -9799,12 +9808,4 @@ public final class ViewRootImpl implements ViewParent, boolean useBLAST() { return mUseBLASTAdapter && !mForceDisableBLAST; } - - /** - * Returns true if we are about to or currently processing a draw directed - * in to a BLAST transaction. - */ - boolean isDrawingToBLASTTransaction() { - return mNextReportConsumeBLAST; - } }