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