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

Commit b55ac7fd authored by Seigo Nonaka's avatar Seigo Nonaka Committed by android-build-merger
Browse files

Merge "Introduce new perf test for TextView with precomputed text" into pi-dev

am: 0efaf208

Change-Id: Ie55c487cae56f61b0d1ea404843b6cbbb97f3de6
parents f686a34d 0efaf208
Loading
Loading
Loading
Loading
+370 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.widget;

import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.UNSPECIFIED;

import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.graphics.Canvas;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.PrecomputedText;
import android.text.Layout;
import android.text.BoringLayout;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.TextAppearanceSpan;
import android.view.LayoutInflater;
import android.text.TextPerfUtils;
import android.view.View.MeasureSpec;
import android.view.DisplayListCanvas;
import android.view.RenderNode;

import com.android.perftests.core.R;

import java.util.Random;
import java.util.Locale;

import org.junit.Before;
import org.junit.Test;
import org.junit.Rule;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertTrue;

import static android.widget.TextView.UNKNOWN_BORING;

@LargeTest
@RunWith(AndroidJUnit4.class)
public class TextViewPrecomputedTextPerfTest {
    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
    private static final boolean NO_STYLE_TEXT = false;
    private static final boolean STYLE_TEXT = true;

    private static TextPaint PAINT = new TextPaint();
    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();

    public TextViewPrecomputedTextPerfTest() {}

    private static class TestableTextView extends TextView {
        public TestableTextView(Context ctx) {
            super(ctx);
        }

        public void onMeasure(int w, int h) {
            super.onMeasure(w, h);
        }

        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        }
    }

    @Rule
    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    private TextPerfUtils mTextUtil = new TextPerfUtils();

    @Before
    public void setUp() {
        mTextUtil.resetRandom(0 /* seed */);
    }

    private static Context getContext() {
        return InstrumentationRegistry.getTargetContext();
    }

    @Test
    public void testNewLayout_RandomText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
        while (state.keepRunning()) {
            state.pauseTiming();
            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
            final TextView textView = new TextView(getContext());
            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
            textView.setText(text);
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
                TEXT_WIDTH, false);
        }
    }

    @Test
    public void testNewLayout_PrecomputedText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TextView textView = new TextView(getContext());
            textView.setTextMetricsParams(params);
            textView.setText(text);
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
                TEXT_WIDTH, false);
        }
    }

    @Test
    public void testNewLayout_PrecomputedText_Selectable() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TextView textView = new TextView(getContext());
            textView.setTextIsSelectable(true);
            textView.setTextMetricsParams(params);
            textView.setText(text);
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
                TEXT_WIDTH, false);
        }
    }

    @Test
    public void testSetText_RandomText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
        while (state.keepRunning()) {
            state.pauseTiming();
            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
            final TextView textView = new TextView(getContext());
            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.setText(text);
        }
    }

    @Test
    public void testSetText_PrecomputedText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TextView textView = new TextView(getContext());
            textView.setTextMetricsParams(params);
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.setText(text);
        }
    }

    @Test
    public void testSetText_PrecomputedText_Selectable() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TextView textView = new TextView(getContext());
            textView.setTextIsSelectable(true);
            textView.setTextMetricsParams(params);
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.setText(text);
        }
    }

    @Test
    public void testOnMeasure_RandomText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
        while (state.keepRunning()) {
            state.pauseTiming();
            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
            final TestableTextView textView = new TestableTextView(getContext());
            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
            textView.setText(text);
            textView.nullLayouts();
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.onMeasure(width, height);
        }
    }

    @Test
    public void testOnMeasure_PrecomputedText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TestableTextView textView = new TestableTextView(getContext());
            textView.setTextMetricsParams(params);
            textView.setText(text);
            textView.nullLayouts();
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.onMeasure(width, height);
        }
    }

    @Test
    public void testOnMeasure_PrecomputedText_Selectable() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TestableTextView textView = new TestableTextView(getContext());
            textView.setTextIsSelectable(true);
            textView.setTextMetricsParams(params);
            textView.setText(text);
            textView.nullLayouts();
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.onMeasure(width, height);
        }
    }

    @Test
    public void testOnDraw_RandomText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
        final RenderNode node = RenderNode.create("benchmark", null);
        while (state.keepRunning()) {
            state.pauseTiming();
            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
            final TestableTextView textView = new TestableTextView(getContext());
            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
            textView.setText(text);
            textView.nullLayouts();
            textView.onMeasure(width, height);
            final DisplayListCanvas c = node.start(
                textView.getMeasuredWidth(), textView.getMeasuredHeight());
            textView.nullLayouts();
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.onDraw(c);
        }
    }

    @Test
    public void testOnDraw_PrecomputedText() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
        final RenderNode node = RenderNode.create("benchmark", null);
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TestableTextView textView = new TestableTextView(getContext());
            textView.setTextMetricsParams(params);
            textView.setText(text);
            textView.nullLayouts();
            textView.onMeasure(width, height);
            final DisplayListCanvas c = node.start(
                textView.getMeasuredWidth(), textView.getMeasuredHeight());
            textView.nullLayouts();
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.onDraw(c);
        }
    }

    @Test
    public void testOnDraw_PrecomputedText_Selectable() {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
        final RenderNode node = RenderNode.create("benchmark", null);
        while (state.keepRunning()) {
            state.pauseTiming();
            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
            final CharSequence text = PrecomputedText.create(
                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
            final TestableTextView textView = new TestableTextView(getContext());
            textView.setTextIsSelectable(true);
            textView.setTextMetricsParams(params);
            textView.setText(text);
            textView.nullLayouts();
            textView.onMeasure(width, height);
            final DisplayListCanvas c = node.start(
                textView.getMeasuredWidth(), textView.getMeasuredHeight());
            textView.nullLayouts();
            Canvas.freeTextLayoutCaches();
            state.resumeTiming();

            textView.onDraw(c);
        }
    }
}
+8 −3
Original line number Diff line number Diff line
@@ -8084,7 +8084,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return false;
    }

    private void nullLayouts() {
    /** @hide */
    @VisibleForTesting
    public void nullLayouts() {
        if (mLayout instanceof BoringLayout && mSavedLayout == null) {
            mSavedLayout = (BoringLayout) mLayout;
        }
@@ -8178,7 +8180,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     * not the full view width with padding.
     * {@hide}
     */
    protected void makeNewLayout(int wantWidth, int hintWidth,
    @VisibleForTesting
    public void makeNewLayout(int wantWidth, int hintWidth,
                                 BoringLayout.Metrics boring,
                                 BoringLayout.Metrics hintBoring,
                                 int ellipsisWidth, boolean bringIntoView) {
@@ -8468,7 +8471,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return mIncludePad;
    }

    private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
    /** @hide */
    @VisibleForTesting
    public static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+4 −8
Original line number Diff line number Diff line
@@ -16,12 +16,10 @@

package android.widget.layout.linear;

import junit.framework.Assert;

import android.widget.EditText;
import android.content.Context;
import android.util.AttributeSet;
import android.text.BoringLayout;
import android.util.AttributeSet;
import android.widget.EditText;


/**
@@ -50,10 +48,8 @@ public class ExceptionTextView extends EditText {
    }

    @Override
    protected void makeNewLayout(int w, int hintWidth,
                                 BoringLayout.Metrics boring,
                                 BoringLayout.Metrics hintMetrics,
                                 int ellipsizedWidth, boolean bringIntoView) {
    public void makeNewLayout(int w, int hintWidth, BoringLayout.Metrics boring,
            BoringLayout.Metrics hintMetrics, int ellipsizedWidth, boolean bringIntoView) {
        if (w < 0) {
            mFailed = true;
            w = 100;