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