Commit 67c41a7b authored by bsheedy's avatar bsheedy Committed by Commit Bot

Improve Android RenderTest speed

Significantly improves the speed at which Android RenderTest comparisons
take place if Bitmap.sameAs returns false. This is done by reducing the
number of times we call methods that have to hop through JNI and by bulk
editing images instead of going through pixel-by-pixel.

Rough benchmarking shows this to result in a ~3x speedup.

Bug: 904012
Change-Id: I51dc259df9124c6023572a8510fa87dd435bf2f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1500413Reviewed-by: default avatarJustin DeWitt <dewittj@chromium.org>
Reviewed-by: default avatarBrian Sheedy <bsheedy@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638233}
parent 08c128cd
......@@ -10,6 +10,7 @@ import static org.hamcrest.Matchers.instanceOf;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.SystemClock;
import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.filters.SmallTest;
import android.support.v7.widget.RecyclerView;
......@@ -106,6 +107,9 @@ public class ExploreSitesPageTest {
onView(instanceOf(RecyclerView.class)).perform(RecyclerViewActions.scrollToPosition(2));
mRenderTestRule.render(mRecyclerView, "recycler_layout");
// TODO(https://crbug.com/938519): Remove this sleep in favor of actually waiting for the
// scroll bar to disappear.
SystemClock.sleep(3000);
mActivityTestRule.loadUrl("about:blank");
ThreadUtils.runOnUiThreadBlocking(() -> mActivityTestRule.getActivity().onBackPressed());
mRenderTestRule.render(mRecyclerView, "recycler_layout_back");
......
......@@ -354,6 +354,9 @@ public class RenderTestRule extends TestWatcher {
Bitmap diff = Bitmap.createBitmap(Math.max(render.getWidth(), golden.getWidth()),
Math.max(render.getHeight(), golden.getHeight()), render.getConfig());
// Assume that the majority of the pixels will be the same and set the diff image to
// transparent by default.
diff.eraseColor(Color.TRANSPARENT);
int maxWidth = Math.max(render.getWidth(), golden.getWidth());
int maxHeight = Math.max(render.getHeight(), golden.getHeight());
......@@ -399,25 +402,52 @@ public class RenderTestRule extends TestWatcher {
int diffThreshold, int startWidth, int endWidth, int startHeight, int endHeight) {
int diffPixels = 0;
for (int x = startWidth; x < endWidth; x++) {
for (int y = startHeight; y < endHeight; y++) {
int goldenColor = goldenImage.getPixel(x, y);
int testColor = testImage.getPixel(x, y);
// Get copies of the pixels and compare using that instead of repeatedly calling getPixel,
// as that's significantly faster since we don't need to repeatedly hop through JNI.
int diffWidth = endWidth - startWidth;
int diffHeight = endHeight - startHeight;
int[] goldenPixels =
writeBitmapToArray(goldenImage, startWidth, startHeight, diffWidth, diffHeight);
int[] testPixels =
writeBitmapToArray(testImage, startWidth, startHeight, diffWidth, diffHeight);
for (int y = 0; y < diffHeight; ++y) {
int rowOffset = y * diffWidth;
for (int x = 0; x < diffWidth; ++x) {
int index = x + rowOffset;
if (goldenPixels[index] == testPixels[index]) continue;
int goldenColor = goldenPixels[index];
int testColor = testPixels[index];
int redDiff = Math.abs(Color.red(goldenColor) - Color.red(testColor));
int blueDiff = Math.abs(Color.green(goldenColor) - Color.green(testColor));
int greenDiff = Math.abs(Color.blue(goldenColor) - Color.blue(testColor));
int greenDiff = Math.abs(Color.green(goldenColor) - Color.green(testColor));
int blueDiff = Math.abs(Color.blue(goldenColor) - Color.blue(testColor));
int alphaDiff = Math.abs(Color.alpha(goldenColor) - Color.alpha(testColor));
if (redDiff > diffThreshold || blueDiff > diffThreshold || greenDiff > diffThreshold
|| alphaDiff > diffThreshold) {
diffPixels++;
diffImage.setPixel(x, y, Color.RED);
} else {
diffImage.setPixel(x, y, Color.TRANSPARENT);
}
}
}
int diffArea = diffHeight * diffWidth;
for (int i = 0; i < diffArea; ++i) {
if (goldenPixels[i] == testPixels[i]) continue;
int goldenColor = goldenPixels[i];
int testColor = testPixels[i];
int redDiff = Math.abs(Color.red(goldenColor) - Color.red(testColor));
int greenDiff = Math.abs(Color.green(goldenColor) - Color.green(testColor));
int blueDiff = Math.abs(Color.blue(goldenColor) - Color.blue(testColor));
int alphaDiff = Math.abs(Color.alpha(goldenColor) - Color.alpha(testColor));
if (redDiff > diffThreshold || blueDiff > diffThreshold || greenDiff > diffThreshold
|| alphaDiff > diffThreshold) {
diffPixels++;
diffImage.setPixel(i % diffWidth, i / diffWidth, Color.RED);
}
}
return diffPixels;
}
......@@ -443,21 +473,30 @@ public class RenderTestRule extends TestWatcher {
Bitmap diffImage, int minWidth, int maxWidth, int minHeight, int maxHeight) {
int diffPixels = 0;
if (maxWidth > minWidth) {
for (int x = minWidth; x < maxWidth; x++) {
for (int y = 0; y < maxHeight; y++) {
diffImage.setPixel(x, y, Color.RED);
}
}
diffPixels += (maxWidth - minWidth) * maxHeight;
int diffWidth = maxWidth - minWidth;
int totalPixels = diffWidth * maxHeight;
// Filling an array of pixels then bulk-setting is faster than looping through each
// individual pixel and setting it.
int[] pixels = new int[totalPixels];
Arrays.fill(pixels, 0, totalPixels, Color.RED);
diffImage.setPixels(pixels, 0 /* offset */, diffWidth /* stride */, minWidth /* x */,
0 /* y */, diffWidth /* width */, maxHeight /* height */);
diffPixels += totalPixels;
}
if (maxHeight > minHeight) {
for (int x = 0; x < maxWidth; x++) {
for (int y = minHeight; y < maxHeight; y++) {
diffImage.setPixel(x, y, Color.RED);
}
}
diffPixels += (maxHeight - minHeight) * minWidth;
int diffHeight = maxHeight - minHeight;
int totalPixels = diffHeight * minHeight;
int[] pixels = new int[totalPixels];
Arrays.fill(pixels, 0, totalPixels, Color.RED);
diffImage.setPixels(pixels, 0 /* offset */, minWidth /* stride */, 0 /* x */,
minHeight /* y */, minWidth /* width */, diffHeight /* height */);
}
return diffPixels;
}
private static int[] writeBitmapToArray(Bitmap bitmap, int x, int y, int width, int height) {
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0 /* offset */, width /* stride */, x, y, width, height);
return pixels;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment