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

Commit a76e512b authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Don't let two close frames to interrupt intermittency" into main

parents 4be6018e 079fc836
Loading
Loading
Loading
Loading
+20 −8
Original line number Diff line number Diff line
@@ -4744,6 +4744,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    @ViewDebug.ExportedProperty(category = "layout")
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    protected int mLeft;
    /**
     * The mLeft from the previous frame. Used for detecting movement for purposes of variable
     * refresh rate.
     */
    private int mLastFrameLeft;
    /**
     * The distance in pixels from the left edge of this view's parent
     * to the right edge of this view.
@@ -4760,6 +4765,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    @ViewDebug.ExportedProperty(category = "layout")
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    protected int mTop;
    /**
     * The mTop from the previous frame. Used for detecting movement for purposes of variable
     * refresh rate.
     */
    private int mLastFrameTop;
    /**
     * The distance in pixels from the top edge of this view's parent
     * to the bottom edge of this view.
@@ -19535,7 +19545,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    public final void setTop(int top) {
        if (top != mTop) {
            mPrivateFlags4 |= PFLAG4_HAS_MOVED;
            final boolean matrixIsIdentity = hasIdentityMatrix();
            if (matrixIsIdentity) {
                if (mAttachInfo != null) {
@@ -20427,7 +20436,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    public void offsetTopAndBottom(int offset) {
        if (offset != 0) {
            mPrivateFlags4 |= PFLAG4_HAS_MOVED;
            final boolean matrixIsIdentity = hasIdentityMatrix();
            if (matrixIsIdentity) {
                if (isHardwareAccelerated()) {
@@ -20479,7 +20487,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    public void offsetLeftAndRight(int offset) {
        if (offset != 0) {
            mPrivateFlags4 |= PFLAG4_HAS_MOVED;
            final boolean matrixIsIdentity = hasIdentityMatrix();
            if (matrixIsIdentity) {
                if (isHardwareAccelerated()) {
@@ -25523,7 +25530,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            mPrivateFlags4 |= PFLAG4_HAS_MOVED;
            changed = true;
            // Remember our drawn bit
@@ -33976,8 +33982,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            // The most common case is when nothing is set, so this special case is called
            // often.
            if (mAttachInfo.mViewVelocityApi
                    && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
                    PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
                    && ((mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
                    PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) || mLastFrameLeft != mLeft
                    || mLastFrameTop != mTop)
                    && viewRootImpl.shouldCheckFrameRate(false)
                    && parent instanceof View
                    && ((View) parent).mFrameContentVelocity <= 0) {
@@ -33990,14 +33997,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                viewRootImpl.votePreferredFrameRateCategory(category, reason, this);
                mLastFrameRateCategory = frameRateCategory;
            }
            mLastFrameLeft = mLeft;
            mLastFrameTop = mTop;
            return;
        }
        if (viewRootImpl.shouldCheckFrameRate(frameRate > 0f)) {
            float velocityFrameRate = 0f;
            if (mAttachInfo.mViewVelocityApi) {
                if (velocity < 0f
                        && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
                        PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
                        && ((mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
                        PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) || mLastFrameLeft != mLeft
                        || mLastFrameTop != mTop)
                        && mParent instanceof View
                        && ((View) mParent).mFrameContentVelocity <= 0
                ) {
@@ -34062,6 +34072,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            viewRootImpl.votePreferredFrameRateCategory(category, reason, this);
            mLastFrameRateCategory = frameRateCategory;
        }
        mLastFrameLeft = mLeft;
        mLastFrameTop = mTop;
    }
    private float convertVelocityToFrameRate(float velocityPps) {
+2 −1
Original line number Diff line number Diff line
@@ -12976,7 +12976,8 @@ public final class ViewRootImpl implements ViewParent,
        mMinusOneFrameIntervalMillis = timeIntervalMillis;
        mLastUpdateTimeMillis = currentTimeMillis;
        if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
        if (timeIntervalMillis + mMinusTwoFrameIntervalMillis
                >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            int infrequentUpdateCount = mInfrequentUpdateCount;
            mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
                    ? infrequentUpdateCount : infrequentUpdateCount + 1;
+75 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.view;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
@@ -435,6 +436,80 @@ public class ViewFrameRateTest {
        waitForAfterDraw();
    }

    /**
     * A common behavior is for two different views to be invalidated in succession, but
     * intermittently. We want to treat this as an intermittent invalidation.
     *
     * This test will only succeed on non-cuttlefish devices, so it is commented out
     * for potential manual testing.
     */
//    @Test
    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
    public void intermittentDoubleInvalidate() throws Throwable {
        View parent = (View) mMovingView.getParent();
        mActivityRule.runOnUiThread(() -> {
            parent.setWillNotDraw(false);
            // Make sure the View is large
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.width = parent.getWidth();
            layoutParams.height = parent.getHeight();
            mMovingView.setLayoutParams(layoutParams);
        });
        waitForFrameRateCategoryToSettle();
        for (int i = 0; i < 5; i++) {
            int expectedCategory;
            if (i < 4) {
                // not intermittent yet.
                // It takes 2 frames of intermittency before Views vote as intermittent.
                // It takes 4 more frames for the category to drop to the next category.
                expectedCategory =
                        toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL
                                : FRAME_RATE_CATEGORY_HIGH;
            } else {
                // intermittent
                expectedCategory = FRAME_RATE_CATEGORY_NORMAL;
            }
            mActivityRule.runOnUiThread(() -> {
                mMovingView.invalidate();
                runAfterDraw(() -> assertEquals(expectedCategory,
                        mViewRoot.getLastPreferredFrameRateCategory()));
            });
            waitForAfterDraw();
            mActivityRule.runOnUiThread(() -> {
                parent.invalidate();
                runAfterDraw(() -> assertEquals(expectedCategory,
                        mViewRoot.getLastPreferredFrameRateCategory()));
            });
            waitForAfterDraw();
            Thread.sleep(90);
        }
    }

    // When a view has two motions that offset each other, the overall motion
    // should be canceled and be considered unmoved.
    @Test
    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
    })
    public void sameFrameMotion() throws Throwable {
        mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
        waitForFrameRateCategoryToSettle();

        mActivityRule.runOnUiThread(() -> {
            mMovingView.offsetLeftAndRight(10);
            mMovingView.offsetLeftAndRight(-10);
            mMovingView.offsetTopAndBottom(100);
            mMovingView.offsetTopAndBottom(-100);
            mMovingView.invalidate();
            runAfterDraw(() -> {
                assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f);
                assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
                        mViewRoot.getLastPreferredFrameRateCategory());
            });
        });
        waitForAfterDraw();
    }
    private void runAfterDraw(@NonNull Runnable runnable) {
        Handler handler = new Handler(Looper.getMainLooper());
        mAfterDrawLatch = new CountDownLatch(1);