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