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