Loading core/java/android/view/View.java +31 −24 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly; import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly; import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly; import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly; import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly; import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; Loading Loading @@ -2443,6 +2444,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, toolkitFrameRateSmallUsesPercentReadOnly(); private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue = toolkitFrameRateViewEnablingReadOnly(); private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue = toolkitFrameRateVelocityMappingReadOnly(); // Used to set frame rate compatibility. @Surface.FrameRateCompatibility int mFrameRateCompatibility = Loading Loading @@ -5739,6 +5742,8 @@ 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; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; Loading Loading @@ -20824,12 +20829,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); } // Reset content capture caches mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; mContentCaptureSessionCached = false; Loading Loading @@ -20932,11 +20931,6 @@ 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 && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); } mParent.onDescendantInvalidated(this, this); } } Loading Loading @@ -23624,12 +23618,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return renderNode; } mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN; // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); updateInfrequentCount(); } mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN; if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.hasDisplayList() || (mRecreateDisplayList)) { Loading Loading @@ -23694,6 +23691,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } mFrameContentVelocity = -1; return renderNode; } Loading Loading @@ -24792,8 +24791,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int privateFlags = mPrivateFlags; mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; mFrameContentVelocity = -1; /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: Loading Loading @@ -33888,32 +33885,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mLastFrameRateCategory; } private void votePreferredFrameRate() { /** * Used to vote the preferred frame rate and frame rate category to ViewRootImpl * * @hide */ protected void votePreferredFrameRate() { // use toolkitSetFrameRate flag to gate the change ViewRootImpl viewRootImpl = getViewRootImpl(); int width = mRight - mLeft; int height = mBottom - mTop; float alpha = mTransformationInfo != null ? mTransformationInfo.mAlpha : 1; int visibility = mViewFlags & VISIBILITY_MASK; if (viewRootImpl != null && (width != 0 && height != 0) && alpha != 0 && visibility == View.VISIBLE ) { if (viewRootImpl != null && (width != 0 && height != 0)) { if (mAttachInfo.mViewVelocityApi) { float velocity = mFrameContentVelocity; int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN; if (velocity < 0f && (mPrivateFlags4 & mask) == mask) { float frameRate = 0; if (velocity < 0f && (mPrivateFlags4 & mask) == mask && 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; } if (velocity > 0f) { float frameRate = convertVelocityToFrameRate(velocity); if (sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { frameRate = convertVelocityToFrameRate(velocity); } viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE); return; } } if (!willNotDraw()) { if (!willNotDraw() && isDirty()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); Loading Loading @@ -33962,7 +33969,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, float density = mAttachInfo.mDensity; float velocityDps = velocityPps / density; // Choose a frame rate in increments of 10fps return Math.min(140f, 60f + (10f * (float) Math.floor(velocityDps / 300f))); return Math.min(MAX_FRAME_RATE, 60f + (10f * (float) Math.floor(velocityDps / 300f))); } /** core/java/android/view/ViewRootImpl.java +2 −0 Original line number Diff line number Diff line Loading @@ -4227,6 +4227,7 @@ public final class ViewRootImpl implements ViewParent, ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; mPreferredFrameRate = -1; mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; mIsFrameRateConflicted = false; } Loading Loading @@ -12496,6 +12497,7 @@ public final class ViewRootImpl implements ViewParent, private void setPreferredFrameRateCategory(int preferredFrameRateCategory) { if (!shouldSetFrameRateCategory() || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mPreferredFrameRate > 0 && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) { return; } core/tests/coretests/src/android/view/ViewFrameRateTest.java +116 −32 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package android.view; import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; import static android.view.Surface.FRAME_RATE_CATEGORY_LOW; import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; Loading @@ -27,18 +26,23 @@ import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly; import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly; import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly; import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.annotation.NonNull; import android.app.Activity; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.DisplayMetrics; import android.widget.FrameLayout; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; Loading Loading @@ -69,6 +73,8 @@ public class ViewFrameRateTest { private Activity mActivity; private View mMovingView; private ViewRootImpl mViewRoot; private CountDownLatch mAfterDrawLatch; private Throwable mAfterDrawThrowable; @Before public void setUp() throws Throwable { Loading @@ -82,23 +88,34 @@ public class ViewFrameRateTest { parent = parent.getParent(); } mViewRoot = (ViewRootImpl) parent; mAfterDrawThrowable = null; } @UiThreadTest @Test @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void frameRateChangesWhenContentMoves() { public void frameRateChangesWhenContentMoves() throws Throwable { waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(100); float frameRate = mViewRoot.getPreferredFrameRate(); runAfterDraw(() -> { if (toolkitFrameRateVelocityMappingReadOnly()) { float frameRate = mViewRoot.getLastPreferredFrameRate(); assertTrue(frameRate > 0); } else { assertEquals(FRAME_RATE_CATEGORY_HIGH, mViewRoot.getLastPreferredFrameRateCategory()); } }); }); waitForAfterDraw(); } @UiThreadTest @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void firstFrameNoMovement() { assertEquals(0f, mViewRoot.getPreferredFrameRate(), 0f); assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f); } @Test Loading Loading @@ -141,9 +158,34 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1f); mMovingView.invalidate(); assertEquals(60f, mViewRoot.getPreferredFrameRate(), 0f); assertEquals(FRAME_RATE_COMPATIBILITY_GTE, mViewRoot.getFrameRateCompatibility()); runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f)); }); waitForAfterDraw(); } @Test @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API, FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void velocityWithChildMovement() throws Throwable { FrameLayout frameLayout = new FrameLayout(mActivity); mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mActivity.setContentView(frameLayout, fullSize); if (mMovingView.getParent() instanceof ViewGroup) { ((ViewGroup) mMovingView.getParent()).removeView(mMovingView); } frameLayout.addView(mMovingView, fullSize); }); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { frameLayout.setFrameContentVelocity(1f); mMovingView.offsetTopAndBottom(100); runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f)); }); waitForAfterDraw(); } @Test Loading @@ -161,23 +203,9 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1_000_000_000f); mMovingView.invalidate(); assertEquals(140f, mViewRoot.getPreferredFrameRate(), 0f); assertEquals(FRAME_RATE_COMPATIBILITY_GTE, mViewRoot.getFrameRateCompatibility()); }); } private void waitForFrameRateCategoryToSettle() throws Throwable { for (int i = 0; i < 5; i++) { final CountDownLatch drawLatch = new CountDownLatch(1); // Now that it is small, any invalidation should have a normal category mActivityRule.runOnUiThread(() -> { mMovingView.invalidate(); mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch::countDown); runAfterDraw(() -> assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f)); }); assertTrue(drawLatch.await(1, TimeUnit.SECONDS)); } waitForAfterDraw(); } @Test Loading Loading @@ -211,8 +239,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -245,8 +275,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -279,8 +311,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -313,8 +347,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -347,8 +383,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading @@ -367,8 +405,54 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw(() -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } private void runAfterDraw(@NonNull Runnable runnable) { Handler handler = new Handler(Looper.getMainLooper()); mAfterDrawLatch = new CountDownLatch(1); ViewTreeObserver.OnDrawListener listener = new ViewTreeObserver.OnDrawListener() { @Override public void onDraw() { handler.postAtFrontOfQueue(() -> { mMovingView.getViewTreeObserver().removeOnDrawListener(this); try { runnable.run(); } catch (Throwable t) { mAfterDrawThrowable = t; } mAfterDrawLatch.countDown(); }); } }; mMovingView.getViewTreeObserver().addOnDrawListener(listener); } private void waitForAfterDraw() throws Throwable { assertTrue(mAfterDrawLatch.await(1, TimeUnit.SECONDS)); if (mAfterDrawThrowable != null) { throw mAfterDrawThrowable; } } private void waitForFrameRateCategoryToSettle() throws Throwable { for (int i = 0; i < 5 || mViewRoot.getIsFrameRateBoosting(); i++) { final CountDownLatch drawLatch = new CountDownLatch(1); // Now that it is small, any invalidation should have a normal category ViewTreeObserver.OnDrawListener listener = drawLatch::countDown; mActivityRule.runOnUiThread(() -> { mMovingView.invalidate(); mMovingView.getViewTreeObserver().addOnDrawListener(listener); }); assertTrue(drawLatch.await(1, TimeUnit.SECONDS)); mActivityRule.runOnUiThread( () -> mMovingView.getViewTreeObserver().removeOnDrawListener(listener)); } } } core/tests/coretests/src/android/view/ViewRootImplTest.java +336 −189 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/View.java +31 −24 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly; import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly; import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly; import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly; import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly; import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; Loading Loading @@ -2443,6 +2444,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, toolkitFrameRateSmallUsesPercentReadOnly(); private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue = toolkitFrameRateViewEnablingReadOnly(); private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue = toolkitFrameRateVelocityMappingReadOnly(); // Used to set frame rate compatibility. @Surface.FrameRateCompatibility int mFrameRateCompatibility = Loading Loading @@ -5739,6 +5742,8 @@ 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; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; Loading Loading @@ -20824,12 +20829,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); } // Reset content capture caches mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; mContentCaptureSessionCached = false; Loading Loading @@ -20932,11 +20931,6 @@ 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 && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); } mParent.onDescendantInvalidated(this, this); } } Loading Loading @@ -23624,12 +23618,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return renderNode; } mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN; // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { votePreferredFrameRate(); updateInfrequentCount(); } mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN; if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.hasDisplayList() || (mRecreateDisplayList)) { Loading Loading @@ -23694,6 +23691,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } mFrameContentVelocity = -1; return renderNode; } Loading Loading @@ -24792,8 +24791,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int privateFlags = mPrivateFlags; mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; mFrameContentVelocity = -1; /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: Loading Loading @@ -33888,32 +33885,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mLastFrameRateCategory; } private void votePreferredFrameRate() { /** * Used to vote the preferred frame rate and frame rate category to ViewRootImpl * * @hide */ protected void votePreferredFrameRate() { // use toolkitSetFrameRate flag to gate the change ViewRootImpl viewRootImpl = getViewRootImpl(); int width = mRight - mLeft; int height = mBottom - mTop; float alpha = mTransformationInfo != null ? mTransformationInfo.mAlpha : 1; int visibility = mViewFlags & VISIBILITY_MASK; if (viewRootImpl != null && (width != 0 && height != 0) && alpha != 0 && visibility == View.VISIBLE ) { if (viewRootImpl != null && (width != 0 && height != 0)) { if (mAttachInfo.mViewVelocityApi) { float velocity = mFrameContentVelocity; int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN; if (velocity < 0f && (mPrivateFlags4 & mask) == mask) { float frameRate = 0; if (velocity < 0f && (mPrivateFlags4 & mask) == mask && 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; } if (velocity > 0f) { float frameRate = convertVelocityToFrameRate(velocity); if (sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { frameRate = convertVelocityToFrameRate(velocity); } viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE); return; } } if (!willNotDraw()) { if (!willNotDraw() && isDirty()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); Loading Loading @@ -33962,7 +33969,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, float density = mAttachInfo.mDensity; float velocityDps = velocityPps / density; // Choose a frame rate in increments of 10fps return Math.min(140f, 60f + (10f * (float) Math.floor(velocityDps / 300f))); return Math.min(MAX_FRAME_RATE, 60f + (10f * (float) Math.floor(velocityDps / 300f))); } /**
core/java/android/view/ViewRootImpl.java +2 −0 Original line number Diff line number Diff line Loading @@ -4227,6 +4227,7 @@ public final class ViewRootImpl implements ViewParent, ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; mPreferredFrameRate = -1; mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; mIsFrameRateConflicted = false; } Loading Loading @@ -12496,6 +12497,7 @@ public final class ViewRootImpl implements ViewParent, private void setPreferredFrameRateCategory(int preferredFrameRateCategory) { if (!shouldSetFrameRateCategory() || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mPreferredFrameRate > 0 && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) { return; }
core/tests/coretests/src/android/view/ViewFrameRateTest.java +116 −32 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package android.view; import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; import static android.view.Surface.FRAME_RATE_CATEGORY_LOW; import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; Loading @@ -27,18 +26,23 @@ import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly; import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly; import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly; import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.annotation.NonNull; import android.app.Activity; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.DisplayMetrics; import android.widget.FrameLayout; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; Loading Loading @@ -69,6 +73,8 @@ public class ViewFrameRateTest { private Activity mActivity; private View mMovingView; private ViewRootImpl mViewRoot; private CountDownLatch mAfterDrawLatch; private Throwable mAfterDrawThrowable; @Before public void setUp() throws Throwable { Loading @@ -82,23 +88,34 @@ public class ViewFrameRateTest { parent = parent.getParent(); } mViewRoot = (ViewRootImpl) parent; mAfterDrawThrowable = null; } @UiThreadTest @Test @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void frameRateChangesWhenContentMoves() { public void frameRateChangesWhenContentMoves() throws Throwable { waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(100); float frameRate = mViewRoot.getPreferredFrameRate(); runAfterDraw(() -> { if (toolkitFrameRateVelocityMappingReadOnly()) { float frameRate = mViewRoot.getLastPreferredFrameRate(); assertTrue(frameRate > 0); } else { assertEquals(FRAME_RATE_CATEGORY_HIGH, mViewRoot.getLastPreferredFrameRateCategory()); } }); }); waitForAfterDraw(); } @UiThreadTest @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void firstFrameNoMovement() { assertEquals(0f, mViewRoot.getPreferredFrameRate(), 0f); assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f); } @Test Loading Loading @@ -141,9 +158,34 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1f); mMovingView.invalidate(); assertEquals(60f, mViewRoot.getPreferredFrameRate(), 0f); assertEquals(FRAME_RATE_COMPATIBILITY_GTE, mViewRoot.getFrameRateCompatibility()); runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f)); }); waitForAfterDraw(); } @Test @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API, FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void velocityWithChildMovement() throws Throwable { FrameLayout frameLayout = new FrameLayout(mActivity); mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mActivity.setContentView(frameLayout, fullSize); if (mMovingView.getParent() instanceof ViewGroup) { ((ViewGroup) mMovingView.getParent()).removeView(mMovingView); } frameLayout.addView(mMovingView, fullSize); }); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { frameLayout.setFrameContentVelocity(1f); mMovingView.offsetTopAndBottom(100); runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f)); }); waitForAfterDraw(); } @Test Loading @@ -161,23 +203,9 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1_000_000_000f); mMovingView.invalidate(); assertEquals(140f, mViewRoot.getPreferredFrameRate(), 0f); assertEquals(FRAME_RATE_COMPATIBILITY_GTE, mViewRoot.getFrameRateCompatibility()); }); } private void waitForFrameRateCategoryToSettle() throws Throwable { for (int i = 0; i < 5; i++) { final CountDownLatch drawLatch = new CountDownLatch(1); // Now that it is small, any invalidation should have a normal category mActivityRule.runOnUiThread(() -> { mMovingView.invalidate(); mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch::countDown); runAfterDraw(() -> assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f)); }); assertTrue(drawLatch.await(1, TimeUnit.SECONDS)); } waitForAfterDraw(); } @Test Loading Loading @@ -211,8 +239,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -245,8 +275,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -279,8 +311,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -313,8 +347,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading Loading @@ -347,8 +383,10 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw( () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } @Test Loading @@ -367,8 +405,54 @@ public class ViewFrameRateTest { mMovingView.invalidate(); int expected = toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; assertEquals(expected, mViewRoot.getPreferredFrameRateCategory()); runAfterDraw(() -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); } private void runAfterDraw(@NonNull Runnable runnable) { Handler handler = new Handler(Looper.getMainLooper()); mAfterDrawLatch = new CountDownLatch(1); ViewTreeObserver.OnDrawListener listener = new ViewTreeObserver.OnDrawListener() { @Override public void onDraw() { handler.postAtFrontOfQueue(() -> { mMovingView.getViewTreeObserver().removeOnDrawListener(this); try { runnable.run(); } catch (Throwable t) { mAfterDrawThrowable = t; } mAfterDrawLatch.countDown(); }); } }; mMovingView.getViewTreeObserver().addOnDrawListener(listener); } private void waitForAfterDraw() throws Throwable { assertTrue(mAfterDrawLatch.await(1, TimeUnit.SECONDS)); if (mAfterDrawThrowable != null) { throw mAfterDrawThrowable; } } private void waitForFrameRateCategoryToSettle() throws Throwable { for (int i = 0; i < 5 || mViewRoot.getIsFrameRateBoosting(); i++) { final CountDownLatch drawLatch = new CountDownLatch(1); // Now that it is small, any invalidation should have a normal category ViewTreeObserver.OnDrawListener listener = drawLatch::countDown; mActivityRule.runOnUiThread(() -> { mMovingView.invalidate(); mMovingView.getViewTreeObserver().addOnDrawListener(listener); }); assertTrue(drawLatch.await(1, TimeUnit.SECONDS)); mActivityRule.runOnUiThread( () -> mMovingView.getViewTreeObserver().removeOnDrawListener(listener)); } } }
core/tests/coretests/src/android/view/ViewRootImplTest.java +336 −189 File changed.Preview size limit exceeded, changes collapsed. Show changes