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

Commit 9adead25 authored by George Mount's avatar George Mount
Browse files

Move intermittent calculation to ViewRootImpl from View

Bug: 334006209

Intermittent calculations can be done on a per-ViewRootImpl
basis rather than per-View basis and still retain 95% of the
effect. This will save calculations on every drawn View and
make one calculation per ViewRootImpl instead.

This CL also makes a fast path for votePreferredFrameRate()
that handles when no values are set for velocity and
frame rate. This is the most common scenario and should
have a fast-path.

Test: ran performance test, ViewRootImplTest, ViewFrameRateTest
Change-Id: Ib103287a976b0b3fc7a246e5780f6d6a8be58133
parent ddc92ec0
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -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.
@@ -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 -> {
+95 −115
Original line number Diff line number Diff line
@@ -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;
@@ -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)
@@ -23642,7 +23629,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        if (sToolkitSetFrameRateReadOnlyFlagValue
                && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
            votePreferredFrameRate();
            updateInfrequentCount();
        }
        mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN;
@@ -33894,15 +33880,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;
    }
    /**
@@ -33913,15 +33899,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)
@@ -33935,27 +33941,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;
@@ -33984,7 +33994,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            mLastFrameRateCategory = frameRateCategory;
        }
    }
    }
    private float convertVelocityToFrameRate(float velocityPps) {
        float density = mAttachInfo.mDensity;
@@ -34065,33 +34074,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;
            }
        }
    }
}
+110 −29
Original line number Diff line number Diff line
@@ -391,6 +391,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.
     */
@@ -622,6 +642,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
@@ -1067,6 +1096,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.
@@ -4199,6 +4229,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);
@@ -4219,6 +4252,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
@@ -12484,6 +12518,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) {
@@ -12629,21 +12672,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;
    }
    /**
@@ -12698,6 +12751,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;
            }
        }
@@ -12729,6 +12784,7 @@ public final class ViewRootImpl implements ViewParent,
        mPreferredFrameRate = nextFrameRate;
        mFrameRateCompatibility = nextFrameRateCompatibility;
        mHasInvalidation = true;
        mDrawnThisFrame = true;
    }
    /**
@@ -12862,4 +12918,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;
        }
    }
}