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. 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. 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 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 184 void DeleteShader(GLuint shader) { 185 if (shader) 186 glDeleteShader(shader); 187 } 188 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 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 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 262 CopyTextureCHROMIUMResourceManager::~CopyTextureCHROMIUMResourceManager() { 263 DCHECK(!buffer_id_); 264 DCHECK(!framebuffer_); 265 } 266 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 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 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 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