1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <stdio.h> 6 #include <cmath> 7 #include <string> 8 #include <vector> 9 10 #include <GLES2/gl2.h> 11 #include <GLES2/gl2ext.h> 12 #include <GLES2/gl2extchromium.h> 13 14 #include "base/at_exit.h" 15 #include "base/bind.h" 16 #include "base/command_line.h" 17 #include "base/file_util.h" 18 #include "base/message_loop/message_loop.h" 19 #include "base/run_loop.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/time/time.h" 22 #include "content/common/gpu/client/gl_helper.h" 23 #include "content/common/gpu/client/gl_helper_scaling.h" 24 #include "content/public/test/unittest_test_suite.h" 25 #include "content/test/content_test_suite.h" 26 #include "gpu/config/gpu_util.h" 27 #include "media/base/video_frame.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 #include "third_party/skia/include/core/SkBitmap.h" 30 #include "third_party/skia/include/core/SkTypes.h" 31 #include "ui/gl/gl_surface.h" 32 #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" 33 34 #if defined(OS_MACOSX) 35 #include "base/mac/scoped_nsautorelease_pool.h" 36 #endif 37 38 #if defined(TOOLKIT_GTK) 39 #include "ui/gfx/gtk_util.h" 40 #endif 41 42 namespace content { 43 44 using WebKit::WebGLId; 45 using WebKit::WebGraphicsContext3D; 46 47 content::GLHelper::ScalerQuality kQualities[] = { 48 content::GLHelper::SCALER_QUALITY_BEST, 49 content::GLHelper::SCALER_QUALITY_GOOD, 50 content::GLHelper::SCALER_QUALITY_FAST, 51 }; 52 53 const char *kQualityNames[] = { 54 "best", 55 "good", 56 "fast", 57 }; 58 59 class GLHelperTest : public testing::Test { 60 protected: 61 virtual void SetUp() { 62 WebGraphicsContext3D::Attributes attributes; 63 context_ = webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl:: 64 CreateOffscreenContext(attributes); 65 context_->makeContextCurrent(); 66 helper_.reset(new content::GLHelper(context_.get())); 67 helper_scaling_.reset(new content::GLHelperScaling( 68 context_.get(), 69 helper_.get())); 70 } 71 72 virtual void TearDown() { 73 helper_scaling_.reset(NULL); 74 helper_.reset(NULL); 75 context_.reset(NULL); 76 } 77 78 // Bicubic filter kernel function. 79 static float Bicubic(float x) { 80 const float a = -0.5; 81 x = std::abs(x); 82 float x2 = x * x; 83 float x3 = x2 * x; 84 if (x <= 1) { 85 return (a + 2) * x3 - (a + 3) * x2 + 1; 86 } else if (x < 2) { 87 return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a; 88 } else { 89 return 0.0f; 90 } 91 } 92 93 // Look up a single R/G/B/A value. 94 // Clamp x/y. 95 int Channel(SkBitmap* pixels, int x, int y, int c) { 96 uint32 *data = pixels->getAddr32( 97 std::max(0, std::min(x, pixels->width() - 1)), 98 std::max(0, std::min(y, pixels->height() - 1))); 99 return (*data) >> (c * 8) & 0xff; 100 } 101 102 // Set a single R/G/B/A value. 103 void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) { 104 DCHECK_GE(x, 0); 105 DCHECK_GE(y, 0); 106 DCHECK_LT(x, pixels->width()); 107 DCHECK_LT(y, pixels->height()); 108 uint32 *data = pixels->getAddr32(x, y); 109 v = std::max(0, std::min(v, 255)); 110 *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8)); 111 } 112 113 // Print all the R, G, B or A values from an SkBitmap in a 114 // human-readable format. 115 void PrintChannel(SkBitmap* pixels, int c) { 116 for (int y = 0; y < pixels->height(); y++) { 117 for (int x = 0; x < pixels->width(); x++) { 118 printf("%3d, ", Channel(pixels, x, y, c)); 119 } 120 printf("\n"); 121 } 122 } 123 124 // Print out the individual steps of a scaler pipeline. 125 std::string PrintStages( 126 const std::vector<GLHelperScaling::ScalerStage> &scaler_stages) { 127 std::string ret; 128 for (size_t i = 0; i < scaler_stages.size(); i++) { 129 ret.append(base::StringPrintf("%dx%d -> %dx%d ", 130 scaler_stages[i].src_size.width(), 131 scaler_stages[i].src_size.height(), 132 scaler_stages[i].dst_size.width(), 133 scaler_stages[i].dst_size.height())); 134 bool xy_matters = false; 135 switch (scaler_stages[i].shader) { 136 case GLHelperScaling::SHADER_BILINEAR: 137 ret.append("bilinear"); 138 break; 139 case GLHelperScaling::SHADER_BILINEAR2: 140 ret.append("bilinear2"); 141 xy_matters = true; 142 break; 143 case GLHelperScaling::SHADER_BILINEAR3: 144 ret.append("bilinear3"); 145 xy_matters = true; 146 break; 147 case GLHelperScaling::SHADER_BILINEAR4: 148 ret.append("bilinear4"); 149 xy_matters = true; 150 break; 151 case GLHelperScaling::SHADER_BILINEAR2X2: 152 ret.append("bilinear2x2"); 153 break; 154 case GLHelperScaling::SHADER_BICUBIC_UPSCALE: 155 ret.append("bicubic upscale"); 156 xy_matters = true; 157 break; 158 case GLHelperScaling::SHADER_BICUBIC_HALF_1D: 159 ret.append("bicubic 1/2"); 160 xy_matters = true; 161 break; 162 case GLHelperScaling::SHADER_PLANAR: 163 ret.append("planar"); 164 break; 165 case GLHelperScaling::SHADER_YUV_MRT_PASS1: 166 ret.append("rgb2yuv pass 1"); 167 break; 168 case GLHelperScaling::SHADER_YUV_MRT_PASS2: 169 ret.append("rgb2yuv pass 2"); 170 break; 171 } 172 173 if (xy_matters) { 174 if (scaler_stages[i].scale_x) { 175 ret.append(" X"); 176 } else { 177 ret.append(" Y"); 178 } 179 } 180 ret.append("\n"); 181 } 182 return ret; 183 } 184 185 bool CheckScale(double scale, int samples, bool already_scaled) { 186 // 1:1 is valid if there is one sample. 187 if (samples == 1 && scale == 1.0) { 188 return true; 189 } 190 // Is it an exact down-scale (50%, 25%, etc.?) 191 if (scale == 2.0 * samples) { 192 return true; 193 } 194 // Upscales, only valid if we haven't already scaled in this dimension. 195 if (!already_scaled) { 196 // Is it a valid bilinear upscale? 197 if (samples == 1 && scale <= 1.0) { 198 return true; 199 } 200 // Multi-sample upscale-downscale combination? 201 if (scale > samples / 2.0 && scale < samples) { 202 return true; 203 } 204 } 205 return false; 206 } 207 208 // Make sure that the stages of the scaler pipeline are sane. 209 void ValidateScalerStages( 210 content::GLHelper::ScalerQuality quality, 211 const std::vector<GLHelperScaling::ScalerStage> &scaler_stages, 212 const std::string& message) { 213 bool previous_error = HasFailure(); 214 // First, check that the input size for each stage is equal to 215 // the output size of the previous stage. 216 for (size_t i = 1; i < scaler_stages.size(); i++) { 217 EXPECT_EQ(scaler_stages[i - 1].dst_size.width(), 218 scaler_stages[i].src_size.width()); 219 EXPECT_EQ(scaler_stages[i - 1].dst_size.height(), 220 scaler_stages[i].src_size.height()); 221 EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0); 222 EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0); 223 EXPECT_EQ(scaler_stages[i].src_subrect.width(), 224 scaler_stages[i].src_size.width()); 225 EXPECT_EQ(scaler_stages[i].src_subrect.height(), 226 scaler_stages[i].src_size.height()); 227 } 228 229 // Used to verify that up-scales are not attempted after some 230 // other scale. 231 bool scaled_x = false; 232 bool scaled_y = false; 233 234 for (size_t i = 0; i < scaler_stages.size(); i++) { 235 // Note: 2.0 means scaling down by 50% 236 double x_scale = 237 static_cast<double>(scaler_stages[i].src_subrect.width()) / 238 static_cast<double>(scaler_stages[i].dst_size.width()); 239 double y_scale = 240 static_cast<double>(scaler_stages[i].src_subrect.height()) / 241 static_cast<double>(scaler_stages[i].dst_size.height()); 242 243 int x_samples = 0; 244 int y_samples = 0; 245 246 // Codify valid scale operations. 247 switch (scaler_stages[i].shader) { 248 case GLHelperScaling::SHADER_PLANAR: 249 case GLHelperScaling::SHADER_YUV_MRT_PASS1: 250 case GLHelperScaling::SHADER_YUV_MRT_PASS2: 251 EXPECT_TRUE(false) << "Invalid shader."; 252 break; 253 254 case GLHelperScaling::SHADER_BILINEAR: 255 if (quality != content::GLHelper::SCALER_QUALITY_FAST) { 256 x_samples = 1; 257 y_samples = 1; 258 } 259 break; 260 case GLHelperScaling::SHADER_BILINEAR2: 261 x_samples = 2; 262 y_samples = 1; 263 break; 264 case GLHelperScaling::SHADER_BILINEAR3: 265 x_samples = 3; 266 y_samples = 1; 267 break; 268 case GLHelperScaling::SHADER_BILINEAR4: 269 x_samples = 4; 270 y_samples = 1; 271 break; 272 case GLHelperScaling::SHADER_BILINEAR2X2: 273 x_samples = 2; 274 y_samples = 2; 275 break; 276 case GLHelperScaling::SHADER_BICUBIC_UPSCALE: 277 if (scaler_stages[i].scale_x) { 278 EXPECT_LT(x_scale, 1.0); 279 EXPECT_EQ(y_scale, 1.0); 280 } else { 281 EXPECT_EQ(x_scale, 1.0); 282 EXPECT_LT(y_scale, 1.0); 283 } 284 break; 285 case GLHelperScaling::SHADER_BICUBIC_HALF_1D: 286 if (scaler_stages[i].scale_x) { 287 EXPECT_EQ(x_scale, 2.0); 288 EXPECT_EQ(y_scale, 1.0); 289 } else { 290 EXPECT_EQ(x_scale, 1.0); 291 EXPECT_EQ(y_scale, 2.0); 292 } 293 break; 294 } 295 296 if (!scaler_stages[i].scale_x) { 297 std::swap(x_samples, y_samples); 298 } 299 300 if (x_samples) { 301 EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x)) 302 << "x_scale = " << x_scale; 303 } 304 if (y_samples) { 305 EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y)) 306 << "y_scale = " << y_scale; 307 } 308 309 if (x_scale != 1.0) { 310 scaled_x = true; 311 } 312 if (y_scale != 1.0) { 313 scaled_y = true; 314 } 315 } 316 317 if (HasFailure() && !previous_error) { 318 printf("Invalid scaler stages: %s\n", message.c_str()); 319 printf("Scaler stages:\n%s", PrintStages(scaler_stages).c_str()); 320 } 321 } 322 323 // Compare two bitmaps, make sure that each component of each pixel 324 // is no more than |maxdiff| apart. If they are not similar enough, 325 // prints out |truth|, |other|, |source|, |scaler_stages| and |message|. 326 void Compare(SkBitmap* truth, 327 SkBitmap* other, 328 int maxdiff, 329 SkBitmap* source, 330 const std::vector<GLHelperScaling::ScalerStage> &scaler_stages, 331 std::string message) { 332 EXPECT_EQ(truth->width(), other->width()); 333 EXPECT_EQ(truth->height(), other->height()); 334 for (int x = 0; x < truth->width(); x++) { 335 for (int y = 0; y < truth->height(); y++) { 336 for (int c = 0; c < 4; c++) { 337 int a = Channel(truth, x, y, c); 338 int b = Channel(other, x, y, c); 339 EXPECT_NEAR(a, b, maxdiff) 340 << " x=" << x 341 << " y=" << y 342 << " c=" << c 343 << " " << message; 344 if (std::abs(a - b) > maxdiff) { 345 printf("-------expected--------\n"); 346 PrintChannel(truth, c); 347 printf("-------actual--------\n"); 348 PrintChannel(other, c); 349 if (source) { 350 printf("-------before scaling--------\n"); 351 PrintChannel(source, c); 352 } 353 printf("-----Scaler stages------\n%s", 354 PrintStages(scaler_stages).c_str()); 355 return; 356 } 357 } 358 } 359 } 360 } 361 362 // Get a single R, G, B or A value as a float. 363 float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) { 364 return Channel(pixels, x, y, c) / 255.0; 365 } 366 367 // Works like a GL_LINEAR lookup on an SkBitmap. 368 float Bilinear(SkBitmap* pixels, float x, float y, int c) { 369 x -= 0.5; 370 y -= 0.5; 371 int base_x = static_cast<int>(floorf(x)); 372 int base_y = static_cast<int>(floorf(y)); 373 x -= base_x; 374 y -= base_y; 375 return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) + 376 ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) + 377 ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y + 378 ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y); 379 } 380 381 // Very slow bicubic / bilinear scaler for reference. 382 void ScaleSlow(SkBitmap* input, 383 SkBitmap* output, 384 content::GLHelper::ScalerQuality quality) { 385 float xscale = static_cast<float>(input->width()) / output->width(); 386 float yscale = static_cast<float>(input->height()) / output->height(); 387 float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale; 388 float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale; 389 for (int dst_y = 0; dst_y < output->height(); dst_y++) { 390 for (int dst_x = 0; dst_x < output->width(); dst_x++) { 391 for (int channel = 0; channel < 4; channel++) { 392 float dst_x_in_src = (dst_x + 0.5f) * xscale; 393 float dst_y_in_src = (dst_y + 0.5f) * yscale; 394 395 float value = 0.0f; 396 float sum = 0.0f; 397 switch (quality) { 398 case content::GLHelper::SCALER_QUALITY_BEST: 399 for (int src_y = -10; src_y < input->height() + 10; ++src_y) { 400 float coeff_y = Bicubic( 401 (src_y + 0.5f - dst_y_in_src) * clamped_yscale); 402 if (coeff_y == 0.0f) { 403 continue; 404 } 405 for (int src_x = -10; src_x < input->width() + 10; ++src_x) { 406 float coeff = coeff_y * Bicubic( 407 (src_x + 0.5f - dst_x_in_src) * clamped_xscale); 408 if (coeff == 0.0f) { 409 continue; 410 } 411 sum += coeff; 412 float c = ChannelAsFloat(input, src_x, src_y, channel); 413 value += c * coeff; 414 } 415 } 416 break; 417 418 case content::GLHelper::SCALER_QUALITY_GOOD: { 419 int xshift = 0, yshift = 0; 420 while ((output->width() << xshift) < input->width()) { 421 xshift++; 422 } 423 while ((output->height() << yshift) < input->height()) { 424 yshift++; 425 } 426 int xmag = 1 << xshift; 427 int ymag = 1 << yshift; 428 if (xmag == 4 && output->width() * 3 >= input->width()) { 429 xmag=3; 430 } 431 if (ymag == 4 && output->height() * 3 >= input->height()) { 432 ymag=3; 433 } 434 for (int x = 0; x < xmag; x++) { 435 for (int y = 0; y < ymag; y++) { 436 value += Bilinear(input, 437 (dst_x * xmag + x + 0.5) * xscale / xmag, 438 (dst_y * ymag + y + 0.5) * yscale / ymag, 439 channel); 440 sum += 1.0; 441 } 442 } 443 break; 444 } 445 446 case content::GLHelper::SCALER_QUALITY_FAST: 447 value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel); 448 sum = 1.0; 449 } 450 value /= sum; 451 SetChannel(output, dst_x, dst_y, channel, 452 static_cast<int>(value * 255.0f + 0.5f)); 453 } 454 } 455 } 456 } 457 458 459 // gl_helper scales recursively, so we'll need to do that 460 // in the reference implementation too. 461 void ScaleSlowRecursive(SkBitmap* input, SkBitmap* output, 462 content::GLHelper::ScalerQuality quality) { 463 if (quality == content::GLHelper::SCALER_QUALITY_FAST || 464 quality == content::GLHelper::SCALER_QUALITY_GOOD) { 465 ScaleSlow(input, output, quality); 466 return; 467 } 468 469 float xscale = static_cast<float>(output->width()) / input->width(); 470 471 // This corresponds to all the operations we can do directly. 472 float yscale = static_cast<float>(output->height()) / input->height(); 473 if ((xscale == 1.0f && yscale == 1.0f) || 474 (xscale == 0.5f && yscale == 1.0f) || 475 (xscale == 1.0f && yscale == 0.5f) || 476 (xscale >= 1.0f && yscale == 1.0f) || 477 (xscale == 1.0f && yscale >= 1.0f)) { 478 ScaleSlow(input, output, quality); 479 return; 480 } 481 482 // Now we break the problem down into smaller pieces, using the 483 // operations available. 484 int xtmp = input->width(); 485 int ytmp = input->height(); 486 487 if (output->height() != input->height()) { 488 ytmp = output->height(); 489 while (ytmp < input->height() && ytmp * 2 != input->height()) { 490 ytmp += ytmp; 491 } 492 } else { 493 xtmp = output->width(); 494 while (xtmp < input->width() && xtmp * 2 != input->width()) { 495 xtmp += xtmp; 496 } 497 } 498 499 SkBitmap tmp; 500 tmp.setConfig(SkBitmap::kARGB_8888_Config, xtmp, ytmp); 501 tmp.allocPixels(); 502 SkAutoLockPixels lock(tmp); 503 504 ScaleSlowRecursive(input, &tmp, quality); 505 ScaleSlowRecursive(&tmp, output, quality); 506 } 507 508 // Scaling test: Create a test image, scale it using GLHelperScaling 509 // and a reference implementation and compare the results. 510 void TestScale(int xsize, int ysize, 511 int scaled_xsize, int scaled_ysize, 512 int test_pattern, 513 size_t quality) { 514 WebGLId src_texture = context_->createTexture(); 515 WebGLId framebuffer = context_->createFramebuffer(); 516 SkBitmap input_pixels; 517 input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize); 518 input_pixels.allocPixels(); 519 SkAutoLockPixels lock(input_pixels); 520 521 for (int x = 0; x < xsize; ++x) { 522 for (int y = 0; y < ysize; ++y) { 523 switch (test_pattern) { 524 case 0: // Smooth test pattern 525 SetChannel(&input_pixels, x, y, 0, x * 10); 526 SetChannel(&input_pixels, x, y, 1, y * 10); 527 SetChannel(&input_pixels, x, y, 2, (x + y) * 10); 528 SetChannel(&input_pixels, x, y, 3, 255); 529 break; 530 case 1: // Small blocks 531 SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); 532 SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); 533 SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); 534 SetChannel(&input_pixels, x, y, 3, 255); 535 break; 536 case 2: // Medium blocks 537 SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50); 538 SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50); 539 SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5); 540 SetChannel(&input_pixels, x, y, 3, 255); 541 break; 542 } 543 } 544 } 545 546 context_->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); 547 context_->bindTexture(GL_TEXTURE_2D, src_texture); 548 context_->texImage2D(GL_TEXTURE_2D, 549 0, 550 GL_RGBA, 551 xsize, 552 ysize, 553 0, 554 GL_RGBA, 555 GL_UNSIGNED_BYTE, 556 input_pixels.getPixels()); 557 558 std::string message = base::StringPrintf("input size: %dx%d " 559 "output size: %dx%d " 560 "pattern: %d quality: %s", 561 xsize, ysize, 562 scaled_xsize, scaled_ysize, 563 test_pattern, 564 kQualityNames[quality]); 565 566 567 std::vector<GLHelperScaling::ScalerStage> stages; 568 helper_scaling_->ComputeScalerStages( 569 kQualities[quality], 570 gfx::Size(xsize, ysize), 571 gfx::Rect(0, 0, xsize, ysize), 572 gfx::Size(scaled_xsize, scaled_ysize), 573 false, 574 false, 575 &stages); 576 ValidateScalerStages(kQualities[quality], stages, message); 577 578 WebGLId dst_texture = helper_->CopyAndScaleTexture( 579 src_texture, 580 gfx::Size(xsize, ysize), 581 gfx::Size(scaled_xsize, scaled_ysize), 582 false, 583 kQualities[quality]); 584 585 SkBitmap output_pixels; 586 output_pixels.setConfig(SkBitmap::kARGB_8888_Config, 587 scaled_xsize, scaled_ysize); 588 output_pixels.allocPixels(); 589 SkAutoLockPixels output_lock(output_pixels); 590 591 helper_->ReadbackTextureSync( 592 dst_texture, 593 gfx::Rect(0, 0, scaled_xsize, scaled_ysize), 594 static_cast<unsigned char *>(output_pixels.getPixels())); 595 596 if (xsize == scaled_xsize && ysize == scaled_ysize) { 597 Compare(&input_pixels, 598 &output_pixels, 599 2, 600 NULL, 601 stages, 602 message + " comparing against input"); 603 } 604 SkBitmap truth_pixels; 605 truth_pixels.setConfig(SkBitmap::kARGB_8888_Config, 606 scaled_xsize, scaled_ysize); 607 truth_pixels.allocPixels(); 608 SkAutoLockPixels truth_lock(truth_pixels); 609 610 ScaleSlowRecursive(&input_pixels, &truth_pixels, kQualities[quality]); 611 Compare(&truth_pixels, 612 &output_pixels, 613 2, 614 &input_pixels, 615 stages, 616 message + " comparing against scaled"); 617 618 context_->deleteTexture(src_texture); 619 context_->deleteTexture(dst_texture); 620 context_->deleteFramebuffer(framebuffer); 621 } 622 623 // Create a scaling pipeline and check that it is made up of 624 // valid scaling operations. 625 void TestScalerPipeline(size_t quality, 626 int xsize, int ysize, 627 int dst_xsize, int dst_ysize) { 628 std::vector<GLHelperScaling::ScalerStage> stages; 629 helper_scaling_->ComputeScalerStages( 630 kQualities[quality], 631 gfx::Size(xsize, ysize), 632 gfx::Rect(0, 0, xsize, ysize), 633 gfx::Size(dst_xsize, dst_ysize), 634 false, 635 false, 636 &stages); 637 ValidateScalerStages(kQualities[quality], stages, 638 base::StringPrintf("input size: %dx%d " 639 "output size: %dx%d " 640 "quality: %s", 641 xsize, ysize, 642 dst_xsize, dst_ysize, 643 kQualityNames[quality])); 644 } 645 646 // Create a scaling pipeline and make sure that the steps 647 // are exactly the steps we expect. 648 void CheckPipeline(content::GLHelper::ScalerQuality quality, 649 int xsize, int ysize, 650 int dst_xsize, int dst_ysize, 651 const std::string &description) { 652 std::vector<GLHelperScaling::ScalerStage> stages; 653 helper_scaling_->ComputeScalerStages( 654 quality, 655 gfx::Size(xsize, ysize), 656 gfx::Rect(0, 0, xsize, ysize), 657 gfx::Size(dst_xsize, dst_ysize), 658 false, 659 false, 660 &stages); 661 ValidateScalerStages( 662 content::GLHelper::SCALER_QUALITY_GOOD, 663 stages, 664 ""); 665 EXPECT_EQ(PrintStages(stages), description); 666 } 667 668 // Note: Left/Right means Top/Bottom when used for Y dimension. 669 enum Margin { 670 MarginLeft, 671 MarginMiddle, 672 MarginRight, 673 MarginInvalid, 674 }; 675 676 static Margin NextMargin(Margin m) { 677 switch (m) { 678 case MarginLeft: 679 return MarginMiddle; 680 case MarginMiddle: 681 return MarginRight; 682 case MarginRight: 683 return MarginInvalid; 684 default: 685 return MarginInvalid; 686 } 687 } 688 689 int compute_margin(int insize, int outsize, Margin m) { 690 int available = outsize - insize; 691 switch (m) { 692 default: 693 EXPECT_TRUE(false) << "This should not happen."; 694 return 0; 695 case MarginLeft: 696 return 0; 697 case MarginMiddle: 698 return (available / 2) & ~1; 699 case MarginRight: 700 return available; 701 } 702 } 703 704 // Convert 0.0 - 1.0 to 0 - 255 705 int float_to_byte(float v) { 706 int ret = static_cast<int>(floorf(v * 255.0f + 0.5f)); 707 if (ret < 0) { 708 return 0; 709 } 710 if (ret > 255) { 711 return 255; 712 } 713 return ret; 714 } 715 716 static void callcallback(const base::Callback<void()>& callback, 717 bool result) { 718 callback.Run(); 719 } 720 721 void PrintPlane(unsigned char *plane, int xsize, int stride, int ysize) { 722 for (int y = 0; y < ysize; y++) { 723 for (int x = 0; x < xsize ; x++) { 724 printf("%3d, ", plane[y * stride + x]); 725 } 726 printf(" (%p)\n", plane + y * stride); 727 } 728 } 729 730 // Compare two planes make sure that each component of each pixel 731 // is no more than |maxdiff| apart. 732 void ComparePlane(unsigned char* truth, 733 unsigned char* other, 734 int maxdiff, 735 int xsize, 736 int stride, 737 int ysize, 738 SkBitmap* source, 739 std::string message) { 740 for (int x = 0; x < xsize; x++) { 741 for (int y = 0; y < ysize; y++) { 742 int a = other[y * stride + x]; 743 int b = truth[y * stride + x]; 744 EXPECT_NEAR(a, b, maxdiff) 745 << " x=" << x 746 << " y=" << y 747 << " " << message; 748 if (std::abs(a - b) > maxdiff) { 749 printf("-------expected--------\n"); 750 PrintPlane(truth, xsize, stride, ysize); 751 printf("-------actual--------\n"); 752 PrintPlane(other, xsize, stride, ysize); 753 if (source) { 754 printf("-------before yuv conversion: red--------\n"); 755 PrintChannel(source, 0); 756 printf("-------before yuv conversion: green------\n"); 757 PrintChannel(source, 1); 758 printf("-------before yuv conversion: blue-------\n"); 759 PrintChannel(source, 2); 760 } 761 return; 762 } 763 } 764 } 765 } 766 767 // YUV readback test. Create a test pattern, convert to YUV 768 // with reference implementation and compare to what gl_helper 769 // returns. 770 void TestYUVReadback(int xsize, 771 int ysize, 772 int output_xsize, 773 int output_ysize, 774 int xmargin, 775 int ymargin, 776 int test_pattern, 777 bool use_mrt) { 778 WebGLId src_texture = context_->createTexture(); 779 SkBitmap input_pixels; 780 input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize); 781 input_pixels.allocPixels(); 782 SkAutoLockPixels lock(input_pixels); 783 784 for (int x = 0; x < xsize; ++x) { 785 for (int y = 0; y < ysize; ++y) { 786 switch (test_pattern) { 787 case 0: // Smooth test pattern 788 SetChannel(&input_pixels, x, y, 0, x * 10); 789 SetChannel(&input_pixels, x, y, 1, y * 10); 790 SetChannel(&input_pixels, x, y, 2, (x + y) * 10); 791 SetChannel(&input_pixels, x, y, 3, 255); 792 break; 793 case 1: // Small blocks 794 SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); 795 SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); 796 SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); 797 SetChannel(&input_pixels, x, y, 3, 255); 798 break; 799 case 2: // Medium blocks 800 SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50); 801 SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50); 802 SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5); 803 SetChannel(&input_pixels, x, y, 3, 255); 804 break; 805 } 806 } 807 } 808 809 context_->bindTexture(GL_TEXTURE_2D, src_texture); 810 context_->texImage2D(GL_TEXTURE_2D, 811 0, 812 GL_RGBA, 813 xsize, 814 ysize, 815 0, 816 GL_RGBA, 817 GL_UNSIGNED_BYTE, 818 input_pixels.getPixels()); 819 820 gpu::Mailbox mailbox; 821 context_->genMailboxCHROMIUM(mailbox.name); 822 EXPECT_FALSE(mailbox.IsZero()); 823 context_->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); 824 uint32 sync_point = context_->insertSyncPoint(); 825 826 std::string message = base::StringPrintf("input size: %dx%d " 827 "output size: %dx%d " 828 "margin: %dx%d " 829 "pattern: %d", 830 xsize, ysize, 831 output_xsize, output_ysize, 832 xmargin, ymargin, 833 test_pattern); 834 scoped_ptr<ReadbackYUVInterface> yuv_reader( 835 helper_->CreateReadbackPipelineYUV( 836 content::GLHelper::SCALER_QUALITY_GOOD, 837 gfx::Size(xsize, ysize), 838 gfx::Rect(0, 0, xsize, ysize), 839 gfx::Size(output_xsize, output_ysize), 840 gfx::Rect(xmargin, ymargin, xsize, ysize), 841 false, 842 use_mrt)); 843 844 scoped_refptr<media::VideoFrame> output_frame = 845 media::VideoFrame::CreateFrame( 846 media::VideoFrame::YV12, 847 gfx::Size(output_xsize, output_ysize), 848 gfx::Rect(0, 0, output_xsize, output_ysize), 849 gfx::Size(output_xsize, output_ysize), 850 base::TimeDelta::FromSeconds(0)); 851 scoped_refptr<media::VideoFrame> truth_frame = 852 media::VideoFrame::CreateFrame( 853 media::VideoFrame::YV12, 854 gfx::Size(output_xsize, output_ysize), 855 gfx::Rect(0, 0, output_xsize, output_ysize), 856 gfx::Size(output_xsize, output_ysize), 857 base::TimeDelta::FromSeconds(0)); 858 859 base::RunLoop run_loop; 860 yuv_reader->ReadbackYUV( 861 mailbox, 862 sync_point, 863 output_frame.get(), 864 base::Bind(&callcallback, run_loop.QuitClosure())); 865 run_loop.Run(); 866 867 unsigned char* Y = truth_frame->data(media::VideoFrame::kYPlane); 868 unsigned char* U = truth_frame->data(media::VideoFrame::kUPlane); 869 unsigned char* V = truth_frame->data(media::VideoFrame::kVPlane); 870 int32 y_stride = truth_frame->stride(media::VideoFrame::kYPlane); 871 int32 u_stride = truth_frame->stride(media::VideoFrame::kUPlane); 872 int32 v_stride = truth_frame->stride(media::VideoFrame::kVPlane); 873 memset(Y, 0x00, y_stride * output_ysize); 874 memset(U, 0x80, u_stride * output_ysize / 2); 875 memset(V, 0x80, v_stride * output_ysize / 2); 876 877 for (int y = 0; y < ysize; y++) { 878 for (int x = 0; x < xsize; x++) { 879 Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte( 880 ChannelAsFloat(&input_pixels, x, y, 0) * 0.257 + 881 ChannelAsFloat(&input_pixels, x, y, 1) * 0.504 + 882 ChannelAsFloat(&input_pixels, x, y, 2) * 0.098 + 883 0.0625); 884 } 885 } 886 887 for (int y = 0; y < ysize / 2; y++) { 888 for (int x = 0; x < xsize / 2; x++) { 889 U[(y + ymargin / 2) * u_stride + x + xmargin / 2] = 890 float_to_byte( 891 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * -0.148 + 892 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.291 + 893 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * 0.439 + 894 0.5); 895 V[(y + ymargin / 2) * v_stride + x + xmargin / 2] = 896 float_to_byte( 897 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * 0.439 + 898 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.368 + 899 Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * -0.071 + 900 0.5); 901 } 902 } 903 904 ComparePlane(Y, output_frame->data(media::VideoFrame::kYPlane), 2, 905 output_xsize, y_stride, output_ysize, 906 &input_pixels, 907 message + " Y plane"); 908 ComparePlane(U, output_frame->data(media::VideoFrame::kUPlane), 2, 909 output_xsize / 2, u_stride, output_ysize / 2, 910 &input_pixels, 911 message + " U plane"); 912 ComparePlane(V, output_frame->data(media::VideoFrame::kVPlane), 2, 913 output_xsize / 2, v_stride, output_ysize / 2, 914 &input_pixels, 915 message + " V plane"); 916 917 context_->deleteTexture(src_texture); 918 } 919 920 void TestAddOps(int src, 921 int dst, 922 bool scale_x, 923 bool allow3) { 924 std::deque<GLHelperScaling::ScaleOp> ops; 925 GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops); 926 // Scale factor 3 is a special case. 927 // It is currently only allowed by itself. 928 if (allow3 && dst * 3 >= src && dst * 2 < src) { 929 EXPECT_EQ(ops[0].scale_factor, 3); 930 EXPECT_EQ(ops.size(), 1U); 931 EXPECT_EQ(ops[0].scale_x, scale_x); 932 EXPECT_EQ(ops[0].scale_size, dst); 933 return; 934 } 935 936 for (size_t i = 0; i < ops.size(); i++) { 937 EXPECT_EQ(ops[i].scale_x, scale_x); 938 if (i == 0) { 939 // Only the first op is allowed to be a scale up. 940 // (Scaling up *after* scaling down would make it fuzzy.) 941 EXPECT_TRUE(ops[0].scale_factor == 0 || 942 ops[0].scale_factor == 2); 943 } else { 944 // All other operations must be 50% downscales. 945 EXPECT_EQ(ops[i].scale_factor, 2); 946 } 947 } 948 // Check that the scale factors make sense and add up. 949 int tmp = dst; 950 for (int i = static_cast<int>(ops.size() - 1); i >= 0; i--) { 951 EXPECT_EQ(tmp, ops[i].scale_size); 952 if (ops[i].scale_factor == 0) { 953 EXPECT_EQ(i, 0); 954 EXPECT_GT(tmp, src); 955 tmp = src; 956 } else { 957 tmp *= ops[i].scale_factor; 958 } 959 } 960 EXPECT_EQ(tmp, src); 961 } 962 963 void CheckPipeline2(int xsize, int ysize, 964 int dst_xsize, int dst_ysize, 965 const std::string &description) { 966 std::vector<GLHelperScaling::ScalerStage> stages; 967 helper_scaling_->ConvertScalerOpsToScalerStages( 968 content::GLHelper::SCALER_QUALITY_GOOD, 969 gfx::Size(xsize, ysize), 970 gfx::Rect(0, 0, xsize, ysize), 971 gfx::Size(dst_xsize, dst_ysize), 972 false, 973 false, 974 &x_ops_, 975 &y_ops_, 976 &stages); 977 EXPECT_EQ(x_ops_.size(), 0U); 978 EXPECT_EQ(y_ops_.size(), 0U); 979 ValidateScalerStages( 980 content::GLHelper::SCALER_QUALITY_GOOD, 981 stages, 982 ""); 983 EXPECT_EQ(PrintStages(stages), description); 984 } 985 986 void CheckOptimizationsTest() { 987 // Basic upscale. X and Y should be combined into one pass. 988 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000)); 989 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000)); 990 CheckPipeline2(1024, 768, 2000, 2000, 991 "1024x768 -> 2000x2000 bilinear\n"); 992 993 // X scaled 1/2, Y upscaled, should still be one pass. 994 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512)); 995 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000)); 996 CheckPipeline2(1024, 768, 512, 2000, 997 "1024x768 -> 512x2000 bilinear\n"); 998 999 // X upscaled, Y scaled 1/2, one bilinear pass 1000 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000)); 1001 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384)); 1002 CheckPipeline2(1024, 768, 2000, 384, 1003 "1024x768 -> 2000x384 bilinear\n"); 1004 1005 // X scaled 1/2, Y scaled 1/2, one bilinear pass 1006 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512)); 1007 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384)); 1008 CheckPipeline2(1024, 768, 2000, 384, 1009 "1024x768 -> 512x384 bilinear\n"); 1010 1011 // X scaled 1/2, Y scaled to 60%, one bilinear2 pass. 1012 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50)); 1013 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); 1014 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); 1015 CheckPipeline2(100, 100, 50, 60, 1016 "100x100 -> 50x60 bilinear2 Y\n"); 1017 1018 // X scaled to 60%, Y scaled 1/2, one bilinear2 pass. 1019 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); 1020 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); 1021 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50)); 1022 CheckPipeline2(100, 100, 50, 60, 1023 "100x100 -> 60x50 bilinear2 X\n"); 1024 1025 // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass. 1026 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); 1027 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); 1028 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); 1029 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); 1030 CheckPipeline2(100, 100, 60, 60, 1031 "100x100 -> 60x60 bilinear2x2\n"); 1032 1033 // X scaled to 40%, Y scaled 40%, two bilinear3 passes. 1034 x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40)); 1035 y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40)); 1036 CheckPipeline2(100, 100, 40, 40, 1037 "100x100 -> 100x40 bilinear3 Y\n" 1038 "100x40 -> 40x40 bilinear3 X\n"); 1039 1040 // X scaled to 60%, Y scaled 40% 1041 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); 1042 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); 1043 y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40)); 1044 CheckPipeline2(100, 100, 60, 40, 1045 "100x100 -> 100x40 bilinear3 Y\n" 1046 "100x40 -> 60x40 bilinear2 X\n"); 1047 1048 // X scaled to 40%, Y scaled 60% 1049 x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40)); 1050 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); 1051 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); 1052 CheckPipeline2(100, 100, 40, 60, 1053 "100x100 -> 100x60 bilinear2 Y\n" 1054 "100x60 -> 40x60 bilinear3 X\n"); 1055 1056 // X scaled to 30%, Y scaled 30% 1057 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); 1058 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); 1059 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30)); 1060 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); 1061 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); 1062 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); 1063 CheckPipeline2(100, 100, 30, 30, 1064 "100x100 -> 100x30 bilinear4 Y\n" 1065 "100x30 -> 30x30 bilinear4 X\n"); 1066 1067 // X scaled to 50%, Y scaled 30% 1068 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50)); 1069 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); 1070 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); 1071 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); 1072 CheckPipeline2(100, 100, 50, 30, 1073 "100x100 -> 50x30 bilinear4 Y\n"); 1074 1075 // X scaled to 150%, Y scaled 30% 1076 // Note that we avoid combinding X and Y passes 1077 // as that would probably be LESS efficient here. 1078 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150)); 1079 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); 1080 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); 1081 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); 1082 CheckPipeline2(100, 100, 150, 30, 1083 "100x100 -> 100x30 bilinear4 Y\n" 1084 "100x30 -> 150x30 bilinear\n"); 1085 1086 // X scaled to 1%, Y scaled 1% 1087 x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128)); 1088 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64)); 1089 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32)); 1090 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16)); 1091 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8)); 1092 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4)); 1093 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2)); 1094 x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1)); 1095 y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128)); 1096 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64)); 1097 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32)); 1098 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16)); 1099 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8)); 1100 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4)); 1101 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2)); 1102 y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1)); 1103 CheckPipeline2(100, 100, 30, 30, 1104 "100x100 -> 100x32 bilinear4 Y\n" 1105 "100x32 -> 100x4 bilinear4 Y\n" 1106 "100x4 -> 64x1 bilinear2x2\n" 1107 "64x1 -> 8x1 bilinear4 X\n" 1108 "8x1 -> 1x1 bilinear4 X\n"); 1109 } 1110 1111 scoped_ptr<WebKit::WebGraphicsContext3D> context_; 1112 scoped_ptr<content::GLHelper> helper_; 1113 scoped_ptr<content::GLHelperScaling> helper_scaling_; 1114 std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; 1115 }; 1116 1117 // Reenable once http://crbug.com/162291 is fixed. 1118 TEST_F(GLHelperTest, YUVReadbackTest) { 1119 int sizes[] = { 2, 4, 14 }; 1120 for (int use_mrt = 0; use_mrt <= 1 ; use_mrt++) { 1121 for (unsigned int x = 0; x < arraysize(sizes); x++) { 1122 for (unsigned int y = 0; y < arraysize(sizes); y++) { 1123 for (unsigned int ox = x; ox < arraysize(sizes); ox++) { 1124 for (unsigned int oy = y; oy < arraysize(sizes); oy++) { 1125 // If output is a subsection of the destination frame, (letterbox) 1126 // then try different variations of where the subsection goes. 1127 for (Margin xm = x < ox ? MarginLeft : MarginRight; 1128 xm <= MarginRight; 1129 xm = NextMargin(xm)) { 1130 for (Margin ym = y < oy ? MarginLeft : MarginRight; 1131 ym <= MarginRight; 1132 ym = NextMargin(ym)) { 1133 for (int pattern = 0; pattern < 3; pattern++) { 1134 TestYUVReadback( 1135 sizes[x], 1136 sizes[y], 1137 sizes[ox], 1138 sizes[oy], 1139 compute_margin(sizes[x], sizes[ox], xm), 1140 compute_margin(sizes[y], sizes[oy], ym), 1141 pattern, 1142 use_mrt == 1); 1143 if (HasFailure()) { 1144 return; 1145 } 1146 } 1147 } 1148 } 1149 } 1150 } 1151 } 1152 } 1153 } 1154 } 1155 1156 1157 // Per pixel tests, all sizes are small so that we can print 1158 // out the generated bitmaps. 1159 TEST_F(GLHelperTest, ScaleTest) { 1160 int sizes[] = {3, 6, 16}; 1161 for (size_t q = 0; q < arraysize(kQualities); q++) { 1162 for (int x = 0; x < 3; x++) { 1163 for (int y = 0; y < 3; y++) { 1164 for (int dst_x = 0; dst_x < 3; dst_x++) { 1165 for (int dst_y = 0; dst_y < 3; dst_y++) { 1166 for (int pattern = 0; pattern < 3; pattern++) { 1167 TestScale(sizes[x], 1168 sizes[y], 1169 sizes[dst_x], 1170 sizes[dst_y], 1171 pattern, 1172 q); 1173 if (HasFailure()) { 1174 return; 1175 } 1176 } 1177 } 1178 } 1179 } 1180 } 1181 } 1182 } 1183 1184 // Validate that all scaling generates valid pipelines. 1185 TEST_F(GLHelperTest, ValidateScalerPipelines) { 1186 int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096}; 1187 for (size_t q = 0; q < arraysize(kQualities); q++) { 1188 for (size_t x = 0; x < arraysize(sizes); x++) { 1189 for (size_t y = 0; y < arraysize(sizes); y++) { 1190 for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) { 1191 for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) { 1192 TestScalerPipeline(q, 1193 sizes[x], sizes[y], 1194 sizes[dst_x], sizes[dst_y]); 1195 if (HasFailure()) { 1196 return; 1197 } 1198 } 1199 } 1200 } 1201 } 1202 } 1203 } 1204 1205 // Make sure we don't create overly complicated pipelines 1206 // for a few common use cases. 1207 TEST_F(GLHelperTest, CheckSpecificPipelines) { 1208 // Upscale should be single pass. 1209 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD, 1210 1024, 700, 1280, 720, 1211 "1024x700 -> 1280x720 bilinear\n"); 1212 // Slight downscale should use BILINEAR2X2. 1213 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD, 1214 1280, 720, 1024, 700, 1215 "1280x720 -> 1024x700 bilinear2x2\n"); 1216 // Most common tab capture pipeline on the Pixel. 1217 // Should be using two BILINEAR3 passes. 1218 CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD, 1219 2560, 1476, 1249, 720, 1220 "2560x1476 -> 2560x720 bilinear3 Y\n" 1221 "2560x720 -> 1249x720 bilinear3 X\n"); 1222 } 1223 1224 TEST_F(GLHelperTest, ScalerOpTest) { 1225 for (int allow3 = 0; allow3 <= 1; allow3++) { 1226 for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) { 1227 for (int src = 1; src < 2049; src++) { 1228 TestAddOps(src, dst, allow3 == 1, (src & 1) == 1); 1229 if (HasFailure()) { 1230 LOG(ERROR) << "Failed for src=" << src 1231 << " dst=" << dst 1232 << " allow3=" << allow3; 1233 return; 1234 } 1235 } 1236 } 1237 } 1238 } 1239 1240 TEST_F(GLHelperTest, CheckOptimizations) { 1241 // Test in baseclass since it is friends with GLHelperScaling 1242 CheckOptimizationsTest(); 1243 } 1244 1245 } // namespace 1246 1247 // These tests needs to run against a proper GL environment, so we 1248 // need to set it up before we can run the tests. 1249 int main(int argc, char** argv) { 1250 CommandLine::Init(argc, argv); 1251 base::TestSuite* suite = new content::ContentTestSuite(argc, argv); 1252 #if defined(OS_MACOSX) 1253 base::mac::ScopedNSAutoreleasePool pool; 1254 #endif 1255 #if defined(TOOLKIT_GTK) 1256 gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess()); 1257 #endif 1258 gfx::GLSurface::InitializeOneOff(); 1259 gpu::ApplyGpuDriverBugWorkarounds(CommandLine::ForCurrentProcess()); 1260 1261 content::UnitTestTestSuite runner(suite); 1262 base::MessageLoop message_loop; 1263 return runner.Run(); 1264 } 1265