Merge "Replace SyncTarget with SurfaceSyncGroup"
This commit is contained in:
committed by
Android (Google) Code Review
commit
4a90281999
@@ -140,13 +140,13 @@ public interface AttachedSurfaceControl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SyncTarget that can be used to sync {@link AttachedSurfaceControl} in a
|
||||
* Returns a SurfaceSyncGroup that can be used to sync {@link AttachedSurfaceControl} in a
|
||||
* {@link SurfaceSyncGroup}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
default SurfaceSyncGroup.SyncTarget getSyncTarget() {
|
||||
default SurfaceSyncGroup getOrCreateSurfaceSyncGroup() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,14 +403,6 @@ public class SurfaceControlViewHost {
|
||||
return mWm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public void relayout(WindowManager.LayoutParams attrs) {
|
||||
relayout(attrs, SurfaceControl.Transaction::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces relayout and draw and allows to set a custom callback when it is finished
|
||||
* @hide
|
||||
@@ -422,6 +414,14 @@ public class SurfaceControlViewHost {
|
||||
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public void relayout(WindowManager.LayoutParams attrs) {
|
||||
mViewRoot.setLayoutParams(attrs, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the size of the root view.
|
||||
*
|
||||
|
||||
@@ -982,8 +982,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
|
||||
final boolean redrawNeeded = sizeChanged || creating || hintChanged
|
||||
|| (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged;
|
||||
boolean shouldSyncBuffer =
|
||||
redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync();
|
||||
boolean shouldSyncBuffer = redrawNeeded && viewRoot.wasRelayoutRequested()
|
||||
&& viewRoot.isInWMSRequestedSync();
|
||||
SyncBufferTransactionCallback syncBufferTransactionCallback = null;
|
||||
if (shouldSyncBuffer) {
|
||||
syncBufferTransactionCallback = new SyncBufferTransactionCallback();
|
||||
@@ -1073,35 +1073,34 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
private void handleSyncBufferCallback(SurfaceHolder.Callback[] callbacks,
|
||||
SyncBufferTransactionCallback syncBufferTransactionCallback) {
|
||||
|
||||
getViewRootImpl().addToSync((parentSyncGroup, syncBufferCallback) ->
|
||||
redrawNeededAsync(callbacks, () -> {
|
||||
Transaction t = null;
|
||||
if (mBlastBufferQueue != null) {
|
||||
mBlastBufferQueue.stopContinuousSyncTransaction();
|
||||
t = syncBufferTransactionCallback.waitForTransaction();
|
||||
}
|
||||
final SurfaceSyncGroup surfaceSyncGroup = new SurfaceSyncGroup();
|
||||
getViewRootImpl().addToSync(surfaceSyncGroup);
|
||||
redrawNeededAsync(callbacks, () -> {
|
||||
Transaction t = null;
|
||||
if (mBlastBufferQueue != null) {
|
||||
mBlastBufferQueue.stopContinuousSyncTransaction();
|
||||
t = syncBufferTransactionCallback.waitForTransaction();
|
||||
}
|
||||
|
||||
syncBufferCallback.onTransactionReady(t);
|
||||
onDrawFinished();
|
||||
}));
|
||||
surfaceSyncGroup.onTransactionReady(t);
|
||||
onDrawFinished();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSyncNoBuffer(SurfaceHolder.Callback[] callbacks) {
|
||||
final SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
|
||||
final SurfaceSyncGroup surfaceSyncGroup = new SurfaceSyncGroup();
|
||||
synchronized (mSyncGroups) {
|
||||
mSyncGroups.add(syncGroup);
|
||||
mSyncGroups.add(surfaceSyncGroup);
|
||||
}
|
||||
|
||||
syncGroup.addToSync((parentSyncGroup, syncBufferCallback) ->
|
||||
redrawNeededAsync(callbacks, () -> {
|
||||
syncBufferCallback.onTransactionReady(null);
|
||||
onDrawFinished();
|
||||
synchronized (mSyncGroups) {
|
||||
mSyncGroups.remove(syncGroup);
|
||||
}
|
||||
}));
|
||||
redrawNeededAsync(callbacks, () -> {
|
||||
synchronized (mSyncGroups) {
|
||||
mSyncGroups.remove(surfaceSyncGroup);
|
||||
}
|
||||
surfaceSyncGroup.onTransactionReady(null);
|
||||
onDrawFinished();
|
||||
});
|
||||
|
||||
syncGroup.markSyncReady();
|
||||
}
|
||||
|
||||
private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks,
|
||||
@@ -1119,7 +1118,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
if (viewRoot != null) {
|
||||
synchronized (mSyncGroups) {
|
||||
for (SurfaceSyncGroup syncGroup : mSyncGroups) {
|
||||
viewRoot.mergeSync(syncGroup);
|
||||
viewRoot.addToSync(syncGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,19 +595,21 @@ public final class ViewRootImpl implements ViewParent,
|
||||
String mLastPerformDrawSkippedReason;
|
||||
/** The reason the last call to performTraversals() returned without drawing */
|
||||
String mLastPerformTraversalsSkipDrawReason;
|
||||
/** The state of the local sync, if one is in progress. Can be one of the states below. */
|
||||
int mLocalSyncState;
|
||||
/** The state of the WMS requested sync, if one is in progress. Can be one of the states
|
||||
* below. */
|
||||
int mWmsRequestSyncGroupState;
|
||||
|
||||
// The possible states of the local sync, see createSyncIfNeeded()
|
||||
private final int LOCAL_SYNC_NONE = 0;
|
||||
private final int LOCAL_SYNC_PENDING = 1;
|
||||
private final int LOCAL_SYNC_RETURNED = 2;
|
||||
private final int LOCAL_SYNC_MERGED = 3;
|
||||
// The possible states of the WMS requested sync, see createSyncIfNeeded()
|
||||
private static final int WMS_SYNC_NONE = 0;
|
||||
private static final int WMS_SYNC_PENDING = 1;
|
||||
private static final int WMS_SYNC_RETURNED = 2;
|
||||
private static final int WMS_SYNC_MERGED = 3;
|
||||
|
||||
/**
|
||||
* Set whether the draw should send the buffer to system server. When set to true, VRI will
|
||||
* create a sync transaction with BBQ and send the resulting buffer to system server. If false,
|
||||
* VRI will not try to sync a buffer in BBQ, but still report when a draw occurred.
|
||||
* Set whether the requested SurfaceSyncGroup should sync the buffer. When set to true, VRI will
|
||||
* create a sync transaction with BBQ and send the resulting buffer back to the
|
||||
* SurfaceSyncGroup. If false, VRI will not try to sync a buffer in BBQ, but still report when a
|
||||
* draw occurred.
|
||||
*/
|
||||
private boolean mSyncBuffer = false;
|
||||
|
||||
@@ -849,8 +851,19 @@ public final class ViewRootImpl implements ViewParent,
|
||||
return mHandwritingInitiator;
|
||||
}
|
||||
|
||||
private SurfaceSyncGroup mSyncGroup;
|
||||
private SurfaceSyncGroup.TransactionReadyCallback mTransactionReadyCallback;
|
||||
/**
|
||||
* A SurfaceSyncGroup that is created when WMS requested to sync the buffer
|
||||
*/
|
||||
private SurfaceSyncGroup mWmsRequestSyncGroup;
|
||||
|
||||
/**
|
||||
* The SurfaceSyncGroup that represents the active VRI SurfaceSyncGroup. This is non null if
|
||||
* anyone requested the SurfaceSyncGroup for this VRI to ensure that anyone trying to sync with
|
||||
* this VRI are collected together. The SurfaceSyncGroup is cleared when the VRI draws since
|
||||
* that is the stop point where all changes are have been applied. A new SurfaceSyncGroup is
|
||||
* created after that point when something wants to sync VRI again.
|
||||
*/
|
||||
private SurfaceSyncGroup mActiveSurfaceSyncGroup;
|
||||
|
||||
private static final Object sSyncProgressLock = new Object();
|
||||
// The count needs to be static since it's used to enable or disable RT animations which is
|
||||
@@ -3643,6 +3656,12 @@ public final class ViewRootImpl implements ViewParent,
|
||||
boolean cancelAndRedraw = cancelDueToPreDrawListener
|
||||
|| (cancelDraw && mDrewOnceForSync);
|
||||
if (!cancelAndRedraw) {
|
||||
// A sync was already requested before the WMS requested sync. This means we need to
|
||||
// sync the buffer, regardless if WMS wants to sync the buffer.
|
||||
if (mActiveSurfaceSyncGroup != null) {
|
||||
mSyncBuffer = true;
|
||||
}
|
||||
|
||||
createSyncIfNeeded();
|
||||
mDrewOnceForSync = true;
|
||||
}
|
||||
@@ -3656,8 +3675,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mPendingTransitions.clear();
|
||||
}
|
||||
|
||||
if (mTransactionReadyCallback != null) {
|
||||
mTransactionReadyCallback.onTransactionReady(null);
|
||||
if (mActiveSurfaceSyncGroup != null) {
|
||||
mActiveSurfaceSyncGroup.onTransactionReady(null);
|
||||
}
|
||||
} else if (cancelAndRedraw) {
|
||||
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
|
||||
@@ -3672,8 +3691,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
mPendingTransitions.clear();
|
||||
}
|
||||
if (!performDraw() && mTransactionReadyCallback != null) {
|
||||
mTransactionReadyCallback.onTransactionReady(null);
|
||||
if (!performDraw() && mActiveSurfaceSyncGroup != null) {
|
||||
mActiveSurfaceSyncGroup.onTransactionReady(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3687,39 +3706,40 @@ public final class ViewRootImpl implements ViewParent,
|
||||
if (!cancelAndRedraw) {
|
||||
mReportNextDraw = false;
|
||||
mLastReportNextDrawReason = null;
|
||||
mTransactionReadyCallback = null;
|
||||
mActiveSurfaceSyncGroup = null;
|
||||
mSyncBuffer = false;
|
||||
if (isInLocalSync()) {
|
||||
mSyncGroup.markSyncReady();
|
||||
mSyncGroup = null;
|
||||
mLocalSyncState = LOCAL_SYNC_NONE;
|
||||
if (isInWMSRequestedSync()) {
|
||||
mWmsRequestSyncGroup.onTransactionReady(null);
|
||||
mWmsRequestSyncGroup = null;
|
||||
mWmsRequestSyncGroupState = WMS_SYNC_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createSyncIfNeeded() {
|
||||
// Started a sync already or there's nothing needing to sync
|
||||
if (isInLocalSync() || !mReportNextDraw) {
|
||||
// WMS requested sync already started or there's nothing needing to sync
|
||||
if (isInWMSRequestedSync() || !mReportNextDraw) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int seqId = mSyncSeqId;
|
||||
mLocalSyncState = LOCAL_SYNC_PENDING;
|
||||
mSyncGroup = new SurfaceSyncGroup(transaction -> {
|
||||
mLocalSyncState = LOCAL_SYNC_RETURNED;
|
||||
mWmsRequestSyncGroupState = WMS_SYNC_PENDING;
|
||||
mWmsRequestSyncGroup = new SurfaceSyncGroup(t -> {
|
||||
mWmsRequestSyncGroupState = WMS_SYNC_RETURNED;
|
||||
// Callback will be invoked on executor thread so post to main thread.
|
||||
mHandler.postAtFrontOfQueue(() -> {
|
||||
if (transaction != null) {
|
||||
mSurfaceChangedTransaction.merge(transaction);
|
||||
if (t != null) {
|
||||
mSurfaceChangedTransaction.merge(t);
|
||||
}
|
||||
mLocalSyncState = LOCAL_SYNC_MERGED;
|
||||
mWmsRequestSyncGroupState = WMS_SYNC_MERGED;
|
||||
reportDrawFinished(seqId);
|
||||
});
|
||||
});
|
||||
if (DEBUG_BLAST) {
|
||||
Log.d(mTag, "Setup new sync id=" + mSyncGroup);
|
||||
Log.d(mTag, "Setup new sync id=" + mWmsRequestSyncGroup);
|
||||
}
|
||||
mSyncGroup.addToSync(mSyncTarget);
|
||||
|
||||
mWmsRequestSyncGroup.addToSync(this);
|
||||
notifySurfaceSyncStarted();
|
||||
}
|
||||
|
||||
@@ -4370,19 +4390,11 @@ public final class ViewRootImpl implements ViewParent,
|
||||
return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
|
||||
}
|
||||
|
||||
void addToSync(SurfaceSyncGroup.SyncTarget syncable) {
|
||||
if (!isInLocalSync()) {
|
||||
return;
|
||||
}
|
||||
mSyncGroup.addToSync(syncable);
|
||||
}
|
||||
|
||||
/**
|
||||
* This VRI is currently in the middle of a sync request, but specifically one initiated from
|
||||
* within VRI.
|
||||
* This VRI is currently in the middle of a sync request that was initiated by WMS.
|
||||
*/
|
||||
public boolean isInLocalSync() {
|
||||
return mSyncGroup != null;
|
||||
public boolean isInWMSRequestedSync() {
|
||||
return mWmsRequestSyncGroup != null;
|
||||
}
|
||||
|
||||
private void addFrameCommitCallbackIfNeeded() {
|
||||
@@ -4449,7 +4461,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean fullRedrawNeeded = mFullRedrawNeeded || mTransactionReadyCallback != null;
|
||||
final boolean fullRedrawNeeded = mFullRedrawNeeded || mActiveSurfaceSyncGroup != null;
|
||||
mFullRedrawNeeded = false;
|
||||
|
||||
mIsDrawing = true;
|
||||
@@ -4457,9 +4469,9 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
addFrameCommitCallbackIfNeeded();
|
||||
|
||||
boolean usingAsyncReport = isHardwareEnabled() && mTransactionReadyCallback != null;
|
||||
boolean usingAsyncReport = isHardwareEnabled() && mActiveSurfaceSyncGroup != null;
|
||||
if (usingAsyncReport) {
|
||||
registerCallbacksForSync(mSyncBuffer, mTransactionReadyCallback);
|
||||
registerCallbacksForSync(mSyncBuffer, mActiveSurfaceSyncGroup);
|
||||
} else if (mHasPendingTransactions) {
|
||||
// These callbacks are only needed if there's no sync involved and there were calls to
|
||||
// applyTransactionOnDraw. These callbacks check if the draw failed for any reason and
|
||||
@@ -4510,11 +4522,10 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
|
||||
if (mSurfaceHolder != null && mSurface.isValid()) {
|
||||
final SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback =
|
||||
mTransactionReadyCallback;
|
||||
final SurfaceSyncGroup surfaceSyncGroup = mActiveSurfaceSyncGroup;
|
||||
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() ->
|
||||
mHandler.post(() -> transactionReadyCallback.onTransactionReady(null)));
|
||||
mTransactionReadyCallback = null;
|
||||
mHandler.post(() -> surfaceSyncGroup.onTransactionReady(null)));
|
||||
mActiveSurfaceSyncGroup = null;
|
||||
|
||||
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
|
||||
|
||||
@@ -4525,8 +4536,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mTransactionReadyCallback != null && !usingAsyncReport) {
|
||||
mTransactionReadyCallback.onTransactionReady(null);
|
||||
if (mActiveSurfaceSyncGroup != null && !usingAsyncReport) {
|
||||
mActiveSurfaceSyncGroup.onTransactionReady(null);
|
||||
}
|
||||
if (mPerformContentCapture) {
|
||||
performContentCaptureInitialReport();
|
||||
@@ -8616,8 +8627,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
writer.println(innerPrefix + "mLastPerformDrawFailedReason="
|
||||
+ mLastPerformDrawSkippedReason);
|
||||
}
|
||||
if (mLocalSyncState != LOCAL_SYNC_NONE) {
|
||||
writer.println(innerPrefix + "mLocalSyncState=" + mLocalSyncState);
|
||||
if (mWmsRequestSyncGroupState != WMS_SYNC_NONE) {
|
||||
writer.println(innerPrefix + "mWmsRequestSyncGroupState=" + mWmsRequestSyncGroupState);
|
||||
}
|
||||
writer.println(innerPrefix + "mLastReportedMergedConfiguration="
|
||||
+ mLastReportedMergedConfiguration);
|
||||
@@ -11205,7 +11216,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
|
||||
private void registerCallbacksForSync(boolean syncBuffer,
|
||||
final SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback) {
|
||||
final SurfaceSyncGroup surfaceSyncGroup) {
|
||||
if (!isHardwareEnabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -11232,7 +11243,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
// pendingDrawFinished.
|
||||
if ((syncResult
|
||||
& (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
|
||||
transactionReadyCallback.onTransactionReady(
|
||||
surfaceSyncGroup.onTransactionReady(
|
||||
mBlastBufferQueue.gatherPendingTransactions(frame));
|
||||
return null;
|
||||
}
|
||||
@@ -11243,7 +11254,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
if (syncBuffer) {
|
||||
mBlastBufferQueue.syncNextTransaction(
|
||||
transactionReadyCallback::onTransactionReady);
|
||||
surfaceSyncGroup::onTransactionReady);
|
||||
}
|
||||
|
||||
return didProduceBuffer -> {
|
||||
@@ -11263,7 +11274,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
// 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.
|
||||
transactionReadyCallback.onTransactionReady(
|
||||
surfaceSyncGroup.onTransactionReady(
|
||||
mBlastBufferQueue.gatherPendingTransactions(frame));
|
||||
return;
|
||||
}
|
||||
@@ -11272,35 +11283,23 @@ public final class ViewRootImpl implements ViewParent,
|
||||
// syncNextTransaction callback. Instead, just report back to the Syncer so it
|
||||
// knows that this sync request is complete.
|
||||
if (!syncBuffer) {
|
||||
transactionReadyCallback.onTransactionReady(null);
|
||||
surfaceSyncGroup.onTransactionReady(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public final SurfaceSyncGroup.SyncTarget mSyncTarget = new SurfaceSyncGroup.SyncTarget() {
|
||||
@Override
|
||||
public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
|
||||
SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback) {
|
||||
updateSyncInProgressCount(parentSyncGroup);
|
||||
if (!isInLocalSync()) {
|
||||
// Always sync the buffer if the sync request did not come from VRI.
|
||||
mSyncBuffer = true;
|
||||
}
|
||||
|
||||
if (mTransactionReadyCallback != null) {
|
||||
Log.d(mTag, "Already set sync for the next draw.");
|
||||
mTransactionReadyCallback.onTransactionReady(null);
|
||||
}
|
||||
if (DEBUG_BLAST) {
|
||||
Log.d(mTag, "Setting syncFrameCallback");
|
||||
}
|
||||
mTransactionReadyCallback = transactionReadyCallback;
|
||||
@Override
|
||||
public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() {
|
||||
if (mActiveSurfaceSyncGroup == null) {
|
||||
mActiveSurfaceSyncGroup = new SurfaceSyncGroup();
|
||||
updateSyncInProgressCount(mActiveSurfaceSyncGroup);
|
||||
if (!mIsInTraversal && !mTraversalScheduled) {
|
||||
scheduleTraversals();
|
||||
}
|
||||
}
|
||||
return mActiveSurfaceSyncGroup;
|
||||
};
|
||||
|
||||
private final Executor mSimpleExecutor = Runnable::run;
|
||||
@@ -11325,15 +11324,10 @@ public final class ViewRootImpl implements ViewParent,
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceSyncGroup.SyncTarget getSyncTarget() {
|
||||
return mSyncTarget;
|
||||
}
|
||||
|
||||
void mergeSync(SurfaceSyncGroup otherSyncGroup) {
|
||||
if (!isInLocalSync()) {
|
||||
void addToSync(SurfaceSyncGroup syncable) {
|
||||
if (mActiveSurfaceSyncGroup == null) {
|
||||
return;
|
||||
}
|
||||
mSyncGroup.merge(otherSyncGroup);
|
||||
mActiveSurfaceSyncGroup.addToSync(syncable, false /* parentSyncGroupMerge */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,63 +40,64 @@ import java.util.function.Supplier;
|
||||
* mechanism so each sync implementation doesn't need to handle it themselves. The SurfaceSyncGroup
|
||||
* class is used the following way.
|
||||
*
|
||||
* 1. {@link #SurfaceSyncGroup()} constructor is called
|
||||
* 2. {@link #addToSync(SyncTarget)} is called for every SyncTarget object that wants to be
|
||||
* included in the sync. If the addSync is called for an {@link AttachedSurfaceControl} or
|
||||
* {@link SurfaceView} it needs to be called on the UI thread. When addToSync is called, it's
|
||||
* 1. {@link #addToSync(SurfaceSyncGroup, boolean)} is called for every SurfaceSyncGroup object that
|
||||
* wants to be included in the sync. If the addSync is called for an {@link AttachedSurfaceControl}
|
||||
* or {@link 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. {@link #markSyncReady()} should be called when all the {@link SyncTarget}s have been added
|
||||
* to the SurfaceSyncGroup. At this point, the SurfaceSyncGroup is closed and no more SyncTargets
|
||||
* can be added to it.
|
||||
* 4. The SurfaceSyncGroup will gather the data for each SyncTarget using the steps described below.
|
||||
* When all the SyncTargets have finished, the syncRequestComplete will be invoked and the
|
||||
* transaction will either be applied or sent to the caller. In most cases, only the
|
||||
* SurfaceSyncGroup should be handling the Transaction object directly. However, there are some
|
||||
* 2. {@link #markSyncReady()} should be called when all the {@link SurfaceSyncGroup}s have been
|
||||
* added to the SurfaceSyncGroup. At this point, the SurfaceSyncGroup is closed and no more
|
||||
* SurfaceSyncGroups can be added to it.
|
||||
* 3. The SurfaceSyncGroup will gather the data for each SurfaceSyncGroup using the steps described
|
||||
* below. When all the SurfaceSyncGroups have finished, the syncRequestComplete will be invoked and
|
||||
* the transaction will either be applied or sent to the caller. In most cases, only the
|
||||
* SurfaceSyncGroup should be handling the Transaction object directly. However, there are some
|
||||
* cases where the framework needs to send the Transaction elsewhere, like in ViewRootImpl, so that
|
||||
* option is provided.
|
||||
*
|
||||
* The following is what happens within the {@link SurfaceSyncGroup}
|
||||
* 1. Each SyncTarget will get a {@link SyncTarget#onAddedToSyncGroup} callback that contains a
|
||||
* {@link TransactionReadyCallback}.
|
||||
* 2. Each {@link SyncTarget} needs to invoke
|
||||
* {@link TransactionReadyCallback#onTransactionReady(Transaction)}. This makes sure the
|
||||
* SurfaceSyncGroup knows when the SyncTarget is complete, allowing the SurfaceSyncGroup to get the
|
||||
* Transaction that contains the buffer.
|
||||
* 3. When the final TransactionReadyCallback finishes for the SurfaceSyncGroup, in most cases the
|
||||
* transaction is applied and then the sync complete callbacks are invoked, letting the callers know
|
||||
* the sync is now complete.
|
||||
* The following is what happens within the {@link android.window.SurfaceSyncGroup}
|
||||
* 1. Each SurfaceSyncGroup will get a
|
||||
* {@link SurfaceSyncGroup#onAddedToSyncGroup(SurfaceSyncGroup, TransactionReadyCallback)} callback
|
||||
* that contains a {@link TransactionReadyCallback}.
|
||||
* 2. Each {@link SurfaceSyncGroup} needs to invoke
|
||||
* {@link SurfaceSyncGroup#onTransactionReady(Transaction)}.
|
||||
* This makes sure the parent SurfaceSyncGroup knows when the SurfaceSyncGroup is complete, allowing
|
||||
* the parent SurfaceSyncGroup to get the Transaction that contains the changes for the child
|
||||
* SurfaceSyncGroup
|
||||
* 3. When the final TransactionReadyCallback finishes for the child SurfaceSyncGroups, the
|
||||
* transaction is either applied if it's the top most parent or the final merged transaction is sent
|
||||
* up to its parent SurfaceSyncGroup.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class SurfaceSyncGroup {
|
||||
public class SurfaceSyncGroup {
|
||||
private static final String TAG = "SurfaceSyncGroup";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static Supplier<Transaction> sTransactionFactory = Transaction::new;
|
||||
|
||||
/**
|
||||
* Class that collects the {@link SyncTarget}s and notifies when all the surfaces have
|
||||
* Class that collects the {@link SurfaceSyncGroup}s and notifies when all the surfaces have
|
||||
* a frame ready.
|
||||
*/
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final Set<Integer> mPendingSyncs = new ArraySet<>();
|
||||
private final Set<TransactionReadyCallback> mPendingSyncs = new ArraySet<>();
|
||||
@GuardedBy("mLock")
|
||||
private final Transaction mTransaction = sTransactionFactory.get();
|
||||
@GuardedBy("mLock")
|
||||
private boolean mSyncReady;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private Consumer<Transaction> mSyncRequestCompleteCallback;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final Set<SurfaceSyncGroup> mMergedSyncGroups = new ArraySet<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean mFinished;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private TransactionReadyCallback mTransactionReadyCallback;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private SurfaceSyncGroup mParentSyncGroup;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final ArraySet<Pair<Executor, Runnable>> mSyncCompleteCallbacks = new ArraySet<>();
|
||||
|
||||
@@ -122,16 +123,16 @@ public final class SurfaceSyncGroup {
|
||||
/**
|
||||
* Creates a sync.
|
||||
*
|
||||
* @param syncRequestComplete The complete callback that contains the syncId and transaction
|
||||
* with all the sync data merged. The Transaction passed back can be
|
||||
* null.
|
||||
* @param transactionReadyCallback The complete callback that contains the syncId and
|
||||
* transaction with all the sync data merged. The Transaction
|
||||
* passed back can be null.
|
||||
*
|
||||
* NOTE: Only should be used by ViewRootImpl
|
||||
* @hide
|
||||
*/
|
||||
public SurfaceSyncGroup(Consumer<Transaction> syncRequestComplete) {
|
||||
mSyncRequestCompleteCallback = transaction -> {
|
||||
syncRequestComplete.accept(transaction);
|
||||
public SurfaceSyncGroup(Consumer<Transaction> transactionReadyCallback) {
|
||||
mTransactionReadyCallback = transaction -> {
|
||||
transactionReadyCallback.accept(transaction);
|
||||
synchronized (mLock) {
|
||||
for (Pair<Executor, Runnable> callback : mSyncCompleteCallbacks) {
|
||||
callback.first.execute(callback.second);
|
||||
@@ -156,6 +157,31 @@ public final class SurfaceSyncGroup {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the sync set as ready to complete. No more data can be added to the specified
|
||||
* syncId.
|
||||
* Once the sync set is marked as ready, it will be able to complete once all Syncables in the
|
||||
* set have completed their sync
|
||||
*/
|
||||
public void markSyncReady() {
|
||||
onTransactionReady(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #markSyncReady()}, but a transaction is passed in to merge with the
|
||||
* SurfaceSyncGroup.
|
||||
* @param t The transaction that merges into the main Transaction for the SurfaceSyncGroup.
|
||||
*/
|
||||
public void onTransactionReady(@Nullable Transaction t) {
|
||||
synchronized (mLock) {
|
||||
mSyncReady = true;
|
||||
if (t != null) {
|
||||
mTransaction.merge(t);
|
||||
}
|
||||
checkIfSyncIsComplete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a SurfaceView to a sync set. This is different than
|
||||
* {@link #addToSync(AttachedSurfaceControl)} because it requires the caller to notify the start
|
||||
@@ -171,7 +197,13 @@ public final class SurfaceSyncGroup {
|
||||
@UiThread
|
||||
public boolean addToSync(SurfaceView surfaceView,
|
||||
Consumer<SurfaceViewFrameCallback> frameCallbackConsumer) {
|
||||
return addToSync(new SurfaceViewSyncTarget(surfaceView, frameCallbackConsumer));
|
||||
SurfaceSyncGroup surfaceSyncGroup = new SurfaceSyncGroup();
|
||||
if (addToSync(surfaceSyncGroup, false /* parentSyncGroupMerge */)) {
|
||||
frameCallbackConsumer.accept(
|
||||
() -> surfaceView.syncNextFrame(surfaceSyncGroup::onTransactionReady));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,29 +217,38 @@ public final class SurfaceSyncGroup {
|
||||
if (viewRoot == null) {
|
||||
return false;
|
||||
}
|
||||
SyncTarget syncTarget = viewRoot.getSyncTarget();
|
||||
if (syncTarget == null) {
|
||||
SurfaceSyncGroup surfaceSyncGroup = viewRoot.getOrCreateSurfaceSyncGroup();
|
||||
if (surfaceSyncGroup == null) {
|
||||
return false;
|
||||
}
|
||||
return addToSync(syncTarget);
|
||||
return addToSync(surfaceSyncGroup, false /* parentSyncGroupMerge */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link SyncTarget} to a sync set. The sync set will wait for all
|
||||
* Add a {@link SurfaceSyncGroup} to a sync set. The sync set will wait for all
|
||||
* SyncableSurfaces to complete before notifying.
|
||||
*
|
||||
* @param syncTarget A SyncTarget that implements how to handle syncing transactions.
|
||||
* @return true if the SyncTarget was successfully added to the SyncGroup, false otherwise.
|
||||
* @param surfaceSyncGroup A SyncableSurface that implements how to handle syncing
|
||||
* buffers.
|
||||
* @return true if the SyncGroup was successfully added to the current SyncGroup, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean addToSync(SyncTarget syncTarget) {
|
||||
public boolean addToSync(SurfaceSyncGroup surfaceSyncGroup, boolean parentSyncGroupMerge) {
|
||||
TransactionReadyCallback transactionReadyCallback = new TransactionReadyCallback() {
|
||||
@Override
|
||||
public void onTransactionReady(Transaction t) {
|
||||
synchronized (mLock) {
|
||||
if (t != null) {
|
||||
// When an older parent sync group is added due to a child syncGroup getting
|
||||
// added to multiple groups, we need to maintain merge order so the older
|
||||
// parentSyncGroup transactions are overwritten by anything in the newer
|
||||
// parentSyncGroup.
|
||||
if (parentSyncGroupMerge) {
|
||||
t.merge(mTransaction);
|
||||
}
|
||||
mTransaction.merge(t);
|
||||
}
|
||||
mPendingSyncs.remove(hashCode());
|
||||
mPendingSyncs.remove(this);
|
||||
checkIfSyncIsComplete();
|
||||
}
|
||||
}
|
||||
@@ -216,47 +257,15 @@ public final class SurfaceSyncGroup {
|
||||
synchronized (mLock) {
|
||||
if (mSyncReady) {
|
||||
Log.e(TAG, "Sync " + this + " was already marked as ready. No more "
|
||||
+ "SyncTargets can be added.");
|
||||
+ "SurfaceSyncGroups can be added.");
|
||||
return false;
|
||||
}
|
||||
mPendingSyncs.add(transactionReadyCallback.hashCode());
|
||||
mPendingSyncs.add(transactionReadyCallback);
|
||||
}
|
||||
syncTarget.onAddedToSyncGroup(this, transactionReadyCallback);
|
||||
surfaceSyncGroup.onAddedToSyncGroup(this, transactionReadyCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the sync set as ready to complete. No more data can be added to the specified
|
||||
* syncId.
|
||||
* Once the sync set is marked as ready, it will be able to complete once all Syncables in the
|
||||
* set have completed their sync
|
||||
*/
|
||||
public void markSyncReady() {
|
||||
synchronized (mLock) {
|
||||
mSyncReady = true;
|
||||
checkIfSyncIsComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void checkIfSyncIsComplete() {
|
||||
if (!mSyncReady || !mPendingSyncs.isEmpty() || !mMergedSyncGroups.isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Syncable is not complete. mSyncReady=" + mSyncReady
|
||||
+ " mPendingSyncs=" + mPendingSyncs.size() + " mergedSyncs="
|
||||
+ mMergedSyncGroups.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Successfully finished sync id=" + this);
|
||||
}
|
||||
|
||||
mSyncRequestCompleteCallback.accept(mTransaction);
|
||||
mFinished = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Transaction to this sync set. This allows the caller to provide other info that
|
||||
* should be synced with the transactions.
|
||||
@@ -267,99 +276,68 @@ public final class SurfaceSyncGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCallback(Consumer<Transaction> transactionConsumer) {
|
||||
@GuardedBy("mLock")
|
||||
private void checkIfSyncIsComplete() {
|
||||
if (mFinished) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "SurfaceSyncGroup=" + this + " is already complete");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSyncReady || !mPendingSyncs.isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "SurfaceSyncGroup=" + this + " is not complete. mSyncReady="
|
||||
+ mSyncReady + " mPendingSyncs=" + mPendingSyncs.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Successfully finished sync id=" + this);
|
||||
}
|
||||
mTransactionReadyCallback.onTransactionReady(mTransaction);
|
||||
mFinished = true;
|
||||
}
|
||||
|
||||
private void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
|
||||
TransactionReadyCallback transactionReadyCallback) {
|
||||
boolean finished = false;
|
||||
synchronized (mLock) {
|
||||
if (mFinished) {
|
||||
Log.e(TAG, "Attempting to merge SyncGroup " + this + " when sync is"
|
||||
+ " already complete");
|
||||
transactionConsumer.accept(null);
|
||||
}
|
||||
|
||||
final Consumer<Transaction> oldCallback = mSyncRequestCompleteCallback;
|
||||
mSyncRequestCompleteCallback = transaction -> {
|
||||
oldCallback.accept(null);
|
||||
transactionConsumer.accept(transaction);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a SyncGroup into this SyncGroup. Since SyncGroups could still have pending SyncTargets,
|
||||
* we need to make sure those can still complete before the mergeTo SyncGroup is considered
|
||||
* complete.
|
||||
*
|
||||
* We keep track of all the merged SyncGroups until they are marked as done, and then they
|
||||
* are removed from the set. This SyncGroup is not considered done until all the merged
|
||||
* SyncGroups are done.
|
||||
*
|
||||
* When the merged SyncGroup is complete, it will invoke the original syncRequestComplete
|
||||
* callback but send an empty transaction to ensure the changes are applied early. This
|
||||
* is needed in case the original sync is relying on the callback to continue processing.
|
||||
*
|
||||
* @param otherSyncGroup The other SyncGroup to merge into this one.
|
||||
*/
|
||||
public void merge(SurfaceSyncGroup otherSyncGroup) {
|
||||
synchronized (mLock) {
|
||||
mMergedSyncGroups.add(otherSyncGroup);
|
||||
}
|
||||
otherSyncGroup.updateCallback(transaction -> {
|
||||
synchronized (mLock) {
|
||||
mMergedSyncGroups.remove(otherSyncGroup);
|
||||
if (transaction != null) {
|
||||
mTransaction.merge(transaction);
|
||||
finished = true;
|
||||
} else {
|
||||
// If this SurfaceSyncGroup was already added to a different SurfaceSyncGroup, we
|
||||
// need to combine everything. We can add the old SurfaceSyncGroup parent to the new
|
||||
// parent so the new parent doesn't complete until the old parent does.
|
||||
// Additionally, the old parent will not get the final transaction object and
|
||||
// instead will send it to the new parent, ensuring that any other SurfaceSyncGroups
|
||||
// from the original parent are also combined with the new parent SurfaceSyncGroup.
|
||||
if (mParentSyncGroup != null) {
|
||||
Log.d(TAG, "Already part of sync group " + mParentSyncGroup + " " + this);
|
||||
parentSyncGroup.addToSync(mParentSyncGroup, true /* parentSyncGroupMerge */);
|
||||
}
|
||||
checkIfSyncIsComplete();
|
||||
mParentSyncGroup = parentSyncGroup;
|
||||
final TransactionReadyCallback lastCallback = mTransactionReadyCallback;
|
||||
mTransactionReadyCallback = t -> {
|
||||
lastCallback.onTransactionReady(null);
|
||||
transactionReadyCallback.onTransactionReady(t);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class to help synchronize SurfaceViews
|
||||
*/
|
||||
private static class SurfaceViewSyncTarget implements SyncTarget {
|
||||
private final SurfaceView mSurfaceView;
|
||||
private final Consumer<SurfaceViewFrameCallback> mFrameCallbackConsumer;
|
||||
|
||||
SurfaceViewSyncTarget(SurfaceView surfaceView,
|
||||
Consumer<SurfaceViewFrameCallback> frameCallbackConsumer) {
|
||||
mSurfaceView = surfaceView;
|
||||
mFrameCallbackConsumer = frameCallbackConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
|
||||
TransactionReadyCallback transactionReadyCallback) {
|
||||
mFrameCallbackConsumer.accept(
|
||||
() -> mSurfaceView.syncNextFrame(transactionReadyCallback::onTransactionReady));
|
||||
// Invoke the callback outside of the lock when the SurfaceSyncGroup being added was already
|
||||
// complete.
|
||||
if (finished) {
|
||||
transactionReadyCallback.onTransactionReady(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SyncTarget that can be added to a sync set.
|
||||
*/
|
||||
public interface SyncTarget {
|
||||
/**
|
||||
* Called when the SyncTarget has been added to a SyncGroup as is ready to begin handing a
|
||||
* sync request. When invoked, the implementor is required to call
|
||||
* {@link TransactionReadyCallback#onTransactionReady(Transaction)} in order for this
|
||||
* SurfaceSyncGroup to fully complete.
|
||||
*
|
||||
* Always invoked on the thread that initiated the call to {@link #addToSync(SyncTarget)}
|
||||
*
|
||||
* @param parentSyncGroup The sync group this target has been added to.
|
||||
* @param transactionReadyCallback A TransactionReadyCallback that the caller must invoke
|
||||
* onTransactionReady
|
||||
*/
|
||||
void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
|
||||
TransactionReadyCallback transactionReadyCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface so the SurfaceSyncer can know when it's safe to start and when everything has been
|
||||
* completed. The caller should invoke the calls when the rendering has started and finished a
|
||||
* frame.
|
||||
*/
|
||||
public interface TransactionReadyCallback {
|
||||
private interface TransactionReadyCallback {
|
||||
/**
|
||||
* Invoked when the transaction is ready to sync.
|
||||
*
|
||||
|
||||
@@ -20,6 +20,9 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.SurfaceControl;
|
||||
@@ -31,12 +34,15 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class SurfaceSyncGroupTest {
|
||||
|
||||
private final Executor mExecutor = Runnable::run;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
SurfaceSyncGroup.setTransactionFactory(StubTransaction::new);
|
||||
@@ -45,10 +51,11 @@ public class SurfaceSyncGroupTest {
|
||||
@Test
|
||||
public void testSyncOne() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(transaction -> finishedLatch.countDown());
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
|
||||
syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
|
||||
SyncTarget syncTarget = new SyncTarget();
|
||||
syncGroup.addToSync(syncTarget);
|
||||
syncGroup.markSyncReady();
|
||||
syncGroup.addToSync(syncTarget, false /* parentSyncGroupMerge */);
|
||||
syncGroup.onTransactionReady(null);
|
||||
|
||||
syncTarget.onBufferReady();
|
||||
|
||||
@@ -59,15 +66,16 @@ public class SurfaceSyncGroupTest {
|
||||
@Test
|
||||
public void testSyncMultiple() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(transaction -> finishedLatch.countDown());
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
|
||||
syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
SyncTarget syncTarget3 = new SyncTarget();
|
||||
|
||||
syncGroup.addToSync(syncTarget1);
|
||||
syncGroup.addToSync(syncTarget2);
|
||||
syncGroup.addToSync(syncTarget3);
|
||||
syncGroup.markSyncReady();
|
||||
syncGroup.addToSync(syncTarget1, false /* parentSyncGroupMerge */);
|
||||
syncGroup.addToSync(syncTarget2, false /* parentSyncGroupMerge */);
|
||||
syncGroup.addToSync(syncTarget3, false /* parentSyncGroupMerge */);
|
||||
syncGroup.onTransactionReady(null);
|
||||
|
||||
syncTarget1.onBufferReady();
|
||||
assertNotEquals(0, finishedLatch.getCount());
|
||||
@@ -83,35 +91,35 @@ public class SurfaceSyncGroupTest {
|
||||
|
||||
@Test
|
||||
public void testAddSyncWhenSyncComplete() {
|
||||
final CountDownLatch finishedLatch = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(transaction -> finishedLatch.countDown());
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
|
||||
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
|
||||
assertTrue(syncGroup.addToSync(syncTarget1));
|
||||
syncGroup.markSyncReady();
|
||||
assertTrue(syncGroup.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
syncGroup.onTransactionReady(null);
|
||||
// Adding to a sync that has been completed is also invalid since the sync id has been
|
||||
// cleared.
|
||||
assertFalse(syncGroup.addToSync(syncTarget2));
|
||||
assertFalse(syncGroup.addToSync(syncTarget2, false /* parentSyncGroupMerge */));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplesyncGroups() throws InterruptedException {
|
||||
public void testMultipleSyncGroups() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch1 = new CountDownLatch(1);
|
||||
final CountDownLatch finishedLatch2 = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(
|
||||
transaction -> finishedLatch1.countDown());
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(
|
||||
transaction -> finishedLatch2.countDown());
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup();
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup();
|
||||
|
||||
syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown);
|
||||
syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown);
|
||||
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget2));
|
||||
syncGroup1.markSyncReady();
|
||||
syncGroup2.markSyncReady();
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget2, false /* parentSyncGroupMerge */));
|
||||
syncGroup1.onTransactionReady(null);
|
||||
syncGroup2.onTransactionReady(null);
|
||||
|
||||
syncTarget1.onBufferReady();
|
||||
|
||||
@@ -126,22 +134,23 @@ public class SurfaceSyncGroupTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeSync() throws InterruptedException {
|
||||
public void testAddSyncGroup() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch1 = new CountDownLatch(1);
|
||||
final CountDownLatch finishedLatch2 = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(
|
||||
transaction -> finishedLatch1.countDown());
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(
|
||||
transaction -> finishedLatch2.countDown());
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup();
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup();
|
||||
|
||||
syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown);
|
||||
syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown);
|
||||
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget2));
|
||||
syncGroup1.markSyncReady();
|
||||
syncGroup2.merge(syncGroup1);
|
||||
syncGroup2.markSyncReady();
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget2, false /* parentSyncGroupMerge */));
|
||||
syncGroup1.onTransactionReady(null);
|
||||
syncGroup2.addToSync(syncGroup1, false /* parentSyncGroupMerge */);
|
||||
syncGroup2.onTransactionReady(null);
|
||||
|
||||
// Finish syncTarget2 first to test that the syncGroup is not complete until the merged sync
|
||||
// is also done.
|
||||
@@ -161,28 +170,29 @@ public class SurfaceSyncGroupTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeSyncAlreadyComplete() throws InterruptedException {
|
||||
public void testAddSyncAlreadyComplete() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch1 = new CountDownLatch(1);
|
||||
final CountDownLatch finishedLatch2 = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(
|
||||
transaction -> finishedLatch1.countDown());
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(
|
||||
transaction -> finishedLatch2.countDown());
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup();
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup();
|
||||
|
||||
syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown);
|
||||
syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown);
|
||||
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget2));
|
||||
syncGroup1.markSyncReady();
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget2, false /* parentSyncGroupMerge */));
|
||||
syncGroup1.onTransactionReady(null);
|
||||
syncTarget1.onBufferReady();
|
||||
|
||||
// The first sync will still get a callback when it's sync requirements are done.
|
||||
finishedLatch1.await(5, TimeUnit.SECONDS);
|
||||
assertEquals(0, finishedLatch1.getCount());
|
||||
|
||||
syncGroup2.merge(syncGroup1);
|
||||
syncGroup2.markSyncReady();
|
||||
syncGroup2.addToSync(syncGroup1, false /* parentSyncGroupMerge */);
|
||||
syncGroup2.onTransactionReady(null);
|
||||
syncTarget2.onBufferReady();
|
||||
|
||||
// Verify that the second sync will receive complete since the merged sync was already
|
||||
@@ -191,18 +201,145 @@ public class SurfaceSyncGroupTest {
|
||||
assertEquals(0, finishedLatch2.getCount());
|
||||
}
|
||||
|
||||
private static class SyncTarget implements SurfaceSyncGroup.SyncTarget {
|
||||
private SurfaceSyncGroup.TransactionReadyCallback mTransactionReadyCallback;
|
||||
@Test
|
||||
public void testAddSyncAlreadyInASync_NewSyncReadyFirst() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch1 = new CountDownLatch(1);
|
||||
final CountDownLatch finishedLatch2 = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup();
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup();
|
||||
|
||||
@Override
|
||||
public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
|
||||
SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback) {
|
||||
mTransactionReadyCallback = transactionReadyCallback;
|
||||
}
|
||||
syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown);
|
||||
syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown);
|
||||
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
SyncTarget syncTarget3 = new SyncTarget();
|
||||
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup1.addToSync(syncTarget2, false /* parentSyncGroupMerge */));
|
||||
|
||||
// Add syncTarget1 to syncGroup2 so it forces syncGroup1 into syncGroup2
|
||||
assertTrue(syncGroup2.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget3, false /* parentSyncGroupMerge */));
|
||||
|
||||
syncGroup1.onTransactionReady(null);
|
||||
syncGroup2.onTransactionReady(null);
|
||||
|
||||
// Make target1 and target3 ready, but not target2. SyncGroup2 should not be ready since
|
||||
// SyncGroup2 also waits for all of SyncGroup1 to finish, which includes target2
|
||||
syncTarget1.onBufferReady();
|
||||
syncTarget3.onBufferReady();
|
||||
|
||||
// Neither SyncGroup will be ready.
|
||||
finishedLatch1.await(1, TimeUnit.SECONDS);
|
||||
finishedLatch2.await(1, TimeUnit.SECONDS);
|
||||
|
||||
assertEquals(1, finishedLatch1.getCount());
|
||||
assertEquals(1, finishedLatch2.getCount());
|
||||
|
||||
syncTarget2.onBufferReady();
|
||||
|
||||
// Both sync groups should be ready after target2 completed.
|
||||
finishedLatch1.await(5, TimeUnit.SECONDS);
|
||||
finishedLatch2.await(5, TimeUnit.SECONDS);
|
||||
assertEquals(0, finishedLatch1.getCount());
|
||||
assertEquals(0, finishedLatch2.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSyncAlreadyInASync_OldSyncFinishesFirst() throws InterruptedException {
|
||||
final CountDownLatch finishedLatch1 = new CountDownLatch(1);
|
||||
final CountDownLatch finishedLatch2 = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup();
|
||||
SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup();
|
||||
|
||||
syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown);
|
||||
syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown);
|
||||
|
||||
SyncTarget syncTarget1 = new SyncTarget();
|
||||
SyncTarget syncTarget2 = new SyncTarget();
|
||||
SyncTarget syncTarget3 = new SyncTarget();
|
||||
|
||||
assertTrue(syncGroup1.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup1.addToSync(syncTarget2, false /* parentSyncGroupMerge */));
|
||||
syncTarget2.onBufferReady();
|
||||
|
||||
// Add syncTarget1 to syncGroup2 so it forces syncGroup1 into syncGroup2
|
||||
assertTrue(syncGroup2.addToSync(syncTarget1, false /* parentSyncGroupMerge */));
|
||||
assertTrue(syncGroup2.addToSync(syncTarget3, false /* parentSyncGroupMerge */));
|
||||
|
||||
syncGroup1.onTransactionReady(null);
|
||||
syncGroup2.onTransactionReady(null);
|
||||
|
||||
syncTarget1.onBufferReady();
|
||||
|
||||
// Only SyncGroup1 will be ready, but SyncGroup2 still needs its own targets to be ready.
|
||||
finishedLatch1.await(1, TimeUnit.SECONDS);
|
||||
finishedLatch2.await(1, TimeUnit.SECONDS);
|
||||
|
||||
assertEquals(0, finishedLatch1.getCount());
|
||||
assertEquals(1, finishedLatch2.getCount());
|
||||
|
||||
syncTarget3.onBufferReady();
|
||||
|
||||
// SyncGroup2 is finished after target3 completed.
|
||||
finishedLatch2.await(1, TimeUnit.SECONDS);
|
||||
assertEquals(0, finishedLatch2.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentSyncGroupMerge_true() {
|
||||
// Temporarily set a new transaction factory so it will return the stub transaction for
|
||||
// the sync group.
|
||||
SurfaceControl.Transaction parentTransaction = spy(new StubTransaction());
|
||||
SurfaceSyncGroup.setTransactionFactory(() -> parentTransaction);
|
||||
|
||||
final CountDownLatch finishedLatch = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
|
||||
syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
|
||||
|
||||
SurfaceControl.Transaction targetTransaction = spy(new StubTransaction());
|
||||
SurfaceSyncGroup.setTransactionFactory(() -> targetTransaction);
|
||||
|
||||
SyncTarget syncTarget = new SyncTarget();
|
||||
assertTrue(syncGroup.addToSync(syncTarget, true /* parentSyncGroupMerge */));
|
||||
syncTarget.onTransactionReady(null);
|
||||
|
||||
// When parentSyncGroupMerge is true, the transaction passed in merges the main SyncGroup
|
||||
// transaction first because it knows the previous parentSyncGroup is older so it should
|
||||
// be overwritten by anything newer.
|
||||
verify(targetTransaction).merge(parentTransaction);
|
||||
verify(parentTransaction).merge(targetTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentSyncGroupMerge_false() {
|
||||
// Temporarily set a new transaction factory so it will return the stub transaction for
|
||||
// the sync group.
|
||||
SurfaceControl.Transaction parentTransaction = spy(new StubTransaction());
|
||||
SurfaceSyncGroup.setTransactionFactory(() -> parentTransaction);
|
||||
|
||||
final CountDownLatch finishedLatch = new CountDownLatch(1);
|
||||
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
|
||||
syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
|
||||
|
||||
SurfaceControl.Transaction targetTransaction = spy(new StubTransaction());
|
||||
SurfaceSyncGroup.setTransactionFactory(() -> targetTransaction);
|
||||
|
||||
SyncTarget syncTarget = new SyncTarget();
|
||||
assertTrue(syncGroup.addToSync(syncTarget, false /* parentSyncGroupMerge */));
|
||||
syncTarget.onTransactionReady(null);
|
||||
|
||||
// When parentSyncGroupMerge is false, the transaction passed in should not merge
|
||||
// the main SyncGroup since we don't need to change the transaction order
|
||||
verify(targetTransaction, never()).merge(parentTransaction);
|
||||
verify(parentTransaction).merge(targetTransaction);
|
||||
}
|
||||
|
||||
private static class SyncTarget extends SurfaceSyncGroup {
|
||||
void onBufferReady() {
|
||||
SurfaceControl.Transaction t = new StubTransaction();
|
||||
mTransactionReadyCallback.onTransactionReady(t);
|
||||
onTransactionReady(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user