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

Commit ee8e8d16 authored by Evan Rosky's avatar Evan Rosky Committed by Automerger Merge Worker
Browse files

Merge "Add a non-blast mode to sync-engine" into tm-qpr-dev am: f2e3e58c am: f4d5421f

parents 6e4a25b8 f4d5421f
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -2671,6 +2671,12 @@
      "group": "WM_DEBUG_ANIM",
      "group": "WM_DEBUG_ANIM",
      "at": "com\/android\/server\/wm\/WindowContainer.java"
      "at": "com\/android\/server\/wm\/WindowContainer.java"
    },
    },
    "390947100": {
      "message": "Screenshotting %s [%s]",
      "level": "VERBOSE",
      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
      "at": "com\/android\/server\/wm\/Transition.java"
    },
    "397382873": {
    "397382873": {
      "message": "Moving to PAUSED: %s %s",
      "message": "Moving to PAUSED: %s %s",
      "level": "VERBOSE",
      "level": "VERBOSE",
@@ -4189,6 +4195,12 @@
      "group": "WM_DEBUG_FOCUS_LIGHT",
      "group": "WM_DEBUG_FOCUS_LIGHT",
      "at": "com\/android\/server\/wm\/InputMonitor.java"
      "at": "com\/android\/server\/wm\/InputMonitor.java"
    },
    },
    "2004282287": {
      "message": "Override sync-method for %s because seamless rotating",
      "level": "VERBOSE",
      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
      "at": "com\/android\/server\/wm\/Transition.java"
    },
    "2010476671": {
    "2010476671": {
      "message": "Animation done in %s: reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
      "message": "Animation done in %s: reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
      "level": "VERBOSE",
      "level": "VERBOSE",
+23 −9
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Trace;
import android.os.Trace;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;
@@ -63,6 +64,15 @@ import java.util.ArrayList;
class BLASTSyncEngine {
class BLASTSyncEngine {
    private static final String TAG = "BLASTSyncEngine";
    private static final String TAG = "BLASTSyncEngine";


    /** No specific method. Used by override specifiers. */
    public static final int METHOD_UNDEFINED = -1;

    /** No sync method. Apps will draw/present internally and just report. */
    public static final int METHOD_NONE = 0;

    /** Sync with BLAST. Apps will draw and then send the buffer to be applied in sync. */
    public static final int METHOD_BLAST = 1;

    interface TransactionReadyListener {
    interface TransactionReadyListener {
        void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
        void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
    }
    }
@@ -85,6 +95,7 @@ class BLASTSyncEngine {
     */
     */
    class SyncGroup {
    class SyncGroup {
        final int mSyncId;
        final int mSyncId;
        final int mSyncMethod;
        final TransactionReadyListener mListener;
        final TransactionReadyListener mListener;
        final Runnable mOnTimeout;
        final Runnable mOnTimeout;
        boolean mReady = false;
        boolean mReady = false;
@@ -92,8 +103,9 @@ class BLASTSyncEngine {
        private SurfaceControl.Transaction mOrphanTransaction = null;
        private SurfaceControl.Transaction mOrphanTransaction = null;
        private String mTraceName;
        private String mTraceName;


        private SyncGroup(TransactionReadyListener listener, int id, String name) {
        private SyncGroup(TransactionReadyListener listener, int id, String name, int method) {
            mSyncId = id;
            mSyncId = id;
            mSyncMethod = method;
            mListener = listener;
            mListener = listener;
            mOnTimeout = () -> {
            mOnTimeout = () -> {
                Slog.w(TAG, "Sync group " + mSyncId + " timeout");
                Slog.w(TAG, "Sync group " + mSyncId + " timeout");
@@ -271,16 +283,13 @@ class BLASTSyncEngine {
     * Prepares a {@link SyncGroup} that is not active yet. Caller must call {@link #startSyncSet}
     * Prepares a {@link SyncGroup} that is not active yet. Caller must call {@link #startSyncSet}
     * before calling {@link #addToSyncSet(int, WindowContainer)} on any {@link WindowContainer}.
     * before calling {@link #addToSyncSet(int, WindowContainer)} on any {@link WindowContainer}.
     */
     */
    SyncGroup prepareSyncSet(TransactionReadyListener listener, String name) {
    SyncGroup prepareSyncSet(TransactionReadyListener listener, String name, int method) {
        return new SyncGroup(listener, mNextSyncId++, name);
        return new SyncGroup(listener, mNextSyncId++, name, method);
    }
    }


    int startSyncSet(TransactionReadyListener listener) {
    int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name,
        return startSyncSet(listener, BLAST_TIMEOUT_DURATION, "");
            int method) {
    }
        final SyncGroup s = prepareSyncSet(listener, name, method);

    int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name) {
        final SyncGroup s = prepareSyncSet(listener, name);
        startSyncSet(s, timeoutMs);
        startSyncSet(s, timeoutMs);
        return s.mSyncId;
        return s.mSyncId;
    }
    }
@@ -302,6 +311,11 @@ class BLASTSyncEngine {
        scheduleTimeout(s, timeoutMs);
        scheduleTimeout(s, timeoutMs);
    }
    }


    @Nullable
    SyncGroup getSyncSet(int id) {
        return mActiveSyncs.get(id);
    }

    boolean hasActiveSync() {
    boolean hasActiveSync() {
        return mActiveSyncs.size() != 0;
        return mActiveSyncs.size() != 0;
    }
    }
+8 −0
Original line number Original line Diff line number Diff line
@@ -3316,6 +3316,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
                    mAsyncRotationController.keepAppearanceInPreviousRotation();
                    mAsyncRotationController.keepAppearanceInPreviousRotation();
                }
                }
            } else if (isRotationChanging()) {
            } else if (isRotationChanging()) {
                if (displayChange != null) {
                    final boolean seamless = mDisplayRotation.shouldRotateSeamlessly(
                            displayChange.getStartRotation(), displayChange.getEndRotation(),
                            false /* forceUpdate */);
                    if (seamless) {
                        t.onSeamlessRotating(this);
                    }
                }
                mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
                mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
                controller.mTransitionMetricsReporter.associate(t,
                controller.mTransitionMetricsReporter.associate(t,
                        startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
                        startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
+8 −3
Original line number Original line Diff line number Diff line
@@ -1883,8 +1883,7 @@ class Task extends TaskFragment {
        }
        }


        final int newWinMode = getWindowingMode();
        final int newWinMode = getWindowingMode();
        if ((prevWinMode != newWinMode) && (mDisplayContent != null)
        if (shouldStartChangeTransition(prevWinMode, mTmpPrevBounds)) {
                && shouldStartChangeTransition(prevWinMode, newWinMode)) {
            initializeChangeTransition(mTmpPrevBounds);
            initializeChangeTransition(mTmpPrevBounds);
        }
        }


@@ -2135,10 +2134,16 @@ class Task extends TaskFragment {
        bounds.offset(horizontalDiff, verticalDiff);
        bounds.offset(horizontalDiff, verticalDiff);
    }
    }


    private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
    private boolean shouldStartChangeTransition(int prevWinMode, @NonNull Rect prevBounds) {
        if (!isLeafTask() || !canStartChangeTransition()) {
        if (!isLeafTask() || !canStartChangeTransition()) {
            return false;
            return false;
        }
        }
        final int newWinMode = getWindowingMode();
        if (mTransitionController.inTransition(this)) {
            final Rect newBounds = getConfiguration().windowConfiguration.getBounds();
            return prevWinMode != newWinMode || prevBounds.width() != newBounds.width()
                    || prevBounds.height() != newBounds.height();
        }
        // Only do an animation into and out-of freeform mode for now. Other mode
        // Only do an animation into and out-of freeform mode for now. Other mode
        // transition animations are currently handled by system-ui.
        // transition animations are currently handled by system-ui.
        return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
        return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
+179 −2
Original line number Original line Diff line number Diff line
@@ -68,6 +68,7 @@ import android.app.ActivityManager;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Binder;
import android.os.IBinder;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.IRemoteCallback;
@@ -205,6 +206,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
    /** @see #setCanPipOnFinish */
    /** @see #setCanPipOnFinish */
    private boolean mCanPipOnFinish = true;
    private boolean mCanPipOnFinish = true;


    private boolean mIsSeamlessRotation = false;
    private IContainerFreezer mContainerFreezer = null;

    Transition(@TransitionType int type, @TransitionFlags int flags,
    Transition(@TransitionType int type, @TransitionFlags int flags,
            TransitionController controller, BLASTSyncEngine syncEngine) {
            TransitionController controller, BLASTSyncEngine syncEngine) {
        mType = type;
        mType = type;
@@ -265,10 +269,31 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        return mTargetDisplays.contains(dc);
        return mTargetDisplays.contains(dc);
    }
    }


    /** Set a transition to be a seamless-rotation. */
    void setSeamlessRotation(@NonNull WindowContainer wc) {
    void setSeamlessRotation(@NonNull WindowContainer wc) {
        final ChangeInfo info = mChanges.get(wc);
        final ChangeInfo info = mChanges.get(wc);
        if (info == null) return;
        if (info == null) return;
        info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
        info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
        onSeamlessRotating(wc.getDisplayContent());
    }

    /**
     * Called when it's been determined that this is transition is a seamless rotation. This should
     * be called before any WM changes have happened.
     */
    void onSeamlessRotating(@NonNull DisplayContent dc) {
        // Don't need to do anything special if everything is using BLAST sync already.
        if (mSyncEngine.getSyncSet(mSyncId).mSyncMethod == BLASTSyncEngine.METHOD_BLAST) return;
        if (mContainerFreezer == null) {
            mContainerFreezer = new ScreenshotFreezer();
        }
        mIsSeamlessRotation = true;
        final WindowState top = dc.getDisplayPolicy().getTopFullscreenOpaqueWindow();
        if (top != null) {
            top.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Override sync-method for %s "
                    + "because seamless rotating", top.getName());
        }
    }
    }


    /**
    /**
@@ -285,6 +310,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        }
        }
    }
    }


    /** Only for testing. */
    void setContainerFreezer(IContainerFreezer freezer) {
        mContainerFreezer = freezer;
    }

    @TransitionState
    @TransitionState
    int getState() {
    int getState() {
        return mState;
        return mState;
@@ -314,13 +344,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        return mState == STATE_COLLECTING || mState == STATE_STARTED;
        return mState == STATE_COLLECTING || mState == STATE_STARTED;
    }
    }


    /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
    @VisibleForTesting
    void startCollecting(long timeoutMs) {
    void startCollecting(long timeoutMs) {
        startCollecting(timeoutMs, TransitionController.SYNC_METHOD);
    }

    /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
    void startCollecting(long timeoutMs, int method) {
        if (mState != STATE_PENDING) {
        if (mState != STATE_PENDING) {
            throw new IllegalStateException("Attempting to re-use a transition");
            throw new IllegalStateException("Attempting to re-use a transition");
        }
        }
        mState = STATE_COLLECTING;
        mState = STATE_COLLECTING;
        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG, method);


        mController.mTransitionTracer.logState(this);
        mController.mTransitionTracer.logState(this);
    }
    }
@@ -414,6 +449,37 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        mChanges.get(wc).mExistenceChanged = true;
        mChanges.get(wc).mExistenceChanged = true;
    }
    }


    /**
     * Records that a particular container is changing visibly (ie. something about it is changing
     * while it remains visible). This only effects windows that are already in the collecting
     * transition.
     */
    void collectVisibleChange(WindowContainer wc) {
        if (mSyncEngine.getSyncSet(mSyncId).mSyncMethod == BLASTSyncEngine.METHOD_BLAST) {
            // All windows are synced already.
            return;
        }
        if (!isInTransition(wc)) return;

        if (mContainerFreezer == null) {
            mContainerFreezer = new ScreenshotFreezer();
        }
        Transition.ChangeInfo change = mChanges.get(wc);
        if (change == null || !change.mVisible || !wc.isVisibleRequested()) return;
        // Note: many more tests have already been done by caller.
        mContainerFreezer.freeze(wc, change.mAbsoluteBounds);
    }

    /**
     * @return {@code true} if `wc` is a participant or is a descendant of one.
     */
    boolean isInTransition(WindowContainer wc) {
        for (WindowContainer p = wc; p != null; p = p.getParent()) {
            if (mParticipants.contains(p)) return true;
        }
        return false;
    }

    /**
    /**
     * Specifies configuration change explicitly for the window container, so it can be chosen as
     * Specifies configuration change explicitly for the window container, so it can be chosen as
     * transition target. This is usually used with transition mode
     * transition target. This is usually used with transition mode
@@ -531,6 +597,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
                displays.add(target.getDisplayContent());
                displays.add(target.getDisplayContent());
            }
            }
        }
        }
        // Remove screenshot layers if necessary
        if (mContainerFreezer != null) {
            mContainerFreezer.cleanUp(t);
        }
        // Need to update layers on involved displays since they were all paused while
        // Need to update layers on involved displays since they were all paused while
        // the animation played. This puts the layers back into the correct order.
        // the animation played. This puts the layers back into the correct order.
        mController.mBuildingFinishLayers = true;
        mController.mBuildingFinishLayers = true;
@@ -1982,4 +2052,111 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
            return sortedTargets;
            return sortedTargets;
        }
        }
    }
    }

    /**
     * Interface for freezing a container's content during sync preparation. Really just one impl
     * but broken into an interface for testing (since you can't take screenshots in unit tests).
     */
    interface IContainerFreezer {
        /**
         * Makes sure a particular window is "frozen" for the remainder of a sync.
         *
         * @return whether the freeze was successful. It fails if `wc` is already in a frozen window
         *         or is not visible/ready.
         */
        boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds);

        /** Populates `t` with operations that clean-up any state created to set-up the freeze. */
        void cleanUp(SurfaceControl.Transaction t);
    }

    /**
     * Freezes container content by taking a screenshot. Because screenshots are heavy, usage of
     * any container "freeze" is currently explicit. WM code needs to be prudent about which
     * containers to freeze.
     */
    @VisibleForTesting
    private class ScreenshotFreezer implements IContainerFreezer {
        /** Values are the screenshot "surfaces" or null if it was frozen via BLAST override. */
        private final ArrayMap<WindowContainer, SurfaceControl> mSnapshots = new ArrayMap<>();

        /** Takes a screenshot and puts it at the top of the container's surface. */
        @Override
        public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
            if (!wc.isVisibleRequested()) return false;

            // Check if any parents have already been "frozen". If so, `wc` is already part of that
            // snapshot, so just skip it.
            for (WindowContainer p = wc; p != null; p = p.getParent()) {
                if (mSnapshots.containsKey(p)) return false;
            }

            if (mIsSeamlessRotation) {
                WindowState top = wc.getDisplayContent() == null ? null
                        : wc.getDisplayContent().getDisplayPolicy().getTopFullscreenOpaqueWindow();
                if (top != null && (top == wc || top.isDescendantOf(wc))) {
                    // Don't use screenshots for seamless windows: these will use BLAST even if not
                    // BLAST mode.
                    mSnapshots.put(wc, null);
                    return true;
                }
            }

            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Screenshotting %s [%s]",
                    wc.toString(), bounds.toString());

            Rect cropBounds = new Rect(bounds);
            cropBounds.offsetTo(0, 0);
            SurfaceControl.LayerCaptureArgs captureArgs =
                    new SurfaceControl.LayerCaptureArgs.Builder(wc.getSurfaceControl())
                            .setSourceCrop(cropBounds)
                            .setCaptureSecureLayers(true)
                            .setAllowProtected(true)
                            .build();
            SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                    SurfaceControl.captureLayers(captureArgs);
            final HardwareBuffer buffer = screenshotBuffer == null ? null
                    : screenshotBuffer.getHardwareBuffer();
            if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
                // This can happen when display is not ready.
                Slog.w(TAG, "Failed to capture screenshot for " + wc);
                return false;
            }
            SurfaceControl snapshotSurface = wc.makeAnimationLeash()
                    .setName("transition snapshot: " + wc.toString())
                    .setOpaque(true)
                    .setParent(wc.getSurfaceControl())
                    .setSecure(screenshotBuffer.containsSecureLayers())
                    .setCallsite("Transition.ScreenshotSync")
                    .setBLASTLayer()
                    .build();
            mSnapshots.put(wc, snapshotSurface);
            SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get();

            t.setBuffer(snapshotSurface, buffer);
            t.setDataSpace(snapshotSurface, screenshotBuffer.getColorSpace().getDataSpace());
            t.show(snapshotSurface);

            // Place it on top of anything else in the container.
            t.setLayer(snapshotSurface, Integer.MAX_VALUE);
            t.apply();
            t.close();

            // Detach the screenshot on the sync transaction (the screenshot is just meant to
            // freeze the window until the sync transaction is applied (with all its other
            // corresponding changes), so this is how we unfreeze it.
            wc.getSyncTransaction().reparent(snapshotSurface, null /* newParent */);
            return true;
        }

        @Override
        public void cleanUp(SurfaceControl.Transaction t) {
            for (int i = 0; i < mSnapshots.size(); ++i) {
                SurfaceControl snap = mSnapshots.valueAt(i);
                // May be null if it was frozen via BLAST override.
                if (snap == null) continue;
                t.remove(snap);
            }
        }
    }
}
}
Loading