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.
5 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
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"
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
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";
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";
36 // Only the bare-bones calculations here for speed.
37 const char kvsBlit
[] = GLSL_PROGRAM_AS_STRING(
38 varying vec2 texture_coord
;
40 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
41 texture_coord
= gl_MultiTexCoord0
.xy
;
45 // Just samples the texture.
46 const char kfsBlit
[] = GLSL_PROGRAM_AS_STRING(
47 uniform sampler2DRect texture_
;
48 varying vec2 texture_coord
;
50 gl_FragColor
= vec4(texture2DRect(texture_
, texture_coord
).rgb
, 1.0);
55 // Only calculates position.
56 const char kvsSolidWhite
[] = GLSL_PROGRAM_AS_STRING(
58 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
63 const char kfsSolidWhite
[] = GLSL_PROGRAM_AS_STRING(
65 gl_FragColor
= vec4(1.0, 1.0, 1.0, 1.0);
70 ///////////////////////////////////////////////////////////////////////
71 // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
73 // YV12 is full-resolution luma and half-resolution blue/red chroma.
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
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
91 // | (u plane) (v plane)
93 // pass +--> { UUUU + VVVV }
96 ///////////////////////////////////////////////////////////////////////
98 // Phase one of RGB24->YV12 conversion: vsFetch4Pixels/fsConvertRGBtoY8UV44
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
;
110 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
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
;
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
;
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
;
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
;
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;
156 gl_FragData
[0] = yyyy
.OUTPUT_PIXEL_ORDERING
;
157 gl_FragData
[1] = vec4(uu
, vv
) + uv_bias
;
161 // Phase two of RGB24->YV12 conversion: vsFetch2Pixels/fsConvertUV44toU2V2
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
;
171 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
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);
179 const char kRGBtoYV12_fsConvertUV44toU2V2
[] = GLSL_PROGRAM_AS_STRING(
180 uniform sampler2DRect texture_
;
181 varying vec2 texture_coord0
;
182 varying vec2 texture_coord1
;
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
;
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
,
203 // The code snippets that together make up an entire vertex shader program.
204 const char* kVertexShaderSourceCodeMap
[] = {
205 // SHADER_PROGRAM_BLIT
207 // SHADER_PROGRAM_SOLID_WHITE
210 // SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
211 kRGBtoYV12_vsFetch4Pixels
,
212 // SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
213 kRGBtoYV12_vsFetch2Pixels
,
216 // The code snippets that together make up an entire fragment shader program.
217 const char* kFragmentShaderSourceCodeMap
[] = {
218 // SHADER_PROGRAM_BLIT
220 // SHADER_PROGRAM_SOLID_WHITE
223 // SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
224 kRGBtoYV12_fsConvertRGBtoY8UV44
,
225 // SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
226 kRGBtoYV12_fsConvertUV44toU2V2
,
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");
235 DCHECK_GE(shader_program
, 0);
236 DCHECK_LT(shader_program
, NUM_SHADER_PROGRAMS
);
238 const GLuint shader
= glCreateShader(shader_type
);
239 DCHECK_NE(shader
, 0u);
241 // Select and compile the shader program source code.
242 if (shader_type
== GL_VERTEX_SHADER
) {
243 const GLchar
* source_snippets
[] = {
245 kVertexShaderSourceCodeMap
[shader_program
],
247 glShaderSource(shader
, arraysize(source_snippets
), source_snippets
, NULL
);
249 DCHECK(shader_type
== GL_FRAGMENT_SHADER
);
250 const GLchar
* source_snippets
[] = {
252 output_swap_rb
? kOutputSwizzleMacroSwapRB
: kOutputSwizzleMacroNormal
,
253 kFragmentShaderSourceCodeMap
[shader_program
],
255 glShaderSource(shader
, arraysize(source_snippets
), source_snippets
, NULL
);
257 glCompileShader(shader
);
259 // Check for successful compilation. On error in debug builds, pull the info
260 // log and emit the compiler messages.
262 glGetShaderiv(shader
, GL_COMPILE_STATUS
, &error
);
263 if (error
!= GL_TRUE
) {
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
,
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"
275 << (length_returned
>= kMaxInfoLogLength
?
276 "\n*** TRUNCATED! ***" : "");
278 glDeleteShader(shader
);
286 GLuint
CompileAndLinkProgram(ShaderProgram which
, bool output_swap_rb
) {
287 TRACE_EVENT1("gpu", "CompileAndLinkProgram", "program", which
);
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
);
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
);
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
);
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
);
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;
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();
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
;
346 DVLOG(1) << "Using RGBToYV12 fragment shader output format: "
347 << (rgb_to_yv12_output_format_
== GL_BGRA
? "BGRA" : "RGBA");
350 CompositingIOSurfaceShaderPrograms::~CompositingIOSurfaceShaderPrograms() {
352 for (size_t i
= 0; i
< arraysize(shader_programs_
); ++i
)
353 DCHECK_EQ(shader_programs_
[i
], 0u) << "Failed to call Reset().";
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 shader_programs_
[i
] = 0u;
364 for (size_t i
= 0; i
< arraysize(texture_var_locations_
); ++i
)
365 texture_var_locations_
[i
] = -1;
366 for (size_t i
= 0; i
< arraysize(texel_scale_x_var_locations_
); ++i
)
367 texel_scale_x_var_locations_
[i
] = -1;
370 bool CompositingIOSurfaceShaderPrograms::UseBlitProgram() {
371 const GLuint program
= GetShaderProgram(SHADER_PROGRAM_BLIT
);
374 glUseProgram(program
);
375 BindUniformTextureVariable(SHADER_PROGRAM_BLIT
, 0);
379 bool CompositingIOSurfaceShaderPrograms::UseSolidWhiteProgram() {
380 const GLuint program
= GetShaderProgram(SHADER_PROGRAM_SOLID_WHITE
);
383 glUseProgram(program
);
387 bool CompositingIOSurfaceShaderPrograms::UseRGBToYV12Program(
388 int pass_number
, float texel_scale_x
) {
389 const int which
= SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
+ pass_number
- 1;
390 DCHECK_GE(which
, SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
);
391 DCHECK_LE(which
, SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
);
393 const GLuint program
= GetShaderProgram(which
);
396 glUseProgram(program
);
397 BindUniformTextureVariable(which
, 0);
398 if (which
== SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
) {
399 BindUniformTexelScaleXVariable(which
, texel_scale_x
);
401 // The second pass doesn't have a texel_scale_x uniform variable since it's
402 // never supposed to be doing any scaling (i.e., outside of the usual
403 // 2x2-->1x1 that's already built into the process).
404 DCHECK_EQ(texel_scale_x
, 1.0f
);
409 void CompositingIOSurfaceShaderPrograms::SetOutputFormatForTesting(
411 rgb_to_yv12_output_format_
= format
;
415 GLuint
CompositingIOSurfaceShaderPrograms::GetShaderProgram(int which
) {
416 if (shader_programs_
[which
] == 0u) {
417 shader_programs_
[which
] =
418 CompileAndLinkProgram(static_cast<ShaderProgram
>(which
),
419 rgb_to_yv12_output_format_
== GL_RGBA
);
420 DCHECK_NE(shader_programs_
[which
], 0u)
421 << "Failed to create ShaderProgram " << which
;
423 return shader_programs_
[which
];
426 void CompositingIOSurfaceShaderPrograms::BindUniformTextureVariable(
427 int which
, int texture_unit_offset
) {
428 if (texture_var_locations_
[which
] == -1) {
429 texture_var_locations_
[which
] =
430 glGetUniformLocation(GetShaderProgram(which
), "texture_");
431 DCHECK_NE(texture_var_locations_
[which
], -1)
432 << "Failed to find location of uniform variable: texture_";
434 glUniform1i(texture_var_locations_
[which
], texture_unit_offset
);
437 void CompositingIOSurfaceShaderPrograms::BindUniformTexelScaleXVariable(
438 int which
, float texel_scale_x
) {
439 if (texel_scale_x_var_locations_
[which
] == -1) {
440 texel_scale_x_var_locations_
[which
] =
441 glGetUniformLocation(GetShaderProgram(which
), "texel_scale_x_");
442 DCHECK_NE(texel_scale_x_var_locations_
[which
], -1)
443 << "Failed to find location of uniform variable: texel_scale_x_";
445 glUniform1f(texel_scale_x_var_locations_
[which
], texel_scale_x
);
448 } // namespace content