Merge "Introduce new perf test for TextView with precomputed text" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0efaf2088d
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user