1 // Copyright (c) 2013 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 "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h" 6 7 #include <string> 8 #include <OpenGL/gl.h> 9 10 #include "base/basictypes.h" 11 #include "base/debug/trace_event.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/values.h" 15 #include "content/browser/gpu/gpu_data_manager_impl.h" 16 #include "gpu/config/gpu_driver_bug_workaround_type.h" 17 18 namespace content { 19 20 namespace { 21 22 // Convenience macro allowing GLSL programs to be specified inline, and to be 23 // automatically converted into string form by the C preprocessor. 24 #define GLSL_PROGRAM_AS_STRING(shader_code) #shader_code 25 26 // As required by the spec, add the version directive to the beginning of each 27 // program to activate the expected syntax and built-in features. GLSL version 28 // 1.2 is the latest version supported by MacOS 10.6. 29 const char kVersionDirective[] = "#version 120\n"; 30 31 // Allow switchable output swizzling from RGBToYV12 fragment shaders (needed for 32 // workaround; see comments in CompositingIOSurfaceShaderPrograms ctor). 33 const char kOutputSwizzleMacroNormal[] = "#define OUTPUT_PIXEL_ORDERING bgra\n"; 34 const char kOutputSwizzleMacroSwapRB[] = "#define OUTPUT_PIXEL_ORDERING rgba\n"; 35 36 // Only the bare-bones calculations here for speed. 37 const char kvsBlit[] = GLSL_PROGRAM_AS_STRING( 38 varying vec2 texture_coord; 39 void main() { 40 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 41 texture_coord = gl_MultiTexCoord0.xy; 42 } 43 ); 44 45 // Just samples the texture. 46 const char kfsBlit[] = GLSL_PROGRAM_AS_STRING( 47 uniform sampler2DRect texture_; 48 varying vec2 texture_coord; 49 void main() { 50 gl_FragColor = vec4(texture2DRect(texture_, texture_coord).rgb, 1.0); 51 } 52 ); 53 54 55 // Only calculates position. 56 const char kvsSolidWhite[] = GLSL_PROGRAM_AS_STRING( 57 void main() { 58 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 59 } 60 ); 61 62 // Always white. 63 const char kfsSolidWhite[] = GLSL_PROGRAM_AS_STRING( 64 void main() { 65 gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 66 } 67 ); 68 69 70 /////////////////////////////////////////////////////////////////////// 71 // RGB24 to YV12 in two passes; writing two 8888 targets each pass. 72 // 73 // YV12 is full-resolution luma and half-resolution blue/red chroma. 74 // 75 // (original) 76 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB 77 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB 78 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB 79 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB 80 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB 81 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB 82 // | 83 // | (y plane) (temporary) 84 // | YYYY YYYY UUVV UUVV 85 // +--> { YYYY YYYY + UUVV UUVV } 86 // YYYY YYYY UUVV UUVV 87 // First YYYY YYYY UUVV UUVV 88 // pass YYYY YYYY UUVV UUVV 89 // YYYY YYYY UUVV UUVV 90 // | 91 // | (u plane) (v plane) 92 // Second | UUUU VVVV 93 // pass +--> { UUUU + VVVV } 94 // UUUU VVVV 95 // 96 /////////////////////////////////////////////////////////////////////// 97 98 // Phase one of RGB24->YV12 conversion: vsFetch4Pixels/fsConvertRGBtoY8UV44 99 // 100 // Writes four source pixels at a time to a full-size Y plane and a half-width 101 // interleaved UV plane. After execution, the Y plane is complete but the UV 102 // planes still need to be de-interleaved and vertically scaled. 103 const char kRGBtoYV12_vsFetch4Pixels[] = GLSL_PROGRAM_AS_STRING( 104 uniform float texel_scale_x_; 105 varying vec2 texture_coord0; 106 varying vec2 texture_coord1; 107 varying vec2 texture_coord2; 108 varying vec2 texture_coord3; 109 void main() { 110 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 111 112 vec2 texcoord_base = gl_MultiTexCoord0.xy; 113 vec2 one_texel_x = vec2(texel_scale_x_, 0.0); 114 texture_coord0 = texcoord_base - 1.5 * one_texel_x; 115 texture_coord1 = texcoord_base - 0.5 * one_texel_x; 116 texture_coord2 = texcoord_base + 0.5 * one_texel_x; 117 texture_coord3 = texcoord_base + 1.5 * one_texel_x; 118 } 119 ); 120 121 const char kRGBtoYV12_fsConvertRGBtoY8UV44[] = GLSL_PROGRAM_AS_STRING( 122 const vec3 rgb_to_y = vec3(0.257, 0.504, 0.098); 123 const vec3 rgb_to_u = vec3(-0.148, -0.291, 0.439); 124 const vec3 rgb_to_v = vec3(0.439, -0.368, -0.071); 125 const float y_bias = 0.0625; 126 const float uv_bias = 0.5; 127 uniform sampler2DRect texture_; 128 varying vec2 texture_coord0; 129 varying vec2 texture_coord1; 130 varying vec2 texture_coord2; 131 varying vec2 texture_coord3; 132 void main() { 133 // Load the four texture samples. 134 vec3 pixel0 = texture2DRect(texture_, texture_coord0).rgb; 135 vec3 pixel1 = texture2DRect(texture_, texture_coord1).rgb; 136 vec3 pixel2 = texture2DRect(texture_, texture_coord2).rgb; 137 vec3 pixel3 = texture2DRect(texture_, texture_coord3).rgb; 138 139 // RGB -> Y conversion (x4). 140 vec4 yyyy = vec4(dot(pixel0, rgb_to_y), 141 dot(pixel1, rgb_to_y), 142 dot(pixel2, rgb_to_y), 143 dot(pixel3, rgb_to_y)) + y_bias; 144 145 // Average adjacent texture samples while converting RGB->UV. This is the 146 // same as color converting then averaging, but slightly less math. These 147 // values will be in the range [-0.439f, +0.439f] and still need to have 148 // the bias term applied. 149 vec3 blended_pixel0 = pixel0 + pixel1; 150 vec3 blended_pixel1 = pixel2 + pixel3; 151 vec2 uu = vec2(dot(blended_pixel0, rgb_to_u), 152 dot(blended_pixel1, rgb_to_u)) / 2.0; 153 vec2 vv = vec2(dot(blended_pixel0, rgb_to_v), 154 dot(blended_pixel1, rgb_to_v)) / 2.0; 155 156 gl_FragData[0] = yyyy.OUTPUT_PIXEL_ORDERING; 157 gl_FragData[1] = vec4(uu, vv) + uv_bias; 158 } 159 ); 160 161 // Phase two of RGB24->YV12 conversion: vsFetch2Pixels/fsConvertUV44toU2V2 162 // 163 // Deals with UV only. Input is two UUVV quads. The pixels have already been 164 // scaled horizontally prior to this point, and vertical scaling will now happen 165 // via bilinear interpolation during texture sampling. Output is two color 166 // planes U and V, packed four pixels to a "RGBA" quad. 167 const char kRGBtoYV12_vsFetch2Pixels[] = GLSL_PROGRAM_AS_STRING( 168 varying vec2 texture_coord0; 169 varying vec2 texture_coord1; 170 void main() { 171 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 172 173 vec2 texcoord_base = gl_MultiTexCoord0.xy; 174 texture_coord0 = texcoord_base - vec2(0.5, 0.0); 175 texture_coord1 = texcoord_base + vec2(0.5, 0.0); 176 } 177 ); 178 179 const char kRGBtoYV12_fsConvertUV44toU2V2[] = GLSL_PROGRAM_AS_STRING( 180 uniform sampler2DRect texture_; 181 varying vec2 texture_coord0; 182 varying vec2 texture_coord1; 183 void main() { 184 // We're just sampling two pixels and unswizzling them. There's no need 185 // to do vertical scaling with math, since bilinear interpolation in the 186 // sampler takes care of that. 187 vec4 lo_uuvv = texture2DRect(texture_, texture_coord0); 188 vec4 hi_uuvv = texture2DRect(texture_, texture_coord1); 189 gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg).OUTPUT_PIXEL_ORDERING; 190 gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba).OUTPUT_PIXEL_ORDERING; 191 } 192 ); 193 194 195 enum ShaderProgram { 196 SHADER_PROGRAM_BLIT = 0, 197 SHADER_PROGRAM_SOLID_WHITE, 198 SHADER_PROGRAM_RGB_TO_YV12__1_OF_2, 199 SHADER_PROGRAM_RGB_TO_YV12__2_OF_2, 200 NUM_SHADER_PROGRAMS 201 }; 202 203 // The code snippets that together make up an entire vertex shader program. 204 const char* kVertexShaderSourceCodeMap[] = { 205 // SHADER_PROGRAM_BLIT 206 kvsBlit, 207 // SHADER_PROGRAM_SOLID_WHITE 208 kvsSolidWhite, 209 210 // SHADER_PROGRAM_RGB_TO_YV12__1_OF_2 211 kRGBtoYV12_vsFetch4Pixels, 212 // SHADER_PROGRAM_RGB_TO_YV12__2_OF_2 213 kRGBtoYV12_vsFetch2Pixels, 214 }; 215 216 // The code snippets that together make up an entire fragment shader program. 217 const char* kFragmentShaderSourceCodeMap[] = { 218 // SHADER_PROGRAM_BLIT 219 kfsBlit, 220 // SHADER_PROGRAM_SOLID_WHITE 221 kfsSolidWhite, 222 223 // SHADER_PROGRAM_RGB_TO_YV12__1_OF_2 224 kRGBtoYV12_fsConvertRGBtoY8UV44, 225 // SHADER_PROGRAM_RGB_TO_YV12__2_OF_2 226 kRGBtoYV12_fsConvertUV44toU2V2, 227 }; 228 229 GLuint CompileShaderGLSL(ShaderProgram shader_program, GLenum shader_type, 230 bool output_swap_rb) { 231 TRACE_EVENT2("gpu", "CompileShaderGLSL", 232 "program", shader_program, 233 "type", shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment"); 234 235 DCHECK_GE(shader_program, 0); 236 DCHECK_LT(shader_program, NUM_SHADER_PROGRAMS); 237 238 const GLuint shader = glCreateShader(shader_type); 239 DCHECK_NE(shader, 0u); 240 241 // Select and compile the shader program source code. 242 if (shader_type == GL_VERTEX_SHADER) { 243 const GLchar* source_snippets[] = { 244 kVersionDirective, 245 kVertexShaderSourceCodeMap[shader_program], 246 }; 247 glShaderSource(shader, arraysize(source_snippets), source_snippets, NULL); 248 } else { 249 DCHECK(shader_type == GL_FRAGMENT_SHADER); 250 const GLchar* source_snippets[] = { 251 kVersionDirective, 252 output_swap_rb ? kOutputSwizzleMacroSwapRB : kOutputSwizzleMacroNormal, 253 kFragmentShaderSourceCodeMap[shader_program], 254 }; 255 glShaderSource(shader, arraysize(source_snippets), source_snippets, NULL); 256 } 257 glCompileShader(shader); 258 259 // Check for successful compilation. On error in debug builds, pull the info 260 // log and emit the compiler messages. 261 GLint error; 262 glGetShaderiv(shader, GL_COMPILE_STATUS, &error); 263 if (error != GL_TRUE) { 264 #ifndef NDEBUG 265 static const int kMaxInfoLogLength = 8192; 266 scoped_ptr<char[]> buffer(new char[kMaxInfoLogLength]); 267 GLsizei length_returned = 0; 268 glGetShaderInfoLog(shader, kMaxInfoLogLength - 1, &length_returned, 269 buffer.get()); 270 buffer[kMaxInfoLogLength - 1] = '\0'; 271 DLOG(ERROR) << "Failed to compile " 272 << (shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment") 273 << " shader for program " << shader_program << ":\n" 274 << buffer.get() 275 << (length_returned >= kMaxInfoLogLength ? 276 "\n*** TRUNCATED! ***" : ""); 277 #endif 278 glDeleteShader(shader); 279 return 0; 280 } 281 282 // Success! 283 return shader; 284 } 285 286 GLuint CompileAndLinkProgram(ShaderProgram which, bool output_swap_rb) { 287 TRACE_EVENT1("gpu", "CompileAndLinkProgram", "program", which); 288 289 // Compile and link a new shader program. 290 const GLuint vertex_shader = 291 CompileShaderGLSL(which, GL_VERTEX_SHADER, false); 292 const GLuint fragment_shader = 293 CompileShaderGLSL(which, GL_FRAGMENT_SHADER, output_swap_rb); 294 const GLuint program = glCreateProgram(); 295 DCHECK_NE(program, 0u); 296 glAttachShader(program, vertex_shader); 297 glAttachShader(program, fragment_shader); 298 glLinkProgram(program); 299 300 // Flag shaders for deletion so that they will be deleted automatically when 301 // the program is later deleted. 302 glDeleteShader(vertex_shader); 303 glDeleteShader(fragment_shader); 304 305 // Check that the program successfully linked. 306 GLint error = GL_FALSE; 307 glGetProgramiv(program, GL_LINK_STATUS, &error); 308 if (error != GL_TRUE) { 309 glDeleteProgram(program); 310 return 0; 311 } 312 return program; 313 } 314 315 } // namespace 316 317 318 CompositingIOSurfaceShaderPrograms::CompositingIOSurfaceShaderPrograms() 319 : rgb_to_yv12_output_format_(GL_BGRA) { 320 COMPILE_ASSERT(kNumShaderPrograms == NUM_SHADER_PROGRAMS, 321 header_constant_disagrees_with_enum); 322 COMPILE_ASSERT(arraysize(kVertexShaderSourceCodeMap) == NUM_SHADER_PROGRAMS, 323 vertex_shader_source_code_map_incorrect_size); 324 COMPILE_ASSERT(arraysize(kFragmentShaderSourceCodeMap) == NUM_SHADER_PROGRAMS, 325 fragment_shader_source_code_map_incorrect_size); 326 327 memset(shader_programs_, 0, sizeof(shader_programs_)); 328 for (size_t i = 0; i < arraysize(texture_var_locations_); ++i) 329 texture_var_locations_[i] = -1; 330 for (size_t i = 0; i < arraysize(texel_scale_x_var_locations_); ++i) 331 texel_scale_x_var_locations_[i] = -1; 332 333 // Look for the swizzle_rgba_for_async_readpixels driver bug workaround and 334 // modify rgb_to_yv12_output_format_ if necessary. 335 // See: http://crbug.com/265115 336 GpuDataManagerImpl* const manager = GpuDataManagerImpl::GetInstance(); 337 if (manager) { 338 base::ListValue workarounds; 339 manager->GetDriverBugWorkarounds(&workarounds); 340 base::ListValue::const_iterator it = workarounds.Find( 341 base::StringValue(gpu::GpuDriverBugWorkaroundTypeToString( 342 gpu::SWIZZLE_RGBA_FOR_ASYNC_READPIXELS))); 343 if (it != workarounds.end()) 344 rgb_to_yv12_output_format_ = GL_RGBA; 345 } 346 DVLOG(1) << "Using RGBToYV12 fragment shader output format: " 347 << (rgb_to_yv12_output_format_ == GL_BGRA ? "BGRA" : "RGBA"); 348 } 349 350 CompositingIOSurfaceShaderPrograms::~CompositingIOSurfaceShaderPrograms() { 351 #ifndef NDEBUG 352 for (size_t i = 0; i < arraysize(shader_programs_); ++i) 353 DCHECK_EQ(shader_programs_[i], 0u) << "Failed to call Reset()."; 354 #endif 355 } 356 357 void CompositingIOSurfaceShaderPrograms::Reset() { 358 for (size_t i = 0; i < arraysize(shader_programs_); ++i) { 359 if (shader_programs_[i] != 0u) { 360 glDeleteProgram(shader_programs_[i]); 361 DCHECK(glGetError() == GL_NO_ERROR) 362 << "when calling glDeleteProgram(shader_programs_[" << i << "])"; 363 shader_programs_[i] = 0u; 364 } 365 } 366 for (size_t i = 0; i < arraysize(texture_var_locations_); ++i) 367 texture_var_locations_[i] = -1; 368 for (size_t i = 0; i < arraysize(texel_scale_x_var_locations_); ++i) 369 texel_scale_x_var_locations_[i] = -1; 370 } 371 372 bool CompositingIOSurfaceShaderPrograms::UseBlitProgram() { 373 const GLuint program = GetShaderProgram(SHADER_PROGRAM_BLIT); 374 if (program == 0u) 375 return false; 376 glUseProgram(program); 377 BindUniformTextureVariable(SHADER_PROGRAM_BLIT, 0); 378 return true; 379 } 380 381 bool CompositingIOSurfaceShaderPrograms::UseSolidWhiteProgram() { 382 const GLuint program = GetShaderProgram(SHADER_PROGRAM_SOLID_WHITE); 383 if (program == 0u) 384 return false; 385 glUseProgram(program); 386 return true; 387 } 388 389 bool CompositingIOSurfaceShaderPrograms::UseRGBToYV12Program( 390 int pass_number, float texel_scale_x) { 391 const int which = SHADER_PROGRAM_RGB_TO_YV12__1_OF_2 + pass_number - 1; 392 DCHECK_GE(which, SHADER_PROGRAM_RGB_TO_YV12__1_OF_2); 393 DCHECK_LE(which, SHADER_PROGRAM_RGB_TO_YV12__2_OF_2); 394 395 const GLuint program = GetShaderProgram(which); 396 if (program == 0u) 397 return false; 398 glUseProgram(program); 399 BindUniformTextureVariable(which, 0); 400 if (which == SHADER_PROGRAM_RGB_TO_YV12__1_OF_2) { 401 BindUniformTexelScaleXVariable(which, texel_scale_x); 402 } else { 403 // The second pass doesn't have a texel_scale_x uniform variable since it's 404 // never supposed to be doing any scaling (i.e., outside of the usual 405 // 2x2-->1x1 that's already built into the process). 406 DCHECK_EQ(texel_scale_x, 1.0f); 407 } 408 return true; 409 } 410 411 void CompositingIOSurfaceShaderPrograms::SetOutputFormatForTesting( 412 GLenum format) { 413 rgb_to_yv12_output_format_ = format; 414 Reset(); 415 } 416 417 GLuint CompositingIOSurfaceShaderPrograms::GetShaderProgram(int which) { 418 if (shader_programs_[which] == 0u) { 419 shader_programs_[which] = 420 CompileAndLinkProgram(static_cast<ShaderProgram>(which), 421 rgb_to_yv12_output_format_ == GL_RGBA); 422 DCHECK_NE(shader_programs_[which], 0u) 423 << "Failed to create ShaderProgram " << which; 424 } 425 return shader_programs_[which]; 426 } 427 428 void CompositingIOSurfaceShaderPrograms::BindUniformTextureVariable( 429 int which, int texture_unit_offset) { 430 if (texture_var_locations_[which] == -1) { 431 texture_var_locations_[which] = 432 glGetUniformLocation(GetShaderProgram(which), "texture_"); 433 DCHECK_NE(texture_var_locations_[which], -1) 434 << "Failed to find location of uniform variable: texture_"; 435 } 436 glUniform1i(texture_var_locations_[which], texture_unit_offset); 437 } 438 439 void CompositingIOSurfaceShaderPrograms::BindUniformTexelScaleXVariable( 440 int which, float texel_scale_x) { 441 if (texel_scale_x_var_locations_[which] == -1) { 442 texel_scale_x_var_locations_[which] = 443 glGetUniformLocation(GetShaderProgram(which), "texel_scale_x_"); 444 DCHECK_NE(texel_scale_x_var_locations_[which], -1) 445 << "Failed to find location of uniform variable: texel_scale_x_"; 446 } 447 glUniform1f(texel_scale_x_var_locations_[which], texel_scale_x); 448 } 449 450 } // namespace content 451