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