• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
6 
7 #include <algorithm>
8 
9 #include "base/basictypes.h"
10 #include "gpu/command_buffer/service/gl_utils.h"
11 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
12 
13 #define SHADER(src)                     \
14   "#ifdef GL_ES\n"                      \
15   "precision mediump float;\n"          \
16   "#define TexCoordPrecision mediump\n" \
17   "#else\n"                             \
18   "#define TexCoordPrecision\n"         \
19   "#endif\n" #src
20 #define SHADER_2D(src)              \
21   "#define SamplerType sampler2D\n" \
22   "#define TextureLookup texture2D\n" SHADER(src)
23 #define SHADER_RECTANGLE_ARB(src)     \
24   "#define SamplerType samplerRect\n" \
25   "#define TextureLookup textureRect\n" SHADER(src)
26 #define SHADER_EXTERNAL_OES(src)                     \
27   "#extension GL_OES_EGL_image_external : require\n" \
28   "#define SamplerType samplerExternalOES\n"         \
29   "#define TextureLookup texture2D\n" SHADER(src)
30 #define FRAGMENT_SHADERS(src) \
31   SHADER_2D(src), SHADER_RECTANGLE_ARB(src), SHADER_EXTERNAL_OES(src)
32 
33 namespace {
34 
35 enum VertexShaderId {
36   VERTEX_SHADER_COPY_TEXTURE,
37   VERTEX_SHADER_COPY_TEXTURE_FLIP_Y,
38   NUM_VERTEX_SHADERS,
39 };
40 
41 enum FragmentShaderId {
42   FRAGMENT_SHADER_COPY_TEXTURE_2D,
43   FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
44   FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
45   FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_2D,
46   FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_RECTANGLE_ARB,
47   FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_EXTERNAL_OES,
48   FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_2D,
49   FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_RECTANGLE_ARB,
50   FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_EXTERNAL_OES,
51   NUM_FRAGMENT_SHADERS,
52 };
53 
54 const char* vertex_shader_source[NUM_VERTEX_SHADERS] = {
55   // VERTEX_SHADER_COPY_TEXTURE
56   SHADER(
57     uniform mat4 u_matrix;
58     uniform vec2 u_half_size;
59     attribute vec4 a_position;
60     varying TexCoordPrecision vec2 v_uv;
61     void main(void) {
62       gl_Position = u_matrix * a_position;
63       v_uv = a_position.xy * vec2(u_half_size.s, u_half_size.t) +
64              vec2(u_half_size.s, u_half_size.t);
65     }),
66   // VERTEX_SHADER_COPY_TEXTURE_FLIP_Y
67   SHADER(
68     uniform mat4 u_matrix;
69     uniform vec2 u_half_size;
70     attribute vec4 a_position;
71     varying TexCoordPrecision vec2 v_uv;
72     void main(void) {
73       gl_Position = u_matrix * a_position;
74       v_uv = a_position.xy * vec2(u_half_size.s, -u_half_size.t) +
75              vec2(u_half_size.s, u_half_size.t);
76     }),
77 };
78 
79 const char* fragment_shader_source[NUM_FRAGMENT_SHADERS] = {
80   // FRAGMENT_SHADER_COPY_TEXTURE_*
81   FRAGMENT_SHADERS(
82     uniform SamplerType u_sampler;
83     varying TexCoordPrecision vec2 v_uv;
84     void main(void) {
85       gl_FragColor = TextureLookup(u_sampler, v_uv.st);
86     }),
87   // FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_*
88   FRAGMENT_SHADERS(
89     uniform SamplerType u_sampler;
90     varying TexCoordPrecision vec2 v_uv;
91     void main(void) {
92       gl_FragColor = TextureLookup(u_sampler, v_uv.st);
93       gl_FragColor.rgb *= gl_FragColor.a;
94     }),
95   // FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_*
96   FRAGMENT_SHADERS(
97     uniform SamplerType u_sampler;
98     varying TexCoordPrecision vec2 v_uv;
99     void main(void) {
100       gl_FragColor = TextureLookup(u_sampler, v_uv.st);
101       if (gl_FragColor.a > 0.0)
102         gl_FragColor.rgb /= gl_FragColor.a;
103     }),
104 };
105 
106 // Returns the correct vertex shader id to evaluate the copy operation for
107 // the CHROMIUM_flipy setting.
GetVertexShaderId(bool flip_y)108 VertexShaderId GetVertexShaderId(bool flip_y) {
109   // bit 0: flip y
110   static VertexShaderId shader_ids[] = {
111       VERTEX_SHADER_COPY_TEXTURE,
112       VERTEX_SHADER_COPY_TEXTURE_FLIP_Y,
113   };
114 
115   unsigned index = flip_y ? 1 : 0;
116   return shader_ids[index];
117 }
118 
119 // Returns the correct fragment shader id to evaluate the copy operation for
120 // the premultiply alpha pixel store settings and target.
GetFragmentShaderId(bool premultiply_alpha,bool unpremultiply_alpha,GLenum target)121 FragmentShaderId GetFragmentShaderId(bool premultiply_alpha,
122                                      bool unpremultiply_alpha,
123                                      GLenum target) {
124   enum {
125     SAMPLER_2D,
126     SAMPLER_RECTANGLE_ARB,
127     SAMPLER_EXTERNAL_OES,
128     NUM_SAMPLERS
129   };
130 
131   // bit 0: premultiply alpha
132   // bit 1: unpremultiply alpha
133   static FragmentShaderId shader_ids[][NUM_SAMPLERS] = {
134       {
135        FRAGMENT_SHADER_COPY_TEXTURE_2D,
136        FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
137        FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
138       },
139       {
140        FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_2D,
141        FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_RECTANGLE_ARB,
142        FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_EXTERNAL_OES,
143       },
144       {
145        FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_2D,
146        FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_RECTANGLE_ARB,
147        FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_EXTERNAL_OES,
148       },
149       {
150        FRAGMENT_SHADER_COPY_TEXTURE_2D,
151        FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
152        FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
153       }};
154 
155   unsigned index = (premultiply_alpha   ? (1 << 0) : 0) |
156                    (unpremultiply_alpha ? (1 << 1) : 0);
157 
158   switch (target) {
159     case GL_TEXTURE_2D:
160       return shader_ids[index][SAMPLER_2D];
161     case GL_TEXTURE_RECTANGLE_ARB:
162       return shader_ids[index][SAMPLER_RECTANGLE_ARB];
163     case GL_TEXTURE_EXTERNAL_OES:
164       return shader_ids[index][SAMPLER_EXTERNAL_OES];
165     default:
166       break;
167   }
168 
169   NOTREACHED();
170   return shader_ids[0][SAMPLER_2D];
171 }
172 
CompileShader(GLuint shader,const char * shader_source)173 void CompileShader(GLuint shader, const char* shader_source) {
174   glShaderSource(shader, 1, &shader_source, 0);
175   glCompileShader(shader);
176 #ifndef NDEBUG
177   GLint compile_status;
178   glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
179   if (GL_TRUE != compile_status)
180     DLOG(ERROR) << "CopyTextureCHROMIUM: shader compilation failure.";
181 #endif
182 }
183 
DeleteShader(GLuint shader)184 void DeleteShader(GLuint shader) {
185   if (shader)
186     glDeleteShader(shader);
187 }
188 
BindFramebufferTexture2D(GLenum target,GLuint texture_id,GLint level,GLuint framebuffer)189 bool BindFramebufferTexture2D(GLenum target,
190                               GLuint texture_id,
191                               GLint level,
192                               GLuint framebuffer) {
193   DCHECK(target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_ARB);
194   glActiveTexture(GL_TEXTURE0);
195   glBindTexture(target, texture_id);
196   // NVidia drivers require texture settings to be a certain way
197   // or they won't report FRAMEBUFFER_COMPLETE.
198   glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
199   glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
200   glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
201   glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
202   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
203   glFramebufferTexture2DEXT(
204       GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, texture_id, level);
205 
206 #ifndef NDEBUG
207   GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
208   if (GL_FRAMEBUFFER_COMPLETE != fb_status) {
209     DLOG(ERROR) << "CopyTextureCHROMIUM: Incomplete framebuffer.";
210     return false;
211   }
212 #endif
213   return true;
214 }
215 
DoCopyTexImage2D(const gpu::gles2::GLES2Decoder * decoder,GLenum source_target,GLuint source_id,GLuint dest_id,GLint dest_level,GLenum dest_internal_format,GLsizei width,GLsizei height,GLuint framebuffer)216 void DoCopyTexImage2D(const gpu::gles2::GLES2Decoder* decoder,
217                       GLenum source_target,
218                       GLuint source_id,
219                       GLuint dest_id,
220                       GLint dest_level,
221                       GLenum dest_internal_format,
222                       GLsizei width,
223                       GLsizei height,
224                       GLuint framebuffer) {
225   DCHECK(source_target == GL_TEXTURE_2D ||
226          source_target == GL_TEXTURE_RECTANGLE_ARB);
227   if (BindFramebufferTexture2D(
228           source_target, source_id, 0 /* level */, framebuffer)) {
229     glBindTexture(GL_TEXTURE_2D, dest_id);
230     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
231     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
232     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
233     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
234     glCopyTexImage2D(GL_TEXTURE_2D,
235                      dest_level,
236                      dest_internal_format,
237                      0 /* x */,
238                      0 /* y */,
239                      width,
240                      height,
241                      0 /* border */);
242   }
243 
244   decoder->RestoreTextureState(source_id);
245   decoder->RestoreTextureState(dest_id);
246   decoder->RestoreTextureUnitBindings(0);
247   decoder->RestoreActiveTexture();
248   decoder->RestoreFramebufferBindings();
249 }
250 
251 }  // namespace
252 
253 namespace gpu {
254 
CopyTextureCHROMIUMResourceManager()255 CopyTextureCHROMIUMResourceManager::CopyTextureCHROMIUMResourceManager()
256     : initialized_(false),
257       vertex_shaders_(NUM_VERTEX_SHADERS, 0u),
258       fragment_shaders_(NUM_FRAGMENT_SHADERS, 0u),
259       buffer_id_(0u),
260       framebuffer_(0u) {}
261 
~CopyTextureCHROMIUMResourceManager()262 CopyTextureCHROMIUMResourceManager::~CopyTextureCHROMIUMResourceManager() {
263   DCHECK(!buffer_id_);
264   DCHECK(!framebuffer_);
265 }
266 
Initialize(const gles2::GLES2Decoder * decoder)267 void CopyTextureCHROMIUMResourceManager::Initialize(
268     const gles2::GLES2Decoder* decoder) {
269   COMPILE_ASSERT(
270       kVertexPositionAttrib == 0u,
271       Position_attribs_must_be_0);
272   DCHECK(!buffer_id_);
273   DCHECK(!framebuffer_);
274   DCHECK(programs_.empty());
275 
276   // Initialize all of the GPU resources required to perform the copy.
277   glGenBuffersARB(1, &buffer_id_);
278   glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
279   const GLfloat kQuadVertices[] = {-1.0f, -1.0f,
280                                     1.0f, -1.0f,
281                                     1.0f,  1.0f,
282                                    -1.0f,  1.0f};
283   glBufferData(
284       GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
285 
286   glGenFramebuffersEXT(1, &framebuffer_);
287 
288   decoder->RestoreBufferBindings();
289 
290   initialized_ = true;
291 }
292 
Destroy()293 void CopyTextureCHROMIUMResourceManager::Destroy() {
294   if (!initialized_)
295     return;
296 
297   glDeleteFramebuffersEXT(1, &framebuffer_);
298   framebuffer_ = 0;
299 
300   std::for_each(vertex_shaders_.begin(), vertex_shaders_.end(), DeleteShader);
301   std::for_each(
302       fragment_shaders_.begin(), fragment_shaders_.end(), DeleteShader);
303 
304   for (ProgramMap::const_iterator it = programs_.begin(); it != programs_.end();
305        ++it) {
306     const ProgramInfo& info = it->second;
307     glDeleteProgram(info.program);
308   }
309 
310   glDeleteBuffersARB(1, &buffer_id_);
311   buffer_id_ = 0;
312 }
313 
DoCopyTexture(const gles2::GLES2Decoder * decoder,GLenum source_target,GLuint source_id,GLenum source_internal_format,GLuint dest_id,GLint dest_level,GLenum dest_internal_format,GLsizei width,GLsizei height,bool flip_y,bool premultiply_alpha,bool unpremultiply_alpha)314 void CopyTextureCHROMIUMResourceManager::DoCopyTexture(
315     const gles2::GLES2Decoder* decoder,
316     GLenum source_target,
317     GLuint source_id,
318     GLenum source_internal_format,
319     GLuint dest_id,
320     GLint dest_level,
321     GLenum dest_internal_format,
322     GLsizei width,
323     GLsizei height,
324     bool flip_y,
325     bool premultiply_alpha,
326     bool unpremultiply_alpha) {
327   bool premultiply_alpha_change = premultiply_alpha ^ unpremultiply_alpha;
328   // GL_INVALID_OPERATION is generated if the currently bound framebuffer's
329   // format does not contain a superset of the components required by the base
330   // format of internalformat.
331   // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml
332   bool source_format_contain_superset_of_dest_format =
333       source_internal_format == dest_internal_format ||
334       (source_internal_format == GL_RGBA && dest_internal_format == GL_RGB);
335   // GL_TEXTURE_RECTANGLE_ARB on FBO is supported by OpenGL, not GLES2,
336   // so restrict this to GL_TEXTURE_2D.
337   if (source_target == GL_TEXTURE_2D && !flip_y && !premultiply_alpha_change &&
338       source_format_contain_superset_of_dest_format) {
339     DoCopyTexImage2D(decoder,
340                      source_target,
341                      source_id,
342                      dest_id,
343                      dest_level,
344                      dest_internal_format,
345                      width,
346                      height,
347                      framebuffer_);
348     return;
349   }
350 
351   // Use default transform matrix if no transform passed in.
352   const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f,
353                                              0.0f, 1.0f, 0.0f, 0.0f,
354                                              0.0f, 0.0f, 1.0f, 0.0f,
355                                              0.0f, 0.0f, 0.0f, 1.0f};
356   DoCopyTextureWithTransform(decoder,
357                              source_target,
358                              source_id,
359                              dest_id,
360                              dest_level,
361                              width,
362                              height,
363                              flip_y,
364                              premultiply_alpha,
365                              unpremultiply_alpha,
366                              default_matrix);
367 }
368 
DoCopyTextureWithTransform(const gles2::GLES2Decoder * decoder,GLenum source_target,GLuint source_id,GLuint dest_id,GLint dest_level,GLsizei width,GLsizei height,bool flip_y,bool premultiply_alpha,bool unpremultiply_alpha,const GLfloat transform_matrix[16])369 void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform(
370     const gles2::GLES2Decoder* decoder,
371     GLenum source_target,
372     GLuint source_id,
373     GLuint dest_id,
374     GLint dest_level,
375     GLsizei width,
376     GLsizei height,
377     bool flip_y,
378     bool premultiply_alpha,
379     bool unpremultiply_alpha,
380     const GLfloat transform_matrix[16]) {
381   DCHECK(source_target == GL_TEXTURE_2D ||
382          source_target == GL_TEXTURE_RECTANGLE_ARB ||
383          source_target == GL_TEXTURE_EXTERNAL_OES);
384   if (!initialized_) {
385     DLOG(ERROR) << "CopyTextureCHROMIUM: Uninitialized manager.";
386     return;
387   }
388 
389   VertexShaderId vertex_shader_id = GetVertexShaderId(flip_y);
390   DCHECK_LT(static_cast<size_t>(vertex_shader_id), vertex_shaders_.size());
391   FragmentShaderId fragment_shader_id = GetFragmentShaderId(
392       premultiply_alpha, unpremultiply_alpha, source_target);
393   DCHECK_LT(static_cast<size_t>(fragment_shader_id), fragment_shaders_.size());
394 
395   ProgramMapKey key(vertex_shader_id, fragment_shader_id);
396   ProgramInfo* info = &programs_[key];
397   // Create program if necessary.
398   if (!info->program) {
399     info->program = glCreateProgram();
400     GLuint* vertex_shader = &vertex_shaders_[vertex_shader_id];
401     if (!*vertex_shader) {
402       *vertex_shader = glCreateShader(GL_VERTEX_SHADER);
403       CompileShader(*vertex_shader, vertex_shader_source[vertex_shader_id]);
404     }
405     glAttachShader(info->program, *vertex_shader);
406     GLuint* fragment_shader = &fragment_shaders_[fragment_shader_id];
407     if (!*fragment_shader) {
408       *fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
409       CompileShader(*fragment_shader,
410                     fragment_shader_source[fragment_shader_id]);
411     }
412     glAttachShader(info->program, *fragment_shader);
413     glBindAttribLocation(info->program, kVertexPositionAttrib, "a_position");
414     glLinkProgram(info->program);
415 #ifndef NDEBUG
416     GLint linked;
417     glGetProgramiv(info->program, GL_LINK_STATUS, &linked);
418     if (!linked)
419       DLOG(ERROR) << "CopyTextureCHROMIUM: program link failure.";
420 #endif
421     info->matrix_handle = glGetUniformLocation(info->program, "u_matrix");
422     info->half_size_handle = glGetUniformLocation(info->program, "u_half_size");
423     info->sampler_handle = glGetUniformLocation(info->program, "u_sampler");
424   }
425   glUseProgram(info->program);
426 
427 #ifndef NDEBUG
428   glValidateProgram(info->program);
429   GLint validation_status;
430   glGetProgramiv(info->program, GL_VALIDATE_STATUS, &validation_status);
431   if (GL_TRUE != validation_status) {
432     DLOG(ERROR) << "CopyTextureCHROMIUM: Invalid shader.";
433     return;
434   }
435 #endif
436 
437   glUniformMatrix4fv(info->matrix_handle, 1, GL_FALSE, transform_matrix);
438   if (source_target == GL_TEXTURE_RECTANGLE_ARB)
439     glUniform2f(info->half_size_handle, width / 2.0f, height / 2.0f);
440   else
441     glUniform2f(info->half_size_handle, 0.5f, 0.5f);
442 
443   if (BindFramebufferTexture2D(
444           GL_TEXTURE_2D, dest_id, dest_level, framebuffer_)) {
445     decoder->ClearAllAttributes();
446     glEnableVertexAttribArray(kVertexPositionAttrib);
447 
448     glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
449     glVertexAttribPointer(kVertexPositionAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
450 
451     glUniform1i(info->sampler_handle, 0);
452 
453     glBindTexture(source_target, source_id);
454     glTexParameterf(source_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
455     glTexParameterf(source_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
456     glTexParameteri(source_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
457     glTexParameteri(source_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
458 
459     glDisable(GL_DEPTH_TEST);
460     glDisable(GL_SCISSOR_TEST);
461     glDisable(GL_STENCIL_TEST);
462     glDisable(GL_CULL_FACE);
463     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
464     glDepthMask(GL_FALSE);
465     glDisable(GL_BLEND);
466 
467     glViewport(0, 0, width, height);
468     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
469   }
470 
471   decoder->RestoreAllAttributes();
472   decoder->RestoreTextureState(source_id);
473   decoder->RestoreTextureState(dest_id);
474   decoder->RestoreTextureUnitBindings(0);
475   decoder->RestoreActiveTexture();
476   decoder->RestoreProgramBindings();
477   decoder->RestoreBufferBindings();
478   decoder->RestoreFramebufferBindings();
479   decoder->RestoreGlobalState();
480 }
481 
482 }  // namespace gpu
483