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 "content/common/gpu/client/gl_helper_scaling.h" 6 7 #include <deque> 8 #include <string> 9 #include <vector> 10 11 #include "base/bind.h" 12 #include "base/debug/trace_event.h" 13 #include "base/lazy_instance.h" 14 #include "base/logging.h" 15 #include "base/memory/ref_counted.h" 16 #include "base/message_loop/message_loop.h" 17 #include "base/time/time.h" 18 #include "third_party/WebKit/public/platform/WebCString.h" 19 #include "third_party/skia/include/core/SkRegion.h" 20 #include "ui/gfx/rect.h" 21 #include "ui/gfx/size.h" 22 #include "ui/gl/gl_bindings.h" 23 24 using blink::WebGLId; 25 using blink::WebGraphicsContext3D; 26 27 namespace content { 28 29 GLHelperScaling::GLHelperScaling(blink::WebGraphicsContext3D* context, 30 GLHelper* helper) 31 : context_(context), 32 helper_(helper), 33 vertex_attributes_buffer_(context_, context_->createBuffer()) { 34 InitBuffer(); 35 } 36 37 GLHelperScaling::~GLHelperScaling() { 38 } 39 40 // Used to keep track of a generated shader program. The program 41 // is passed in as text through Setup and is used by calling 42 // UseProgram() with the right parameters. Note that |context_| 43 // and |helper_| are assumed to live longer than this program. 44 class ShaderProgram : public base::RefCounted<ShaderProgram> { 45 public: 46 ShaderProgram(WebGraphicsContext3D* context, 47 GLHelper* helper) 48 : context_(context), 49 helper_(helper), 50 program_(context, context->createProgram()) { 51 } 52 53 // Compile shader program, return true if successful. 54 bool Setup(const blink::WGC3Dchar* vertex_shader_text, 55 const blink::WGC3Dchar* fragment_shader_text); 56 57 // UseProgram must be called with GL_TEXTURE_2D bound to the 58 // source texture and GL_ARRAY_BUFFER bound to a vertex 59 // attribute buffer. 60 void UseProgram(const gfx::Size& src_size, 61 const gfx::Rect& src_subrect, 62 const gfx::Size& dst_size, 63 bool scale_x, 64 bool flip_y, 65 GLfloat color_weights[4]); 66 67 private: 68 friend class base::RefCounted<ShaderProgram>; 69 ~ShaderProgram() {} 70 71 WebGraphicsContext3D* context_; 72 GLHelper* helper_; 73 74 // A program for copying a source texture into a destination texture. 75 ScopedProgram program_; 76 77 // The location of the position in the program. 78 blink::WGC3Dint position_location_; 79 // The location of the texture coordinate in the program. 80 blink::WGC3Dint texcoord_location_; 81 // The location of the source texture in the program. 82 blink::WGC3Dint texture_location_; 83 // The location of the texture coordinate of 84 // the sub-rectangle in the program. 85 blink::WGC3Dint src_subrect_location_; 86 // Location of size of source image in pixels. 87 blink::WGC3Dint src_pixelsize_location_; 88 // Location of size of destination image in pixels. 89 blink::WGC3Dint dst_pixelsize_location_; 90 // Location of vector for scaling direction. 91 blink::WGC3Dint scaling_vector_location_; 92 // Location of color weights. 93 blink::WGC3Dint color_weights_location_; 94 95 DISALLOW_COPY_AND_ASSIGN(ShaderProgram); 96 }; 97 98 99 // Implementation of a single stage in a scaler pipeline. If the pipeline has 100 // multiple stages, it calls Scale() on the subscaler, then further scales the 101 // output. Caches textures and framebuffers to avoid allocating/deleting 102 // them once per frame, which can be expensive on some drivers. 103 class ScalerImpl : 104 public GLHelper::ScalerInterface, 105 public GLHelperScaling::ShaderInterface { 106 public: 107 // |context| and |copy_impl| are expected to live longer than this object. 108 // |src_size| is the size of the input texture in pixels. 109 // |dst_size| is the size of the output texutre in pixels. 110 // |src_subrect| is the portion of the src to copy to the output texture. 111 // If |scale_x| is true, we are scaling along the X axis, otherwise Y. 112 // If we are scaling in both X and Y, |scale_x| is ignored. 113 // If |vertically_flip_texture| is true, output will be upside-down. 114 // If |swizzle| is true, RGBA will be transformed into BGRA. 115 // |color_weights| are only used together with SHADER_PLANAR to specify 116 // how to convert RGB colors into a single value. 117 ScalerImpl(WebGraphicsContext3D* context, 118 GLHelperScaling* scaler_helper, 119 const GLHelperScaling::ScalerStage &scaler_stage, 120 ScalerImpl* subscaler, 121 const float* color_weights) : 122 context_(context), 123 scaler_helper_(scaler_helper), 124 spec_(scaler_stage), 125 intermediate_texture_(0), 126 dst_framebuffer_(context, context_->createFramebuffer()), 127 subscaler_(subscaler) { 128 if (color_weights) { 129 color_weights_[0] = color_weights[0]; 130 color_weights_[1] = color_weights[1]; 131 color_weights_[2] = color_weights[2]; 132 color_weights_[3] = color_weights[3]; 133 } else { 134 color_weights_[0] = 0.0; 135 color_weights_[1] = 0.0; 136 color_weights_[2] = 0.0; 137 color_weights_[3] = 0.0; 138 } 139 shader_program_ = scaler_helper_->GetShaderProgram(spec_.shader, 140 spec_.swizzle); 141 142 if (subscaler_) { 143 intermediate_texture_ = context_->createTexture(); 144 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder( 145 context_, 146 intermediate_texture_); 147 context_->texImage2D(GL_TEXTURE_2D, 148 0, 149 GL_RGBA, 150 spec_.src_size.width(), 151 spec_.src_size.height(), 152 0, 153 GL_RGBA, 154 GL_UNSIGNED_BYTE, 155 NULL); 156 } 157 } 158 159 virtual ~ScalerImpl() { 160 if (intermediate_texture_) { 161 context_->deleteTexture(intermediate_texture_); 162 } 163 } 164 165 // GLHelperShader::ShaderInterface implementation. 166 virtual void Execute( 167 blink::WebGLId source_texture, 168 const std::vector<blink::WebGLId>& dest_textures) OVERRIDE { 169 if (subscaler_) { 170 subscaler_->Scale(source_texture, intermediate_texture_); 171 source_texture = intermediate_texture_; 172 } 173 174 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( 175 context_, 176 dst_framebuffer_); 177 DCHECK_GT(dest_textures.size(), 0U); 178 scoped_ptr<blink::WGC3Denum[]> buffers( 179 new blink::WGC3Denum[dest_textures.size()]); 180 for (size_t t = 0; t < dest_textures.size(); t++) { 181 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, 182 dest_textures[t]); 183 context_->framebufferTexture2D(GL_FRAMEBUFFER, 184 GL_COLOR_ATTACHMENT0 + t, 185 GL_TEXTURE_2D, 186 dest_textures[t], 187 0); 188 buffers[t] = GL_COLOR_ATTACHMENT0 + t; 189 } 190 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, 191 source_texture); 192 193 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 194 GL_LINEAR); 195 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 196 GL_LINEAR); 197 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 198 GL_CLAMP_TO_EDGE); 199 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 200 GL_CLAMP_TO_EDGE); 201 202 ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder( 203 context_, 204 scaler_helper_->vertex_attributes_buffer_); 205 shader_program_->UseProgram(spec_.src_size, 206 spec_.src_subrect, 207 spec_.dst_size, 208 spec_.scale_x, 209 spec_.vertically_flip_texture, 210 color_weights_); 211 context_->viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height()); 212 213 if (dest_textures.size() > 1) { 214 DCHECK_LE(static_cast<int>(dest_textures.size()), 215 scaler_helper_->helper_->MaxDrawBuffers()); 216 context_->drawBuffersEXT(dest_textures.size(), buffers.get()); 217 } 218 // Conduct texture mapping by drawing a quad composed of two triangles. 219 context_->drawArrays(GL_TRIANGLE_STRIP, 0, 4); 220 if (dest_textures.size() > 1) { 221 // Set the draw buffers back to not confuse others. 222 context_->drawBuffersEXT(1, &buffers[0]); 223 } 224 } 225 226 // GLHelper::ScalerInterface implementation. 227 virtual void Scale(blink::WebGLId source_texture, 228 blink::WebGLId dest_texture) OVERRIDE { 229 std::vector<blink::WebGLId> tmp(1); 230 tmp[0] = dest_texture; 231 Execute(source_texture, tmp); 232 } 233 234 virtual const gfx::Size& SrcSize() OVERRIDE { 235 if (subscaler_) { 236 return subscaler_->SrcSize(); 237 } 238 return spec_.src_size; 239 } 240 virtual const gfx::Rect& SrcSubrect() OVERRIDE { 241 if (subscaler_) { 242 return subscaler_->SrcSubrect(); 243 } 244 return spec_.src_subrect; 245 } 246 virtual const gfx::Size& DstSize() OVERRIDE { 247 return spec_.dst_size; 248 } 249 250 private: 251 WebGraphicsContext3D* context_; 252 GLHelperScaling* scaler_helper_; 253 GLHelperScaling::ScalerStage spec_; 254 GLfloat color_weights_[4]; 255 blink::WebGLId intermediate_texture_; 256 scoped_refptr<ShaderProgram> shader_program_; 257 ScopedFramebuffer dst_framebuffer_; 258 scoped_ptr<ScalerImpl> subscaler_; 259 }; 260 261 GLHelperScaling::ScalerStage::ScalerStage( 262 ShaderType shader_, 263 gfx::Size src_size_, 264 gfx::Rect src_subrect_, 265 gfx::Size dst_size_, 266 bool scale_x_, 267 bool vertically_flip_texture_, 268 bool swizzle_) 269 : shader(shader_), 270 src_size(src_size_), 271 src_subrect(src_subrect_), 272 dst_size(dst_size_), 273 scale_x(scale_x_), 274 vertically_flip_texture(vertically_flip_texture_), 275 swizzle(swizzle_) { 276 } 277 278 // The important inputs for this function is |x_ops| and 279 // |y_ops|. They represent scaling operations to be done 280 // on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST, 281 // then we will interpret these scale operations literally and we'll 282 // create one scaler stage for each ScaleOp. However, if |quality| 283 // is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations 284 // by combining two or more ScaleOps in to a single scaler stage. 285 // Normally we process ScaleOps from |y_ops| first and |x_ops| after 286 // all |y_ops| are processed, but sometimes we can combine one or more 287 // operation from both queues essentially for free. This is the reason 288 // why |x_ops| and |y_ops| aren't just one single queue. 289 void GLHelperScaling::ConvertScalerOpsToScalerStages( 290 GLHelper::ScalerQuality quality, 291 gfx::Size src_size, 292 gfx::Rect src_subrect, 293 const gfx::Size& dst_size, 294 bool vertically_flip_texture, 295 bool swizzle, 296 std::deque<GLHelperScaling::ScaleOp>* x_ops, 297 std::deque<GLHelperScaling::ScaleOp>* y_ops, 298 std::vector<ScalerStage> *scaler_stages) { 299 while (!x_ops->empty() || !y_ops->empty()) { 300 gfx::Size intermediate_size = src_subrect.size(); 301 std::deque<ScaleOp>* current_queue = NULL; 302 303 if (!y_ops->empty()) { 304 current_queue = y_ops; 305 } else { 306 current_queue = x_ops; 307 } 308 309 ShaderType current_shader = SHADER_BILINEAR; 310 switch (current_queue->front().scale_factor) { 311 case 0: 312 if (quality == GLHelper::SCALER_QUALITY_BEST) { 313 current_shader = SHADER_BICUBIC_UPSCALE; 314 } 315 break; 316 case 2: 317 if (quality == GLHelper::SCALER_QUALITY_BEST) { 318 current_shader = SHADER_BICUBIC_HALF_1D; 319 } 320 break; 321 case 3: 322 DCHECK(quality != GLHelper::SCALER_QUALITY_BEST); 323 current_shader = SHADER_BILINEAR3; 324 break; 325 default: 326 NOTREACHED(); 327 } 328 bool scale_x = current_queue->front().scale_x; 329 current_queue->front().UpdateSize(&intermediate_size); 330 current_queue->pop_front(); 331 332 // Optimization: Sometimes we can combine 2-4 scaling operations into 333 // one operation. 334 if (quality == GLHelper::SCALER_QUALITY_GOOD) { 335 if (!current_queue->empty() && current_shader == SHADER_BILINEAR) { 336 // Combine two steps in the same dimension. 337 current_queue->front().UpdateSize(&intermediate_size); 338 current_queue->pop_front(); 339 current_shader = SHADER_BILINEAR2; 340 if (!current_queue->empty()) { 341 // Combine three steps in the same dimension. 342 current_queue->front().UpdateSize(&intermediate_size); 343 current_queue->pop_front(); 344 current_shader = SHADER_BILINEAR4; 345 } 346 } 347 // Check if we can combine some steps in the other dimension as well. 348 // Since all shaders currently use GL_LINEAR, we can easily scale up 349 // or scale down by exactly 2x at the same time as we do another 350 // operation. Currently, the following mergers are supported: 351 // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down) 352 // * 2 bilinear Y-passes with 2 bilinear X-passes 353 // * 1 bilinear Y-pass with N bilinear X-pass 354 // * N bilinear Y-passes with 1 bilinear X-pass (down only) 355 // Measurements indicate that generalizing this for 3x3 and 4x4 356 // makes it slower on some platforms, such as the Pixel. 357 if (!scale_x && x_ops->size() > 0 && 358 x_ops->front().scale_factor <= 2) { 359 int x_passes = 0; 360 if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) { 361 // 2y + 2x passes 362 x_passes = 2; 363 current_shader = SHADER_BILINEAR2X2; 364 } else if (current_shader == SHADER_BILINEAR) { 365 // 1y + Nx passes 366 scale_x = true; 367 switch (x_ops->size()) { 368 case 0: 369 NOTREACHED(); 370 case 1: 371 if (x_ops->front().scale_factor == 3) { 372 current_shader = SHADER_BILINEAR3; 373 } 374 x_passes = 1; 375 break; 376 case 2: 377 x_passes = 2; 378 current_shader = SHADER_BILINEAR2; 379 break; 380 default: 381 x_passes = 3; 382 current_shader = SHADER_BILINEAR4; 383 break; 384 } 385 } else if (x_ops->front().scale_factor == 2) { 386 // Ny + 1x-downscale 387 x_passes = 1; 388 } 389 390 for (int i = 0; i < x_passes; i++) { 391 x_ops->front().UpdateSize(&intermediate_size); 392 x_ops->pop_front(); 393 } 394 } 395 } 396 397 scaler_stages->push_back(ScalerStage(current_shader, 398 src_size, 399 src_subrect, 400 intermediate_size, 401 scale_x, 402 vertically_flip_texture, 403 swizzle)); 404 src_size = intermediate_size; 405 src_subrect = gfx::Rect(intermediate_size); 406 vertically_flip_texture = false; 407 swizzle = false; 408 } 409 } 410 411 void GLHelperScaling::ComputeScalerStages( 412 GLHelper::ScalerQuality quality, 413 const gfx::Size& src_size, 414 const gfx::Rect& src_subrect, 415 const gfx::Size& dst_size, 416 bool vertically_flip_texture, 417 bool swizzle, 418 std::vector<ScalerStage> *scaler_stages) { 419 if (quality == GLHelper::SCALER_QUALITY_FAST || 420 src_subrect.size() == dst_size) { 421 scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, 422 src_size, 423 src_subrect, 424 dst_size, 425 false, 426 vertically_flip_texture, 427 swizzle)); 428 return; 429 } 430 431 std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops; 432 GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), 433 dst_size.width(), 434 true, 435 quality == GLHelper::SCALER_QUALITY_GOOD, 436 &x_ops); 437 GLHelperScaling::ScaleOp::AddOps(src_subrect.height(), 438 dst_size.height(), 439 false, 440 quality == GLHelper::SCALER_QUALITY_GOOD, 441 &y_ops); 442 443 ConvertScalerOpsToScalerStages( 444 quality, 445 src_size, 446 src_subrect, 447 dst_size, 448 vertically_flip_texture, 449 swizzle, 450 &x_ops, 451 &y_ops, 452 scaler_stages); 453 } 454 455 GLHelper::ScalerInterface* 456 GLHelperScaling::CreateScaler(GLHelper::ScalerQuality quality, 457 gfx::Size src_size, 458 gfx::Rect src_subrect, 459 const gfx::Size& dst_size, 460 bool vertically_flip_texture, 461 bool swizzle) { 462 std::vector<ScalerStage> scaler_stages; 463 ComputeScalerStages(quality, 464 src_size, 465 src_subrect, 466 dst_size, 467 vertically_flip_texture, 468 swizzle, 469 &scaler_stages); 470 471 ScalerImpl* ret = NULL; 472 for (unsigned int i = 0; i < scaler_stages.size(); i++) { 473 ret = new ScalerImpl(context_, this, scaler_stages[i], ret, NULL); 474 } 475 return ret; 476 } 477 478 GLHelper::ScalerInterface* 479 GLHelperScaling::CreatePlanarScaler( 480 const gfx::Size& src_size, 481 const gfx::Rect& src_subrect, 482 const gfx::Size& dst_size, 483 bool vertically_flip_texture, 484 const float color_weights[4]) { 485 ScalerStage stage(SHADER_PLANAR, 486 src_size, 487 src_subrect, 488 dst_size, 489 true, 490 vertically_flip_texture, 491 false); 492 return new ScalerImpl(context_, this, stage, NULL, color_weights); 493 } 494 495 GLHelperScaling::ShaderInterface* 496 GLHelperScaling::CreateYuvMrtShader( 497 const gfx::Size& src_size, 498 const gfx::Rect& src_subrect, 499 const gfx::Size& dst_size, 500 bool vertically_flip_texture, 501 ShaderType shader) { 502 DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2); 503 ScalerStage stage(shader, 504 src_size, 505 src_subrect, 506 dst_size, 507 true, 508 vertically_flip_texture, 509 false); 510 return new ScalerImpl(context_, this, stage, NULL, NULL); 511 } 512 513 const blink::WGC3Dfloat GLHelperScaling::kVertexAttributes[] = { 514 -1.0f, -1.0f, 0.0f, 0.0f, 515 1.0f, -1.0f, 1.0f, 0.0f, 516 -1.0f, 1.0f, 0.0f, 1.0f, 517 1.0f, 1.0f, 1.0f, 1.0f, 518 }; 519 520 void GLHelperScaling::InitBuffer() { 521 ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder( 522 context_, vertex_attributes_buffer_); 523 context_->bufferData(GL_ARRAY_BUFFER, 524 sizeof(kVertexAttributes), 525 kVertexAttributes, 526 GL_STATIC_DRAW); 527 } 528 529 scoped_refptr<ShaderProgram> 530 GLHelperScaling::GetShaderProgram(ShaderType type, 531 bool swizzle) { 532 ShaderProgramKeyType key(type, swizzle); 533 scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]); 534 if (!cache_entry.get()) { 535 cache_entry = new ShaderProgram(context_, helper_); 536 std::basic_string<blink::WGC3Dchar> vertex_program; 537 std::basic_string<blink::WGC3Dchar> fragment_program; 538 std::basic_string<blink::WGC3Dchar> vertex_header; 539 std::basic_string<blink::WGC3Dchar> fragment_directives; 540 std::basic_string<blink::WGC3Dchar> fragment_header; 541 std::basic_string<blink::WGC3Dchar> shared_variables; 542 543 vertex_header.append( 544 "precision highp float;\n" 545 "attribute vec2 a_position;\n" 546 "attribute vec2 a_texcoord;\n" 547 "uniform vec4 src_subrect;\n"); 548 549 fragment_header.append( 550 "precision mediump float;\n" 551 "uniform sampler2D s_texture;\n"); 552 553 vertex_program.append( 554 " gl_Position = vec4(a_position, 0.0, 1.0);\n" 555 " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"); 556 557 switch (type) { 558 case SHADER_BILINEAR: 559 shared_variables.append("varying vec2 v_texcoord;\n"); 560 vertex_program.append(" v_texcoord = texcoord;\n"); 561 fragment_program.append( 562 " gl_FragColor = texture2D(s_texture, v_texcoord);\n"); 563 break; 564 565 case SHADER_BILINEAR2: 566 // This is equivialent to two passes of the BILINEAR shader above. 567 // It can be used to scale an image down 1.0x-2.0x in either dimension, 568 // or exactly 4x. 569 shared_variables.append( 570 "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad 571 vertex_header.append( 572 "uniform vec2 scaling_vector;\n" 573 "uniform vec2 dst_pixelsize;\n"); 574 vertex_program.append( 575 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 576 " step /= 4.0;\n" 577 " v_texcoords.xy = texcoord + step;\n" 578 " v_texcoords.zw = texcoord - step;\n"); 579 580 fragment_program.append( 581 " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n" 582 " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n"); 583 break; 584 585 case SHADER_BILINEAR3: 586 // This is kind of like doing 1.5 passes of the BILINEAR shader. 587 // It can be used to scale an image down 1.5x-3.0x, or exactly 6x. 588 shared_variables.append( 589 "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad 590 "varying vec2 v_texcoords2;\n"); 591 vertex_header.append( 592 "uniform vec2 scaling_vector;\n" 593 "uniform vec2 dst_pixelsize;\n"); 594 vertex_program.append( 595 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 596 " step /= 3.0;\n" 597 " v_texcoords1.xy = texcoord + step;\n" 598 " v_texcoords1.zw = texcoord;\n" 599 " v_texcoords2 = texcoord - step;\n"); 600 fragment_program.append( 601 " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n" 602 " texture2D(s_texture, v_texcoords1.zw) +\n" 603 " texture2D(s_texture, v_texcoords2)) / 3.0;\n"); 604 break; 605 606 case SHADER_BILINEAR4: 607 // This is equivialent to three passes of the BILINEAR shader above, 608 // It can be used to scale an image down 2.0x-4.0x or exactly 8x. 609 shared_variables.append( 610 "varying vec4 v_texcoords[2];\n"); 611 vertex_header.append( 612 "uniform vec2 scaling_vector;\n" 613 "uniform vec2 dst_pixelsize;\n"); 614 vertex_program.append( 615 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 616 " step /= 8.0;\n" 617 " v_texcoords[0].xy = texcoord - step * 3.0;\n" 618 " v_texcoords[0].zw = texcoord - step;\n" 619 " v_texcoords[1].xy = texcoord + step;\n" 620 " v_texcoords[1].zw = texcoord + step * 3.0;\n"); 621 fragment_program.append( 622 " gl_FragColor = (\n" 623 " texture2D(s_texture, v_texcoords[0].xy) +\n" 624 " texture2D(s_texture, v_texcoords[0].zw) +\n" 625 " texture2D(s_texture, v_texcoords[1].xy) +\n" 626 " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); 627 break; 628 629 case SHADER_BILINEAR2X2: 630 // This is equivialent to four passes of the BILINEAR shader above. 631 // Two in each dimension. It can be used to scale an image down 632 // 1.0x-2.0x in both X and Y directions. Or, it could be used to 633 // scale an image down by exactly 4x in both dimensions. 634 shared_variables.append( 635 "varying vec4 v_texcoords[2];\n"); 636 vertex_header.append( 637 "uniform vec2 dst_pixelsize;\n"); 638 vertex_program.append( 639 " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n" 640 " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" 641 " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" 642 " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" 643 " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n"); 644 fragment_program.append( 645 " gl_FragColor = (\n" 646 " texture2D(s_texture, v_texcoords[0].xy) +\n" 647 " texture2D(s_texture, v_texcoords[0].zw) +\n" 648 " texture2D(s_texture, v_texcoords[1].xy) +\n" 649 " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); 650 break; 651 652 case SHADER_BICUBIC_HALF_1D: 653 // This scales down texture by exactly half in one dimension. 654 // directions in one pass. We use bilinear lookup to reduce 655 // the number of texture reads from 8 to 4 656 shared_variables.append( 657 "const float CenterDist = 99.0 / 140.0;\n" 658 "const float LobeDist = 11.0 / 4.0;\n" 659 "const float CenterWeight = 35.0 / 64.0;\n" 660 "const float LobeWeight = -3.0 / 64.0;\n" 661 "varying vec4 v_texcoords[2];\n"); 662 vertex_header.append( 663 "uniform vec2 scaling_vector;\n" 664 "uniform vec2 src_pixelsize;\n"); 665 vertex_program.append( 666 " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n" 667 " v_texcoords[0].xy = texcoord - LobeDist * step;\n" 668 " v_texcoords[0].zw = texcoord - CenterDist * step;\n" 669 " v_texcoords[1].xy = texcoord + CenterDist * step;\n" 670 " v_texcoords[1].zw = texcoord + LobeDist * step;\n"); 671 fragment_program.append( 672 " gl_FragColor = \n" 673 // Lobe pixels 674 " (texture2D(s_texture, v_texcoords[0].xy) +\n" 675 " texture2D(s_texture, v_texcoords[1].zw)) *\n" 676 " LobeWeight +\n" 677 // Center pixels 678 " (texture2D(s_texture, v_texcoords[0].zw) +\n" 679 " texture2D(s_texture, v_texcoords[1].xy)) *\n" 680 " CenterWeight;\n"); 681 break; 682 683 case SHADER_BICUBIC_UPSCALE: 684 // When scaling up, we need 4 texture reads, but we can 685 // save some instructions because will know in which range of 686 // the bicubic function each call call to the bicubic function 687 // will be in. 688 // Also, when sampling the bicubic function like this, the sum 689 // is always exactly one, so we can skip normalization as well. 690 shared_variables.append( 691 "varying vec2 v_texcoord;\n"); 692 vertex_program.append( 693 " v_texcoord = texcoord;\n"); 694 fragment_header.append( 695 "uniform vec2 src_pixelsize;\n" 696 "uniform vec2 scaling_vector;\n" 697 "const float a = -0.5;\n" 698 // This function is equivialent to calling the bicubic 699 // function with x-1, x, 1-x and 2-x 700 // (assuming 0 <= x < 1) 701 "vec4 filt4(float x) {\n" 702 " return vec4(x * x * x, x * x, x, 1) *\n" 703 " mat4( a, -2.0 * a, a, 0.0,\n" 704 " a + 2.0, -a - 3.0, 0.0, 1.0,\n" 705 " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n" 706 " -a, a, 0.0, 0.0);\n" 707 "}\n" 708 "mat4 pixels_x(vec2 pos, vec2 step) {\n" 709 " return mat4(\n" 710 " texture2D(s_texture, pos - step),\n" 711 " texture2D(s_texture, pos),\n" 712 " texture2D(s_texture, pos + step),\n" 713 " texture2D(s_texture, pos + step * 2.0));\n" 714 "}\n"); 715 fragment_program.append( 716 " vec2 pixel_pos = v_texcoord * src_pixelsize - \n" 717 " scaling_vector / 2.0;\n" 718 " float frac = fract(dot(pixel_pos, scaling_vector));\n" 719 " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n" 720 " vec2 step = scaling_vector / src_pixelsize;\n" 721 " gl_FragColor = pixels_x(base, step) * filt4(frac);\n"); 722 break; 723 724 case SHADER_PLANAR: 725 // Converts four RGBA pixels into one pixel. Each RGBA 726 // pixel will be dot-multiplied with the color weights and 727 // then placed into a component of the output. This is used to 728 // convert RGBA textures into Y, U and V textures. We do this 729 // because single-component textures are not renderable on all 730 // architectures. 731 shared_variables.append( 732 "varying vec4 v_texcoords[2];\n"); 733 vertex_header.append( 734 "uniform vec2 scaling_vector;\n" 735 "uniform vec2 dst_pixelsize;\n"); 736 vertex_program.append( 737 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 738 " step /= 4.0;\n" 739 " v_texcoords[0].xy = texcoord - step * 1.5;\n" 740 " v_texcoords[0].zw = texcoord - step * 0.5;\n" 741 " v_texcoords[1].xy = texcoord + step * 0.5;\n" 742 " v_texcoords[1].zw = texcoord + step * 1.5;\n"); 743 fragment_header.append( 744 "uniform vec4 color_weights;\n"); 745 fragment_program.append( 746 " gl_FragColor = color_weights * mat4(\n" 747 " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n" 748 " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n" 749 " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n" 750 " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n"); 751 // Swizzle makes no sense for this shader. 752 DCHECK(!swizzle); 753 break; 754 755 case SHADER_YUV_MRT_PASS1: 756 // RGB24 to YV12 in two passes; writing two 8888 targets each pass. 757 // 758 // YV12 is full-resolution luma and half-resolution blue/red chroma. 759 // 760 // (original) 761 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 762 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 763 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 764 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 765 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 766 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 767 // | 768 // | (y plane) (temporary) 769 // | YYYY YYYY UUVV UUVV 770 // +--> { YYYY YYYY + UUVV UUVV } 771 // YYYY YYYY UUVV UUVV 772 // First YYYY YYYY UUVV UUVV 773 // pass YYYY YYYY UUVV UUVV 774 // YYYY YYYY UUVV UUVV 775 // | 776 // | (u plane) (v plane) 777 // Second | UUUU VVVV 778 // pass +--> { UUUU + VVVV } 779 // UUUU VVVV 780 // 781 shared_variables.append( 782 "varying vec4 v_texcoords[2];\n"); 783 vertex_header.append( 784 "uniform vec2 scaling_vector;\n" 785 "uniform vec2 dst_pixelsize;\n"); 786 vertex_program.append( 787 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 788 " step /= 4.0;\n" 789 " v_texcoords[0].xy = texcoord - step * 1.5;\n" 790 " v_texcoords[0].zw = texcoord - step * 0.5;\n" 791 " v_texcoords[1].xy = texcoord + step * 0.5;\n" 792 " v_texcoords[1].zw = texcoord + step * 1.5;\n"); 793 fragment_directives.append( 794 "#extension GL_EXT_draw_buffers : enable\n"); 795 fragment_header.append( 796 "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n" 797 "const float kYBias = 0.0625;\n" 798 // Divide U and V by two to compensate for averaging below. 799 "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n" 800 "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n" 801 "const float kUVBias = 0.5;\n"); 802 fragment_program.append( 803 " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" 804 " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" 805 " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" 806 " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" 807 " vec3 pixel12 = pixel1 + pixel2;\n" 808 " vec3 pixel34 = pixel3 + pixel4;\n" 809 " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n" 810 " dot(pixel2, kRGBtoY),\n" 811 " dot(pixel3, kRGBtoY),\n" 812 " dot(pixel4, kRGBtoY)) + kYBias;\n" 813 " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n" 814 " dot(pixel34, kRGBtoU),\n" 815 " dot(pixel12, kRGBtoV),\n" 816 " dot(pixel34, kRGBtoV)) + kUVBias;\n"); 817 // Swizzle makes no sense for this shader. 818 DCHECK(!swizzle); 819 break; 820 821 case SHADER_YUV_MRT_PASS2: 822 // We're just sampling two pixels and unswizzling them. There's 823 // no need to do vertical scaling with math, since bilinear 824 // interpolation in the sampler takes care of that. 825 shared_variables.append( 826 "varying vec4 v_texcoords;\n"); 827 vertex_header.append( 828 "uniform vec2 scaling_vector;\n" 829 "uniform vec2 dst_pixelsize;\n"); 830 vertex_program.append( 831 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 832 " step /= 2.0;\n" 833 " v_texcoords.xy = texcoord - step * 0.5;\n" 834 " v_texcoords.zw = texcoord + step * 0.5;\n"); 835 fragment_directives.append( 836 "#extension GL_EXT_draw_buffers : enable\n"); 837 fragment_program.append( 838 " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n" 839 " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n" 840 " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n" 841 " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n"); 842 // Swizzle makes no sense for this shader. 843 DCHECK(!swizzle); 844 break; 845 } 846 if (swizzle) { 847 fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n"); 848 } 849 850 vertex_program = 851 vertex_header + 852 shared_variables + 853 "void main() {\n" + 854 vertex_program + 855 "}\n"; 856 857 fragment_program = 858 fragment_directives + 859 fragment_header + 860 shared_variables + 861 "void main() {\n" + 862 fragment_program + 863 "}\n"; 864 865 bool result = cache_entry->Setup(vertex_program.c_str(), 866 fragment_program.c_str()); 867 DCHECK(result || context_->isContextLost()) 868 << "vertex_program =\n" << vertex_program 869 << "fragment_program =\n" << fragment_program; 870 } 871 return cache_entry; 872 } 873 874 bool ShaderProgram::Setup(const blink::WGC3Dchar* vertex_shader_text, 875 const blink::WGC3Dchar* fragment_shader_text) { 876 // Shaders to map the source texture to |dst_texture_|. 877 ScopedShader vertex_shader(context_, helper_->CompileShaderFromSource( 878 vertex_shader_text, GL_VERTEX_SHADER)); 879 if (vertex_shader.id() == 0) { 880 return false; 881 } 882 context_->attachShader(program_, vertex_shader); 883 ScopedShader fragment_shader(context_, helper_->CompileShaderFromSource( 884 fragment_shader_text, GL_FRAGMENT_SHADER)); 885 if (fragment_shader.id() == 0) { 886 return false; 887 } 888 context_->attachShader(program_, fragment_shader); 889 context_->linkProgram(program_); 890 891 blink::WGC3Dint link_status = 0; 892 context_->getProgramiv(program_, GL_LINK_STATUS, &link_status); 893 if (!link_status) { 894 LOG(ERROR) << std::string(context_->getProgramInfoLog(program_).utf8()); 895 return false; 896 } 897 position_location_ = context_->getAttribLocation(program_, "a_position"); 898 texcoord_location_ = context_->getAttribLocation(program_, "a_texcoord"); 899 texture_location_ = context_->getUniformLocation(program_, "s_texture"); 900 src_subrect_location_ = context_->getUniformLocation(program_, "src_subrect"); 901 src_pixelsize_location_ = context_->getUniformLocation(program_, 902 "src_pixelsize"); 903 dst_pixelsize_location_ = context_->getUniformLocation(program_, 904 "dst_pixelsize"); 905 scaling_vector_location_ = context_->getUniformLocation(program_, 906 "scaling_vector"); 907 color_weights_location_ = context_->getUniformLocation(program_, 908 "color_weights"); 909 return true; 910 } 911 912 void ShaderProgram::UseProgram( 913 const gfx::Size& src_size, 914 const gfx::Rect& src_subrect, 915 const gfx::Size& dst_size, 916 bool scale_x, 917 bool flip_y, 918 GLfloat color_weights[4]) { 919 context_->useProgram(program_); 920 921 blink::WGC3Dintptr offset = 0; 922 context_->vertexAttribPointer(position_location_, 923 2, 924 GL_FLOAT, 925 GL_FALSE, 926 4 * sizeof(blink::WGC3Dfloat), 927 offset); 928 context_->enableVertexAttribArray(position_location_); 929 930 offset += 2 * sizeof(blink::WGC3Dfloat); 931 context_->vertexAttribPointer(texcoord_location_, 932 2, 933 GL_FLOAT, 934 GL_FALSE, 935 4 * sizeof(blink::WGC3Dfloat), 936 offset); 937 context_->enableVertexAttribArray(texcoord_location_); 938 939 context_->uniform1i(texture_location_, 0); 940 941 // Convert |src_subrect| to texture coordinates. 942 GLfloat src_subrect_texcoord[] = { 943 static_cast<float>(src_subrect.x()) / src_size.width(), 944 static_cast<float>(src_subrect.y()) / src_size.height(), 945 static_cast<float>(src_subrect.width()) / src_size.width(), 946 static_cast<float>(src_subrect.height()) / src_size.height(), 947 }; 948 if (flip_y) { 949 src_subrect_texcoord[1] += src_subrect_texcoord[3]; 950 src_subrect_texcoord[3] *= -1.0; 951 } 952 context_->uniform4fv(src_subrect_location_, 1, src_subrect_texcoord); 953 954 context_->uniform2f(src_pixelsize_location_, 955 src_size.width(), 956 src_size.height()); 957 context_->uniform2f(dst_pixelsize_location_, 958 static_cast<float>(dst_size.width()), 959 static_cast<float>(dst_size.height())); 960 961 context_->uniform2f(scaling_vector_location_, 962 scale_x ? 1.0 : 0.0, 963 scale_x ? 0.0 : 1.0); 964 context_->uniform4fv(color_weights_location_, 1, color_weights); 965 } 966 967 } // namespace content 968