Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / common / gpu / client / gl_helper_scaling.cc
blobee90016805337e15d9b232ce481007c5eece3d7c
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.
5 #include "content/common/gpu/client/gl_helper_scaling.h"
7 #include <deque>
8 #include <string>
9 #include <vector>
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"
24 using WebKit::WebGLId;
25 using WebKit::WebGraphicsContext3D;
27 namespace content {
29 GLHelperScaling::GLHelperScaling(WebKit::WebGraphicsContext3D* context,
30 GLHelper* helper)
31 : context_(context),
32 helper_(helper),
33 vertex_attributes_buffer_(context_, context_->createBuffer()) {
34 InitBuffer();
37 GLHelperScaling::~GLHelperScaling() {
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()) {
53 // Compile shader program, return true if successful.
54 bool Setup(const WebKit::WGC3Dchar* vertex_shader_text,
55 const WebKit::WGC3Dchar* fragment_shader_text);
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]);
67 private:
68 friend class base::RefCounted<ShaderProgram>;
69 ~ShaderProgram() {}
71 WebGraphicsContext3D* context_;
72 GLHelper* helper_;
74 // A program for copying a source texture into a destination texture.
75 ScopedProgram program_;
77 // The location of the position in the program.
78 WebKit::WGC3Dint position_location_;
79 // The location of the texture coordinate in the program.
80 WebKit::WGC3Dint texcoord_location_;
81 // The location of the source texture in the program.
82 WebKit::WGC3Dint texture_location_;
83 // The location of the texture coordinate of
84 // the sub-rectangle in the program.
85 WebKit::WGC3Dint src_subrect_location_;
86 // Location of size of source image in pixels.
87 WebKit::WGC3Dint src_pixelsize_location_;
88 // Location of size of destination image in pixels.
89 WebKit::WGC3Dint dst_pixelsize_location_;
90 // Location of vector for scaling direction.
91 WebKit::WGC3Dint scaling_vector_location_;
92 // Location of color weights.
93 WebKit::WGC3Dint color_weights_location_;
95 DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
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;
139 shader_program_ = scaler_helper_->GetShaderProgram(spec_.shader,
140 spec_.swizzle);
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,
149 GL_RGBA,
150 spec_.src_size.width(),
151 spec_.src_size.height(),
153 GL_RGBA,
154 GL_UNSIGNED_BYTE,
155 NULL);
159 virtual ~ScalerImpl() {
160 if (intermediate_texture_) {
161 context_->deleteTexture(intermediate_texture_);
165 // GLHelperShader::ShaderInterface implementation.
166 virtual void Execute(
167 WebKit::WebGLId source_texture,
168 const std::vector<WebKit::WebGLId>& dest_textures) OVERRIDE {
169 if (subscaler_) {
170 subscaler_->Scale(source_texture, intermediate_texture_);
171 source_texture = intermediate_texture_;
174 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
175 context_,
176 dst_framebuffer_);
177 DCHECK_GT(dest_textures.size(), 0U);
178 scoped_ptr<WebKit::WGC3Denum[]> buffers(
179 new WebKit::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],
188 buffers[t] = GL_COLOR_ATTACHMENT0 + t;
190 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
191 source_texture);
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);
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());
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());
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]);
226 // GLHelper::ScalerInterface implementation.
227 virtual void Scale(WebKit::WebGLId source_texture,
228 WebKit::WebGLId dest_texture) OVERRIDE {
229 std::vector<WebKit::WebGLId> tmp(1);
230 tmp[0] = dest_texture;
231 Execute(source_texture, tmp);
234 virtual const gfx::Size& SrcSize() OVERRIDE {
235 if (subscaler_) {
236 return subscaler_->SrcSize();
238 return spec_.src_size;
240 virtual const gfx::Rect& SrcSubrect() OVERRIDE {
241 if (subscaler_) {
242 return subscaler_->SrcSubrect();
244 return spec_.src_subrect;
246 virtual const gfx::Size& DstSize() OVERRIDE {
247 return spec_.dst_size;
250 private:
251 WebGraphicsContext3D* context_;
252 GLHelperScaling* scaler_helper_;
253 GLHelperScaling::ScalerStage spec_;
254 GLfloat color_weights_[4];
255 WebKit::WebGLId intermediate_texture_;
256 scoped_refptr<ShaderProgram> shader_program_;
257 ScopedFramebuffer dst_framebuffer_;
258 scoped_ptr<ScalerImpl> subscaler_;
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_) {
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;
303 if (!y_ops->empty()) {
304 current_queue = y_ops;
305 } else {
306 current_queue = x_ops;
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;
315 break;
316 case 2:
317 if (quality == GLHelper::SCALER_QUALITY_BEST) {
318 current_shader = SHADER_BICUBIC_HALF_1D;
320 break;
321 case 3:
322 DCHECK(quality != GLHelper::SCALER_QUALITY_BEST);
323 current_shader = SHADER_BILINEAR3;
324 break;
325 default:
326 NOTREACHED();
328 bool scale_x = current_queue->front().scale_x;
329 current_queue->front().UpdateSize(&intermediate_size);
330 current_queue->pop_front();
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;
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;
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;
385 } else if (x_ops->front().scale_factor == 2) {
386 // Ny + 1x-downscale
387 x_passes = 1;
390 for (int i = 0; i < x_passes; i++) {
391 x_ops->front().UpdateSize(&intermediate_size);
392 x_ops->pop_front();
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;
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;
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);
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);
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);
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);
475 return ret;
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);
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);
513 const WebKit::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,
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);
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<WebKit::WGC3Dchar> vertex_program;
537 std::basic_string<WebKit::WGC3Dchar> fragment_program;
538 std::basic_string<WebKit::WGC3Dchar> vertex_header;
539 std::basic_string<WebKit::WGC3Dchar> fragment_directives;
540 std::basic_string<WebKit::WGC3Dchar> fragment_header;
541 std::basic_string<WebKit::WGC3Dchar> shared_variables;
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");
549 fragment_header.append(
550 "precision mediump float;\n"
551 "uniform sampler2D s_texture;\n");
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");
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;
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");
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;
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;
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;
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;
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;
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;
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;
755 case SHADER_YUV_MRT_PASS1:
756 // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
758 // YV12 is full-resolution luma and half-resolution blue/red chroma.
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
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;
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;
846 if (swizzle) {
847 fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n");
850 vertex_program =
851 vertex_header +
852 shared_variables +
853 "void main() {\n" +
854 vertex_program +
855 "}\n";
857 fragment_program =
858 fragment_directives +
859 fragment_header +
860 shared_variables +
861 "void main() {\n" +
862 fragment_program +
863 "}\n";
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;
871 return cache_entry;
874 bool ShaderProgram::Setup(const WebKit::WGC3Dchar* vertex_shader_text,
875 const WebKit::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;
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;
888 context_->attachShader(program_, fragment_shader);
889 context_->linkProgram(program_);
891 WebKit::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;
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;
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_);
921 WebKit::WGC3Dintptr offset = 0;
922 context_->vertexAttribPointer(position_location_,
924 GL_FLOAT,
925 GL_FALSE,
926 4 * sizeof(WebKit::WGC3Dfloat),
927 offset);
928 context_->enableVertexAttribArray(position_location_);
930 offset += 2 * sizeof(WebKit::WGC3Dfloat);
931 context_->vertexAttribPointer(texcoord_location_,
933 GL_FLOAT,
934 GL_FALSE,
935 4 * sizeof(WebKit::WGC3Dfloat),
936 offset);
937 context_->enableVertexAttribArray(texcoord_location_);
939 context_->uniform1i(texture_location_, 0);
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(),
948 if (flip_y) {
949 src_subrect_texcoord[1] += src_subrect_texcoord[3];
950 src_subrect_texcoord[3] *= -1.0;
952 context_->uniform4fv(src_subrect_location_, 1, src_subrect_texcoord);
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()));
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);
967 } // namespace content