Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(635)

Unified Diff: content/common/gpu/client/gl_helper_unittest.cc

Issue 388953002: Improving GestureNav screenshotting performance - Part 1 - gl_helper (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Unit Test for CropScaleReadbackAndCleanTexture Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/common/gpu/client/gl_helper_unittest.cc
diff --git a/content/common/gpu/client/gl_helper_unittest.cc b/content/common/gpu/client/gl_helper_unittest.cc
index e22c3f7966bc6eaa19b1c7af6078d4deb8d3f726..a124acce11d2777a3d689a9b890aa1a9f30bc2c4 100644
--- a/content/common/gpu/client/gl_helper_unittest.cc
+++ b/content/common/gpu/client/gl_helper_unittest.cc
@@ -139,24 +139,40 @@ class GLHelperTest : public testing::Test {
}
}
- // Look up a single R/G/B/A value.
- // Clamp x/y.
+ // Look up a single channel value. Works for 4-channel and single channel
+ // bitmaps. Clamp x/y.
int Channel(SkBitmap* pixels, int x, int y, int c) {
- uint32* data =
- pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
- std::max(0, std::min(y, pixels->height() - 1)));
- return (*data) >> (c * 8) & 0xff;
+ if (pixels->bytesPerPixel() == 4) {
+ uint32* data =
+ pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
+ std::max(0, std::min(y, pixels->height() - 1)));
+ return (*data) >> (c * 8) & 0xff;
+ } else {
+ DCHECK_EQ(pixels->bytesPerPixel(), 1);
+ DCHECK_EQ(c, 0);
+ return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
+ std::max(0, std::min(y, pixels->height() - 1)));
+ }
}
- // Set a single R/G/B/A value.
+ // Set a single channel value. Works for 4-channel and single channel
+ // bitmaps. Clamp x/y.
void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
DCHECK_GE(x, 0);
DCHECK_GE(y, 0);
DCHECK_LT(x, pixels->width());
DCHECK_LT(y, pixels->height());
- uint32* data = pixels->getAddr32(x, y);
- v = std::max(0, std::min(v, 255));
- *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
+ if (pixels->bytesPerPixel() == 4) {
+ uint32* data = pixels->getAddr32(x, y);
+ v = std::max(0, std::min(v, 255));
+ *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
+ } else {
+ DCHECK_EQ(pixels->bytesPerPixel(), 1);
+ DCHECK_EQ(c, 0);
+ uint8* data = pixels->getAddr8(x, y);
+ v = std::max(0, std::min(v, 255));
+ *data = v;
+ }
}
// Print all the R, G, B or A values from an SkBitmap in a
@@ -389,21 +405,32 @@ class GLHelperTest : public testing::Test {
std::string message) {
EXPECT_EQ(truth->width(), other->width());
EXPECT_EQ(truth->height(), other->height());
+ EXPECT_EQ(truth->bytesPerPixel(), other->bytesPerPixel());
+ int bpp = truth->bytesPerPixel();
for (int x = 0; x < truth->width(); x++) {
for (int y = 0; y < truth->height(); y++) {
- for (int c = 0; c < 4; c++) {
+ for (int c = 0; c < bpp; c++) {
int a = Channel(truth, x, y, c);
int b = Channel(other, x, y, c);
EXPECT_NEAR(a, b, maxdiff) << " x=" << x << " y=" << y << " c=" << c
<< " " << message;
if (std::abs(a - b) > maxdiff) {
LOG(ERROR) << "-------expected--------";
- PrintChannel(truth, c);
+ for (int i = 0; i < bpp; i++) {
+ LOG(ERROR) << "Channel " << i << ":";
+ PrintChannel(truth, i);
+ }
LOG(ERROR) << "-------actual--------";
- PrintChannel(other, c);
+ for (int i = 0; i < bpp; i++) {
+ LOG(ERROR) << "Channel " << i << ":";
+ PrintChannel(other, i);
+ }
if (source) {
- LOG(ERROR) << "-------before scaling--------";
- PrintChannel(source, c);
+ LOG(ERROR) << "-------original--------";
no sievers 2014/08/20 20:29:52 nit: indent
mfomitchev 2014/08/22 18:01:33 Done.
+ for (int i = 0; i < source->bytesPerPixel(); i++) {
+ LOG(ERROR) << "Channel " << i << ":";
+ PrintChannel(source, i);
+ }
}
LOG(ERROR) << "-----Scaler stages------";
LOG(ERROR) << PrintStages(scaler_stages);
@@ -433,6 +460,34 @@ class GLHelperTest : public testing::Test {
ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
}
+ // Encodes an RGBA bitmap to grayscale.
+ // Reference implementation for
+ // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale.
+ void EncodeToGrayscaleSlow(SkBitmap* input,
+ SkBitmap* output) {
+ const float kRGBtoGrayscaleColorWeights[3] = {0.213f, 0.715f, 0.072f};
no sievers 2014/08/20 20:29:52 Should these constants be shared?
mfomitchev 2014/08/22 18:01:33 It wouldn't be terribly straightforward. This arra
no sievers 2014/08/22 18:44:13 If you expose them in gl_helper.h somehow, can the
mfomitchev 2014/08/25 19:21:53 Yes, I could construct the 3-element array in gl_h
+ CHECK_EQ(kAlpha_8_SkColorType, output->colorType());
+ CHECK_EQ(input->width(), output->width());
+ CHECK_EQ(input->height(), output->height());
+ CHECK_EQ(input->colorType(), kRGBA_8888_SkColorType);
+
+ for (int dst_y = 0; dst_y < output->height(); dst_y++) {
+ for (int dst_x = 0; dst_x < output->width(); dst_x++) {
+ float c0 = ChannelAsFloat(input, dst_x, dst_y, 0);
+ float c1 = ChannelAsFloat(input, dst_x, dst_y, 1);
+ float c2 = ChannelAsFloat(input, dst_x, dst_y, 2);
+ float value = c0 * kRGBtoGrayscaleColorWeights[0] +
+ c1 * kRGBtoGrayscaleColorWeights[1] +
+ c2 * kRGBtoGrayscaleColorWeights[2];
+ SetChannel(output,
+ dst_x,
+ dst_y,
+ 0,
+ static_cast<int>(value * 255.0f + 0.5f));
no sievers 2014/08/20 20:29:52 hmm why +0.5?
mfomitchev 2014/08/22 18:01:33 So that it rounds to the closest int rather than r
+ }
+ }
+ }
+
// Very slow bicubic / bilinear scaler for reference.
void ScaleSlow(SkBitmap* input,
SkBitmap* output,
@@ -515,18 +570,38 @@ class GLHelperTest : public testing::Test {
}
void FlipSKBitmap(SkBitmap* bitmap) {
+ int bpp = bitmap->bytesPerPixel();
+ DCHECK(bpp == 4 || bpp == 1);
int top_line = 0;
int bottom_line = bitmap->height() - 1;
while (top_line < bottom_line) {
for (int x = 0; x < bitmap->width(); x++) {
- std::swap(*bitmap->getAddr32(x, top_line),
- *bitmap->getAddr32(x, bottom_line));
+ bpp == 4 ?
+ std::swap(*bitmap->getAddr32(x, top_line),
+ *bitmap->getAddr32(x, bottom_line))
+ : std::swap(*bitmap->getAddr8(x, top_line),
+ *bitmap->getAddr8(x, bottom_line));
}
top_line++;
bottom_line--;
}
}
+ // Swaps red and blue channels in each pixel in a 32-bit bitmap.
+ void SwizzleSKBitmap(SkBitmap* bitmap) {
+ int bpp = bitmap->bytesPerPixel();
+ DCHECK(bpp == 4);
+ for (int y = 0; y < bitmap->height(); y++) {
+ for (int x = 0; x < bitmap->width(); x++) {
+ // Swap channels 0 and 2 (red and blue)
+ int c0 = Channel(bitmap, x, y, 0);
+ int c2 = Channel(bitmap, x, y, 2);
+ SetChannel(bitmap, x, y, 2, c0);
+ SetChannel(bitmap, x, y, 0, c2);
+ }
+ }
+ }
+
// gl_helper scales recursively, so we'll need to do that
// in the reference implementation too.
void ScaleSlowRecursive(SkBitmap* input,
@@ -575,56 +650,179 @@ class GLHelperTest : public testing::Test {
ScaleSlowRecursive(&tmp, output, quality);
}
- // Scaling test: Create a test image, scale it using GLHelperScaling
- // and a reference implementation and compare the results.
- void TestScale(int xsize,
- int ysize,
- int scaled_xsize,
- int scaled_ysize,
- int test_pattern,
- size_t quality,
- bool flip) {
- WebGLId src_texture = context_->createTexture();
- WebGLId framebuffer = context_->createFramebuffer();
- SkBitmap input_pixels;
- input_pixels.allocN32Pixels(xsize, ysize);
+ // Creates an RGBA SkBitmap
+ scoped_ptr<SkBitmap> CreateTestBitmap(int width,
+ int height,
+ int test_pattern) {
+ scoped_ptr<SkBitmap> bitmap(new SkBitmap);
+ bitmap->allocPixels(SkImageInfo::Make(
+ width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
- for (int x = 0; x < xsize; ++x) {
- for (int y = 0; y < ysize; ++y) {
+ for (int x = 0; x < width; ++x) {
+ for (int y = 0; y < height; ++y) {
switch (test_pattern) {
case 0: // Smooth test pattern
- SetChannel(&input_pixels, x, y, 0, x * 10);
- SetChannel(&input_pixels, x, y, 1, y * 10);
- SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
- SetChannel(&input_pixels, x, y, 3, 255);
+ SetChannel(bitmap.get(), x, y, 0, x * 10);
+ SetChannel(bitmap.get(), x, y, 0, y == 0 ? x * 50 : x * 10);
+ SetChannel(bitmap.get(), x, y, 1, y * 10);
+ SetChannel(bitmap.get(), x, y, 2, (x + y) * 10);
+ SetChannel(bitmap.get(), x, y, 3, 255);
break;
case 1: // Small blocks
- SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
- SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
- SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
- SetChannel(&input_pixels, x, y, 3, 255);
+ SetChannel(bitmap.get(), x, y, 0, x & 1 ? 255 : 0);
+ SetChannel(bitmap.get(), x, y, 1, y & 1 ? 255 : 0);
+ SetChannel(bitmap.get(), x, y, 2, (x + y) & 1 ? 255 : 0);
+ SetChannel(bitmap.get(), x, y, 3, 255);
break;
case 2: // Medium blocks
- SetChannel(&input_pixels, x, y, 0, 10 + x / 2 * 50);
- SetChannel(&input_pixels, x, y, 1, 10 + y / 3 * 50);
- SetChannel(&input_pixels, x, y, 2, (x + y) / 5 * 50 + 5);
- SetChannel(&input_pixels, x, y, 3, 255);
+ SetChannel(bitmap.get(), x, y, 0, 10 + x / 2 * 50);
+ SetChannel(bitmap.get(), x, y, 1, 10 + y / 3 * 50);
+ SetChannel(bitmap.get(), x, y, 2, (x + y) / 5 * 50 + 5);
+ SetChannel(bitmap.get(), x, y, 3, 255);
break;
}
}
}
+ return bitmap.Pass();
+ }
+ // Binds texture and framebuffer and loads the bitmap pixels into the texture.
+ void BindTextureAndFrameBuffer(WebGLId texture,
+ WebGLId framebuffer,
+ SkBitmap* bitmap,
+ int width,
+ int height) {
context_->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- context_->bindTexture(GL_TEXTURE_2D, src_texture);
+ context_->bindTexture(GL_TEXTURE_2D, texture);
context_->texImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
- xsize,
- ysize,
+ width,
+ height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
- input_pixels.getPixels());
+ bitmap->getPixels());
+ }
+
+ // Create a test image, transform it using
+ // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation
+ // and compare the results.
+ void TestCropScaleReadbackAndCleanTexture(int xsize,
+ int ysize,
+ int scaled_xsize,
+ int scaled_ysize,
+ int test_pattern,
+ SkColorType out_color_type,
+ bool swizzle,
+ size_t quality) {
no sievers 2014/08/20 20:29:52 nit: maybe less error-prone to just pass the Scale
mfomitchev 2014/08/22 18:01:33 Yeah, this is modeled after TestScale() below, but
no sievers 2014/08/22 18:44:13 I see, maybe just keep it as is then if it matches
+ DCHECK(out_color_type == kAlpha_8_SkColorType ||
+ out_color_type == kRGBA_8888_SkColorType ||
+ out_color_type == kBGRA_8888_SkColorType);
+ WebGLId src_texture = context_->createTexture();
+ WebGLId framebuffer = context_->createFramebuffer();
+ scoped_ptr<SkBitmap> input_pixels =
+ CreateTestBitmap(xsize, ysize, test_pattern).Pass();
+ BindTextureAndFrameBuffer(
+ src_texture, framebuffer, input_pixels.get(), xsize, ysize);
+
+ std::string message = base::StringPrintf(
+ "input size: %dx%d "
+ "output size: %dx%d "
+ "pattern: %d , quality: %s, "
+ "out_color_type: %d",
+ xsize,
+ ysize,
+ scaled_xsize,
+ scaled_ysize,
+ test_pattern,
+ kQualityNames[quality],
+ out_color_type);
+
+ // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture.
+ SkBitmap output_pixels;
+ output_pixels.allocPixels(SkImageInfo::Make(
+ scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
+ base::RunLoop run_loop;
+ gfx::Size encoded_texture_size;
+ helper_->CropScaleReadbackAndCleanTexture(
+ src_texture,
+ gfx::Size(xsize, ysize),
+ gfx::Rect(xsize, ysize),
+ gfx::Size(scaled_xsize, scaled_ysize),
+ static_cast<unsigned char*>(output_pixels.getPixels()),
+ out_color_type,
+ base::Bind(&callcallback, run_loop.QuitClosure()),
+ kQualities[quality]);
+ run_loop.Run();
+ // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back.
+ FlipSKBitmap(&output_pixels);
no sievers 2014/08/20 20:29:52 Why do we end up with an upside down image? That s
mfomitchev 2014/08/22 18:01:34 The old version of CropScaleReadbackAndCleanTextur
no sievers 2014/08/22 18:44:13 This is intriguing, as I believe we use this path
+
+ // Now transofrm the bitmap using the reference implementation.
no sievers 2014/08/20 20:29:52 nit, typo: 'transform'
mfomitchev 2014/08/22 18:01:33 Done.
+ SkBitmap scaled_pixels;
+ scaled_pixels.allocPixels(SkImageInfo::Make(
+ scaled_xsize,
+ scaled_ysize,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType));
+ SkBitmap truth_pixels;
+ // Step 1: Scale
+ ScaleSlowRecursive(input_pixels.get(), &scaled_pixels, kQualities[quality]);
+ // Step 2: Convert to the specified output format, encoding to grayscale if
+ // needed.
+ switch (out_color_type) {
+ case kRGBA_8888_SkColorType:
+ truth_pixels = scaled_pixels;
+ break;
+ case kBGRA_8888_SkColorType:
no sievers 2014/08/20 20:29:52 nit: indent here and below
mfomitchev 2014/08/22 18:01:33 Done.
+ // Swizzle pixels of the scaled bitmap if the output needs to be BGRA
no sievers 2014/08/20 20:29:52 or could Compare() just learn to handle the format
mfomitchev 2014/08/22 18:01:33 Problem is, scale tests here use Compare() to do a
no sievers 2014/08/22 18:44:13 hmm maybe put a TODO then?
mfomitchev 2014/08/25 19:21:53 Ok, I've just updated TestScale to consistently us
+ truth_pixels.installPixels(
+ SkImageInfo::Make(scaled_xsize,
+ scaled_ysize,
+ out_color_type,
+ kPremul_SkAlphaType),
+ scaled_pixels.getPixels(),
+ scaled_pixels.width() * 4);
+ SwizzleSKBitmap(&truth_pixels);
+ break;
+ case kAlpha_8_SkColorType:
+ truth_pixels.allocPixels(SkImageInfo::Make(
+ scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
+ EncodeToGrayscaleSlow(&scaled_pixels, &truth_pixels);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Now compare the results.
+ SkAutoLockPixels lock_input(truth_pixels);
+ const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
+ Compare(&truth_pixels,
+ &output_pixels,
+ 2,
no sievers 2014/08/20 20:29:52 Just wondering, why 2? Maybe hubbe knows. I did no
mfomitchev 2014/08/22 18:01:33 It does fail on my machine for 16x16 -> 3x6 best-q
+ input_pixels.get(),
+ dummy_stages,
+ message + " comparing against transformed/scaled");
+
+ context_->deleteTexture(src_texture);
+ context_->deleteFramebuffer(framebuffer);
+ }
+
+ // Scaling test: Create a test image, scale it using GLHelperScaling
+ // and a reference implementation and compare the results.
+ void TestScale(int xsize,
+ int ysize,
+ int scaled_xsize,
+ int scaled_ysize,
+ int test_pattern,
+ size_t quality,
+ bool flip) {
+ WebGLId src_texture = context_->createTexture();
+ WebGLId framebuffer = context_->createFramebuffer();
+ scoped_ptr<SkBitmap> input_pixels =
+ CreateTestBitmap(xsize, ysize, test_pattern).Pass();
+ BindTextureAndFrameBuffer(
+ src_texture, framebuffer, input_pixels.get(), xsize, ysize);
std::string message = base::StringPrintf(
"input size: %dx%d "
@@ -670,7 +868,7 @@ class GLHelperTest : public testing::Test {
FlipSKBitmap(&output_pixels);
}
if (xsize == scaled_xsize && ysize == scaled_ysize) {
- Compare(&input_pixels,
+ Compare(input_pixels.get(),
&output_pixels,
2,
NULL,
@@ -680,11 +878,11 @@ class GLHelperTest : public testing::Test {
SkBitmap truth_pixels;
truth_pixels.allocN32Pixels(scaled_xsize, scaled_ysize);
- ScaleSlowRecursive(&input_pixels, &truth_pixels, kQualities[quality]);
+ ScaleSlowRecursive(input_pixels.get(), &truth_pixels, kQualities[quality]);
Compare(&truth_pixels,
&output_pixels,
2,
- &input_pixels,
+ input_pixels.get(),
stages,
message + " comparing against scaled");
@@ -1013,7 +1211,6 @@ class GLHelperTest : public testing::Test {
color_type);
}
}
-
// Test basic format readback.
bool TestTextureFormatReadback(const gfx::Size& src_size,
SkColorType color_type,
@@ -1625,6 +1822,39 @@ TEST_F(GLHelperPixelTest, ScaleTest) {
}
}
+// Per pixel tests, all sizes are small so that we can print
+// out the generated bitmaps.
+TEST_F(GLHelperPixelTest, CropScaleReadbackAndCleanTextureTest) {
+ int sizes[] = {3, 6, 16};
+ SkColorType color_types[] =
no sievers 2014/08/20 20:29:52 nit: const int kSizes[] = ... const SkColorType k
mfomitchev 2014/08/22 18:01:33 Done.
+ {kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType};
+ // Test BEST and FAST qualities, skip GOOD
+ for (size_t q = 0; q < arraysize(kQualities); q += 2) {
no sievers 2014/08/20 20:29:52 why q+=2 and not q++?
mfomitchev 2014/08/22 18:01:33 FAST uses a different code path in CropScaleReadba
no sievers 2014/08/22 18:44:13 Ok, maybe it's more straightforward to define your
mfomitchev 2014/08/25 19:21:53 The index is also used to fetch the name from kQua
+ for (int color_type = 0; color_type < 3; color_type++) {
no sievers 2014/08/20 20:29:52 color_type < arraysize(kColorTypes)
mfomitchev 2014/08/22 18:01:33 Done.
+ for (int x = 0; x < 3; x++) {
+ for (int y = 0; y < 3; y++) {
+ for (int dst_x = 0; dst_x < 3; dst_x++) {
+ for (int dst_y = 0; dst_y < 3; dst_y++) {
+ for (int pattern = 0; pattern < 3; pattern++) {
+ TestCropScaleReadbackAndCleanTexture(sizes[x],
+ sizes[y],
+ sizes[dst_x],
+ sizes[dst_y],
+ pattern,
+ color_types[color_type],
+ false,
+ q);
+ if (HasFailure())
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
// Validate that all scaling generates valid pipelines.
TEST_F(GLHelperTest, ValidateScalerPipelines) {
int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096};
« content/common/gpu/client/gl_helper.cc ('K') | « content/common/gpu/client/gl_helper.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698