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

Commit 487fc020 authored by Shane's avatar Shane
Browse files

Fix frame rate idleness and TextureView video play logic

Previously we check whether setFrameRateCategoy is called to detect idleness. However, if the frame rate cateogry remain the same when View are invalided, setFrameRateCategoy will not be called. Therefore, moving the logic to votePreferredFrameRate and votePreferredFrameRateCategory. Also, increase the timeout for revaluating idleness from 500 ms to 1000 ms.

Morever, update video play logic in TextureView to do the following:
1. Store the last 3 invalidates time - FT1, FT2, FT3.
2. If FT2-FT1 > 15ms && FT3-FT2 > 15ms -> vote for NORMAL category

Also, don't take the too frequent invalidation (interval between 2 ms)
into account when determining whether it's a infrequent or frequent
update.

Bug: 322398850
Test: atest ViewRootImplTest
Change-Id: I6181f8d25dcd0922cbc2921d1e2972ce347e8028
parent b24bcc88
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view;

import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -883,6 +885,17 @@ public class TextureView extends View {
        mListener = listener;
    }

    /**
     * @hide
     */
    @Override
    protected int calculateFrameRateCategory(float sizePercentage) {
        if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) {
            return FRAME_RATE_CATEGORY_NORMAL;
        }
        return super.calculateFrameRateCategory(sizePercentage);
    }

    @UnsupportedAppUsage
    private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
            surfaceTexture -> {
+19 −5
Original line number Diff line number Diff line
@@ -5639,9 +5639,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private int mInfrequentUpdateCount = 0;
    private long mLastUpdateTimeMillis = 0;
    private long mMinusOneFrameIntervalMillis = 0;
    private long mMinusTwoFrameIntervalMillis = 0;
    private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
    /**
     * @hide
     */
    protected long mMinusOneFrameIntervalMillis = 0;
    /**
     * @hide
     */
    protected long mMinusTwoFrameIntervalMillis = 0;
    private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN;
@@ -33505,7 +33511,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return (float) viewSize / screenSize;
    }
    private int calculateFrameRateCategory(float sizePercentage) {
    /**
     * Used to calculate the frame rate category of a View.
     *
     * @hide
     */
    protected int calculateFrameRateCategory(float sizePercentage) {
        if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
                < INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
@@ -33639,7 +33650,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis;
        mMinusOneFrameIntervalMillis = timeIntervalMillis;
        mLastUpdateTimeMillis = currentTimeMillis;
        if (mMinusOneFrameIntervalMillis - mMinusTwoFrameIntervalMillis >= 30
                && timeIntervalMillis < 2) {
            return;
        }
        if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
                        ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1;
+17 −11
Original line number Diff line number Diff line
@@ -1040,7 +1040,7 @@ public final class ViewRootImpl implements ViewParent,
    // time for checking idle status periodically.
    private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
    // time for revaluating the idle status before lowering the frame rate.
    private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
    private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 1000;
    // time for evaluating the interval between current time and
    // the time when frame rate was set previously.
    private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
@@ -6507,6 +6507,7 @@ public final class ViewRootImpl implements ViewParent,
                        mHasInvalidation = false;
                        mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
                                FRAME_RATE_IDLENESS_REEVALUATE_TIME);
                        mHasIdledMessage = true;
                    }
                    break;
                case MSG_REFRESH_POINTER_ICON:
@@ -12330,14 +12331,6 @@ public final class ViewRootImpl implements ViewParent,
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
            // Check where the display is idled periodically.
            // If so, set the frame rate category to NO_PREFERENCE
            mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
                    FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
            mHasIdledMessage = true;
        }
    }
    private void setPreferredFrameRate(float preferredFrameRate) {
@@ -12351,7 +12344,8 @@ public final class ViewRootImpl implements ViewParent,
                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                    Trace.traceBegin(
                            Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
                                + preferredFrameRate);
                                + preferredFrameRate + " compatibility "
                                + mFrameRateCompatibility);
                }
                mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
                    mFrameRateCompatibility).applyAsyncUnsafe();
@@ -12377,7 +12371,7 @@ public final class ViewRootImpl implements ViewParent,
    private boolean shouldSetFrameRate() {
        // use toolkitSetFrameRate flag to gate the change
        return mSurface.isValid() && mPreferredFrameRate > 0
        return mSurface.isValid() && mPreferredFrameRate >= 0
                && shouldEnableDvrr() && !mIsFrameRateConflicted;
    }
@@ -12418,6 +12412,7 @@ public final class ViewRootImpl implements ViewParent,
            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
        }
        mHasInvalidation = true;
        checkIdleness();
    }
    /**
@@ -12460,6 +12455,7 @@ public final class ViewRootImpl implements ViewParent,
            mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
                    FRAME_RATE_SETTING_REEVALUATE_TIME);
        }
        checkIdleness();
    }
    /**
@@ -12565,4 +12561,14 @@ public final class ViewRootImpl implements ViewParent,
    private boolean shouldEnableDvrr() {
        return sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced;
    }
    private void checkIdleness() {
        if (!mHasIdledMessage) {
            // Check where the display is idled periodically.
            // If so, set the frame rate category to NO_PREFERENCE
            mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
                    FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
            mHasIdledMessage = true;
        }
    }
}
+54 −0
Original line number Diff line number Diff line
@@ -1017,6 +1017,60 @@ public class ViewRootImplTest {
        assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), true);
    }

    /**
     * Test the TextureView heuristic:
     * 1. Store the last 3 invalidates time - FT1, FT2, FT3.
     * 2. If FT2-FT1 > 15ms && FT3-FT2 > 15ms -> vote for NORMAL category
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_applyTextureViewHeuristic() throws InterruptedException {
        final long delay = 30L;

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

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

        ViewRootImpl viewRootImpl = view.getViewRootImpl();

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

         // reset the frame rate category counts
        for (int i = 0; i < 5; i++) {
            Thread.sleep(delay);
            sInstrumentation.runOnMainSync(() -> {
                view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
                view.invalidate();
            });
            sInstrumentation.waitForIdleSync();
        }

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

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