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"
11 #include "base/bind.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/time/time.h"
17 #include "base/trace_event/trace_event.h"
18 #include "gpu/command_buffer/client/gles2_interface.h"
19 #include "third_party/skia/include/core/SkRegion.h"
20 #include "ui/gfx/geometry/rect.h"
21 #include "ui/gfx/geometry/size.h"
23 using gpu::gles2::GLES2Interface
;
27 GLHelperScaling::GLHelperScaling(GLES2Interface
* gl
, GLHelper
* helper
)
28 : gl_(gl
), helper_(helper
), vertex_attributes_buffer_(gl_
) {
32 GLHelperScaling::~GLHelperScaling() {}
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
> {
40 ShaderProgram(GLES2Interface
* gl
, GLHelper
* 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) {}
52 // Compile shader program.
53 void Setup(const GLchar
* vertex_shader_text
,
54 const GLchar
* fragment_shader_text
);
56 // UseProgram must be called with GL_TEXTURE_2D bound to the
57 // source texture and GL_ARRAY_BUFFER bound to a vertex
59 void UseProgram(const gfx::Size
& src_size
,
60 const gfx::Rect
& src_subrect
,
61 const gfx::Size
& dst_size
,
64 GLfloat color_weights
[4]);
66 bool Initialized() const { return position_location_
!= -1; }
69 friend class base::RefCounted
<ShaderProgram
>;
70 ~ShaderProgram() { gl_
->DeleteProgram(program_
); }
75 // A program for copying a source texture into a destination texture.
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_
;
96 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
: public GLHelper::ScalerInterface
,
104 public GLHelperScaling::ShaderInterface
{
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
)
122 scaler_helper_(scaler_helper
),
124 intermediate_texture_(0),
125 dst_framebuffer_(gl
),
126 subscaler_(subscaler
) {
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];
133 color_weights_
[0] = 0.0;
134 color_weights_
[1] = 0.0;
135 color_weights_
[2] = 0.0;
136 color_weights_
[3] = 0.0;
139 scaler_helper_
->GetShaderProgram(spec_
.shader
, spec_
.swizzle
);
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
,
149 spec_
.src_size
.width(),
150 spec_
.src_size
.height(),
158 ~ScalerImpl() override
{
159 if (intermediate_texture_
) {
160 gl_
->DeleteTextures(1, &intermediate_texture_
);
164 // GLHelperShader::ShaderInterface implementation.
165 void Execute(GLuint source_texture
,
166 const std::vector
<GLuint
>& dest_textures
) override
{
168 subscaler_
->Scale(source_texture
, intermediate_texture_
);
169 source_texture
= intermediate_texture_
;
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
,
183 buffers
[t
] = GL_COLOR_ATTACHMENT0
+ t
;
185 ScopedTextureBinder
<GL_TEXTURE_2D
> texture_binder(gl_
, source_texture
);
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
);
192 ScopedBufferBinder
<GL_ARRAY_BUFFER
> buffer_binder(
193 gl_
, scaler_helper_
->vertex_attributes_buffer_
);
194 shader_program_
->UseProgram(spec_
.src_size
,
198 spec_
.vertically_flip_texture
,
200 gl_
->Viewport(0, 0, spec_
.dst_size
.width(), spec_
.dst_size
.height());
202 if (dest_textures
.size() > 1) {
203 DCHECK_LE(static_cast<int>(dest_textures
.size()),
204 scaler_helper_
->helper_
->MaxDrawBuffers());
205 gl_
->DrawBuffersEXT(dest_textures
.size(), buffers
.get());
207 // Conduct texture mapping by drawing a quad composed of two triangles.
208 gl_
->DrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
209 if (dest_textures
.size() > 1) {
210 // Set the draw buffers back to not confuse others.
211 gl_
->DrawBuffersEXT(1, &buffers
[0]);
215 // GLHelper::ScalerInterface implementation.
216 void Scale(GLuint source_texture
, GLuint dest_texture
) override
{
217 std::vector
<GLuint
> tmp(1);
218 tmp
[0] = dest_texture
;
219 Execute(source_texture
, tmp
);
222 const gfx::Size
& SrcSize() override
{
224 return subscaler_
->SrcSize();
226 return spec_
.src_size
;
228 const gfx::Rect
& SrcSubrect() override
{
230 return subscaler_
->SrcSubrect();
232 return spec_
.src_subrect
;
234 const gfx::Size
& DstSize() override
{ return spec_
.dst_size
; }
238 GLHelperScaling
* scaler_helper_
;
239 GLHelperScaling::ScalerStage spec_
;
240 GLfloat color_weights_
[4];
241 GLuint intermediate_texture_
;
242 scoped_refptr
<ShaderProgram
> shader_program_
;
243 ScopedFramebuffer dst_framebuffer_
;
244 scoped_ptr
<ScalerImpl
> subscaler_
;
247 GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_
,
249 gfx::Rect src_subrect_
,
252 bool vertically_flip_texture_
,
256 src_subrect(src_subrect_
),
259 vertically_flip_texture(vertically_flip_texture_
),
262 // The important inputs for this function is |x_ops| and
263 // |y_ops|. They represent scaling operations to be done
264 // on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST,
265 // then we will interpret these scale operations literally and we'll
266 // create one scaler stage for each ScaleOp. However, if |quality|
267 // is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations
268 // by combining two or more ScaleOps in to a single scaler stage.
269 // Normally we process ScaleOps from |y_ops| first and |x_ops| after
270 // all |y_ops| are processed, but sometimes we can combine one or more
271 // operation from both queues essentially for free. This is the reason
272 // why |x_ops| and |y_ops| aren't just one single queue.
273 void GLHelperScaling::ConvertScalerOpsToScalerStages(
274 GLHelper::ScalerQuality quality
,
276 gfx::Rect src_subrect
,
277 const gfx::Size
& dst_size
,
278 bool vertically_flip_texture
,
280 std::deque
<GLHelperScaling::ScaleOp
>* x_ops
,
281 std::deque
<GLHelperScaling::ScaleOp
>* y_ops
,
282 std::vector
<ScalerStage
>* scaler_stages
) {
283 while (!x_ops
->empty() || !y_ops
->empty()) {
284 gfx::Size intermediate_size
= src_subrect
.size();
285 std::deque
<ScaleOp
>* current_queue
= NULL
;
287 if (!y_ops
->empty()) {
288 current_queue
= y_ops
;
290 current_queue
= x_ops
;
293 ShaderType current_shader
= SHADER_BILINEAR
;
294 switch (current_queue
->front().scale_factor
) {
296 if (quality
== GLHelper::SCALER_QUALITY_BEST
) {
297 current_shader
= SHADER_BICUBIC_UPSCALE
;
301 if (quality
== GLHelper::SCALER_QUALITY_BEST
) {
302 current_shader
= SHADER_BICUBIC_HALF_1D
;
306 DCHECK(quality
!= GLHelper::SCALER_QUALITY_BEST
);
307 current_shader
= SHADER_BILINEAR3
;
312 bool scale_x
= current_queue
->front().scale_x
;
313 current_queue
->front().UpdateSize(&intermediate_size
);
314 current_queue
->pop_front();
316 // Optimization: Sometimes we can combine 2-4 scaling operations into
318 if (quality
== GLHelper::SCALER_QUALITY_GOOD
) {
319 if (!current_queue
->empty() && current_shader
== SHADER_BILINEAR
) {
320 // Combine two steps in the same dimension.
321 current_queue
->front().UpdateSize(&intermediate_size
);
322 current_queue
->pop_front();
323 current_shader
= SHADER_BILINEAR2
;
324 if (!current_queue
->empty()) {
325 // Combine three steps in the same dimension.
326 current_queue
->front().UpdateSize(&intermediate_size
);
327 current_queue
->pop_front();
328 current_shader
= SHADER_BILINEAR4
;
331 // Check if we can combine some steps in the other dimension as well.
332 // Since all shaders currently use GL_LINEAR, we can easily scale up
333 // or scale down by exactly 2x at the same time as we do another
334 // operation. Currently, the following mergers are supported:
335 // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down)
336 // * 2 bilinear Y-passes with 2 bilinear X-passes
337 // * 1 bilinear Y-pass with N bilinear X-pass
338 // * N bilinear Y-passes with 1 bilinear X-pass (down only)
339 // Measurements indicate that generalizing this for 3x3 and 4x4
340 // makes it slower on some platforms, such as the Pixel.
341 if (!scale_x
&& x_ops
->size() > 0 && x_ops
->front().scale_factor
<= 2) {
343 if (current_shader
== SHADER_BILINEAR2
&& x_ops
->size() >= 2) {
346 current_shader
= SHADER_BILINEAR2X2
;
347 } else if (current_shader
== SHADER_BILINEAR
) {
350 switch (x_ops
->size()) {
354 if (x_ops
->front().scale_factor
== 3) {
355 current_shader
= SHADER_BILINEAR3
;
361 current_shader
= SHADER_BILINEAR2
;
365 current_shader
= SHADER_BILINEAR4
;
368 } else if (x_ops
->front().scale_factor
== 2) {
373 for (int i
= 0; i
< x_passes
; i
++) {
374 x_ops
->front().UpdateSize(&intermediate_size
);
380 scaler_stages
->push_back(ScalerStage(current_shader
,
385 vertically_flip_texture
,
387 src_size
= intermediate_size
;
388 src_subrect
= gfx::Rect(intermediate_size
);
389 vertically_flip_texture
= false;
394 void GLHelperScaling::ComputeScalerStages(
395 GLHelper::ScalerQuality quality
,
396 const gfx::Size
& src_size
,
397 const gfx::Rect
& src_subrect
,
398 const gfx::Size
& dst_size
,
399 bool vertically_flip_texture
,
401 std::vector
<ScalerStage
>* scaler_stages
) {
402 if (quality
== GLHelper::SCALER_QUALITY_FAST
||
403 src_subrect
.size() == dst_size
) {
404 scaler_stages
->push_back(ScalerStage(SHADER_BILINEAR
,
409 vertically_flip_texture
,
414 std::deque
<GLHelperScaling::ScaleOp
> x_ops
, y_ops
;
415 GLHelperScaling::ScaleOp::AddOps(src_subrect
.width(),
418 quality
== GLHelper::SCALER_QUALITY_GOOD
,
420 GLHelperScaling::ScaleOp::AddOps(src_subrect
.height(),
423 quality
== GLHelper::SCALER_QUALITY_GOOD
,
426 ConvertScalerOpsToScalerStages(quality
,
430 vertically_flip_texture
,
437 GLHelper::ScalerInterface
* GLHelperScaling::CreateScaler(
438 GLHelper::ScalerQuality quality
,
440 gfx::Rect src_subrect
,
441 const gfx::Size
& dst_size
,
442 bool vertically_flip_texture
,
444 std::vector
<ScalerStage
> scaler_stages
;
445 ComputeScalerStages(quality
,
449 vertically_flip_texture
,
453 ScalerImpl
* ret
= NULL
;
454 for (unsigned int i
= 0; i
< scaler_stages
.size(); i
++) {
455 ret
= new ScalerImpl(gl_
, this, scaler_stages
[i
], ret
, NULL
);
460 GLHelper::ScalerInterface
* GLHelperScaling::CreatePlanarScaler(
461 const gfx::Size
& src_size
,
462 const gfx::Rect
& src_subrect
,
463 const gfx::Size
& dst_size
,
464 bool vertically_flip_texture
,
466 const float color_weights
[4]) {
467 ScalerStage
stage(SHADER_PLANAR
,
472 vertically_flip_texture
,
474 return new ScalerImpl(gl_
, this, stage
, NULL
, color_weights
);
477 GLHelperScaling::ShaderInterface
* GLHelperScaling::CreateYuvMrtShader(
478 const gfx::Size
& src_size
,
479 const gfx::Rect
& src_subrect
,
480 const gfx::Size
& dst_size
,
481 bool vertically_flip_texture
,
484 DCHECK(shader
== SHADER_YUV_MRT_PASS1
|| shader
== SHADER_YUV_MRT_PASS2
);
485 ScalerStage
stage(shader
,
490 vertically_flip_texture
,
492 return new ScalerImpl(gl_
, this, stage
, NULL
, NULL
);
495 const GLfloat
GLHelperScaling::kVertexAttributes
[] = {
496 -1.0f
, -1.0f
, 0.0f
, 0.0f
, // vertex 0
497 1.0f
, -1.0f
, 1.0f
, 0.0f
, // vertex 1
498 -1.0f
, 1.0f
, 0.0f
, 1.0f
, // vertex 2
499 1.0f
, 1.0f
, 1.0f
, 1.0f
, }; // vertex 3
501 void GLHelperScaling::InitBuffer() {
502 ScopedBufferBinder
<GL_ARRAY_BUFFER
> buffer_binder(gl_
,
503 vertex_attributes_buffer_
);
504 gl_
->BufferData(GL_ARRAY_BUFFER
,
505 sizeof(kVertexAttributes
),
510 scoped_refptr
<ShaderProgram
> GLHelperScaling::GetShaderProgram(ShaderType type
,
512 ShaderProgramKeyType
key(type
, swizzle
);
513 scoped_refptr
<ShaderProgram
>& cache_entry(shader_programs_
[key
]);
514 if (!cache_entry
.get()) {
515 cache_entry
= new ShaderProgram(gl_
, helper_
);
516 std::basic_string
<GLchar
> vertex_program
;
517 std::basic_string
<GLchar
> fragment_program
;
518 std::basic_string
<GLchar
> vertex_header
;
519 std::basic_string
<GLchar
> fragment_directives
;
520 std::basic_string
<GLchar
> fragment_header
;
521 std::basic_string
<GLchar
> shared_variables
;
523 vertex_header
.append(
524 "precision highp float;\n"
525 "attribute vec2 a_position;\n"
526 "attribute vec2 a_texcoord;\n"
527 "uniform vec4 src_subrect;\n");
529 fragment_header
.append(
530 "precision mediump float;\n"
531 "uniform sampler2D s_texture;\n");
533 vertex_program
.append(
534 " gl_Position = vec4(a_position, 0.0, 1.0);\n"
535 " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
538 case SHADER_BILINEAR
:
539 shared_variables
.append("varying vec2 v_texcoord;\n");
540 vertex_program
.append(" v_texcoord = texcoord;\n");
541 fragment_program
.append(
542 " gl_FragColor = texture2D(s_texture, v_texcoord);\n");
545 case SHADER_BILINEAR2
:
546 // This is equivialent to two passes of the BILINEAR shader above.
547 // It can be used to scale an image down 1.0x-2.0x in either dimension,
549 shared_variables
.append(
550 "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad
551 vertex_header
.append(
552 "uniform vec2 scaling_vector;\n"
553 "uniform vec2 dst_pixelsize;\n");
554 vertex_program
.append(
555 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
557 " v_texcoords.xy = texcoord + step;\n"
558 " v_texcoords.zw = texcoord - step;\n");
560 fragment_program
.append(
561 " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n"
562 " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n");
565 case SHADER_BILINEAR3
:
566 // This is kind of like doing 1.5 passes of the BILINEAR shader.
567 // It can be used to scale an image down 1.5x-3.0x, or exactly 6x.
568 shared_variables
.append(
569 "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad
570 "varying vec2 v_texcoords2;\n");
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"
577 " v_texcoords1.xy = texcoord + step;\n"
578 " v_texcoords1.zw = texcoord;\n"
579 " v_texcoords2 = texcoord - step;\n");
580 fragment_program
.append(
581 " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n"
582 " texture2D(s_texture, v_texcoords1.zw) +\n"
583 " texture2D(s_texture, v_texcoords2)) / 3.0;\n");
586 case SHADER_BILINEAR4
:
587 // This is equivialent to three passes of the BILINEAR shader above,
588 // It can be used to scale an image down 2.0x-4.0x or exactly 8x.
589 shared_variables
.append("varying vec4 v_texcoords[2];\n");
590 vertex_header
.append(
591 "uniform vec2 scaling_vector;\n"
592 "uniform vec2 dst_pixelsize;\n");
593 vertex_program
.append(
594 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
596 " v_texcoords[0].xy = texcoord - step * 3.0;\n"
597 " v_texcoords[0].zw = texcoord - step;\n"
598 " v_texcoords[1].xy = texcoord + step;\n"
599 " v_texcoords[1].zw = texcoord + step * 3.0;\n");
600 fragment_program
.append(
601 " gl_FragColor = (\n"
602 " texture2D(s_texture, v_texcoords[0].xy) +\n"
603 " texture2D(s_texture, v_texcoords[0].zw) +\n"
604 " texture2D(s_texture, v_texcoords[1].xy) +\n"
605 " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
608 case SHADER_BILINEAR2X2
:
609 // This is equivialent to four passes of the BILINEAR shader above.
610 // Two in each dimension. It can be used to scale an image down
611 // 1.0x-2.0x in both X and Y directions. Or, it could be used to
612 // scale an image down by exactly 4x in both dimensions.
613 shared_variables
.append("varying vec4 v_texcoords[2];\n");
614 vertex_header
.append("uniform vec2 dst_pixelsize;\n");
615 vertex_program
.append(
616 " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
617 " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
618 " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
619 " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
620 " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\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");
629 case SHADER_BICUBIC_HALF_1D
:
630 // This scales down texture by exactly half in one dimension.
631 // directions in one pass. We use bilinear lookup to reduce
632 // the number of texture reads from 8 to 4
633 shared_variables
.append(
634 "const float CenterDist = 99.0 / 140.0;\n"
635 "const float LobeDist = 11.0 / 4.0;\n"
636 "const float CenterWeight = 35.0 / 64.0;\n"
637 "const float LobeWeight = -3.0 / 64.0;\n"
638 "varying vec4 v_texcoords[2];\n");
639 vertex_header
.append(
640 "uniform vec2 scaling_vector;\n"
641 "uniform vec2 src_pixelsize;\n");
642 vertex_program
.append(
643 " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
644 " v_texcoords[0].xy = texcoord - LobeDist * step;\n"
645 " v_texcoords[0].zw = texcoord - CenterDist * step;\n"
646 " v_texcoords[1].xy = texcoord + CenterDist * step;\n"
647 " v_texcoords[1].zw = texcoord + LobeDist * step;\n");
648 fragment_program
.append(
651 " (texture2D(s_texture, v_texcoords[0].xy) +\n"
652 " texture2D(s_texture, v_texcoords[1].zw)) *\n"
655 " (texture2D(s_texture, v_texcoords[0].zw) +\n"
656 " texture2D(s_texture, v_texcoords[1].xy)) *\n"
660 case SHADER_BICUBIC_UPSCALE
:
661 // When scaling up, we need 4 texture reads, but we can
662 // save some instructions because will know in which range of
663 // the bicubic function each call call to the bicubic function
665 // Also, when sampling the bicubic function like this, the sum
666 // is always exactly one, so we can skip normalization as well.
667 shared_variables
.append("varying vec2 v_texcoord;\n");
668 vertex_program
.append(" v_texcoord = texcoord;\n");
669 fragment_header
.append(
670 "uniform vec2 src_pixelsize;\n"
671 "uniform vec2 scaling_vector;\n"
672 "const float a = -0.5;\n"
673 // This function is equivialent to calling the bicubic
674 // function with x-1, x, 1-x and 2-x
675 // (assuming 0 <= x < 1)
676 "vec4 filt4(float x) {\n"
677 " return vec4(x * x * x, x * x, x, 1) *\n"
678 " mat4( a, -2.0 * a, a, 0.0,\n"
679 " a + 2.0, -a - 3.0, 0.0, 1.0,\n"
680 " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n"
681 " -a, a, 0.0, 0.0);\n"
683 "mat4 pixels_x(vec2 pos, vec2 step) {\n"
685 " texture2D(s_texture, pos - step),\n"
686 " texture2D(s_texture, pos),\n"
687 " texture2D(s_texture, pos + step),\n"
688 " texture2D(s_texture, pos + step * 2.0));\n"
690 fragment_program
.append(
691 " vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
692 " scaling_vector / 2.0;\n"
693 " float frac = fract(dot(pixel_pos, scaling_vector));\n"
694 " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
695 " vec2 step = scaling_vector / src_pixelsize;\n"
696 " gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
700 // Converts four RGBA pixels into one pixel. Each RGBA
701 // pixel will be dot-multiplied with the color weights and
702 // then placed into a component of the output. This is used to
703 // convert RGBA textures into Y, U and V textures. We do this
704 // because single-component textures are not renderable on all
706 shared_variables
.append("varying vec4 v_texcoords[2];\n");
707 vertex_header
.append(
708 "uniform vec2 scaling_vector;\n"
709 "uniform vec2 dst_pixelsize;\n");
710 vertex_program
.append(
711 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
713 " v_texcoords[0].xy = texcoord - step * 1.5;\n"
714 " v_texcoords[0].zw = texcoord - step * 0.5;\n"
715 " v_texcoords[1].xy = texcoord + step * 0.5;\n"
716 " v_texcoords[1].zw = texcoord + step * 1.5;\n");
717 fragment_header
.append("uniform vec4 color_weights;\n");
718 fragment_program
.append(
719 " gl_FragColor = color_weights * mat4(\n"
720 " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
721 " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
722 " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
723 " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
726 case SHADER_YUV_MRT_PASS1
:
727 // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
729 // YV12 is full-resolution luma and half-resolution blue/red chroma.
732 // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
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
739 // | (y plane) (temporary)
740 // | YYYY YYYY UUVV UUVV
741 // +--> { YYYY YYYY + UUVV UUVV }
742 // YYYY YYYY UUVV UUVV
743 // First YYYY YYYY UUVV UUVV
744 // pass YYYY YYYY UUVV UUVV
745 // YYYY YYYY UUVV UUVV
747 // | (u plane) (v plane)
748 // Second | UUUU VVVV
749 // pass +--> { UUUU + VVVV }
752 shared_variables
.append("varying vec4 v_texcoords[2];\n");
753 vertex_header
.append(
754 "uniform vec2 scaling_vector;\n"
755 "uniform vec2 dst_pixelsize;\n");
756 vertex_program
.append(
757 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
759 " v_texcoords[0].xy = texcoord - step * 1.5;\n"
760 " v_texcoords[0].zw = texcoord - step * 0.5;\n"
761 " v_texcoords[1].xy = texcoord + step * 0.5;\n"
762 " v_texcoords[1].zw = texcoord + step * 1.5;\n");
763 fragment_directives
.append("#extension GL_EXT_draw_buffers : enable\n");
764 fragment_header
.append(
765 "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n"
766 "const float kYBias = 0.0625;\n"
767 // Divide U and V by two to compensate for averaging below.
768 "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n"
769 "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n"
770 "const float kUVBias = 0.5;\n");
771 fragment_program
.append(
772 " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
773 " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
774 " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
775 " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
776 " vec3 pixel12 = pixel1 + pixel2;\n"
777 " vec3 pixel34 = pixel3 + pixel4;\n"
778 " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n"
779 " dot(pixel2, kRGBtoY),\n"
780 " dot(pixel3, kRGBtoY),\n"
781 " dot(pixel4, kRGBtoY)) + kYBias;\n"
782 " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n"
783 " dot(pixel34, kRGBtoU),\n"
784 " dot(pixel12, kRGBtoV),\n"
785 " dot(pixel34, kRGBtoV)) + kUVBias;\n");
788 case SHADER_YUV_MRT_PASS2
:
789 // We're just sampling two pixels and unswizzling them. There's
790 // no need to do vertical scaling with math, since bilinear
791 // interpolation in the sampler takes care of that.
792 shared_variables
.append("varying vec4 v_texcoords;\n");
793 vertex_header
.append(
794 "uniform vec2 scaling_vector;\n"
795 "uniform vec2 dst_pixelsize;\n");
796 vertex_program
.append(
797 " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
799 " v_texcoords.xy = texcoord - step * 0.5;\n"
800 " v_texcoords.zw = texcoord + step * 0.5;\n");
801 fragment_directives
.append("#extension GL_EXT_draw_buffers : enable\n");
802 fragment_program
.append(
803 " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n"
804 " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n"
805 " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n"
806 " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n");
811 case SHADER_YUV_MRT_PASS1
:
812 fragment_program
.append(" gl_FragData[0] = gl_FragData[0].bgra;\n");
814 case SHADER_YUV_MRT_PASS2
:
815 fragment_program
.append(" gl_FragData[0] = gl_FragData[0].bgra;\n");
816 fragment_program
.append(" gl_FragData[1] = gl_FragData[1].bgra;\n");
819 fragment_program
.append(" gl_FragColor = gl_FragColor.bgra;\n");
824 vertex_program
= vertex_header
+ shared_variables
+ "void main() {\n" +
825 vertex_program
+ "}\n";
827 fragment_program
= fragment_directives
+ fragment_header
+
828 shared_variables
+ "void main() {\n" + fragment_program
+
831 cache_entry
->Setup(vertex_program
.c_str(), fragment_program
.c_str());
836 void ShaderProgram::Setup(const GLchar
* vertex_shader_text
,
837 const GLchar
* fragment_shader_text
) {
838 // Shaders to map the source texture to |dst_texture_|.
839 GLuint vertex_shader
=
840 helper_
->CompileShaderFromSource(vertex_shader_text
, GL_VERTEX_SHADER
);
841 if (vertex_shader
== 0)
844 gl_
->AttachShader(program_
, vertex_shader
);
845 gl_
->DeleteShader(vertex_shader
);
847 GLuint fragment_shader
= helper_
->CompileShaderFromSource(
848 fragment_shader_text
, GL_FRAGMENT_SHADER
);
849 if (fragment_shader
== 0)
851 gl_
->AttachShader(program_
, fragment_shader
);
852 gl_
->DeleteShader(fragment_shader
);
854 gl_
->LinkProgram(program_
);
856 GLint link_status
= 0;
857 gl_
->GetProgramiv(program_
, GL_LINK_STATUS
, &link_status
);
861 position_location_
= gl_
->GetAttribLocation(program_
, "a_position");
862 texcoord_location_
= gl_
->GetAttribLocation(program_
, "a_texcoord");
863 texture_location_
= gl_
->GetUniformLocation(program_
, "s_texture");
864 src_subrect_location_
= gl_
->GetUniformLocation(program_
, "src_subrect");
865 src_pixelsize_location_
= gl_
->GetUniformLocation(program_
, "src_pixelsize");
866 dst_pixelsize_location_
= gl_
->GetUniformLocation(program_
, "dst_pixelsize");
867 scaling_vector_location_
=
868 gl_
->GetUniformLocation(program_
, "scaling_vector");
869 color_weights_location_
= gl_
->GetUniformLocation(program_
, "color_weights");
870 // The only reason fetching these attribute locations should fail is
871 // if the context was spontaneously lost (i.e., because the GPU
872 // process crashed, perhaps deliberately for testing).
873 DCHECK_IMPLIES(!Initialized(),
874 gl_
->GetGraphicsResetStatusKHR() != GL_NO_ERROR
);
877 void ShaderProgram::UseProgram(const gfx::Size
& src_size
,
878 const gfx::Rect
& src_subrect
,
879 const gfx::Size
& dst_size
,
882 GLfloat color_weights
[4]) {
883 gl_
->UseProgram(program_
);
885 // OpenGL defines the last parameter to VertexAttribPointer as type
886 // "const GLvoid*" even though it is actually an offset into the buffer
887 // object's data store and not a pointer to the client's address space.
888 const void* offsets
[2] = {
889 0, reinterpret_cast<const void*>(2 * sizeof(GLfloat
))
892 gl_
->VertexAttribPointer(position_location_
,
898 gl_
->EnableVertexAttribArray(position_location_
);
900 gl_
->VertexAttribPointer(texcoord_location_
,
906 gl_
->EnableVertexAttribArray(texcoord_location_
);
908 gl_
->Uniform1i(texture_location_
, 0);
910 // Convert |src_subrect| to texture coordinates.
911 GLfloat src_subrect_texcoord
[] = {
912 static_cast<float>(src_subrect
.x()) / src_size
.width(),
913 static_cast<float>(src_subrect
.y()) / src_size
.height(),
914 static_cast<float>(src_subrect
.width()) / src_size
.width(),
915 static_cast<float>(src_subrect
.height()) / src_size
.height(), };
917 src_subrect_texcoord
[1] += src_subrect_texcoord
[3];
918 src_subrect_texcoord
[3] *= -1.0;
920 gl_
->Uniform4fv(src_subrect_location_
, 1, src_subrect_texcoord
);
922 gl_
->Uniform2f(src_pixelsize_location_
, src_size
.width(), src_size
.height());
923 gl_
->Uniform2f(dst_pixelsize_location_
,
924 static_cast<float>(dst_size
.width()),
925 static_cast<float>(dst_size
.height()));
928 scaling_vector_location_
, scale_x
? 1.0 : 0.0, scale_x
? 0.0 : 1.0);
929 gl_
->Uniform4fv(color_weights_location_
, 1, color_weights
);
932 } // namespace content