Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bc23a030 authored by Chavi Weingarten's avatar Chavi Weingarten Committed by Android (Google) Code Review
Browse files

Merge "Refactor SurfaceSyncer to use SurfaceSyncGroups instead of syncId"

parents 95fab76b ea372b7f
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -17,10 +17,10 @@ package android.view;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UiThread;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.window.SurfaceSyncGroup;

/**
 * Provides an interface to the root-Surface of a View Hierarchy or Window. This
@@ -138,4 +138,12 @@ public interface AttachedSurfaceControl {
     */
    default void setTouchableRegion(@Nullable Region r) {
    }

    /**
     * Returns a SyncTarget that can be used to sync {@link AttachedSurfaceControl} in a
     * {@link SurfaceSyncGroup}
     *
     * @hide
     */
    SurfaceSyncGroup.SyncTarget getSyncTarget();
}
+14 −14
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.window.SurfaceSyncer;
import android.window.SurfaceSyncGroup;

import com.android.internal.view.SurfaceCallbackHelper;

@@ -210,8 +210,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall

    private int mSurfaceFlags = SurfaceControl.HIDDEN;

    private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
    private final ArraySet<Integer> mSyncIds = new ArraySet<>();
    private final ArraySet<SurfaceSyncGroup> mSyncGroups = new ArraySet<>();

    /**
     * Transaction that should be used from the render thread. This transaction is only thread safe
@@ -1077,20 +1076,21 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    }

    private void handleSyncNoBuffer(SurfaceHolder.Callback[] callbacks) {
        final int syncId = mSurfaceSyncer.setupSync(this::onDrawFinished);
        final SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
        synchronized (mSyncGroups) {
            mSyncGroups.add(syncGroup);
        }

        mSurfaceSyncer.addToSync(syncId, syncBufferCallback -> redrawNeededAsync(callbacks,
        syncGroup.addToSync(syncBufferCallback -> redrawNeededAsync(callbacks,
                () -> {
                    syncBufferCallback.onBufferReady(null);
                    synchronized (mSyncIds) {
                        mSyncIds.remove(syncId);
                    onDrawFinished();
                    synchronized (mSyncGroups) {
                        mSyncGroups.remove(syncGroup);
                    }
                }));

        mSurfaceSyncer.markSyncReady(syncId);
        synchronized (mSyncIds) {
            mSyncIds.add(syncId);
        }
        syncGroup.markSyncReady();
    }

    private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks,
@@ -1106,9 +1106,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    public void surfaceSyncStarted() {
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null) {
            synchronized (mSyncIds) {
                for (int syncId : mSyncIds) {
                    viewRoot.mergeSync(syncId, mSurfaceSyncer);
            synchronized (mSyncGroups) {
                for (SurfaceSyncGroup syncGroup : mSyncGroups) {
                    viewRoot.mergeSync(syncGroup);
                }
            }
        }
+26 −20
Original line number Diff line number Diff line
@@ -197,7 +197,7 @@ import android.window.ClientWindowFrames;
import android.window.CompatOnBackInvokedCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.SurfaceSyncer;
import android.window.SurfaceSyncGroup;
import android.window.WindowOnBackInvokedDispatcher;

import com.android.internal.R;
@@ -829,9 +829,8 @@ public final class ViewRootImpl implements ViewParent,
        return mHandwritingInitiator;
    }

    private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
    private int mSyncId = UNSET_SYNC_ID;
    private SurfaceSyncer.SyncBufferCallback mSyncBufferCallback;
    private SurfaceSyncGroup mSyncGroup;
    private SurfaceSyncGroup.SyncBufferCallback mSyncBufferCallback;
    private int mNumSyncsInProgress = 0;

    private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
@@ -3594,8 +3593,8 @@ public final class ViewRootImpl implements ViewParent,
            mSyncBufferCallback = null;
            mSyncBuffer = false;
            if (isInLocalSync()) {
                mSurfaceSyncer.markSyncReady(mSyncId);
                mSyncId = UNSET_SYNC_ID;
                mSyncGroup.markSyncReady();
                mSyncGroup = null;
            }
        }
    }
@@ -3607,17 +3606,19 @@ public final class ViewRootImpl implements ViewParent,
        }

        final int seqId = mSyncSeqId;
        mSyncId = mSurfaceSyncer.setupSync(transaction -> {
        mSyncGroup = new SurfaceSyncGroup(transaction -> {
            // Callback will be invoked on executor thread so post to main thread.
            mHandler.postAtFrontOfQueue(() -> {
                if (transaction != null) {
                    mSurfaceChangedTransaction.merge(transaction);
                }
                reportDrawFinished(seqId);
            });
        });
        if (DEBUG_BLAST) {
            Log.d(mTag, "Setup new sync id=" + mSyncId);
            Log.d(mTag, "Setup new sync id=" + mSyncGroup);
        }
        mSurfaceSyncer.addToSync(mSyncId, mSyncTarget);
        mSyncGroup.addToSync(mSyncTarget);
        notifySurfaceSyncStarted();
    }

@@ -4255,11 +4256,11 @@ public final class ViewRootImpl implements ViewParent,
        return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
    }

    void addToSync(SurfaceSyncer.SyncTarget syncable) {
    void addToSync(SurfaceSyncGroup.SyncTarget syncable) {
        if (!isInLocalSync()) {
            return;
        }
        mSurfaceSyncer.addToSync(mSyncId, syncable);
        mSyncGroup.addToSync(syncable);
    }

    /**
@@ -4267,7 +4268,7 @@ public final class ViewRootImpl implements ViewParent,
     * within VRI.
     */
    public boolean isInLocalSync() {
        return mSyncId != UNSET_SYNC_ID;
        return mSyncGroup != null;
    }

    private void addFrameCommitCallbackIfNeeded() {
@@ -4392,7 +4393,7 @@ public final class ViewRootImpl implements ViewParent,
            }

            if (mSurfaceHolder != null && mSurface.isValid()) {
                final SurfaceSyncer.SyncBufferCallback syncBufferCallback = mSyncBufferCallback;
                final SurfaceSyncGroup.SyncBufferCallback syncBufferCallback = mSyncBufferCallback;
                SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() ->
                        mHandler.post(() -> syncBufferCallback.onBufferReady(null)));
                mSyncBufferCallback = null;
@@ -10929,7 +10930,7 @@ public final class ViewRootImpl implements ViewParent,
    }

    private void registerCallbacksForSync(boolean syncBuffer,
            final SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
            final SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
        if (!isHardwareEnabled()) {
            return;
        }
@@ -11002,9 +11003,9 @@ public final class ViewRootImpl implements ViewParent,
        });
    }

    public final SurfaceSyncer.SyncTarget mSyncTarget = new SurfaceSyncer.SyncTarget() {
    public final SurfaceSyncGroup.SyncTarget mSyncTarget = new SurfaceSyncGroup.SyncTarget() {
        @Override
        public void onReadyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
        public void onReadyToSync(SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
            readyToSync(syncBufferCallback);
        }

@@ -11018,7 +11019,12 @@ public final class ViewRootImpl implements ViewParent,
        }
    };

    private void readyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
    @Override
    public SurfaceSyncGroup.SyncTarget getSyncTarget() {
        return mSyncTarget;
    }

    private void readyToSync(SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
        mNumSyncsInProgress++;
        if (!isInLocalSync()) {
            // Always sync the buffer if the sync request did not come from VRI.
@@ -11041,10 +11047,10 @@ public final class ViewRootImpl implements ViewParent,
        }
    }

    void mergeSync(int syncId, SurfaceSyncer otherSyncer) {
    void mergeSync(SurfaceSyncGroup otherSyncGroup) {
        if (!isInLocalSync()) {
            return;
        }
        mSurfaceSyncer.merge(mSyncId, syncId, otherSyncer);
        mSyncGroup.merge(otherSyncGroup);
    }
}
+86 −0
Original line number Diff line number Diff line
## SurfaceSyncer
## SurfaceSyncGroup

### Overview

@@ -6,30 +6,36 @@ A generic way for data to be gathered so multiple surfaces can be synced. This i

### Code

SurfaceSyncer is a class that manages sync requests and reports back when all participants in the sync are ready.
SurfaceSyncGroup is a class that manages sync requests and reports back when all participants in the sync are ready.

##### setupSync
The first step is to create a sync request. This is done by calling `setupSync`.
There are two setupSync methods: one that accepts a `Runnable` and one that accepts a `Consumer<Transaction>`. The `setupSync(Runnable)` will automatically apply the final transaction and invokes the Runnable to notify the caller that the sync is complete. The `Runnable` can be null since not all cases care about the sync being complete. The second `setupSync(Consumer<Transaction>)` should only be used by ViewRootImpl. The purpose of this one is to allow the caller to get back the merged transaction without it being applied. ViewRootImpl uses it to send the transaction to WindowManagerService to be synced there. Using this one for other cases is unsafe because the caller may hold the transaction longer than expected and prevent buffers from being latched and released.

The setupSync returns a syncId which is used for the other APIs
##### Constructor
The first step is to create a sync request. This is done by creating a new `SurfaceSyncGroup`.
There are two constructors: one that accepts a `Consumer<Transaction>` and one that's empty. The empty constructor will automatically apply the final transaction. The second constructor should only be used by ViewRootImpl. The purpose of this one is to allow the caller to get back the merged transaction without it being applied. ViewRootImpl uses it to send the transaction to WindowManagerService to be synced there. Using this one for other cases is unsafe because the caller may hold the transaction longer than expected and prevent buffers from being latched and released.

##### markSyncReady

When the caller has added all the `SyncTarget` to the sync, they should call `markSyncReady(syncId)` If the caller doesn't call this, the sync will never complete since the SurfaceSyncer wants to give the caller a chance to add all the SyncTargets before considering the sync ready. Before `markSyncReady` is called, the `SyncTargets` can actually produce a frame, which will just be held in a transaction until all other `SyncTargets` are ready AND `markSyncReady` has been called. Once markSyncReady has been called, you cannot add any more `SyncTargets` to that particular syncId.
When the caller has added all the `SyncTarget` to the sync, they should call `markSyncReady()` If the caller doesn't call this, the sync will never complete since the SurfaceSyncGroup wants to give the caller a chance to add all the SyncTargets before considering the sync ready. Before `markSyncReady` is called, the `SyncTargets` can actually produce a frame, which will just be held in a transaction until all other `SyncTargets` are ready AND `markSyncReady` has been called. Once markSyncReady has been called, you cannot add any more `SyncTargets` to that particular SurfaceSyncGroup.

##### addToSync

The caller will invoke `addToSync` for every `SyncTarget` that it wants included. The caller must specify which syncId they want to add the `SyncTarget` too since there can be multiple syncs open at the same time. There are a few helper methods since the most common cases are Views and SurfaceView
* `addToSync(int syncId, View)` - This is used for syncing the root of the View, specificially the ViewRootImpl
* `addToSync(int syncId, SurfaceView, Consumer<SurfaceViewFrameCallback>)` - This is to sync a SurfaceView. Since SurfaceViews are rendered by the app, the caller will be expected to provide a way to get back the buffer to sync. More details about that [below](#surfaceviewframecallback)
* `addToSync(int syncId, SyncTarget)` - This is the generic method. It can be used to sync arbitrary info. The SyncTarget interface has required methods that need to be implemented to properly get the transaction to sync.
The caller will invoke `addToSync` for every `SyncTarget` that it wants included. There are a few helper methods since the most common cases are Views and SurfaceView
* `addToSync(AttachedSurfaceControl)` - This is used for syncing the root of the View, specificially the ViewRootImpl
* `addToSync(SurfaceView, Consumer<SurfaceViewFrameCallback>)` - This is to sync a SurfaceView. Since SurfaceViews are rendered by the app, the caller will be expected to provide a way to get back the buffer to sync. More details about that [below](#surfaceviewframecallback)
* `addToSync(SyncTarget)` - This is the generic method. It can be used to sync arbitrary info. The SyncTarget interface has required methods that need to be implemented to properly get the transaction to sync.

When calling addToSync with either View or SurfaceView, it must occur on the UI Thread. This is to ensure consistent behavior, where any UI changes done while still on the UI thread are included in this frame. The next vsync will pick up those changes and request to draw.
When calling addToSync with either AttachedSurfaceControl or SurfaceView, it must be called on the UI Thread. This is to ensure consistent behavior, where any UI changes done while still on the UI thread are included in this frame. The next vsync will pick up those changes and request to draw.

##### addTransactionToSync

This is a simple method that allows callers to add generic Transactions to the sync. The caller invokes `addTransactionToSync(int syncId, Transaction)`. This can be used for things that need to be synced with content, but aren't updating content themselves.
This is a simple method that allows callers to add generic Transactions to the sync. The caller invokes `addTransactionToSync(Transaction)`. This can be used for any additional things that need to be included in the same SyncGroup.

##### merge

To add more flexibility to Syncs, an API is provided to merge SurfaceSyncGroups. The caller provides the SurfaceSyncGroup it wants merged. The current SurfaceSyncGroup will now wait for the passed in SurfaceSyncGroup to complete, as well as its own SyncTargets to complete before invoking the callback. The passed in SurfaceSyncGroup will also get a complete callback but only when its SurfaceSyncGroup completes, not the one it merged into. If a `Consumer<Transaction>` was passed in to the SurfaceSyncGroup, it will get back an emtpy Transaction so it can't accidentally apply things that were meant to be merged.

##### addSyncCompleteCallback

This allows callers to receive a callback when the sync is complete. The method takes in an Executor and a Runnable that will be invoked when the SurfaceSyncGroup has completed. The Executor is used to invoke the callback on the desired thread. You can add more than one callback.

##### SyncTarget

@@ -45,7 +51,7 @@ This interface is used to tell the sync that this SyncTarget is ready. There's o

##### SurfaceViewFrameCallback

As mentioned above, SurfaceViews are a special case because the buffers produced are handled by the app, and not the framework. Because of this, the syncer doesn't know which frame to sync. Therefore, to sync SurfaceViews, the caller must provide a way to notify the syncer that it's going to render a buffer and that this next buffer is the one to sync. The `SurfaceViewFrameCallback` has one method `onFrameStarted()`. When this is invoked, the Syncer sets up a request to sync the next buffer for the SurfaceView.
As mentioned above, SurfaceViews are a special case because the buffers produced are handled by the app, and not the framework. Because of this, the SurfaceSyncGroup doesn't know which frame to sync. Therefore, to sync SurfaceViews, the caller must provide a way to notify the SurfaceSyncGroup that it's going to render a buffer and that this next buffer is the one to sync. The `SurfaceViewFrameCallback` has one method `onFrameStarted()`. When this is invoked, the SurfaceSyncGroup sets up a request to sync the next buffer for the SurfaceView.


### Example
@@ -53,28 +59,28 @@ As mentioned above, SurfaceViews are a special case because the buffers produced
A simple example where you want to sync two windows and also include a transaction in the sync

```java
SurfaceSyncer syncer = new SurfaceSyncer();
int syncId = syncer.setupSync(() -> {
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
SyncGroup.addSyncCompleteCallback(mMainThreadExecutor, () -> {
    Log.d(TAG, "syncComplete");
};
syncer.addToSync(syncId, view1);
syncer.addToSync(syncId, view2);
syncer.addTransactionToSync(syncId, transaction);
syncer.markSyncReady(syncId);
syncGroup.addToSync(view1.getRootSurfaceControl());
syncGroup.addToSync(view2.getRootSurfaceControl());
syncGroup.addTransactionToSync(transaction);
syncGroup.markSyncReady();
```

A SurfaceView example:
See `frameworks/base/tests/SurfaceViewSyncTest` for a working example

```java
SurfaceSyncer syncer = new SurfaceSyncer();
int syncId = syncer.setupSync(() -> {
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
syncGroup.addSyncCompleteCallback(mMainThreadExecutor, () -> {
    Log.d(TAG, "syncComplete");
};
syncer.addToSync(syncId, container);
syncer.addToSync(syncId, surfaceView, frameCallback -> {
syncGroup.addToSync(container.getRootSurfaceControl());
syncGroup.addToSync(surfaceView, frameCallback -> {
    // Call this when the SurfaceView is ready to render a new frame with the changes.
    frameCallback.onFrameStarted()
}
syncer.markSyncReady(syncId);
syncGroup.markSyncReady();
```
 No newline at end of file
Loading