diff options
| author | 2024-02-01 16:44:08 +0000 | |
|---|---|---|
| committer | 2024-02-01 16:44:08 +0000 | |
| commit | e475ab6ac856ed18c36fe3ba0115901e71dc235d (patch) | |
| tree | e1c87eefda7aba7eef0f6be0834d4d7fb28f408a | |
| parent | 924b60cba68a2b4e575f978487b436c9ee435372 (diff) | |
| parent | 710f7a592a17c8ee6807334a7377fe5ddd734390 (diff) | |
Merge "UI toolkit dVRR set frame rate category as HIGH by default" into main
| -rw-r--r-- | core/java/android/view/View.java | 86 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 9 | ||||
| -rw-r--r-- | core/java/android/view/flags/refresh_rate_flags.aconfig | 24 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewRootImplTest.java | 161 |
4 files changed, 254 insertions, 26 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0d2c2ccce122..2366ff77692b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -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 - votePreferredFrameRate(); + 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 - votePreferredFrameRate(); + if (sToolkitSetFrameRateReadOnlyFlagValue) { + updateInfrequentCount(); + votePreferredFrameRate(); + } mParent.onDescendantInvalidated(this, this); } } @@ -33131,11 +33147,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private int calculateFrameRateCategory(float sizePercentage) { - if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) { - return FRAME_RATE_CATEGORY_LOW; - } else { + if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis + < INFREQUENT_UPDATE_INTERVAL_MILLIS) { + if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) { + 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() { @@ -33144,22 +33169,22 @@ 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; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) { - frameRateCateogry = FRAME_RATE_CATEGORY_LOW; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) { - frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) { - frameRateCateogry = FRAME_RATE_CATEGORY_HIGH; - } - } else { - viewRootImpl.votePreferredFrameRate(mPreferredFrameRate); + if (mPreferredFrameRate < 0) { + if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { + frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) { + frameRateCateogry = FRAME_RATE_CATEGORY_LOW; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) { + frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) { + frameRateCateogry = FRAME_RATE_CATEGORY_HIGH; } - viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); + } else { + viewRootImpl.votePreferredFrameRate(mPreferredFrameRate); } + viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); + mLastFrameRateCategory = frameRateCateogry; + if (sToolkitMetricsForFrameRateDecisionFlagValue) { viewRootImpl.recordViewPercentage(sizePercentage); } @@ -33238,4 +33263,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; + } + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ae9c10986a82..c27b2b174587 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1025,6 +1025,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 @@ -12285,7 +12292,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(); diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig index 0aa516e08697..9d613bcae29a 100644 --- a/core/java/android/view/flags/refresh_rate_flags.aconfig +++ b/core/java/android/view/flags/refresh_rate_flags.aconfig @@ -50,4 +50,28 @@ flag { description: "Feature flag for toolkit metrics collecting for frame rate decision" 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 diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index cf3eb12498ca..60769c7acd45 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -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 @@ -559,6 +563,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); |