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

Commit 710f7a59 authored by Shane's avatar Shane
Browse files

UI toolkit dVRR set frame rate category as HIGH by default

This CL made the following changes to enable the Step 1' for the UI
toolkit dVRR
project:

1. Set default frame rate category of a View as HIGH unless the size of
   the View is small (NORMAL).
2. Don't disable boost when typing.
3. Migrate the infrequent layter logic from SurfaceFlinger to Toolkit.

The infrequent layter logic includes:
- NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
- HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
- otherwise, use the previous category value.

Bug: 278731727
Test: atest ViewRootImplTest
Change-Id: I2ba2f20aa819c145eb715ae3aa609e8dae86b3b0
parent ff26a560
Loading
Loading
Loading
Loading
+67 −19
Original line number Diff line number Diff line
@@ -5537,10 +5537,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
    private static final long INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
    private static final int INFREQUENT_UPDATE_COUNTS = 2;
    // The preferred frame rate of the view that is mainly used for
    // touch boosting, view velocity handling, and TextureView.
    private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;
    private int mInfrequentUpdateCount = 0;
    private long mLastUpdateTimeMillis = 0;
    private long mMinusOneFrameIntervalMillis = 0;
    private long mMinusTwoFrameIntervalMillis = 0;
    private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -20253,7 +20263,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        // For VRR to vote the preferred frame rate
        if (sToolkitSetFrameRateReadOnlyFlagValue) {
            updateInfrequentCount();
            votePreferredFrameRate();
        }
        // Reset content capture caches
        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
@@ -20358,7 +20371,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    protected void damageInParent() {
        if (mParent != null && mAttachInfo != null) {
            // For VRR to vote the preferred frame rate
            if (sToolkitSetFrameRateReadOnlyFlagValue) {
                updateInfrequentCount();
                votePreferredFrameRate();
            }
            mParent.onDescendantInvalidated(this, this);
        }
    }
@@ -33124,11 +33140,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    private int calculateFrameRateCategory(float sizePercentage) {
        if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
                < INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
            return FRAME_RATE_CATEGORY_LOW;
                return FRAME_RATE_CATEGORY_NORMAL;
            } else {
                return FRAME_RATE_CATEGORY_HIGH;
            }
        }
        if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
            return FRAME_RATE_CATEGORY_NORMAL;
        }
        return mLastFrameRateCategory;
    }
    private void votePreferredFrameRate() {
@@ -33137,7 +33162,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        float sizePercentage = getSizePercentage();
        int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
        if (viewRootImpl != null && sizePercentage > 0) {
            if (sToolkitSetFrameRateReadOnlyFlagValue) {
            if (mPreferredFrameRate < 0) {
                if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
                    frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
@@ -33152,7 +33176,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
            }
            viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
            }
            mLastFrameRateCategory = frameRateCateogry;
            if (sToolkitMetricsForFrameRateDecisionFlagValue) {
                viewRootImpl.recordViewPercentage(sizePercentage);
            }
@@ -33231,4 +33256,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        return 0;
    }
    /**
     * This function is mainly used for migrating infrequent layer lagic
     * from SurfaceFlinger to Toolkit.
     * The infrequent layter logic includes:
     * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
     * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
     * - otherwise, use the previous category value.
     */
    private void updateInfrequentCount() {
        long currentTimeMillis = AnimationUtils.currentAnimationTimeMillis();
        long timeIntervalMillis = currentTimeMillis - mLastUpdateTimeMillis;
        mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis;
        mMinusOneFrameIntervalMillis = timeIntervalMillis;
        mLastUpdateTimeMillis = currentTimeMillis;
        if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
                        ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1;
        } else {
            mInfrequentUpdateCount = 0;
        }
    }
}
+8 −1
Original line number Diff line number Diff line
@@ -1023,6 +1023,13 @@ public final class ViewRootImpl implements ViewParent,
    // time for revaluating the idle status before lowering the frame rate.
    private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
    /*
     * the variables below are used to determine whther a dVRR feature should be enabled
     */
    // Used to determine whether to suppress boost on typing
    private boolean mShouldSuppressBoostOnTyping = false;
    /**
     * A temporary object used so relayoutWindow can return the latest SyncSeqId
     * system. The SyncSeqId system was designed to work without synchronous relayout
@@ -12258,7 +12265,7 @@ public final class ViewRootImpl implements ViewParent,
        boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
                || motionEventAction == MotionEvent.ACTION_MOVE
                || motionEventAction == MotionEvent.ACTION_UP;
        boolean undesiredType = windowType == TYPE_INPUT_METHOD;
        boolean undesiredType = windowType == TYPE_INPUT_METHOD && mShouldSuppressBoostOnTyping;
        // use toolkitSetFrameRate flag to gate the change
        return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue
                && getFrameRateBoostOnTouchEnabled();
+24 −0
Original line number Diff line number Diff line
@@ -51,3 +51,27 @@ flag {
    bug: "301343249"
    is_fixed_read_only: true
}

flag {
    name: "toolkit_frame_rate_default_normal_read_only"
    namespace: "toolkit"
    description: "Feature flag for setting frame rate category as NORMAL for default"
    bug: "239979904"
    is_fixed_read_only: true
}

flag {
    name: "toolkit_frame_rate_by_size_read_only"
    namespace: "toolkit"
    description: "Feature flag for setting frame rate category based on size"
    bug: "239979904"
    is_fixed_read_only: true
}

flag {
    name: "toolkit_frame_rate_velocity_mapping_read_only"
    namespace: "toolkit"
    description: "Feature flag for setting frame rate based on velocity"
    bug: "239979904"
    is_fixed_read_only: true
}
 No newline at end of file
+155 −6
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view;

import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
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;
@@ -475,8 +476,9 @@ public class ViewRootImplTest {
     * Also, mIsFrameRateBoosting should be true when the visibility becomes visible
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_visibility() {
    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
            FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY})
    public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() {
        View view = new View(sContext);
        attachViewToWindow(view);
        ViewRootImpl viewRootImpl = view.getViewRootImpl();
@@ -507,8 +509,9 @@ public class ViewRootImplTest {
     * <7%: FRAME_RATE_CATEGORY_LOW
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_smallSize() {
    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
            FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY})
    public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() {
        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -534,8 +537,9 @@ public class ViewRootImplTest {
     * >=7% : FRAME_RATE_CATEGORY_NORMAL
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_normalSize() {
    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
            FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY})
    public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() {
        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -558,6 +562,96 @@ public class ViewRootImplTest {
        });
    }

    /**
     * Test the value of the frame rate cateogry based on the visibility of a view
     * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
     * Visible: FRAME_RATE_CATEGORY_HIGH
     * Also, mIsFrameRateBoosting should be true when the visibility becomes visible
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh() {
        View view = new View(sContext);
        attachViewToWindow(view);
        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            view.setVisibility(View.INVISIBLE);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_NO_PREFERENCE);
        });
        sInstrumentation.waitForIdleSync();

        sInstrumentation.runOnMainSync(() -> {
            view.setVisibility(View.VISIBLE);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_HIGH);
        });
        sInstrumentation.waitForIdleSync();

        sInstrumentation.runOnMainSync(() -> {
            assertEquals(viewRootImpl.getIsFrameRateBoosting(), true);
        });
    }

    /**
     * Test the value of the frame rate cateogry based on the size of a view.
     * The current threshold value is 7% of the screen size
     * <7%: FRAME_RATE_CATEGORY_NORMAL
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh() {
        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
        wmlp.width = 1;
        wmlp.height = 1;

        sInstrumentation.runOnMainSync(() -> {
            WindowManager wm = sContext.getSystemService(WindowManager.class);
            wm.addView(view, wmlp);
        });
        sInstrumentation.waitForIdleSync();

        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
        });
    }

    /**
     * Test the value of the frame rate cateogry based on the size of a view.
     * The current threshold value is 7% of the screen size
     * >=7% : FRAME_RATE_CATEGORY_HIGH
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh() {
        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check

        sInstrumentation.runOnMainSync(() -> {
            WindowManager wm = sContext.getSystemService(WindowManager.class);
            Display display = wm.getDefaultDisplay();
            DisplayMetrics metrics = new DisplayMetrics();
            display.getMetrics(metrics);
            wmlp.width = (int) (metrics.widthPixels * 0.9);
            wmlp.height = (int) (metrics.heightPixels * 0.9);
            wm.addView(view, wmlp);
        });
        sInstrumentation.waitForIdleSync();

        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
        });
    }

    /**
     * Test how values of the frame rate cateogry are aggregated.
     * It should take the max value among all of the voted categories per frame.
@@ -701,6 +795,61 @@ public class ViewRootImplTest {
        });
    }

    /**
     * Test the logic of infrequent layer:
     * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
     * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
     * - otherwise, use the previous category value.
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws InterruptedException {
        final long delay = 200L;

        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check

        sInstrumentation.runOnMainSync(() -> {
            WindowManager wm = sContext.getSystemService(WindowManager.class);
            Display display = wm.getDefaultDisplay();
            DisplayMetrics metrics = new DisplayMetrics();
            display.getMetrics(metrics);
            wmlp.width = (int) (metrics.widthPixels * 0.9);
            wmlp.height = (int) (metrics.heightPixels * 0.9);
            wm.addView(view, wmlp);
        });
        sInstrumentation.waitForIdleSync();

        ViewRootImpl viewRootImpl = view.getViewRootImpl();

        // Frequent update
        sInstrumentation.runOnMainSync(() -> {
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_NO_PREFERENCE);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
        });

        // In transistion from frequent update to infrequent update
        Thread.sleep(delay);
        sInstrumentation.runOnMainSync(() -> {
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
        });

        // Infrequent update
        Thread.sleep(delay);
        sInstrumentation.runOnMainSync(() -> {
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
        });
    }

    @Test
    public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);