Loading core/java/android/view/View.java +47 −26 Original line number Diff line number Diff line Loading @@ -2428,6 +2428,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int FRAME_RATE_CATEGORY_REASON_IDLE = 0x0700_0000; /** * This indicates that the frame rate category was chosen because it is currently boosting. * @hide */ public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000; /** * This indicates that the frame rate category was chosen because it is currently having * touch boost. * @hide */ public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000; /** * This indicates that the frame rate category was chosen because it is currently having * touch boost. * @hide */ public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000; private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000; /** Loading Loading @@ -5742,7 +5762,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f; private static final float MAX_FRAME_RATE = 140; static final float MAX_FRAME_RATE = 140; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; Loading Loading @@ -33897,36 +33917,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int height = mBottom - mTop; if (viewRootImpl != null && (width != 0 && height != 0)) { if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) { float velocityFrameRate = 0f; if (mAttachInfo.mViewVelocityApi) { float velocity = mFrameContentVelocity; int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN; float frameRate = 0; if (velocity < 0f && (mPrivateFlags4 & mask) == mask && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) && mParent instanceof View && ((View) mParent).mFrameContentVelocity <= 0 ) { // 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. velocity = Float.POSITIVE_INFINITY; frameRate = MAX_FRAME_RATE; // This current calculation is very simple. If something on the screen // moved, then it votes for the highest velocity. velocityFrameRate = MAX_FRAME_RATE; } else if (velocity > 0f) { velocityFrameRate = convertVelocityToFrameRate(velocity); } if (velocity > 0f) { if (sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { frameRate = convertVelocityToFrameRate(velocity); } viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE); return; if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) { int compatibility = FRAME_RATE_COMPATIBILITY_GTE; float frameRate = velocityFrameRate; if (mPreferredFrameRate > velocityFrameRate) { compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; frameRate = mPreferredFrameRate; } viewRootImpl.votePreferredFrameRate(frameRate, compatibility); } } if (!willNotDraw() && isDirty()) { if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); } int frameRateCategory; int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; if (Float.isNaN(mPreferredFrameRate)) { frameRateCategory = calculateFrameRateCategory(); } else if (mPreferredFrameRate < 0) { Loading @@ -33951,10 +33976,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, | FRAME_RATE_CATEGORY_REASON_INVALID; } } } else { viewRootImpl.votePreferredFrameRate(mPreferredFrameRate, mFrameRateCompatibility); return; } int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK; core/java/android/view/ViewRootImpl.java +107 −93 Original line number Diff line number Diff line Loading @@ -35,14 +35,18 @@ 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_BOOST; import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED; import static android.view.View.FRAME_RATE_CATEGORY_REASON_IDLE; 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.FRAME_RATE_CATEGORY_REASON_TOUCH; import static android.view.View.FRAME_RATE_CATEGORY_REASON_UNKNOWN; import static android.view.View.FRAME_RATE_CATEGORY_REASON_VELOCITY; import static android.view.View.MAX_FRAME_RATE; 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; Loading Loading @@ -4191,8 +4195,15 @@ public final class ViewRootImpl implements ViewParent, // For the variable refresh rate project. // We set the preferred frame rate and frame rate category at the end of performTraversals // when the values are applicable. setCategoryFromCategoryCounts(); setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); if (!mIsFrameRateConflicted) { mHandler.removeMessages(MSG_FRAME_RATE_SETTING); mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, FRAME_RATE_SETTING_REEVALUATE_TIME); } checkIdleness(); mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0 ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount; mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0 Loading @@ -4201,7 +4212,6 @@ public final class ViewRootImpl implements ViewParent, ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT; mPreferredFrameRate = -1; mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; mIsFrameRateConflicted = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } Loading Loading @@ -6630,8 +6640,6 @@ public final class ViewRootImpl implements ViewParent, */ mIsFrameRateBoosting = false; mIsTouchBoosting = false; setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory, mLastPreferredFrameRateCategory)); break; case MSG_CHECK_INVALIDATION_IDLE: if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) { Loading Loading @@ -7677,7 +7685,6 @@ public final class ViewRootImpl implements ViewParent, mWindowAttributes.type)) { // set the frame rate to the maximum value. mIsTouchBoosting = true; setPreferredFrameRateCategory(mPreferredFrameRateCategory); } /** * We want to lower the refresh rate when MotionEvent.ACTION_UP, Loading Loading @@ -12469,59 +12476,50 @@ public final class ViewRootImpl implements ViewParent, EventLog.writeEvent(LOGTAG_VIEWROOT_DRAW_EVENT, mTag, msg); } /** * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts. */ private void setCategoryFromCategoryCounts() { if (mFrameRateCategoryHighCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; } else if (mFrameRateCategoryHighHintCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; } else if (mFrameRateCategoryNormalCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; } else if (mFrameRateCategoryLowCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; } } private void setPreferredFrameRateCategory(int preferredFrameRateCategory) { if (!shouldSetFrameRateCategory() || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mPreferredFrameRate > 0 && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) { if (!shouldSetFrameRateCategory()) { return; } int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_DEFAULT; if (mIsFrameRateConflicted) { categoryFromConflictedFrameRates = mPreferredFrameRate > 60 ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL; } int frameRateCategory = mIsTouchBoosting ? FRAME_RATE_CATEGORY_HIGH_HINT : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates); int frameRateCategory; int frameRateReason; String view; // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction. // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction // (e.g., Window Initialization). if (mIsFrameRateBoosting || mInsetsAnimationRunning || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mPreferredFrameRate > 0)) { if (mIsFrameRateBoosting || mInsetsAnimationRunning) { frameRateCategory = FRAME_RATE_CATEGORY_HIGH; if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { // We've received a velocity, so we'll let the velocity control the // frame rate unless we receive additional motion events. mIsTouchBoosting = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; mFrameRateCategoryView = null; frameRateReason = FRAME_RATE_CATEGORY_REASON_BOOST; view = null; } else if (mIsTouchBoosting && preferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH_HINT) { frameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; frameRateReason = FRAME_RATE_CATEGORY_REASON_TOUCH; view = null; } else { mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } frameRateCategory = preferredFrameRateCategory; frameRateReason = mFrameRateCategoryChangeReason; view = mFrameRateCategoryView; } try { if (frameRateCategory != FRAME_RATE_CATEGORY_DEFAULT && mLastPreferredFrameRateCategory != frameRateCategory) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { String reason = reasonToString(mFrameRateCategoryChangeReason); String sourceView = mFrameRateCategoryView == null ? "-" : mFrameRateCategoryView; if (preferredFrameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) { reason = "touch boost"; sourceView = "-"; } else if (categoryFromConflictedFrameRates == frameRateCategory && frameRateCategory != preferredFrameRateCategory && mIsFrameRateConflicted ) { reason = "conflict"; sourceView = "-"; } String reason = reasonToString(frameRateReason); String sourceView = view == null ? "-" : view; String category = categoryToString(frameRateCategory); Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory " Loading Loading @@ -12565,24 +12563,21 @@ public final class ViewRootImpl implements ViewParent, case FRAME_RATE_CATEGORY_REASON_VELOCITY -> str = "velocity"; case FRAME_RATE_CATEGORY_REASON_IDLE -> str = "idle"; case FRAME_RATE_CATEGORY_REASON_UNKNOWN -> str = "unknown"; case FRAME_RATE_CATEGORY_REASON_BOOST -> str = "boost"; case FRAME_RATE_CATEGORY_REASON_TOUCH -> str = "touch"; case FRAME_RATE_CATEGORY_REASON_CONFLICTED -> str = "conflicted"; default -> str = String.valueOf(reason); } return str; } private void setPreferredFrameRate(float preferredFrameRate) { if (!shouldSetFrameRate()) { return; } if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && preferredFrameRate > 0 && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { mIsTouchBoosting = false; if (!shouldSetFrameRate() || preferredFrameRate < 0) { return; } try { if (mLastPreferredFrameRate != preferredFrameRate && preferredFrameRate >= 0) { if (mLastPreferredFrameRate != preferredFrameRate) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate " Loading @@ -12602,12 +12597,6 @@ public final class ViewRootImpl implements ViewParent, } } private void sendDelayedEmptyMessage(int message, int delayedTime) { mHandler.removeMessages(message); mHandler.sendEmptyMessageDelayed(message, delayedTime); } private boolean shouldSetFrameRateCategory() { // use toolkitSetFrameRate flag to gate the change return mSurface.isValid() && shouldEnableDvrr(); Loading Loading @@ -12645,28 +12634,34 @@ public final class ViewRootImpl implements ViewParent, case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } int oldCategory = mPreferredFrameRateCategory; // For View that votes NO_PREFERENCE if (frameRateCategory > mPreferredFrameRateCategory) { mPreferredFrameRateCategory = frameRateCategory; if (mFrameRateCategoryHighCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; } else if (mFrameRateCategoryHighHintCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; } else if (mFrameRateCategoryNormalCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; } else if (mFrameRateCategoryLowCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; mFrameRateCategoryChangeReason = reason; mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); } mHasInvalidation = true; checkIdleness(); if (mPreferredFrameRateCategory != oldCategory && mPreferredFrameRateCategory == frameRateCategory ) { mFrameRateCategoryChangeReason = reason; mFrameRateCategoryView = view == null ? "null" : view.getClass().getSimpleName(); } /** * Returns whether a View should vote for frame rate category. When the category is HIGH * already, there's no need to calculate the category on the View and vote. */ public boolean shouldCheckFrameRateCategory() { return mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH; } /** * Returns whether a View should vote for frame rate. When the maximum frame rate has already * been voted for, there's no point in calculating and voting for the frame rate. When * isDirect is false, then it will return false when the velocity-calculated frame rate * can be avoided. * @param isDirect true when the frame rate has been set directly on the View or false if * the calculation is based only on velocity. */ public boolean shouldCheckFrameRate(boolean isDirect) { return mPreferredFrameRate < MAX_FRAME_RATE || (!isDirect && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue && mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH); } /** Loading @@ -12692,24 +12687,44 @@ public final class ViewRootImpl implements ViewParent, if (frameRate <= 0) { return; } if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { mIsTouchBoosting = false; if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; mFrameRateCategoryView = null; return; } } float nextFrameRate; int nextFrameRateCompatibility; if (frameRate > mPreferredFrameRate) { nextFrameRate = frameRate; nextFrameRateCompatibility = frameRateCompatibility; } else { nextFrameRate = mPreferredFrameRate; nextFrameRateCompatibility = mFrameRateCompatibility; } if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0 && frameRate % mPreferredFrameRate != 0) { mIsFrameRateConflicted = true; mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; if (nextFrameRate > 60 && mFrameRateCategoryHighCount != FRAME_RATE_CATEGORY_COUNT) { mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED; mFrameRateCategoryView = null; } else if (mFrameRateCategoryHighCount == 0 && mFrameRateCategoryHighHintCount == 0 && mFrameRateCategoryNormalCount < FRAME_RATE_CATEGORY_COUNT) { mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED; mFrameRateCategoryView = null; } if (frameRate > mPreferredFrameRate) { mFrameRateCompatibility = frameRateCompatibility; } mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate); mPreferredFrameRate = nextFrameRate; mFrameRateCompatibility = nextFrameRateCompatibility; mHasInvalidation = true; if (!mIsFrameRateConflicted) { mHandler.removeMessages(MSG_FRAME_RATE_SETTING); mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, FRAME_RATE_SETTING_REEVALUATE_TIME); } checkIdleness(); } /** Loading Loading @@ -12779,7 +12794,6 @@ public final class ViewRootImpl implements ViewParent, private void boostFrameRate(int boostTimeOut) { mIsFrameRateBoosting = true; setPreferredFrameRateCategory(mPreferredFrameRateCategory); mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT); mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT, boostTimeOut); core/tests/coretests/src/android/view/ViewFrameRateTest.java +23 −1 Original line number Diff line number Diff line Loading @@ -203,7 +203,9 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1_000_000_000f); mMovingView.invalidate(); runAfterDraw(() -> assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f)); runAfterDraw(() -> { assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f); }); }); waitForAfterDraw(); } Loading Loading @@ -411,6 +413,26 @@ public class ViewFrameRateTest { waitForAfterDraw(); } @Test @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY }) public void frameRateAndCategory() throws Throwable { waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); mMovingView.setFrameContentVelocity(1f); mMovingView.invalidate(); runAfterDraw(() -> { assertEquals(FRAME_RATE_CATEGORY_LOW, mViewRoot.getLastPreferredFrameRateCategory()); assertEquals(60f, mViewRoot.getLastPreferredFrameRate()); }); }); waitForAfterDraw(); } private void runAfterDraw(@NonNull Runnable runnable) { Handler handler = new Handler(Looper.getMainLooper()); mAfterDrawLatch = new CountDownLatch(1); Loading core/tests/coretests/src/android/view/ViewRootImplTest.java +72 −61 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/View.java +47 −26 Original line number Diff line number Diff line Loading @@ -2428,6 +2428,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int FRAME_RATE_CATEGORY_REASON_IDLE = 0x0700_0000; /** * This indicates that the frame rate category was chosen because it is currently boosting. * @hide */ public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000; /** * This indicates that the frame rate category was chosen because it is currently having * touch boost. * @hide */ public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000; /** * This indicates that the frame rate category was chosen because it is currently having * touch boost. * @hide */ public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000; private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000; /** Loading Loading @@ -5742,7 +5762,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f; private static final float MAX_FRAME_RATE = 140; static final float MAX_FRAME_RATE = 140; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; Loading Loading @@ -33897,36 +33917,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int height = mBottom - mTop; if (viewRootImpl != null && (width != 0 && height != 0)) { if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) { float velocityFrameRate = 0f; if (mAttachInfo.mViewVelocityApi) { float velocity = mFrameContentVelocity; int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN; float frameRate = 0; if (velocity < 0f && (mPrivateFlags4 & mask) == mask && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) && mParent instanceof View && ((View) mParent).mFrameContentVelocity <= 0 ) { // 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. velocity = Float.POSITIVE_INFINITY; frameRate = MAX_FRAME_RATE; // This current calculation is very simple. If something on the screen // moved, then it votes for the highest velocity. velocityFrameRate = MAX_FRAME_RATE; } else if (velocity > 0f) { velocityFrameRate = convertVelocityToFrameRate(velocity); } if (velocity > 0f) { if (sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { frameRate = convertVelocityToFrameRate(velocity); } viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE); return; if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) { int compatibility = FRAME_RATE_COMPATIBILITY_GTE; float frameRate = velocityFrameRate; if (mPreferredFrameRate > velocityFrameRate) { compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; frameRate = mPreferredFrameRate; } viewRootImpl.votePreferredFrameRate(frameRate, compatibility); } } if (!willNotDraw() && isDirty()) { if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); } int frameRateCategory; int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; if (Float.isNaN(mPreferredFrameRate)) { frameRateCategory = calculateFrameRateCategory(); } else if (mPreferredFrameRate < 0) { Loading @@ -33951,10 +33976,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, | FRAME_RATE_CATEGORY_REASON_INVALID; } } } else { viewRootImpl.votePreferredFrameRate(mPreferredFrameRate, mFrameRateCompatibility); return; } int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
core/java/android/view/ViewRootImpl.java +107 −93 Original line number Diff line number Diff line Loading @@ -35,14 +35,18 @@ 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_BOOST; import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED; import static android.view.View.FRAME_RATE_CATEGORY_REASON_IDLE; 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.FRAME_RATE_CATEGORY_REASON_TOUCH; import static android.view.View.FRAME_RATE_CATEGORY_REASON_UNKNOWN; import static android.view.View.FRAME_RATE_CATEGORY_REASON_VELOCITY; import static android.view.View.MAX_FRAME_RATE; 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; Loading Loading @@ -4191,8 +4195,15 @@ public final class ViewRootImpl implements ViewParent, // For the variable refresh rate project. // We set the preferred frame rate and frame rate category at the end of performTraversals // when the values are applicable. setCategoryFromCategoryCounts(); setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); if (!mIsFrameRateConflicted) { mHandler.removeMessages(MSG_FRAME_RATE_SETTING); mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, FRAME_RATE_SETTING_REEVALUATE_TIME); } checkIdleness(); mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0 ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount; mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0 Loading @@ -4201,7 +4212,6 @@ public final class ViewRootImpl implements ViewParent, ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT; mPreferredFrameRate = -1; mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; mIsFrameRateConflicted = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } Loading Loading @@ -6630,8 +6640,6 @@ public final class ViewRootImpl implements ViewParent, */ mIsFrameRateBoosting = false; mIsTouchBoosting = false; setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory, mLastPreferredFrameRateCategory)); break; case MSG_CHECK_INVALIDATION_IDLE: if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) { Loading Loading @@ -7677,7 +7685,6 @@ public final class ViewRootImpl implements ViewParent, mWindowAttributes.type)) { // set the frame rate to the maximum value. mIsTouchBoosting = true; setPreferredFrameRateCategory(mPreferredFrameRateCategory); } /** * We want to lower the refresh rate when MotionEvent.ACTION_UP, Loading Loading @@ -12469,59 +12476,50 @@ public final class ViewRootImpl implements ViewParent, EventLog.writeEvent(LOGTAG_VIEWROOT_DRAW_EVENT, mTag, msg); } /** * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts. */ private void setCategoryFromCategoryCounts() { if (mFrameRateCategoryHighCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; } else if (mFrameRateCategoryHighHintCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; } else if (mFrameRateCategoryNormalCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; } else if (mFrameRateCategoryLowCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; } } private void setPreferredFrameRateCategory(int preferredFrameRateCategory) { if (!shouldSetFrameRateCategory() || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mPreferredFrameRate > 0 && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) { if (!shouldSetFrameRateCategory()) { return; } int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_DEFAULT; if (mIsFrameRateConflicted) { categoryFromConflictedFrameRates = mPreferredFrameRate > 60 ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL; } int frameRateCategory = mIsTouchBoosting ? FRAME_RATE_CATEGORY_HIGH_HINT : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates); int frameRateCategory; int frameRateReason; String view; // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction. // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction // (e.g., Window Initialization). if (mIsFrameRateBoosting || mInsetsAnimationRunning || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mPreferredFrameRate > 0)) { if (mIsFrameRateBoosting || mInsetsAnimationRunning) { frameRateCategory = FRAME_RATE_CATEGORY_HIGH; if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { // We've received a velocity, so we'll let the velocity control the // frame rate unless we receive additional motion events. mIsTouchBoosting = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; mFrameRateCategoryView = null; frameRateReason = FRAME_RATE_CATEGORY_REASON_BOOST; view = null; } else if (mIsTouchBoosting && preferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH_HINT) { frameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; frameRateReason = FRAME_RATE_CATEGORY_REASON_TOUCH; view = null; } else { mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } frameRateCategory = preferredFrameRateCategory; frameRateReason = mFrameRateCategoryChangeReason; view = mFrameRateCategoryView; } try { if (frameRateCategory != FRAME_RATE_CATEGORY_DEFAULT && mLastPreferredFrameRateCategory != frameRateCategory) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { String reason = reasonToString(mFrameRateCategoryChangeReason); String sourceView = mFrameRateCategoryView == null ? "-" : mFrameRateCategoryView; if (preferredFrameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) { reason = "touch boost"; sourceView = "-"; } else if (categoryFromConflictedFrameRates == frameRateCategory && frameRateCategory != preferredFrameRateCategory && mIsFrameRateConflicted ) { reason = "conflict"; sourceView = "-"; } String reason = reasonToString(frameRateReason); String sourceView = view == null ? "-" : view; String category = categoryToString(frameRateCategory); Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory " Loading Loading @@ -12565,24 +12563,21 @@ public final class ViewRootImpl implements ViewParent, case FRAME_RATE_CATEGORY_REASON_VELOCITY -> str = "velocity"; case FRAME_RATE_CATEGORY_REASON_IDLE -> str = "idle"; case FRAME_RATE_CATEGORY_REASON_UNKNOWN -> str = "unknown"; case FRAME_RATE_CATEGORY_REASON_BOOST -> str = "boost"; case FRAME_RATE_CATEGORY_REASON_TOUCH -> str = "touch"; case FRAME_RATE_CATEGORY_REASON_CONFLICTED -> str = "conflicted"; default -> str = String.valueOf(reason); } return str; } private void setPreferredFrameRate(float preferredFrameRate) { if (!shouldSetFrameRate()) { return; } if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && preferredFrameRate > 0 && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { mIsTouchBoosting = false; if (!shouldSetFrameRate() || preferredFrameRate < 0) { return; } try { if (mLastPreferredFrameRate != preferredFrameRate && preferredFrameRate >= 0) { if (mLastPreferredFrameRate != preferredFrameRate) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate " Loading @@ -12602,12 +12597,6 @@ public final class ViewRootImpl implements ViewParent, } } private void sendDelayedEmptyMessage(int message, int delayedTime) { mHandler.removeMessages(message); mHandler.sendEmptyMessageDelayed(message, delayedTime); } private boolean shouldSetFrameRateCategory() { // use toolkitSetFrameRate flag to gate the change return mSurface.isValid() && shouldEnableDvrr(); Loading Loading @@ -12645,28 +12634,34 @@ public final class ViewRootImpl implements ViewParent, case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } int oldCategory = mPreferredFrameRateCategory; // For View that votes NO_PREFERENCE if (frameRateCategory > mPreferredFrameRateCategory) { mPreferredFrameRateCategory = frameRateCategory; if (mFrameRateCategoryHighCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; } else if (mFrameRateCategoryHighHintCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; } else if (mFrameRateCategoryNormalCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; } else if (mFrameRateCategoryLowCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; mFrameRateCategoryChangeReason = reason; mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); } mHasInvalidation = true; checkIdleness(); if (mPreferredFrameRateCategory != oldCategory && mPreferredFrameRateCategory == frameRateCategory ) { mFrameRateCategoryChangeReason = reason; mFrameRateCategoryView = view == null ? "null" : view.getClass().getSimpleName(); } /** * Returns whether a View should vote for frame rate category. When the category is HIGH * already, there's no need to calculate the category on the View and vote. */ public boolean shouldCheckFrameRateCategory() { return mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH; } /** * Returns whether a View should vote for frame rate. When the maximum frame rate has already * been voted for, there's no point in calculating and voting for the frame rate. When * isDirect is false, then it will return false when the velocity-calculated frame rate * can be avoided. * @param isDirect true when the frame rate has been set directly on the View or false if * the calculation is based only on velocity. */ public boolean shouldCheckFrameRate(boolean isDirect) { return mPreferredFrameRate < MAX_FRAME_RATE || (!isDirect && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue && mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH); } /** Loading @@ -12692,24 +12687,44 @@ public final class ViewRootImpl implements ViewParent, if (frameRate <= 0) { return; } if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { mIsTouchBoosting = false; if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; mFrameRateCategoryView = null; return; } } float nextFrameRate; int nextFrameRateCompatibility; if (frameRate > mPreferredFrameRate) { nextFrameRate = frameRate; nextFrameRateCompatibility = frameRateCompatibility; } else { nextFrameRate = mPreferredFrameRate; nextFrameRateCompatibility = mFrameRateCompatibility; } if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0 && frameRate % mPreferredFrameRate != 0) { mIsFrameRateConflicted = true; mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; if (nextFrameRate > 60 && mFrameRateCategoryHighCount != FRAME_RATE_CATEGORY_COUNT) { mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED; mFrameRateCategoryView = null; } else if (mFrameRateCategoryHighCount == 0 && mFrameRateCategoryHighHintCount == 0 && mFrameRateCategoryNormalCount < FRAME_RATE_CATEGORY_COUNT) { mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED; mFrameRateCategoryView = null; } if (frameRate > mPreferredFrameRate) { mFrameRateCompatibility = frameRateCompatibility; } mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate); mPreferredFrameRate = nextFrameRate; mFrameRateCompatibility = nextFrameRateCompatibility; mHasInvalidation = true; if (!mIsFrameRateConflicted) { mHandler.removeMessages(MSG_FRAME_RATE_SETTING); mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, FRAME_RATE_SETTING_REEVALUATE_TIME); } checkIdleness(); } /** Loading Loading @@ -12779,7 +12794,6 @@ public final class ViewRootImpl implements ViewParent, private void boostFrameRate(int boostTimeOut) { mIsFrameRateBoosting = true; setPreferredFrameRateCategory(mPreferredFrameRateCategory); mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT); mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT, boostTimeOut);
core/tests/coretests/src/android/view/ViewFrameRateTest.java +23 −1 Original line number Diff line number Diff line Loading @@ -203,7 +203,9 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1_000_000_000f); mMovingView.invalidate(); runAfterDraw(() -> assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f)); runAfterDraw(() -> { assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f); }); }); waitForAfterDraw(); } Loading Loading @@ -411,6 +413,26 @@ public class ViewFrameRateTest { waitForAfterDraw(); } @Test @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY }) public void frameRateAndCategory() throws Throwable { waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); mMovingView.setFrameContentVelocity(1f); mMovingView.invalidate(); runAfterDraw(() -> { assertEquals(FRAME_RATE_CATEGORY_LOW, mViewRoot.getLastPreferredFrameRateCategory()); assertEquals(60f, mViewRoot.getLastPreferredFrameRate()); }); }); waitForAfterDraw(); } private void runAfterDraw(@NonNull Runnable runnable) { Handler handler = new Handler(Looper.getMainLooper()); mAfterDrawLatch = new CountDownLatch(1); Loading
core/tests/coretests/src/android/view/ViewRootImplTest.java +72 −61 File changed.Preview size limit exceeded, changes collapsed. Show changes