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

Commit 6aa9421a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Change how "small view updates" are calculated." into main

parents 1f51791e c31191be
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -889,11 +889,11 @@ public class TextureView extends View {
     * @hide
     */
    @Override
    protected int calculateFrameRateCategory(float sizePercentage) {
    protected int calculateFrameRateCategory(int width, int height) {
        if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) {
            return FRAME_RATE_CATEGORY_NORMAL;
        }
        return super.calculateFrameRateCategory(sizePercentage);
        return super.calculateFrameRateCategory(width, height);
    }

    @UnsupportedAppUsage
+91 −29
Original line number Diff line number Diff line
@@ -2371,6 +2371,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
    /**
     * This indicates that the frame rate category was chosen because it was a small area update.
     * @hide
     */
    public static final int FRAME_RATE_CATEGORY_REASON_SMALL = 0x0100_0000;
    /**
     * This indicates that the frame rate category was chosen because it was an intermittent update.
     * @hide
     */
    public static final int FRAME_RATE_CATEGORY_REASON_INTERMITTENT = 0x0200_0000;
    /**
     * This indicates that the frame rate category was chosen because it was a large View.
     * @hide
     */
    public static final int FRAME_RATE_CATEGORY_REASON_LARGE = 0x03000000;
    /**
     * This indicates that the frame rate category was chosen because it was requested.
     * @hide
     */
    public static final int FRAME_RATE_CATEGORY_REASON_REQUESTED = 0x0400_0000;
    /**
     * This indicates that the frame rate category was chosen because an invalid frame rate was
     * requested.
     * @hide
     */
    public static final int FRAME_RATE_CATEGORY_REASON_INVALID = 0x0500_0000;
    private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000;
    private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
    private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
    // Used to set frame rate compatibility.
@@ -5637,10 +5670,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private ViewTranslationResponse mViewTranslationResponse;
    /**
     * A threshold value to determine the frame rate category of the View based on the size.
     * Threshold size for something to be considered a small area update (in DP).
     * This is the dimension for both width and height.
     */
    private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
    private static final float FRAME_RATE_SMALL_SIZE_THRESHOLD = 40f;
    /**
     * Threshold size for something to be considered a small area update (in DP) if
     * it is narrow. This is for either width OR height. For example, a narrow progress
     * bar could be considered a small area.
     */
    private static final float FRAME_RATE_NARROW_THRESHOLD = 10f;
    private static final long INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
    private static final int INFREQUENT_UPDATE_COUNTS = 2;
@@ -33655,18 +33695,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *
     * @hide
     */
    protected int calculateFrameRateCategory(float sizePercentage) {
    protected int calculateFrameRateCategory(int width, int height) {
        if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
                < INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
                return FRAME_RATE_CATEGORY_NORMAL;
            DisplayMetrics displayMetrics = mResources.getDisplayMetrics();
            float density = displayMetrics.density;
            if (density == 0f) {
                density = 1f;
            }
            float widthDp = width / density;
            float heightDp = height / density;
            if (widthDp <= FRAME_RATE_NARROW_THRESHOLD
                    || heightDp <= FRAME_RATE_NARROW_THRESHOLD
                    || (widthDp <= FRAME_RATE_SMALL_SIZE_THRESHOLD
                    && heightDp <= FRAME_RATE_SMALL_SIZE_THRESHOLD)) {
                return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_SMALL;
            } else {
                return FRAME_RATE_CATEGORY_HIGH;
                return FRAME_RATE_CATEGORY_HIGH | FRAME_RATE_CATEGORY_REASON_LARGE;
            }
        }
        if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
            return FRAME_RATE_CATEGORY_NORMAL;
            return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
        }
        return mLastFrameRateCategory;
    }
@@ -33674,12 +33724,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private void votePreferredFrameRate() {
        // use toolkitSetFrameRate flag to gate the change
        ViewRootImpl viewRootImpl = getViewRootImpl();
        float sizePercentage = getSizePercentage();
        int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
        if (viewRootImpl != null && sizePercentage > 0) {
            if (sToolkitMetricsForFrameRateDecisionFlagValue) {
                viewRootImpl.recordViewPercentage(sizePercentage);
            }
        int width = mRight - mLeft;
        int height = mBottom - mTop;
        if (viewRootImpl != null && (width != 0 && height != 0)) {
            if (viewVelocityApi()) {
                float velocity = mFrameContentVelocity;
                if (velocity < 0f) {
@@ -33691,25 +33738,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    return;
                }
            }
            if (!Float.isNaN(mPreferredFrameRate)) {
                if (mPreferredFrameRate < 0) {
            int frameRateCategory;
            if (Float.isNaN(mPreferredFrameRate)) {
                frameRateCategory = calculateFrameRateCategory(width, height);
            } else if (mPreferredFrameRate < 0) {
                if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
                        frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
                    frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE
                            | FRAME_RATE_CATEGORY_REASON_REQUESTED;
                } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) {
                        frameRateCateogry = FRAME_RATE_CATEGORY_LOW;
                    frameRateCategory = FRAME_RATE_CATEGORY_LOW
                            | FRAME_RATE_CATEGORY_REASON_REQUESTED;
                } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) {
                        frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL;
                    frameRateCategory = FRAME_RATE_CATEGORY_NORMAL
                            | FRAME_RATE_CATEGORY_REASON_REQUESTED;
                } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) {
                        frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
                    frameRateCategory = FRAME_RATE_CATEGORY_HIGH
                            | FRAME_RATE_CATEGORY_REASON_REQUESTED;
                } else {
                    // invalid frame rate, default to HIGH
                    frameRateCategory = FRAME_RATE_CATEGORY_HIGH
                            | FRAME_RATE_CATEGORY_REASON_INVALID;
                }
            } else {
                viewRootImpl.votePreferredFrameRate(mPreferredFrameRate,
                        mFrameRateCompatibility);
                return;
            }
            int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
            if (sToolkitMetricsForFrameRateDecisionFlagValue) {
                int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK;
                viewRootImpl.recordCategory(category, reason, this);
            }
            viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
            mLastFrameRateCategory = frameRateCateogry;
            viewRootImpl.votePreferredFrameRateCategory(category);
            mLastFrameRateCategory = frameRateCategory;
        }
    }
+25 −10
Original line number Diff line number Diff line
@@ -32,6 +32,11 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INVALID;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_LARGE;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_REQUESTED;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_SMALL;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -841,8 +846,9 @@ public final class ViewRootImpl implements ViewParent,
    private boolean mInsetsAnimationRunning;
    private long mPreviousFrameDrawnTime = -1;
    // The largest view size percentage to the display size. Used on trace to collect metric.
    private float mLargestChildPercentage = 0.0f;
    // The reason the category was changed.
    private int mFrameRateCategoryChangeReason = 0;
    private String mFrameRateCategoryView;
    /**
     * The resolved pointer icon type requested by this window.
@@ -4847,10 +4853,6 @@ public final class ViewRootImpl implements ViewParent,
        long fps = NANOS_PER_SEC / timeDiff;
        Trace.setCounter(mFpsTraceName, fps);
        mPreviousFrameDrawnTime = expectedDrawnTime;
        long percentage = (long) (mLargestChildPercentage * 100);
        Trace.setCounter(mLargestViewTraceName, percentage);
        mLargestChildPercentage = 0.0f;
    }
    private void reportDrawFinished(@Nullable Transaction t, int seqId) {
@@ -12369,9 +12371,20 @@ public final class ViewRootImpl implements ViewParent,
        try {
            if (mLastPreferredFrameRateCategory != frameRateCategory) {
                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                    String reason = "none";
                    switch (mFrameRateCategoryChangeReason) {
                        case FRAME_RATE_CATEGORY_REASON_INTERMITTENT -> reason = "intermittent";
                        case FRAME_RATE_CATEGORY_REASON_SMALL -> reason = "small";
                        case FRAME_RATE_CATEGORY_REASON_LARGE -> reason = "large";
                        case FRAME_RATE_CATEGORY_REASON_REQUESTED -> reason = "requested";
                        case FRAME_RATE_CATEGORY_REASON_INVALID -> reason = "invalid frame rate";
                    }
                    String sourceView = mFrameRateCategoryView == null ? "No View Given"
                            : mFrameRateCategoryView;
                    Trace.traceBegin(
                            Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory "
                                + frameRateCategory);
                                    + frameRateCategory + ", reason " + reason + ", "
                                    + sourceView);
                }
                mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
                        frameRateCategory, false).applyAsyncUnsafe();
@@ -12603,10 +12616,12 @@ public final class ViewRootImpl implements ViewParent,
        mWindowlessBackKeyCallback = callback;
    }
    void recordViewPercentage(float percentage) {
    void recordCategory(int category, int reason, View view) {
        if (!Trace.isEnabled()) return;
        // Record the largest view of percentage to the display size.
        mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage);
        if (category > mPreferredFrameRateCategory) {
            mFrameRateCategoryChangeReason = reason;
            mFrameRateCategoryView = view.getClass().getSimpleName();
        }
    }
    /**
+240 −0
Original line number Diff line number Diff line
@@ -39,9 +39,12 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class ViewVelocityTest {
public class ViewFrameRateTest {

    @Rule
    public ActivityTestRule<ViewCaptureTestActivity> mActivityRule = new ActivityTestRule<>(
@@ -105,4 +108,133 @@ public class ViewVelocityTest {
            assertFalse(mViewRoot.getIsTouchBoosting());
        });
    }

    private void waitForFrameRateCategoryToSettle() throws Throwable {
        for (int i = 0; i < 5; i++) {
            final CountDownLatch drawLatch = new CountDownLatch(1);

            // Now that it is small, any invalidation should have a normal category
            mActivityRule.runOnUiThread(() -> {
                mMovingView.invalidate();
                mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch::countDown);
            });

            assertTrue(drawLatch.await(1, TimeUnit.SECONDS));
        }
    }

    @Test
    public void noVelocityUsesCategorySmall() throws Throwable {
        final CountDownLatch drawLatch1 = new CountDownLatch(1);
        mActivityRule.runOnUiThread(() -> {
            float density = mActivity.getResources().getDisplayMetrics().density;
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.height = (int) (40 * density);
            layoutParams.width = (int) (40 * density);
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });

        assertTrue(drawLatch1.await(1, TimeUnit.SECONDS));
        waitForFrameRateCategoryToSettle();

        // Now that it is small, any invalidation should have a normal category
        mActivityRule.runOnUiThread(() -> {
            mMovingView.invalidate();
            assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL,
                    mViewRoot.getPreferredFrameRateCategory());
        });
    }

    @Test
    public void noVelocityUsesCategoryNarrowWidth() throws Throwable {
        final CountDownLatch drawLatch1 = new CountDownLatch(1);
        mActivityRule.runOnUiThread(() -> {
            float density = mActivity.getResources().getDisplayMetrics().density;
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
            layoutParams.width = (int) (10 * density);
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });

        assertTrue(drawLatch1.await(1, TimeUnit.SECONDS));
        waitForFrameRateCategoryToSettle();

        // Now that it is small, any invalidation should have a normal category
        mActivityRule.runOnUiThread(() -> {
            mMovingView.invalidate();
            assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL,
                    mViewRoot.getPreferredFrameRateCategory());
        });
    }

    @Test
    public void noVelocityUsesCategoryNarrowHeight() throws Throwable {
        final CountDownLatch drawLatch1 = new CountDownLatch(1);
        mActivityRule.runOnUiThread(() -> {
            float density = mActivity.getResources().getDisplayMetrics().density;
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.height = (int) (10 * density);
            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });

        assertTrue(drawLatch1.await(1, TimeUnit.SECONDS));
        waitForFrameRateCategoryToSettle();

        // Now that it is small, any invalidation should have a normal category
        mActivityRule.runOnUiThread(() -> {
            mMovingView.invalidate();
            assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL,
                    mViewRoot.getPreferredFrameRateCategory());
        });
    }

    @Test
    public void noVelocityUsesCategoryLargeWidth() throws Throwable {
        final CountDownLatch drawLatch1 = new CountDownLatch(1);
        mActivityRule.runOnUiThread(() -> {
            float density = mActivity.getResources().getDisplayMetrics().density;
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.height = (int) (40 * density);
            layoutParams.width = (int) Math.ceil(41 * density);
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });

        assertTrue(drawLatch1.await(1, TimeUnit.SECONDS));
        waitForFrameRateCategoryToSettle();

        // Now that it is small, any invalidation should have a high category
        mActivityRule.runOnUiThread(() -> {
            mMovingView.invalidate();
            assertEquals(Surface.FRAME_RATE_CATEGORY_HIGH,
                    mViewRoot.getPreferredFrameRateCategory());
        });
    }

    @Test
    public void noVelocityUsesCategoryLargeHeight() throws Throwable {
        final CountDownLatch drawLatch1 = new CountDownLatch(1);
        mActivityRule.runOnUiThread(() -> {
            float density = mActivity.getResources().getDisplayMetrics().density;
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.height = (int) Math.ceil(41 * density);
            layoutParams.width = (int) (40 * density);
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });

        assertTrue(drawLatch1.await(1, TimeUnit.SECONDS));
        waitForFrameRateCategoryToSettle();

        // Now that it is small, any invalidation should have a high category
        mActivityRule.runOnUiThread(() -> {
            mMovingView.invalidate();
            assertEquals(Surface.FRAME_RATE_CATEGORY_HIGH,
                    mViewRoot.getPreferredFrameRateCategory());
        });
    }
}