Loading core/java/android/view/TextureView.java +23 −1 Original line number Diff line number Diff line Loading @@ -202,6 +202,14 @@ public class TextureView extends View { // Set by native code, do not write! @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private long mNativeWindow; // Used for VRR detecting "normal" frame rate rather than "high". This is the previous // interval for drawing. This can be removed when NORMAL is the default rate for Views. // (b/329156944) private long mMinusTwoFrameIntervalMillis = 0; // Used for VRR detecting "normal" frame rate rather than "high". This is the last // frame time for drawing. This can be removed when NORMAL is the default rate for Views. // (b/329156944) private long mLastFrameTimeMillis = 0; /** * Creates a new TextureView. Loading Loading @@ -890,12 +898,26 @@ public class TextureView extends View { */ @Override protected int calculateFrameRateCategory() { if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) { long now = getDrawingTime(); // This isn't necessary when the default frame rate is NORMAL (b/329156944) if (mMinusTwoFrameIntervalMillis > 15 && (now - mLastFrameTimeMillis) > 15) { return FRAME_RATE_CATEGORY_NORMAL; } return super.calculateFrameRateCategory(); } /** * @hide */ @Override protected void votePreferredFrameRate() { super.votePreferredFrameRate(); // This isn't necessary when the default frame rate is NORMAL (b/329156944) long now = getDrawingTime(); mMinusTwoFrameIntervalMillis = now - mLastFrameTimeMillis; mLastFrameTimeMillis = now; } @UnsupportedAppUsage private final SurfaceTexture.OnFrameAvailableListener mUpdateListener = surfaceTexture -> { Loading core/java/android/view/View.java +95 −115 Original line number Diff line number Diff line Loading @@ -1133,7 +1133,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int FOCUSABLE_MASK = 0x00000011; /** * This view will adjust its padding to fit sytem windows (e.g. status bar) * This view will adjust its padding to fit system windows (e.g. status bar) */ private static final int FITS_SYSTEM_WINDOWS = 0x00000002; Loading Loading @@ -5764,23 +5764,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static final float MAX_FRAME_RATE = 140; private static final int 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; /** * @hide */ protected int mMinusOneFrameIntervalMillis = 0; /** * @hide */ protected int mMinusTwoFrameIntervalMillis = 0; private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) Loading Loading @@ -23651,7 +23638,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (sToolkitSetFrameRateReadOnlyFlagValue && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); updateInfrequentCount(); } mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN; Loading Loading @@ -33903,15 +33889,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ protected int calculateFrameRateCategory() { if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { return mSizeBasedFrameRateCategoryAndReason; } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; int category; switch (getViewRootImpl().intermittentUpdateState()) { case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> category = FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT -> category = mSizeBasedFrameRateCategoryAndReason; default -> category = mLastFrameRateCategory; } return mLastFrameRateCategory; return category; } /** Loading @@ -33922,15 +33908,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected void votePreferredFrameRate() { // use toolkitSetFrameRate flag to gate the change ViewRootImpl viewRootImpl = getViewRootImpl(); int width = mRight - mLeft; int height = mBottom - mTop; if (viewRootImpl != null && (width != 0 && height != 0)) { if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) { if (viewRootImpl == null) { return; // can't vote if not connected } float velocity = mFrameContentVelocity; float frameRate = mPreferredFrameRate; ViewParent parent = mParent; if (velocity <= 0 && Float.isNaN(frameRate)) { // The most common case is when nothing is set, so this special case is called // often. if (mAttachInfo.mViewVelocityApi && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) && viewRootImpl.shouldCheckFrameRate(false) && parent instanceof View && ((View) parent).mFrameContentVelocity <= 0) { viewRootImpl.votePreferredFrameRate(MAX_FRAME_RATE, FRAME_RATE_COMPATIBILITY_GTE); } if (!willNotDraw() && viewRootImpl.shouldCheckFrameRateCategory()) { int frameRateCategory = calculateFrameRateCategory(); int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK; int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK; viewRootImpl.votePreferredFrameRateCategory(category, reason, this); mLastFrameRateCategory = frameRateCategory; } return; } if (viewRootImpl.shouldCheckFrameRate(frameRate > 0f)) { float velocityFrameRate = 0f; if (mAttachInfo.mViewVelocityApi) { float velocity = mFrameContentVelocity; if (velocity < 0f && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) Loading @@ -33944,27 +33950,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, velocityFrameRate = convertVelocityToFrameRate(velocity); } } if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) { int compatibility = FRAME_RATE_COMPATIBILITY_GTE; float frameRate = velocityFrameRate; if (mPreferredFrameRate > velocityFrameRate) { if (velocityFrameRate > 0f || frameRate > 0f) { int compatibility; if (frameRate >= velocityFrameRate) { compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; frameRate = mPreferredFrameRate; } else { compatibility = FRAME_RATE_COMPATIBILITY_GTE; frameRate = velocityFrameRate; } viewRootImpl.votePreferredFrameRate(frameRate, compatibility); } } if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) { if (!willNotDraw() && viewRootImpl.shouldCheckFrameRateCategory()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { int width = mRight - mLeft; int height = mBottom - mTop; float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); } int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; if (Float.isNaN(mPreferredFrameRate)) { if (Float.isNaN(frameRate)) { frameRateCategory = calculateFrameRateCategory(); } else if (mPreferredFrameRate < 0) { switch ((int) mPreferredFrameRate) { } else if (frameRate < 0) { switch ((int) frameRate) { case (int) REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE -> frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE | FRAME_RATE_CATEGORY_REASON_REQUESTED; Loading Loading @@ -33993,7 +34003,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLastFrameRateCategory = frameRateCategory; } } } private float convertVelocityToFrameRate(float velocityPps) { float density = mAttachInfo.mDensity; Loading Loading @@ -34074,33 +34083,4 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return 0; } /** * This function is mainly used for migrating infrequent layer logic * from SurfaceFlinger to Toolkit. * The infrequent layer 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() { if (!willNotDraw()) { long currentTimeMillis = getDrawingTime(); int timeIntervalMillis = (int) Math.min(Integer.MAX_VALUE, currentTimeMillis - mLastUpdateTimeMillis); mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis; mMinusOneFrameIntervalMillis = timeIntervalMillis; mLastUpdateTimeMillis = currentTimeMillis; if (mMinusTwoFrameIntervalMillis >= 30 && timeIntervalMillis < 2) { return; } if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) { mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1; } else { mInfrequentUpdateCount = 0; } } } } core/java/android/view/ViewRootImpl.java +110 −29 Original line number Diff line number Diff line Loading @@ -392,6 +392,26 @@ public final class ViewRootImpl implements ViewParent, private static final int UNSET_SYNC_ID = -1; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; /** * The {@link #intermittentUpdateState()} value when the ViewRootImpl isn't intermittent. */ public static final int INTERMITTENT_STATE_NOT_INTERMITTENT = 1; /** * The {@link #intermittentUpdateState()} value when the ViewRootImpl is transitioning either * to or from intermittent to not intermittent. This indicates that the frame rate shouldn't * change. */ public static final int INTERMITTENT_STATE_IN_TRANSITION = -1; /** * The {@link #intermittentUpdateState()} value when the ViewRootImpl is intermittent. */ public static final int INTERMITTENT_STATE_INTERMITTENT = 0; /** * Minimum time to wait before reporting changes to keep clear areas. */ Loading Loading @@ -623,6 +643,15 @@ public final class ViewRootImpl implements ViewParent, // Is the stylus pointer icon enabled private final boolean mIsStylusPointerIconEnabled; // VRR check for number of infrequent updates private int mInfrequentUpdateCount = 0; // VRR time of last update private long mLastUpdateTimeMillis = 0; // VRR interval since the previous private int mMinusOneFrameIntervalMillis = 0; // VRR interval between the previous and the frame before private int mMinusTwoFrameIntervalMillis = 0; /** * Update the Choreographer's FrameInfo object with the timing information for the current * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next Loading Loading @@ -1068,6 +1097,7 @@ public final class ViewRootImpl implements ViewParent, // Used to check if there is a message in the message queue // for idleness handling. private boolean mHasIdledMessage = false; private boolean mDrawnThisFrame = false; // Used to check if there is a conflict between different frame rate voting. // Take 24 and 30 as an example, 24 is not a divisor of 30. // We consider there is a conflict. Loading Loading @@ -4220,6 +4250,9 @@ 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. if (mDrawnThisFrame) { mDrawnThisFrame = false; updateInfrequentCount(); setCategoryFromCategoryCounts(); setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); Loading @@ -4240,6 +4273,7 @@ public final class ViewRootImpl implements ViewParent, mIsFrameRateConflicted = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } } private void createSyncIfNeeded() { // WMS requested sync already started or there's nothing needing to sync Loading Loading @@ -12516,6 +12550,15 @@ public final class ViewRootImpl implements ViewParent, * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts. */ private void setCategoryFromCategoryCounts() { switch (mPreferredFrameRateCategory) { case FRAME_RATE_CATEGORY_LOW -> mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_NORMAL -> mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH_HINT -> mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } if (mFrameRateCategoryHighCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; } else if (mFrameRateCategoryHighHintCount > 0) { Loading Loading @@ -12661,21 +12704,31 @@ public final class ViewRootImpl implements ViewParent, */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) public void votePreferredFrameRateCategory(int frameRateCategory, int reason, View view) { switch (frameRateCategory) { case FRAME_RATE_CATEGORY_LOW -> mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_NORMAL -> mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH_HINT -> mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } if (frameRateCategory > mPreferredFrameRateCategory) { mPreferredFrameRateCategory = frameRateCategory; mFrameRateCategoryChangeReason = reason; mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); // mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); } mHasInvalidation = true; mDrawnThisFrame = true; } /** * Returns {@link #INTERMITTENT_STATE_INTERMITTENT} when the ViewRootImpl has only been * updated intermittently, {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} when it is * not updated intermittently, and {@link #INTERMITTENT_STATE_IN_TRANSITION} when it * is transitioning between {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} and * {@link #INTERMITTENT_STATE_INTERMITTENT}. */ int intermittentUpdateState() { if (mMinusOneFrameIntervalMillis + mMinusTwoFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { return INTERMITTENT_STATE_NOT_INTERMITTENT; } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return INTERMITTENT_STATE_INTERMITTENT; } return INTERMITTENT_STATE_IN_TRANSITION; } /** Loading Loading @@ -12730,6 +12783,8 @@ public final class ViewRootImpl implements ViewParent, mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; mFrameRateCategoryView = null; mHasInvalidation = true; mDrawnThisFrame = true; return; } } Loading Loading @@ -12761,6 +12816,7 @@ public final class ViewRootImpl implements ViewParent, mPreferredFrameRate = nextFrameRate; mFrameRateCompatibility = nextFrameRateCompatibility; mHasInvalidation = true; mDrawnThisFrame = true; } /** Loading Loading @@ -12894,4 +12950,29 @@ public final class ViewRootImpl implements ViewParent, mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE); mHandler.removeMessages(MSG_FRAME_RATE_SETTING); } /** * This function is mainly used for migrating infrequent layer logic * from SurfaceFlinger to Toolkit. * The infrequent layer 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 = mAttachInfo.mDrawingTime; int timeIntervalMillis = (int) Math.min(Integer.MAX_VALUE, currentTimeMillis - mLastUpdateTimeMillis); mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis; mMinusOneFrameIntervalMillis = timeIntervalMillis; mLastUpdateTimeMillis = currentTimeMillis; if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) { int infrequentUpdateCount = mInfrequentUpdateCount; mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS ? infrequentUpdateCount : infrequentUpdateCount + 1; } else { mInfrequentUpdateCount = 0; } } } Loading
core/java/android/view/TextureView.java +23 −1 Original line number Diff line number Diff line Loading @@ -202,6 +202,14 @@ public class TextureView extends View { // Set by native code, do not write! @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private long mNativeWindow; // Used for VRR detecting "normal" frame rate rather than "high". This is the previous // interval for drawing. This can be removed when NORMAL is the default rate for Views. // (b/329156944) private long mMinusTwoFrameIntervalMillis = 0; // Used for VRR detecting "normal" frame rate rather than "high". This is the last // frame time for drawing. This can be removed when NORMAL is the default rate for Views. // (b/329156944) private long mLastFrameTimeMillis = 0; /** * Creates a new TextureView. Loading Loading @@ -890,12 +898,26 @@ public class TextureView extends View { */ @Override protected int calculateFrameRateCategory() { if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) { long now = getDrawingTime(); // This isn't necessary when the default frame rate is NORMAL (b/329156944) if (mMinusTwoFrameIntervalMillis > 15 && (now - mLastFrameTimeMillis) > 15) { return FRAME_RATE_CATEGORY_NORMAL; } return super.calculateFrameRateCategory(); } /** * @hide */ @Override protected void votePreferredFrameRate() { super.votePreferredFrameRate(); // This isn't necessary when the default frame rate is NORMAL (b/329156944) long now = getDrawingTime(); mMinusTwoFrameIntervalMillis = now - mLastFrameTimeMillis; mLastFrameTimeMillis = now; } @UnsupportedAppUsage private final SurfaceTexture.OnFrameAvailableListener mUpdateListener = surfaceTexture -> { Loading
core/java/android/view/View.java +95 −115 Original line number Diff line number Diff line Loading @@ -1133,7 +1133,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int FOCUSABLE_MASK = 0x00000011; /** * This view will adjust its padding to fit sytem windows (e.g. status bar) * This view will adjust its padding to fit system windows (e.g. status bar) */ private static final int FITS_SYSTEM_WINDOWS = 0x00000002; Loading Loading @@ -5764,23 +5764,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static final float MAX_FRAME_RATE = 140; private static final int 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; /** * @hide */ protected int mMinusOneFrameIntervalMillis = 0; /** * @hide */ protected int mMinusTwoFrameIntervalMillis = 0; private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) Loading Loading @@ -23651,7 +23638,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (sToolkitSetFrameRateReadOnlyFlagValue && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); updateInfrequentCount(); } mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN; Loading Loading @@ -33903,15 +33889,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ protected int calculateFrameRateCategory() { if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { return mSizeBasedFrameRateCategoryAndReason; } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; int category; switch (getViewRootImpl().intermittentUpdateState()) { case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> category = FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT -> category = mSizeBasedFrameRateCategoryAndReason; default -> category = mLastFrameRateCategory; } return mLastFrameRateCategory; return category; } /** Loading @@ -33922,15 +33908,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected void votePreferredFrameRate() { // use toolkitSetFrameRate flag to gate the change ViewRootImpl viewRootImpl = getViewRootImpl(); int width = mRight - mLeft; int height = mBottom - mTop; if (viewRootImpl != null && (width != 0 && height != 0)) { if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) { if (viewRootImpl == null) { return; // can't vote if not connected } float velocity = mFrameContentVelocity; float frameRate = mPreferredFrameRate; ViewParent parent = mParent; if (velocity <= 0 && Float.isNaN(frameRate)) { // The most common case is when nothing is set, so this special case is called // often. if (mAttachInfo.mViewVelocityApi && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) && viewRootImpl.shouldCheckFrameRate(false) && parent instanceof View && ((View) parent).mFrameContentVelocity <= 0) { viewRootImpl.votePreferredFrameRate(MAX_FRAME_RATE, FRAME_RATE_COMPATIBILITY_GTE); } if (!willNotDraw() && viewRootImpl.shouldCheckFrameRateCategory()) { int frameRateCategory = calculateFrameRateCategory(); int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK; int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK; viewRootImpl.votePreferredFrameRateCategory(category, reason, this); mLastFrameRateCategory = frameRateCategory; } return; } if (viewRootImpl.shouldCheckFrameRate(frameRate > 0f)) { float velocityFrameRate = 0f; if (mAttachInfo.mViewVelocityApi) { float velocity = mFrameContentVelocity; if (velocity < 0f && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) Loading @@ -33944,27 +33950,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, velocityFrameRate = convertVelocityToFrameRate(velocity); } } if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) { int compatibility = FRAME_RATE_COMPATIBILITY_GTE; float frameRate = velocityFrameRate; if (mPreferredFrameRate > velocityFrameRate) { if (velocityFrameRate > 0f || frameRate > 0f) { int compatibility; if (frameRate >= velocityFrameRate) { compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; frameRate = mPreferredFrameRate; } else { compatibility = FRAME_RATE_COMPATIBILITY_GTE; frameRate = velocityFrameRate; } viewRootImpl.votePreferredFrameRate(frameRate, compatibility); } } if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) { if (!willNotDraw() && viewRootImpl.shouldCheckFrameRateCategory()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { int width = mRight - mLeft; int height = mBottom - mTop; float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); } int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; if (Float.isNaN(mPreferredFrameRate)) { if (Float.isNaN(frameRate)) { frameRateCategory = calculateFrameRateCategory(); } else if (mPreferredFrameRate < 0) { switch ((int) mPreferredFrameRate) { } else if (frameRate < 0) { switch ((int) frameRate) { case (int) REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE -> frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE | FRAME_RATE_CATEGORY_REASON_REQUESTED; Loading Loading @@ -33993,7 +34003,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLastFrameRateCategory = frameRateCategory; } } } private float convertVelocityToFrameRate(float velocityPps) { float density = mAttachInfo.mDensity; Loading Loading @@ -34074,33 +34083,4 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return 0; } /** * This function is mainly used for migrating infrequent layer logic * from SurfaceFlinger to Toolkit. * The infrequent layer 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() { if (!willNotDraw()) { long currentTimeMillis = getDrawingTime(); int timeIntervalMillis = (int) Math.min(Integer.MAX_VALUE, currentTimeMillis - mLastUpdateTimeMillis); mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis; mMinusOneFrameIntervalMillis = timeIntervalMillis; mLastUpdateTimeMillis = currentTimeMillis; if (mMinusTwoFrameIntervalMillis >= 30 && timeIntervalMillis < 2) { return; } if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) { mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1; } else { mInfrequentUpdateCount = 0; } } } }
core/java/android/view/ViewRootImpl.java +110 −29 Original line number Diff line number Diff line Loading @@ -392,6 +392,26 @@ public final class ViewRootImpl implements ViewParent, private static final int UNSET_SYNC_ID = -1; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; /** * The {@link #intermittentUpdateState()} value when the ViewRootImpl isn't intermittent. */ public static final int INTERMITTENT_STATE_NOT_INTERMITTENT = 1; /** * The {@link #intermittentUpdateState()} value when the ViewRootImpl is transitioning either * to or from intermittent to not intermittent. This indicates that the frame rate shouldn't * change. */ public static final int INTERMITTENT_STATE_IN_TRANSITION = -1; /** * The {@link #intermittentUpdateState()} value when the ViewRootImpl is intermittent. */ public static final int INTERMITTENT_STATE_INTERMITTENT = 0; /** * Minimum time to wait before reporting changes to keep clear areas. */ Loading Loading @@ -623,6 +643,15 @@ public final class ViewRootImpl implements ViewParent, // Is the stylus pointer icon enabled private final boolean mIsStylusPointerIconEnabled; // VRR check for number of infrequent updates private int mInfrequentUpdateCount = 0; // VRR time of last update private long mLastUpdateTimeMillis = 0; // VRR interval since the previous private int mMinusOneFrameIntervalMillis = 0; // VRR interval between the previous and the frame before private int mMinusTwoFrameIntervalMillis = 0; /** * Update the Choreographer's FrameInfo object with the timing information for the current * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next Loading Loading @@ -1068,6 +1097,7 @@ public final class ViewRootImpl implements ViewParent, // Used to check if there is a message in the message queue // for idleness handling. private boolean mHasIdledMessage = false; private boolean mDrawnThisFrame = false; // Used to check if there is a conflict between different frame rate voting. // Take 24 and 30 as an example, 24 is not a divisor of 30. // We consider there is a conflict. Loading Loading @@ -4220,6 +4250,9 @@ 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. if (mDrawnThisFrame) { mDrawnThisFrame = false; updateInfrequentCount(); setCategoryFromCategoryCounts(); setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); Loading @@ -4240,6 +4273,7 @@ public final class ViewRootImpl implements ViewParent, mIsFrameRateConflicted = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } } private void createSyncIfNeeded() { // WMS requested sync already started or there's nothing needing to sync Loading Loading @@ -12516,6 +12550,15 @@ public final class ViewRootImpl implements ViewParent, * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts. */ private void setCategoryFromCategoryCounts() { switch (mPreferredFrameRateCategory) { case FRAME_RATE_CATEGORY_LOW -> mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_NORMAL -> mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH_HINT -> mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } if (mFrameRateCategoryHighCount > 0) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; } else if (mFrameRateCategoryHighHintCount > 0) { Loading Loading @@ -12661,21 +12704,31 @@ public final class ViewRootImpl implements ViewParent, */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) public void votePreferredFrameRateCategory(int frameRateCategory, int reason, View view) { switch (frameRateCategory) { case FRAME_RATE_CATEGORY_LOW -> mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_NORMAL -> mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH_HINT -> mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT; case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } if (frameRateCategory > mPreferredFrameRateCategory) { mPreferredFrameRateCategory = frameRateCategory; mFrameRateCategoryChangeReason = reason; mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); // mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); } mHasInvalidation = true; mDrawnThisFrame = true; } /** * Returns {@link #INTERMITTENT_STATE_INTERMITTENT} when the ViewRootImpl has only been * updated intermittently, {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} when it is * not updated intermittently, and {@link #INTERMITTENT_STATE_IN_TRANSITION} when it * is transitioning between {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} and * {@link #INTERMITTENT_STATE_INTERMITTENT}. */ int intermittentUpdateState() { if (mMinusOneFrameIntervalMillis + mMinusTwoFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { return INTERMITTENT_STATE_NOT_INTERMITTENT; } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return INTERMITTENT_STATE_INTERMITTENT; } return INTERMITTENT_STATE_IN_TRANSITION; } /** Loading Loading @@ -12730,6 +12783,8 @@ public final class ViewRootImpl implements ViewParent, mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; mFrameRateCategoryView = null; mHasInvalidation = true; mDrawnThisFrame = true; return; } } Loading Loading @@ -12761,6 +12816,7 @@ public final class ViewRootImpl implements ViewParent, mPreferredFrameRate = nextFrameRate; mFrameRateCompatibility = nextFrameRateCompatibility; mHasInvalidation = true; mDrawnThisFrame = true; } /** Loading Loading @@ -12894,4 +12950,29 @@ public final class ViewRootImpl implements ViewParent, mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE); mHandler.removeMessages(MSG_FRAME_RATE_SETTING); } /** * This function is mainly used for migrating infrequent layer logic * from SurfaceFlinger to Toolkit. * The infrequent layer 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 = mAttachInfo.mDrawingTime; int timeIntervalMillis = (int) Math.min(Integer.MAX_VALUE, currentTimeMillis - mLastUpdateTimeMillis); mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis; mMinusOneFrameIntervalMillis = timeIntervalMillis; mLastUpdateTimeMillis = currentTimeMillis; if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) { int infrequentUpdateCount = mInfrequentUpdateCount; mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS ? infrequentUpdateCount : infrequentUpdateCount + 1; } else { mInfrequentUpdateCount = 0; } } }