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

Commit 21c39a77 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Lock free animations (1/2)

First CL that introduces SurfaceAnimator/LockFreeAnimator

We start our synchronized app transition journey by showing that
the concept works by using WindowState animations as proof of
concept.

The main class in this CL are SurfaceAnimator and
SurfaceAnimatorRunner. When we start an animation on a Window, we
create a new bufferless surface, called "The Leash", in the
hierarchy and attach the surface of WindowState onto it, while
attaching the leash onto the old surface parent which is still
responsible for z-layering.

Then, we pass off the Leash into SurfaceAnimationRunner, which then
changes the surface properties of Leash in every animation frame,
without holding the WM lock. While it's doing that we can still
update the z-layering of the window, or even relayout the window
of needed - the important surfaces for this are still under WM's
control.

In case the animation is finished the window surface gets
reparented to its original parent, and the leash is abandoned.
Note that the reparenting is done in the same transaction as
processing the animation finish, such that we don't end up with
a flicker in case of a disappearing animation, where the window
surface gets destroyed.

In case the animation needs to be cancelled, WM can revoke control
of the leash by reparenting the window surface. Even if the
cancellation signal is heavily delayed, WM immediately regains
control over the surface by reparenting it within a transaction.

We also introduce the concept of animating a WindowContainer. We
clean up isAnimating:
- isLocalAnimating: is the container itself animating
- isAnimating: is the container or one of its parents animating
- isSelfOrChildAnimating: is local animating or any child
animating.

SurfaceAnimationRunner also needs it's own thread so it's not getting
bogged down by any WM lock contention by processing regular
animation frames. We call that thread android.anim.lf (lockfree).

Now, imagine that SurfaceAnimationAnimator would sit behind an IPC in
another process and instead of animating WindowState, we'd animate
AppWindowToken. Then, synchronized app transitions would be done.

Test: go/wm-smoke
Test: SurfaceAnimatorTest
Test: SurfaceAnimationRunnerTest
Test: WindowContainerTests
Bug: 64674361
Change-Id: I10d41f7a289ab2158da3f2f1c3ddd78edd1efc86
Exempt-From-Owner-Approval: Tiny change unrelated to display management.
parent ffe128d4
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -18,11 +18,18 @@ package android.view;

import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;

import android.annotation.Size;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.IBinder;
@@ -862,6 +869,22 @@ public class SurfaceControl implements Parcelable {
        }
    }

    /**
     * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation matrix.
     *
     * @param matrix The matrix to apply.
     * @param float9 An array of 9 floats to be used to extract the values from the matrix.
     */
    public void setMatrix(Matrix matrix, float[] float9) {
        checkNotReleased();
        matrix.getValues(float9);
        synchronized (SurfaceControl.class) {
            sGlobalTransaction.setMatrix(this, float9[MSCALE_X], float9[MSKEW_Y],
                    float9[MSKEW_X], float9[MSCALE_Y]);
            sGlobalTransaction.setPosition(this, float9[MTRANS_X], float9[MTRANS_Y]);
        }
    }

    public void setWindowCrop(Rect crop) {
        checkNotReleased();
        synchronized (SurfaceControl.class) {
@@ -1348,6 +1371,14 @@ public class SurfaceControl implements Parcelable {
            return this;
        }

        public Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) {
            matrix.getValues(float9);
            setMatrix(sc, float9[MSCALE_X], float9[MSKEW_Y],
                    float9[MSKEW_X], float9[MSCALE_Y]);
            setPosition(sc, float9[MTRANS_X], float9[MTRANS_Y]);
            return this;
        }

        public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
            sc.checkNotReleased();
            if (crop != null) {
+9 −1
Original line number Diff line number Diff line
@@ -26,7 +26,15 @@ import android.view.Choreographer;
 */
public final class SfVsyncFrameCallbackProvider implements AnimationFrameCallbackProvider {

    private final Choreographer mChoreographer = Choreographer.getSfInstance();
    private final Choreographer mChoreographer;

    public SfVsyncFrameCallbackProvider() {
        mChoreographer = Choreographer.getSfInstance();
    }

    public SfVsyncFrameCallbackProvider(Choreographer choreographer) {
        mChoreographer = choreographer;
    }

    @Override
    public void postFrameCallback(Choreographer.FrameCallback callback) {
+2 −2
Original line number Diff line number Diff line
@@ -22,8 +22,8 @@ import android.os.Handler;
import android.os.Trace;

/**
 * Thread for handling all window animations, or anything that's directly impacting animations like
 * starting windows or traversals.
 * Thread for handling all legacy window animations, or anything that's directly impacting
 * animations like starting windows or traversals.
 */
public final class AnimationThread extends ServiceThread {
    private static AnimationThread sInstance;
+3 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -292,6 +293,8 @@ public final class DisplayManagerService extends SystemService {
                Process.THREAD_GROUP_TOP_APP);
        Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(),
                Process.THREAD_GROUP_TOP_APP);
        Process.setThreadGroupAndCpuset(SurfaceAnimationThread.get().getThreadId(),
                Process.THREAD_GROUP_TOP_APP);
    }

    @Override
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.wm;

import android.annotation.ColorInt;
import android.graphics.Point;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;

import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;

/**
 * Interface that describes an animation and bridges the animation start to the component
 * responsible for running the animation.
 */
interface AnimationAdapter {

    /**
     * @return Whether we should detach the wallpaper during the animation.
     * @see Animation#setDetachWallpaper
     */
    boolean getDetachWallpaper();

    /**
     * @return The background color behind the animation.
     */
    @ColorInt int getBackgroundColor();

    /**
     * Requests to start the animation.
     *
     * @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an
     *                       overview of the mechanism. This surface needs to be released by the
     *                       component running the animation after {@code finishCallback} has been
     *                       invoked, or after the animation was cancelled.
     * @param t The Transaction to apply the initial frame of the animation.
     * @param finishCallback The callback to be invoked when the animation has finished.
     */
    void startAnimation(SurfaceControl animationLeash, Transaction t,
            OnAnimationFinishedCallback finishCallback);

    /**
     * Called when the animation that was started with {@link #startAnimation} was cancelled by the
     * window manager.
     *
     * @param animationLeash The leash passed to {@link #startAnimation}.
     */
    void onAnimationCancelled(SurfaceControl animationLeash);

    /**
     * @return The approximate duration of the animation, in milliseconds.
     */
    long getDurationHint();
}
Loading