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

Commit c3328d64 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Stop creating PrecomputedText in StaticLayout

No performance regressions are expected

android.text.PrecomputedTextPerfTest:
  create NoStyled Hyphenation                  : 17,849,966 -> 17,858,570: (+0.0%)
  create NoStyled Hyphenation WidthOnly        : 17,814,338 -> 17,801,205: (-0.1%)
  create NoStyled NoHyphenation                :  7,123,449 ->  7,068,308: (-0.8%)
  create NoStyled NoHyphenation WidthOnly      :  7,108,169 ->  7,074,908: (-0.5%)
  create Styled Hyphenation                    : 12,179,203 -> 12,131,020: (-0.4%)
  create Styled Hyphenation WidthOnly          : 12,112,347 -> 12,241,311: (+1.1%)
  create Styled NoHyphenation                  : 11,870,126 -> 11,880,442: (+0.1%)
  create Styled NoHyphenation WidthOnly        : 11,836,742 -> 11,860,292: (+0.2%)

android.text.StaticLayoutPerfTest:
  create PrecomputedText Balanced Hyphenation  :    697,713 ->    691,148: (-0.9%)
  create PrecomputedText Balanced NoHyphenation:    517,113 ->    498,106: (-3.7%)
  create PrecomputedText Greedy Hyphenation    :    468,243 ->    455,015: (-2.8%)
  create PrecomputedText Greedy NoHyphenation  :    479,514 ->    461,617: (-3.7%)
  create RandomText Balanced Hyphenation       : 17,183,044 -> 17,049,811: (-0.8%)
  create RandomText Balanced NoHyphenation     :  7,183,745 ->  7,025,070: (-2.2%)
  create RandomText Greedy Hyphenation         :  7,130,841 ->  6,995,785: (-1.9%)
  create RandomText Greedy NoHyphenation       :  7,122,398 ->  7,037,074: (-1.2%)

  draw PrecomputedText NoStyled                :    520,306 ->    551,465: (+6.0%)
  draw PrecomputedText NoStyled WithoutCache   :    545,773 ->    566,956: (+3.9%)
  draw PrecomputedText Styled                  :    826,044 ->    838,979: (+1.6%)
  draw PrecomputedText Styled WithoutCache     :    829,958 ->    841,749: (+1.4%)
  draw RandomText NoStyled                     :    537,079 ->    545,428: (+1.6%)
  draw RandomText NoStyled WithoutCache        :  6,473,166 ->  6,445,194: (-0.4%)
  draw RandomText Styled                       :    995,033 ->  1,015,913: (+2.1%)
  draw RandomText Styled WithoutCache          :  2,725,313 ->  2,770,604: (+1.7%)

android.widget.TextViewPrecomputedTextPerfTest:
  newLayout PrecomputedText                    :    754,311 ->    718,130: (-4.8%)
  newLayout PrecomputedText Selectable         : 17,716,239 -> 17,484,046: (-1.3%)
  newLayout RandomText                         : 16,657,952 -> 16,511,625: (-0.9%)
  newLayout RandomText Selectable              : 17,675,222 -> 17,520,653: (-0.9%)
  onDraw PrecomputedText                       :  1,307,123 ->  1,280,009: (-2.1%)
  onDraw PrecomputedText Selectable            : 17,613,031 -> 17,404,379: (-1.2%)
  onDraw RandomText                            : 17,369,256 -> 17,295,363: (-0.4%)
  onDraw RandomText Selectable                 : 18,207,392 -> 18,077,660: (-0.7%)
  onMeasure PrecomputedText                    :    748,537 ->    739,128: (-1.3%)
  onMeasure PrecomputedText Selectable         : 17,842,953 -> 17,784,459: (-0.3%)
  onMeasure RandomText                         : 16,633,454 -> 16,549,182: (-0.5%)
  onMeasure RandomText Selectable              : 18,022,286 -> 17,873,919: (-0.8%)
  setText PrecomputedText                      :    120,769 ->    119,496: (-1.1%)
  setText PrecomputedText Selectable           :    162,411 ->    150,809: (-7.1%)
  setText RandomText                           :     11,096 ->     10,956: (-1.3%)
  setText RandomText Selectable                :     48,852 ->     48,593: (-0.5%)

Bug: 72998298
Test: atest CtsWidgetTestCases:EditTextTest
    CtsWidgetTestCases:TextViewFadingEdgeTest
    FrameworksCoreTests:TextViewFallbackLineSpacingTest
    FrameworksCoreTests:TextViewTest FrameworksCoreTests:TypefaceTest
    CtsGraphicsTestCases:TypefaceTest CtsWidgetTestCases:TextViewTest
    CtsTextTestCases FrameworksCoreTests:android.text
    CtsWidgetTestCases:TextViewPrecomputedTextTest

Change-Id: I3af758ecc5a15975c4e59c6378faf7c14c3bd65b
parent 3483bc7d
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -119,8 +119,12 @@ public class PrecomputedTextMemoryUsageTest {
        // Report median of randomly generated PrecomputedText.
        for (int i = 0; i < TRIAL_COUNT; ++i) {
            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
                .getMemoryUsage();
            PrecomputedText.ParagraphInfo[] paragraphInfo =
                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
            memories[i] = 0;
            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
                memories[i] += info.measured.getMemoryUsage();
            }
        }
        reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
    }
@@ -136,8 +140,12 @@ public class PrecomputedTextMemoryUsageTest {
        // Report median of randomly generated PrecomputedText.
        for (int i = 0; i < TRIAL_COUNT; ++i) {
            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
                .getMemoryUsage();
            PrecomputedText.ParagraphInfo[] paragraphInfo =
                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
            memories[i] = 0;
            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
                memories[i] += info.measured.getMemoryUsage();
            }
        }
        reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
    }
+1 −1
Original line number Diff line number Diff line
@@ -675,7 +675,7 @@ public class MeasuredParagraph {
    /**
     * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
     */
    @IntRange(from = 0) int getMemoryUsage() {
    public @IntRange(from = 0) int getMemoryUsage() {
        return nGetMemoryUsage(mNativePtr);
    }

+50 −41
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package android.text;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.IntArray;

import com.android.internal.util.Preconditions;

@@ -267,6 +266,22 @@ public class PrecomputedText implements Spanned {
        }
    };

    /** @hide */
    public static class ParagraphInfo {
        public final @IntRange(from = 0) int paragraphEnd;
        public final @NonNull MeasuredParagraph measured;

        /**
         * @param paraEnd the end offset of this paragraph
         * @param measured a measured paragraph
         */
        public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) {
            this.paragraphEnd = paraEnd;
            this.measured = measured;
        }
    };


    // The original text.
    private final @NonNull SpannedString mText;

@@ -278,11 +293,8 @@ public class PrecomputedText implements Spanned {

    private final @NonNull Params mParams;

    // The measured paragraph texts.
    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;

    // The sorted paragraph end offsets.
    private final @NonNull int[] mParagraphBreakPoints;
    // The list of measured paragraph info.
    private final @NonNull ParagraphInfo[] mParagraphInfo;

    /**
     * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
@@ -293,28 +305,25 @@ public class PrecomputedText implements Spanned {
     * </p>
     *
     * @param text the text to be measured
     * @param param parameters that define how text will be precomputed
     * @param params parameters that define how text will be precomputed
     * @return A {@link PrecomputedText}
     */
    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
        return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
        ParagraphInfo[] paraInfo = createMeasuredParagraphs(
                text, params, 0, text.length(), true /* computeLayout */);
        return new PrecomputedText(text, 0, text.length(), params, paraInfo);
    }

    /** @hide */
    public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
            @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
        return createInternal(text, param, start, end, false /* compute width only */);
    }

    private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
    public static ParagraphInfo[] createMeasuredParagraphs(
            @NonNull CharSequence text, @NonNull Params params,
            @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
        Preconditions.checkNotNull(text);
        Preconditions.checkNotNull(param);
        final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
                && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
        ArrayList<ParagraphInfo> result = new ArrayList<>();

        final IntArray paragraphEnds = new IntArray();
        final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
        Preconditions.checkNotNull(text);
        Preconditions.checkNotNull(params);
        final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
                && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;

        int paraEnd = 0;
        for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
@@ -327,27 +336,22 @@ public class PrecomputedText implements Spanned {
                paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
            }

            paragraphEnds.add(paraEnd);
            measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
                    param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
                    needHyphenation, computeLayout, null /* no recycle */));
            result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
                    params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
                    needHyphenation, computeLayout, null /* no recycle */)));
        }

        return new PrecomputedText(text, start, end, param,
                                measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
                                paragraphEnds.toArray());
        return result.toArray(new ParagraphInfo[result.size()]);
    }

    // Use PrecomputedText.create instead.
    private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
            @IntRange(from = 0) int end, @NonNull Params param,
            @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
            @IntRange(from = 0) int end, @NonNull Params params,
            @NonNull ParagraphInfo[] paraInfo) {
        mText = new SpannedString(text);
        mStart = start;
        mEnd = end;
        mParams = param;
        mMeasuredParagraphs = measuredTexts;
        mParagraphBreakPoints = paragraphBreakPoints;
        mParams = params;
        mParagraphInfo = paraInfo;
    }

    /**
@@ -384,7 +388,7 @@ public class PrecomputedText implements Spanned {
     * Returns the count of paragraphs.
     */
    public @IntRange(from = 0) int getParagraphCount() {
        return mParagraphBreakPoints.length;
        return mParagraphInfo.length;
    }

    /**
@@ -392,7 +396,7 @@ public class PrecomputedText implements Spanned {
     */
    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
        return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1);
    }

    /**
@@ -400,12 +404,17 @@ public class PrecomputedText implements Spanned {
     */
    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
        return mParagraphBreakPoints[paraIndex];
        return mParagraphInfo[paraIndex].paragraphEnd;
    }

    /** @hide */
    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
        return mMeasuredParagraphs[paraIndex];
        return mParagraphInfo[paraIndex].measured;
    }

    /** @hide */
    public @NonNull ParagraphInfo[] getParagraphInfo() {
        return mParagraphInfo;
    }

    /**
@@ -425,13 +434,13 @@ public class PrecomputedText implements Spanned {
    public int findParaIndex(@IntRange(from = 0) int pos) {
        // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
        //       layout support to StaticLayout.
        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
            if (pos < mParagraphBreakPoints[i]) {
        for (int i = 0; i < mParagraphInfo.length; ++i) {
            if (pos < mParagraphInfo[i].paragraphEnd) {
                return i;
            }
        }
        throw new IndexOutOfBoundsException(
            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
            "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd
            + ", gave " + pos);
    }

+14 −16
Original line number Diff line number Diff line
@@ -651,31 +651,29 @@ public class StaticLayout extends Layout {
                b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                indents, mLeftPaddings, mRightPaddings);

        PrecomputedText measured = null;
        final Spanned spanned;
        PrecomputedText.ParagraphInfo[] paragraphInfo = null;
        final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
        if (source instanceof PrecomputedText) {
            measured = (PrecomputedText) source;
            if (!measured.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, b.mBreakStrategy,
                      b.mHyphenationFrequency)) {
            PrecomputedText precomputed = (PrecomputedText) source;
            if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
                      b.mBreakStrategy, b.mHyphenationFrequency)) {
                // Some parameters are different from the ones when measured text is created.
                measured = null;
                paragraphInfo = precomputed.getParagraphInfo();
            }
        }

        if (measured == null) {
        if (paragraphInfo == null) {
            final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
                    b.mBreakStrategy, b.mHyphenationFrequency);
            measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
            spanned = (source instanceof Spanned) ? (Spanned) source : null;
        } else {
            final CharSequence original = measured.getText();
            spanned = (original instanceof Spanned) ? (Spanned) original : null;
            paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart,
                    bufEnd, false /* computeLayout */);
        }

        try {
            for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
                final int paraStart = measured.getParagraphStart(paraIndex);
                final int paraEnd = measured.getParagraphEnd(paraIndex);
            for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
                final int paraStart = paraIndex == 0
                        ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
                final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;

                int firstWidthLineCount = 1;
                int firstWidth = outerWidth;
@@ -741,7 +739,7 @@ public class StaticLayout extends Layout {
                    }
                }

                final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
                final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
                final char[] chs = measuredPara.getChars();
                final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
                final int[] fmCache = measuredPara.getFontMetrics().getRawArray();