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 <string.h>
8#include "base/basictypes.h"
9#include "gpu/command_buffer/common/types.h"
10#include "gpu/command_buffer/service/gl_utils.h"
11#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
12
13#define SHADER0(src) \
14    "#ifdef GL_ES\n"\
15    "precision mediump float;\n"\
16    "#endif\n"\
17    #src
18#define SHADER(src) { false, SHADER0(src), }
19#define SHADER_EXTERNAL_OES0(src) \
20    "#extension GL_OES_EGL_image_external : require\n"\
21    "#ifdef GL_ES\n"\
22    "precision mediump float;\n"\
23    "#endif\n"\
24    #src
25#define SHADER_EXTERNAL_OES(src) { true, SHADER_EXTERNAL_OES0(src), }
26
27namespace {
28
29const GLfloat kQuadVertices[] = { -1.0f, -1.0f, 0.0f, 1.0f,
30                                   1.0f, -1.0f, 0.0f, 1.0f,
31                                   1.0f,  1.0f, 0.0f, 1.0f,
32                                  -1.0f,  1.0f, 0.0f, 1.0f };
33
34enum ProgramId {
35  PROGRAM_COPY_TEXTURE,
36  PROGRAM_COPY_TEXTURE_FLIP_Y,
37  PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA,
38  PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA,
39  PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA_FLIPY,
40  PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_FLIPY,
41  PROGRAM_COPY_TEXTURE_OES,
42  PROGRAM_COPY_TEXTURE_OES_FLIP_Y,
43  PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA,
44  PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA,
45  PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA_FLIPY,
46  PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA_FLIPY,
47};
48
49struct ShaderInfo {
50  bool needs_egl_image_external;
51  const char* source;
52};
53
54const ShaderInfo shader_infos[] = {
55  // VERTEX_SHADER_POS_TEX
56  SHADER(
57    uniform mat4 u_matrix;
58    attribute vec4 a_position;
59    varying vec2 v_uv;
60    void main(void) {
61      gl_Position = u_matrix * a_position;
62      v_uv = a_position.xy * 0.5 + vec2(0.5, 0.5);
63    }),
64  // FRAGMENT_SHADER_TEX
65  SHADER(
66    uniform sampler2D u_texSampler;
67    varying vec2 v_uv;
68    void main(void) {
69      gl_FragColor = texture2D(u_texSampler, v_uv.st);
70    }),
71  // FRAGMENT_SHADER_TEX_FLIP_Y
72  SHADER(
73    uniform sampler2D u_texSampler;
74    varying vec2 v_uv;
75    void main(void) {
76      gl_FragColor = texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
77    }),
78  // FRAGMENT_SHADER_TEX_PREMULTIPLY_ALPHA
79  SHADER(
80    uniform sampler2D u_texSampler;
81    varying vec2 v_uv;
82    void main(void) {
83      gl_FragColor = texture2D(u_texSampler, v_uv.st);
84      gl_FragColor.rgb *= gl_FragColor.a;
85    }),
86  // FRAGMENT_SHADER_TEX_UNPREMULTIPLY_ALPHA
87  SHADER(
88    uniform sampler2D u_texSampler;
89    varying vec2 v_uv;
90    void main(void) {
91      gl_FragColor = texture2D(u_texSampler, v_uv.st);
92      if (gl_FragColor.a > 0.0)
93        gl_FragColor.rgb /= gl_FragColor.a;
94    }),
95  // FRAGMENT_SHADER_TEX_PREMULTIPLY_ALPHA_FLIP_Y
96  SHADER(
97    uniform sampler2D u_texSampler;
98    varying vec2 v_uv;
99    void main(void) {
100      gl_FragColor = texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
101      gl_FragColor.rgb *= gl_FragColor.a;
102    }),
103  // FRAGMENT_SHADER_TEX_UNPREMULTIPLY_ALPHA_FLIP_Y
104  SHADER(
105    uniform sampler2D u_texSampler;
106    varying vec2 v_uv;
107    void main(void) {
108      gl_FragColor = texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
109      if (gl_FragColor.a > 0.0)
110        gl_FragColor.rgb /= gl_FragColor.a;
111    }),
112  // FRAGMENT_SHADER_TEX_OES
113  SHADER_EXTERNAL_OES(
114    precision mediump float;
115    uniform samplerExternalOES u_texSampler;
116    varying vec2 v_uv;
117    void main(void) {
118      gl_FragColor = texture2D(u_texSampler, v_uv.st);
119    }),
120  // FRAGMENT_SHADER_TEX_OES_FLIP_Y
121  SHADER_EXTERNAL_OES(
122     precision mediump float;
123     uniform samplerExternalOES u_texSampler;
124     varying vec2 v_uv;
125     void main(void) {
126       gl_FragColor =
127           texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
128     }),
129  // FRAGMENT_SHADER_TEX_OES_PREMULTIPLY_ALPHA
130  SHADER_EXTERNAL_OES(
131     precision mediump float;
132     uniform samplerExternalOES u_texSampler;
133     varying vec2 v_uv;
134     void main(void) {
135       gl_FragColor = texture2D(u_texSampler, v_uv.st);
136       gl_FragColor.rgb *= gl_FragColor.a;
137     }),
138  // FRAGMENT_SHADER_TEX_OES_UNPREMULTIPLY_ALPHA
139  SHADER_EXTERNAL_OES(
140    precision mediump float;
141    uniform samplerExternalOES u_texSampler;
142    varying vec2 v_uv;
143    void main(void) {
144      gl_FragColor = texture2D(u_texSampler, v_uv.st);
145      if (gl_FragColor.a > 0.0)
146        gl_FragColor.rgb /= gl_FragColor.a;
147    }),
148  // FRAGMENT_SHADER_TEX_OES_PREMULTIPLY_ALPHA_FLIP_Y
149  SHADER_EXTERNAL_OES(
150      precision mediump float;
151      uniform samplerExternalOES u_texSampler;
152      varying vec2 v_uv;
153      void main(void) {
154        gl_FragColor =
155            texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
156        gl_FragColor.rgb *= gl_FragColor.a;
157      }),
158  // FRAGMENT_SHADER_TEX_OES_UNPREMULTIPLY_ALPHA_FLIP_Y
159  SHADER_EXTERNAL_OES(
160      precision mediump float;
161      uniform samplerExternalOES u_texSampler;
162      varying vec2 v_uv;
163      void main(void) {
164        gl_FragColor =
165            texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
166        if (gl_FragColor.a > 0.0)
167          gl_FragColor.rgb /= gl_FragColor.a;
168      }),
169};
170
171const int kNumShaders = arraysize(shader_infos);
172
173// Returns the correct program to evaluate the copy operation for
174// the CHROMIUM_flipy and premultiply alpha pixel store settings.
175ProgramId GetProgram(
176    bool flip_y,
177    bool premultiply_alpha,
178    bool unpremultiply_alpha,
179    bool is_source_external_oes) {
180  // If both pre-multiply and unpremultiply are requested, then perform no
181  // alpha manipulation.
182  if (premultiply_alpha && unpremultiply_alpha) {
183    premultiply_alpha = false;
184    unpremultiply_alpha = false;
185  }
186
187  // bit 0: Flip_y
188  // bit 1: Premult
189  // bit 2: Unpremult
190  // bit 3: External_oes
191  static ProgramId program_ids[] = {
192    PROGRAM_COPY_TEXTURE,
193    PROGRAM_COPY_TEXTURE_FLIP_Y,                         // F
194    PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA,              //   P
195    PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA_FLIPY,        // F P
196    PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA,            //     U
197    PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_FLIPY,      // F   U
198    PROGRAM_COPY_TEXTURE,                                //   P U
199    PROGRAM_COPY_TEXTURE,                                // F P U
200    PROGRAM_COPY_TEXTURE_OES,                            //       E
201    PROGRAM_COPY_TEXTURE_OES_FLIP_Y,                     // F     E
202    PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA,          //   P   E
203    PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA_FLIPY,    // F P   E
204    PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA,        //     U E
205    PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA_FLIPY,  // F   U E
206    PROGRAM_COPY_TEXTURE_OES,                            //   P U E
207    PROGRAM_COPY_TEXTURE_OES,                            // F P U E
208  };
209
210  unsigned index = (flip_y                 ? (1 << 0) : 0) |
211                   (premultiply_alpha      ? (1 << 1) : 0) |
212                   (unpremultiply_alpha    ? (1 << 2) : 0) |
213                   (is_source_external_oes ? (1 << 3) : 0);
214  return program_ids[index];
215}
216
217}  // namespace
218
219namespace gpu {
220
221CopyTextureCHROMIUMResourceManager::CopyTextureCHROMIUMResourceManager()
222  : initialized_(false),
223    buffer_id_(0),
224    framebuffer_(0) {
225  for (int i = 0; i < kNumPrograms; ++i) {
226    programs_[i] = 0;
227    matrix_handle_[i] = 0;
228    sampler_locations_[i] = 0;
229  }
230}
231
232void CopyTextureCHROMIUMResourceManager::Initialize(
233    const gles2::GLES2Decoder* decoder) {
234  COMPILE_ASSERT(
235      kVertexPositionAttrib == 0u,
236      Position_attribs_must_be_0);
237
238  const char* extensions =
239      reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
240  bool have_egl_image_external = extensions &&
241      strstr(extensions, "GL_OES_EGL_image_external");
242
243  // Initialize all of the GPU resources required to perform the copy.
244  glGenBuffersARB(1, &buffer_id_);
245  glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
246  glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices,
247               GL_STATIC_DRAW);
248
249  glGenFramebuffersEXT(1, &framebuffer_);
250
251  // TODO(gman): Init these on demand.
252  GLuint shaders[kNumShaders];
253  for (int shader = 0; shader < kNumShaders; ++shader) {
254    shaders[shader] = glCreateShader(
255        shader == 0 ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
256    const ShaderInfo& info = shader_infos[shader];
257    if (info.needs_egl_image_external && !have_egl_image_external) {
258      continue;
259    }
260    const char* shader_source = shader_infos[shader].source;
261    glShaderSource(shaders[shader], 1, &shader_source, 0);
262    glCompileShader(shaders[shader]);
263#ifndef NDEBUG
264    GLint compile_status;
265    glGetShaderiv(shaders[shader], GL_COMPILE_STATUS, &compile_status);
266    if (GL_TRUE != compile_status)
267      DLOG(ERROR) << "CopyTextureCHROMIUM: shader compilation failure.";
268#endif
269  }
270
271  // TODO(gman): Init these on demand.
272  for (int program = 0; program < kNumPrograms; ++program) {
273    const ShaderInfo& info = shader_infos[program + 1];
274    if (info.needs_egl_image_external && !have_egl_image_external) {
275      continue;
276    }
277    programs_[program] = glCreateProgram();
278    glAttachShader(programs_[program], shaders[0]);
279    glAttachShader(programs_[program], shaders[program + 1]);
280
281    glBindAttribLocation(programs_[program], kVertexPositionAttrib,
282                         "a_position");
283
284    glLinkProgram(programs_[program]);
285#ifndef NDEBUG
286    GLint linked;
287    glGetProgramiv(programs_[program], GL_LINK_STATUS, &linked);
288    if (!linked)
289      DLOG(ERROR) << "CopyTextureCHROMIUM: program link failure.";
290#endif
291
292    sampler_locations_[program] = glGetUniformLocation(programs_[program],
293                                                      "u_texSampler");
294
295    matrix_handle_[program] = glGetUniformLocation(programs_[program],
296                                                   "u_matrix");
297  }
298
299  for (int shader = 0; shader < kNumShaders; ++shader)
300    glDeleteShader(shaders[shader]);
301
302  decoder->RestoreBufferBindings();
303
304  initialized_ = true;
305}
306
307void CopyTextureCHROMIUMResourceManager::Destroy() {
308  if (!initialized_)
309    return;
310
311  glDeleteFramebuffersEXT(1, &framebuffer_);
312
313  for (int program = 0; program < kNumPrograms; ++program) {
314    if (programs_[program])
315      glDeleteProgram(programs_[program]);
316  }
317
318  glDeleteBuffersARB(1, &buffer_id_);
319}
320
321void CopyTextureCHROMIUMResourceManager::DoCopyTexture(
322    const gles2::GLES2Decoder* decoder,
323    GLenum source_target,
324    GLenum dest_target,
325    GLuint source_id,
326    GLuint dest_id,
327    GLint level,
328    GLsizei width,
329    GLsizei height,
330    bool flip_y,
331    bool premultiply_alpha,
332    bool unpremultiply_alpha) {
333  // Use default transform matrix if no transform passed in.
334  const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f,
335                                             0.0f, 1.0f, 0.0f, 0.0f,
336                                             0.0f, 0.0f, 1.0f, 0.0f,
337                                             0.0f, 0.0f, 0.0f, 1.0f};
338  DoCopyTextureWithTransform(decoder, source_target, dest_target, source_id,
339      dest_id, level, width, height, flip_y, premultiply_alpha,
340      unpremultiply_alpha, default_matrix);
341}
342
343void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform(
344    const gles2::GLES2Decoder* decoder,
345    GLenum source_target,
346    GLenum dest_target,
347    GLuint source_id,
348    GLuint dest_id,
349    GLint level,
350    GLsizei width,
351    GLsizei height,
352    bool flip_y,
353    bool premultiply_alpha,
354    bool unpremultiply_alpha,
355    const GLfloat transform_matrix[16]) {
356  DCHECK(source_target == GL_TEXTURE_2D ||
357         source_target == GL_TEXTURE_EXTERNAL_OES);
358  if (!initialized_) {
359    DLOG(ERROR) << "CopyTextureCHROMIUM: Uninitialized manager.";
360    return;
361  }
362
363  GLuint program = GetProgram(
364      flip_y, premultiply_alpha, unpremultiply_alpha,
365      source_target == GL_TEXTURE_EXTERNAL_OES);
366  glUseProgram(programs_[program]);
367
368#ifndef NDEBUG
369  glValidateProgram(programs_[program]);
370  GLint validation_status;
371  glGetProgramiv(programs_[program], GL_VALIDATE_STATUS, &validation_status);
372  if (GL_TRUE != validation_status) {
373    DLOG(ERROR) << "CopyTextureCHROMIUM: Invalid shader.";
374    return;
375  }
376#endif
377
378  glUniformMatrix4fv(matrix_handle_[program], 1, GL_FALSE, transform_matrix);
379  glActiveTexture(GL_TEXTURE0);
380  glBindTexture(GL_TEXTURE_2D, dest_id);
381  // NVidia drivers require texture settings to be a certain way
382  // or they won't report FRAMEBUFFER_COMPLETE.
383  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
384  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
385  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
386  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
387  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer_);
388  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dest_target,
389                            dest_id, level);
390
391#ifndef NDEBUG
392  GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
393  if (GL_FRAMEBUFFER_COMPLETE != fb_status) {
394    DLOG(ERROR) << "CopyTextureCHROMIUM: Incomplete framebuffer.";
395  } else
396#endif
397  {
398    glEnableVertexAttribArray(kVertexPositionAttrib);
399
400    glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
401    glVertexAttribPointer(kVertexPositionAttrib, 4, GL_FLOAT, GL_FALSE,
402                          4 * sizeof(GLfloat), 0);
403
404    glUniform1i(sampler_locations_[program], 0);
405
406    glBindTexture(source_target, source_id);
407    glTexParameterf(source_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
408    glTexParameterf(source_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
409    glTexParameteri(source_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
410    glTexParameteri(source_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
411
412    glDisable(GL_DEPTH_TEST);
413    glDisable(GL_SCISSOR_TEST);
414    glDisable(GL_STENCIL_TEST);
415    glDisable(GL_CULL_FACE);
416    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
417    glDepthMask(GL_FALSE);
418    glDisable(GL_BLEND);
419
420    glViewport(0, 0, width, height);
421    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
422  }
423
424  decoder->RestoreAttribute(kVertexPositionAttrib);
425  decoder->RestoreTextureState(source_id);
426  decoder->RestoreTextureState(dest_id);
427  decoder->RestoreTextureUnitBindings(0);
428  decoder->RestoreActiveTexture();
429  decoder->RestoreProgramBindings();
430  decoder->RestoreBufferBindings();
431  decoder->RestoreFramebufferBindings();
432  decoder->RestoreGlobalState();
433}
434
435}  // namespace
436
437