Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Side by Side Diff: content/common/gpu/client/gl_helper_scaling.cc

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

Powered by Google App Engine
This is Rietveld 408576698