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

Commit 84ddbe54 authored by George Mount's avatar George Mount
Browse files

Improve performance of VRR

Bug: 330765659

Trace.isTagEnabled() is expensive and has been removed in favor
of the less expensive condition it was protecting.

Size checks are done only when the size of a View changes.
The small size requirement is calculated once rather than
in every View.

For Views that don't draw, only vote when motion is involved.

Test: ran performance check. Improvement of around 30%

Change-Id: I73658e94d41799a7d88fcc568061f8d46c90c757
parent 8bf4990b
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(int width, int height) {
    protected int calculateFrameRateCategory() {
        if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) {
            return FRAME_RATE_CATEGORY_NORMAL;
        }
        return super.calculateFrameRateCategory(width, height);
        return super.calculateFrameRateCategory();
    }

    @UnsupportedAppUsage
+87 −77
Original line number Diff line number Diff line
@@ -5695,17 +5695,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private ViewTranslationResponse mViewTranslationResponse;
    /**
     * Threshold size for something to be considered a small area update (in DP).
     * This is the dimension for both width and height.
     * The multiplier for mAttachInfo.mSmallSizePixels to consider a View to be small
     * if both dimensions are smaller than this.
     */
    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 int FRAME_RATE_SQUARE_SMALL_SIZE_MULTIPLIER = 4;
    private static final long INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
    private static final int INFREQUENT_UPDATE_COUNTS = 2;
@@ -5740,6 +5733,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -4;
    private int mSizeBasedFrameRateCategoryAndReason;
    /**
     * Simple constructor to use when creating a view from code.
     *
@@ -23561,6 +23556,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            return renderNode;
        }
        mLastFrameX = mLeft + mRenderNode.getTranslationX();
        mLastFrameY = mTop + mRenderNode.getTranslationY();
        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
                || !renderNode.hasDisplayList()
                || (mRecreateDisplayList)) {
@@ -24724,8 +24722,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
        mFrameContentVelocity = -1;
        mLastFrameX = mLeft + mRenderNode.getTranslationX();
        mLastFrameY = mTop + mRenderNode.getTranslationY();
        /*
         * Draw traversal performs several drawing steps which must be executed
@@ -25474,6 +25470,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
        if (mAttachInfo != null) {
            int narrowSize = mAttachInfo.mSmallSizePixels;
            int smallSize = narrowSize * FRAME_RATE_SQUARE_SMALL_SIZE_MULTIPLIER;
            if (newWidth <= narrowSize || newHeight <= narrowSize
                    || (newWidth <= smallSize && newHeight <= smallSize)) {
                int category = toolkitFrameRateBySizeReadOnly()
                        ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
                mSizeBasedFrameRateCategoryAndReason = category | FRAME_RATE_CATEGORY_REASON_SMALL;
            } else {
                int category = toolkitFrameRateDefaultNormalReadOnly()
                        ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
                mSizeBasedFrameRateCategoryAndReason = category | FRAME_RATE_CATEGORY_REASON_LARGE;
            }
        }
        onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
        if (mOverlay != null) {
            mOverlay.getOverlayView().setRight(newWidth);
@@ -32039,6 +32050,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         */
        int mSensitiveViewsCount;
        /**
         * The size used for a View to be considered small for the purposes of using
         * low refresh rate by default. This is the size in one direction, so a long, thin
         * item like a progress bar can be compared to this.
         */
        final int mSmallSizePixels;
        /**
         * Creates a new set of attachment information with the specified
         * events handler and thread.
@@ -32056,6 +32074,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            mHandler = handler;
            mRootCallbacks = effectPlayer;
            mTreeObserver = new ViewTreeObserver(context);
            mSmallSizePixels = (int) (context.getResources().getDisplayMetrics().density * 10);
        }
        void increaseSensitiveViewsCount() {
@@ -33784,28 +33803,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *
     * @hide
     */
    protected int calculateFrameRateCategory(int width, int height) {
    protected int calculateFrameRateCategory() {
        if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
                < INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            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)) {
                int category = toolkitFrameRateBySizeReadOnly()
                        ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
                return category | FRAME_RATE_CATEGORY_REASON_SMALL;
            } else {
                int category = toolkitFrameRateDefaultNormalReadOnly()
                        ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
                return category | FRAME_RATE_CATEGORY_REASON_LARGE;
            }
            return mSizeBasedFrameRateCategoryAndReason;
        }
        if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
@@ -33823,7 +33824,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            if (viewVelocityApi()) {
                float velocity = mFrameContentVelocity;
                if (velocity < 0f) {
                    velocity = calculateVelocity();
                    // This current calculation is very simple. If something on the screen moved,
                    // then it votes for the highest velocity. If it doesn't move, then return 0.
                    RenderNode renderNode = mRenderNode;
                    float x = mLeft + renderNode.getTranslationX();
                    float y = mTop + renderNode.getTranslationY();
                    velocity = (!Float.isNaN(mLastFrameX) && (x != mLastFrameX || y != mLastFrameY))
                            ? 100_000f : 0f;
                }
                if (velocity > 0f) {
                    float frameRate = convertVelocityToFrameRate(velocity);
@@ -33831,13 +33839,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    return;
                }
            }
            if (!willNotDraw()) {
                if (sToolkitMetricsForFrameRateDecisionFlagValue) {
                    float sizePercentage = getSizePercentage();
                    viewRootImpl.recordViewPercentage(sizePercentage);
                }
                int frameRateCategory;
                if (Float.isNaN(mPreferredFrameRate)) {
                frameRateCategory = calculateFrameRateCategory(width, height);
                    if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
                            < INFREQUENT_UPDATE_INTERVAL_MILLIS && mAttachInfo != null) {
                        frameRateCategory = mSizeBasedFrameRateCategoryAndReason;
                    } else if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
                        frameRateCategory =
                                FRAME_RATE_CATEGORY_NORMAL
                                        | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
                    } else {
                        frameRateCategory = mLastFrameRateCategory;
                    }
                } else if (mPreferredFrameRate < 0) {
                    if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
                        frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE
@@ -33870,6 +33889,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                mLastFrameRateCategory = frameRateCategory;
            }
        }
    }
    private float convertVelocityToFrameRate(float velocityPps) {
        float density = getResources().getDisplayMetrics().density;
@@ -33878,16 +33898,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return Math.min(140f, 60f + (10f * (float) Math.floor(velocityDps / 300f)));
    }
    private float calculateVelocity() {
        // This current calculation is very simple. If something on the screen moved, then
        // it votes for the highest velocity. If it doesn't move, then return 0.
        float x = mLeft + mRenderNode.getTranslationX();
        float y = mTop + mRenderNode.getTranslationY();
        return (!Float.isNaN(mLastFrameX) && (x != mLastFrameX || y != mLastFrameY))
                ? 100_000f : 0f;
    }
    /**
     * Set the current velocity of the View, we only track positive value.
     * We will use the velocity information to adjust the frame rate when applicable.
+2 −4
Original line number Diff line number Diff line
@@ -12631,12 +12631,11 @@ public final class ViewRootImpl implements ViewParent,
        }
        mHasInvalidation = true;
        checkIdleness();
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)
                && mPreferredFrameRateCategory != oldCategory
        if (mPreferredFrameRateCategory != oldCategory
                && mPreferredFrameRateCategory == frameRateCategory
        ) {
            mFrameRateCategoryChangeReason = reason;
            mFrameRateCategoryView = view.getClass().getSimpleName();
            mFrameRateCategoryView = view == null ? "null" : view.getClass().getSimpleName();
        }
    }
@@ -12803,7 +12802,6 @@ public final class ViewRootImpl implements ViewParent,
        // uncomment this when we are ready for enabling dVRR
        // return sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced;
        return false;
    }
    private void checkIdleness() {
+10 −10
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ 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.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_DEFAULT_NORMAL_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
@@ -137,8 +136,8 @@ public class ViewFrameRateTest {
        mActivityRule.runOnUiThread(() -> {
            float density = mActivity.getResources().getDisplayMetrics().density;
            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
            layoutParams.height = (int) (40 * density);
            layoutParams.width = (int) (40 * density);
            layoutParams.height = 4 * ((int) (10 * density));
            layoutParams.width = 4 * ((int) (10 * density));
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });
@@ -212,8 +211,8 @@ public class ViewFrameRateTest {
        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);
            layoutParams.height = 4 * ((int) (10 * density));
            layoutParams.width = 4 * ((int) Math.ceil(10 * density)) + 1;
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });
@@ -237,8 +236,8 @@ public class ViewFrameRateTest {
        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);
            layoutParams.height = 4 * ((int) Math.ceil(10 * density)) + 1;
            layoutParams.width = 4 * ((int) (10 * density));
            mMovingView.setLayoutParams(layoutParams);
            mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown);
        });
@@ -256,13 +255,14 @@ public class ViewFrameRateTest {
    }

    @Test
    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
            FLAG_TOOLKIT_FRAME_RATE_DEFAULT_NORMAL_READ_ONLY})
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void defaultNormal() throws Throwable {
        waitForFrameRateCategoryToSettle();
        mActivityRule.runOnUiThread(() -> {
            mMovingView.invalidate();
            assertEquals(FRAME_RATE_CATEGORY_NORMAL,
            int expected = toolkitFrameRateDefaultNormalReadOnly()
                    ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
            assertEquals(expected,
                    mViewRoot.getPreferredFrameRateCategory());
        });
    }
+1 −1
Original line number Diff line number Diff line
@@ -667,7 +667,7 @@ public class ViewRootImplTest {
    }

    /**
     * Test how values of the frame rate cateogry are aggregated.
     * Test how values of the frame rate category are aggregated.
     * It should take the max value among all of the voted categories per frame.
     */
    @Test