Make GL bindings conditional
[chromium-blink-merge.git] / gpu / command_buffer / service / gles2_cmd_copy_texture_chromium.cc
blob9098aba839010a9950684fa87b95108e4109582d
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
7 #include <algorithm>
9 #include "base/basictypes.h"
10 #include "gpu/command_buffer/service/gl_utils.h"
11 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
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 sampler2DRect\n" \
25 "#define TextureLookup texture2DRect\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)
33 namespace {
35 enum VertexShaderId {
36 VERTEX_SHADER_COPY_TEXTURE,
37 VERTEX_SHADER_COPY_TEXTURE_FLIP_Y,
38 NUM_VERTEX_SHADERS,
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,
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 }),
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;
106 // Returns the correct vertex shader id to evaluate the copy operation for
107 // the CHROMIUM_flipy setting.
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,
115 unsigned index = flip_y ? 1 : 0;
116 return shader_ids[index];
119 // Returns the correct fragment shader id to evaluate the copy operation for
120 // the premultiply alpha pixel store settings and 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
131 // bit 0: premultiply alpha
132 // bit 1: unpremultiply alpha
133 static FragmentShaderId shader_ids[][NUM_SAMPLERS] = {
135 FRAGMENT_SHADER_COPY_TEXTURE_2D,
136 FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
137 FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
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,
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,
150 FRAGMENT_SHADER_COPY_TEXTURE_2D,
151 FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
152 FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
155 unsigned index = (premultiply_alpha ? (1 << 0) : 0) |
156 (unpremultiply_alpha ? (1 << 1) : 0);
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;
169 NOTREACHED();
170 return shader_ids[0][SAMPLER_2D];
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
184 void DeleteShader(GLuint shader) {
185 if (shader)
186 glDeleteShader(shader);
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);
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;
212 #endif
213 return true;
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 */);
244 decoder->RestoreTextureState(source_id);
245 decoder->RestoreTextureState(dest_id);
246 decoder->RestoreTextureUnitBindings(0);
247 decoder->RestoreActiveTexture();
248 decoder->RestoreFramebufferBindings();
251 } // namespace
253 namespace gpu {
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) {}
262 CopyTextureCHROMIUMResourceManager::~CopyTextureCHROMIUMResourceManager() {
263 // |buffer_id_| and |framebuffer_| can be not-null because when GPU context is
264 // lost, this class can be deleted without releasing resources like
265 // GLES2DecoderImpl.
268 void CopyTextureCHROMIUMResourceManager::Initialize(
269 const gles2::GLES2Decoder* decoder) {
270 static_assert(
271 kVertexPositionAttrib == 0u,
272 "kVertexPositionAttrib must be 0");
273 DCHECK(!buffer_id_);
274 DCHECK(!framebuffer_);
275 DCHECK(programs_.empty());
277 // Initialize all of the GPU resources required to perform the copy.
278 glGenBuffersARB(1, &buffer_id_);
279 glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
280 const GLfloat kQuadVertices[] = {-1.0f, -1.0f,
281 1.0f, -1.0f,
282 1.0f, 1.0f,
283 -1.0f, 1.0f};
284 glBufferData(
285 GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
287 glGenFramebuffersEXT(1, &framebuffer_);
289 decoder->RestoreBufferBindings();
291 initialized_ = true;
294 void CopyTextureCHROMIUMResourceManager::Destroy() {
295 if (!initialized_)
296 return;
298 glDeleteFramebuffersEXT(1, &framebuffer_);
299 framebuffer_ = 0;
301 std::for_each(vertex_shaders_.begin(), vertex_shaders_.end(), DeleteShader);
302 std::for_each(
303 fragment_shaders_.begin(), fragment_shaders_.end(), DeleteShader);
305 for (ProgramMap::const_iterator it = programs_.begin(); it != programs_.end();
306 ++it) {
307 const ProgramInfo& info = it->second;
308 glDeleteProgram(info.program);
311 glDeleteBuffersARB(1, &buffer_id_);
312 buffer_id_ = 0;
315 void CopyTextureCHROMIUMResourceManager::DoCopyTexture(
316 const gles2::GLES2Decoder* decoder,
317 GLenum source_target,
318 GLuint source_id,
319 GLenum source_internal_format,
320 GLuint dest_id,
321 GLint dest_level,
322 GLenum dest_internal_format,
323 GLsizei width,
324 GLsizei height,
325 bool flip_y,
326 bool premultiply_alpha,
327 bool unpremultiply_alpha) {
328 bool premultiply_alpha_change = premultiply_alpha ^ unpremultiply_alpha;
329 // GL_INVALID_OPERATION is generated if the currently bound framebuffer's
330 // format does not contain a superset of the components required by the base
331 // format of internalformat.
332 // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml
333 bool source_format_contain_superset_of_dest_format =
334 (source_internal_format == dest_internal_format &&
335 source_internal_format != GL_BGRA_EXT) ||
336 (source_internal_format == GL_RGBA && dest_internal_format == GL_RGB);
337 // GL_TEXTURE_RECTANGLE_ARB on FBO is supported by OpenGL, not GLES2,
338 // so restrict this to GL_TEXTURE_2D.
339 if (source_target == GL_TEXTURE_2D && !flip_y && !premultiply_alpha_change &&
340 source_format_contain_superset_of_dest_format) {
341 DoCopyTexImage2D(decoder,
342 source_target,
343 source_id,
344 dest_id,
345 dest_level,
346 dest_internal_format,
347 width,
348 height,
349 framebuffer_);
350 return;
353 // Use default transform matrix if no transform passed in.
354 const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f,
355 0.0f, 1.0f, 0.0f, 0.0f,
356 0.0f, 0.0f, 1.0f, 0.0f,
357 0.0f, 0.0f, 0.0f, 1.0f};
358 DoCopyTextureWithTransform(decoder,
359 source_target,
360 source_id,
361 dest_id,
362 dest_level,
363 width,
364 height,
365 flip_y,
366 premultiply_alpha,
367 unpremultiply_alpha,
368 default_matrix);
371 void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform(
372 const gles2::GLES2Decoder* decoder,
373 GLenum source_target,
374 GLuint source_id,
375 GLuint dest_id,
376 GLint dest_level,
377 GLsizei width,
378 GLsizei height,
379 bool flip_y,
380 bool premultiply_alpha,
381 bool unpremultiply_alpha,
382 const GLfloat transform_matrix[16]) {
383 DCHECK(source_target == GL_TEXTURE_2D ||
384 source_target == GL_TEXTURE_RECTANGLE_ARB ||
385 source_target == GL_TEXTURE_EXTERNAL_OES);
386 if (!initialized_) {
387 DLOG(ERROR) << "CopyTextureCHROMIUM: Uninitialized manager.";
388 return;
391 VertexShaderId vertex_shader_id = GetVertexShaderId(flip_y);
392 DCHECK_LT(static_cast<size_t>(vertex_shader_id), vertex_shaders_.size());
393 FragmentShaderId fragment_shader_id = GetFragmentShaderId(
394 premultiply_alpha, unpremultiply_alpha, source_target);
395 DCHECK_LT(static_cast<size_t>(fragment_shader_id), fragment_shaders_.size());
397 ProgramMapKey key(vertex_shader_id, fragment_shader_id);
398 ProgramInfo* info = &programs_[key];
399 // Create program if necessary.
400 if (!info->program) {
401 info->program = glCreateProgram();
402 GLuint* vertex_shader = &vertex_shaders_[vertex_shader_id];
403 if (!*vertex_shader) {
404 *vertex_shader = glCreateShader(GL_VERTEX_SHADER);
405 CompileShader(*vertex_shader, vertex_shader_source[vertex_shader_id]);
407 glAttachShader(info->program, *vertex_shader);
408 GLuint* fragment_shader = &fragment_shaders_[fragment_shader_id];
409 if (!*fragment_shader) {
410 *fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
411 CompileShader(*fragment_shader,
412 fragment_shader_source[fragment_shader_id]);
414 glAttachShader(info->program, *fragment_shader);
415 glBindAttribLocation(info->program, kVertexPositionAttrib, "a_position");
416 glLinkProgram(info->program);
417 #ifndef NDEBUG
418 GLint linked;
419 glGetProgramiv(info->program, GL_LINK_STATUS, &linked);
420 if (!linked)
421 DLOG(ERROR) << "CopyTextureCHROMIUM: program link failure.";
422 #endif
423 info->matrix_handle = glGetUniformLocation(info->program, "u_matrix");
424 info->half_size_handle = glGetUniformLocation(info->program, "u_half_size");
425 info->sampler_handle = glGetUniformLocation(info->program, "u_sampler");
427 glUseProgram(info->program);
429 #ifndef NDEBUG
430 glValidateProgram(info->program);
431 GLint validation_status;
432 glGetProgramiv(info->program, GL_VALIDATE_STATUS, &validation_status);
433 if (GL_TRUE != validation_status) {
434 DLOG(ERROR) << "CopyTextureCHROMIUM: Invalid shader.";
435 return;
437 #endif
439 glUniformMatrix4fv(info->matrix_handle, 1, GL_FALSE, transform_matrix);
440 if (source_target == GL_TEXTURE_RECTANGLE_ARB)
441 glUniform2f(info->half_size_handle, width / 2.0f, height / 2.0f);
442 else
443 glUniform2f(info->half_size_handle, 0.5f, 0.5f);
445 if (BindFramebufferTexture2D(
446 GL_TEXTURE_2D, dest_id, dest_level, framebuffer_)) {
447 decoder->ClearAllAttributes();
448 glEnableVertexAttribArray(kVertexPositionAttrib);
450 glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
451 glVertexAttribPointer(kVertexPositionAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
453 glUniform1i(info->sampler_handle, 0);
455 glBindTexture(source_target, source_id);
456 glTexParameterf(source_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
457 glTexParameterf(source_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
458 glTexParameteri(source_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
459 glTexParameteri(source_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
461 glDisable(GL_DEPTH_TEST);
462 glDisable(GL_SCISSOR_TEST);
463 glDisable(GL_STENCIL_TEST);
464 glDisable(GL_CULL_FACE);
465 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
466 glDepthMask(GL_FALSE);
467 glDisable(GL_BLEND);
469 glViewport(0, 0, width, height);
470 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
473 decoder->RestoreAllAttributes();
474 decoder->RestoreTextureState(source_id);
475 decoder->RestoreTextureState(dest_id);
476 decoder->RestoreTextureUnitBindings(0);
477 decoder->RestoreActiveTexture();
478 decoder->RestoreProgramBindings();
479 decoder->RestoreBufferBindings();
480 decoder->RestoreFramebufferBindings();
481 decoder->RestoreGlobalState();
484 } // namespace gpu