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"
19 // Convenience macro allowing GLSL programs to be specified inline, and to be
20 // automatically converted into string form by the C preprocessor. As required
21 // by the spec, add the version directive to the beginning of each program to
22 // activate the expected syntax and built-in features. GLSL version 1.2 is the
23 // latest version supported by MacOS 10.6.
24 #define GLSL_PROGRAM_AS_STRING(shader_code) "#version 120\n" #shader_code
27 // Only the bare-bones calculations here for speed.
28 const char kvsBlit
[] = GLSL_PROGRAM_AS_STRING(
29 varying vec2 texture_coord
;
31 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
32 texture_coord
= gl_MultiTexCoord0
.xy
;
36 // Just samples the texture.
37 const char kfsBlit
[] = GLSL_PROGRAM_AS_STRING(
38 uniform sampler2DRect texture_
;
39 varying vec2 texture_coord
;
41 gl_FragColor
= vec4(texture2DRect(texture_
, texture_coord
).rgb
, 1.0);
46 // Only calculates position.
47 const char kvsSolidWhite
[] = GLSL_PROGRAM_AS_STRING(
49 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
54 const char kfsSolidWhite
[] = GLSL_PROGRAM_AS_STRING(
56 gl_FragColor
= vec4(1.0, 1.0, 1.0, 1.0);
61 ///////////////////////////////////////////////////////////////////////
62 // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
64 // YV12 is full-resolution luma and half-resolution blue/red chroma.
67 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB
68 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB
69 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB
70 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB
71 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB
72 // XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB
74 // | (y plane) (temporary)
75 // | YYYY YYYY UUVV UUVV
76 // +--> { YYYY YYYY + UUVV UUVV }
77 // YYYY YYYY UUVV UUVV
78 // First YYYY YYYY UUVV UUVV
79 // pass YYYY YYYY UUVV UUVV
80 // YYYY YYYY UUVV UUVV
82 // | (u plane) (v plane)
84 // pass +--> { UUUU + VVVV }
87 ///////////////////////////////////////////////////////////////////////
89 // Phase one of RGB24->YV12 conversion: vsFetch4Pixels/fsConvertRGBtoY8UV44
91 // Writes four source pixels at a time to a full-size Y plane and a half-width
92 // interleaved UV plane. After execution, the Y plane is complete but the UV
93 // planes still need to be de-interleaved and vertically scaled.
94 const char kRGBtoYV12_vsFetch4Pixels
[] = GLSL_PROGRAM_AS_STRING(
95 uniform
float texel_scale_x_
;
96 varying vec2 texture_coord0
;
97 varying vec2 texture_coord1
;
98 varying vec2 texture_coord2
;
99 varying vec2 texture_coord3
;
101 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
103 vec2 texcoord_base
= gl_MultiTexCoord0
.xy
;
104 vec2 one_texel_x
= vec2(texel_scale_x_
, 0.0);
105 texture_coord0
= texcoord_base
- 1.5 * one_texel_x
;
106 texture_coord1
= texcoord_base
- 0.5 * one_texel_x
;
107 texture_coord2
= texcoord_base
+ 0.5 * one_texel_x
;
108 texture_coord3
= texcoord_base
+ 1.5 * one_texel_x
;
112 const char kRGBtoYV12_fsConvertRGBtoY8UV44
[] = GLSL_PROGRAM_AS_STRING(
113 const vec3 rgb_to_y
= vec3(0.257, 0.504, 0.098);
114 const vec3 rgb_to_u
= vec3(-0.148, -0.291, 0.439);
115 const vec3 rgb_to_v
= vec3(0.439, -0.368, -0.071);
116 const float y_bias
= 0.0625;
117 const float uv_bias
= 0.5;
118 uniform sampler2DRect texture_
;
119 varying vec2 texture_coord0
;
120 varying vec2 texture_coord1
;
121 varying vec2 texture_coord2
;
122 varying vec2 texture_coord3
;
124 // Load the four texture samples.
125 vec3 pixel0
= texture2DRect(texture_
, texture_coord0
).rgb
;
126 vec3 pixel1
= texture2DRect(texture_
, texture_coord1
).rgb
;
127 vec3 pixel2
= texture2DRect(texture_
, texture_coord2
).rgb
;
128 vec3 pixel3
= texture2DRect(texture_
, texture_coord3
).rgb
;
130 // RGB -> Y conversion (x4).
131 vec4 yyyy
= vec4(dot(pixel0
, rgb_to_y
),
132 dot(pixel1
, rgb_to_y
),
133 dot(pixel2
, rgb_to_y
),
134 dot(pixel3
, rgb_to_y
)) + y_bias
;
136 // Average adjacent texture samples while converting RGB->UV. This is the
137 // same as color converting then averaging, but slightly less math. These
138 // values will be in the range [-0.439f, +0.439f] and still need to have
139 // the bias term applied.
140 vec3 blended_pixel0
= pixel0
+ pixel1
;
141 vec3 blended_pixel1
= pixel2
+ pixel3
;
142 vec2 uu
= vec2(dot(blended_pixel0
, rgb_to_u
),
143 dot(blended_pixel1
, rgb_to_u
)) / 2.0;
144 vec2 vv
= vec2(dot(blended_pixel0
, rgb_to_v
),
145 dot(blended_pixel1
, rgb_to_v
)) / 2.0;
147 // Note: Packaging the result to account for BGRA byte ordering.
148 gl_FragData
[0] = yyyy
.bgra
;
149 gl_FragData
[1] = vec4(uu
, vv
) + uv_bias
;
153 // Phase two of RGB24->YV12 conversion: vsFetch2Pixels/fsConvertUV44toU2V2
155 // Deals with UV only. Input is two UUVV quads. The pixels have already been
156 // scaled horizontally prior to this point, and vertical scaling will now happen
157 // via bilinear interpolation during texture sampling. Output is two color
158 // planes U and V, packed four pixels to a "RGBA" quad.
159 const char kRGBtoYV12_vsFetch2Pixels
[] = GLSL_PROGRAM_AS_STRING(
160 varying vec2 texture_coord0
;
161 varying vec2 texture_coord1
;
163 gl_Position
= gl_ModelViewProjectionMatrix
* gl_Vertex
;
165 vec2 texcoord_base
= gl_MultiTexCoord0
.xy
;
166 texture_coord0
= texcoord_base
- vec2(0.5, 0.0);
167 texture_coord1
= texcoord_base
+ vec2(0.5, 0.0);
171 const char kRGBtoYV12_fsConvertUV44toU2V2
[] = GLSL_PROGRAM_AS_STRING(
172 uniform sampler2DRect texture_
;
173 varying vec2 texture_coord0
;
174 varying vec2 texture_coord1
;
176 // We're just sampling two pixels and unswizzling them. There's no need
177 // to do vertical scaling with math, since bilinear interpolation in the
178 // sampler takes care of that.
179 vec4 lo_uuvv
= texture2DRect(texture_
, texture_coord0
);
180 vec4 hi_uuvv
= texture2DRect(texture_
, texture_coord1
);
181 // Note: Packaging the result to account for BGRA byte ordering.
182 gl_FragData
[0] = vec4(lo_uuvv
.rg
, hi_uuvv
.rg
).bgra
;
183 gl_FragData
[1] = vec4(lo_uuvv
.ba
, hi_uuvv
.ba
).bgra
;
189 SHADER_PROGRAM_BLIT
= 0,
190 SHADER_PROGRAM_SOLID_WHITE
,
191 SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
,
192 SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
,
196 // The code snippets that together make up an entire vertex shader program.
197 const char* kVertexShaderSourceCodeMap
[] = {
198 // SHADER_PROGRAM_BLIT
200 // SHADER_PROGRAM_SOLID_WHITE
203 // SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
204 kRGBtoYV12_vsFetch4Pixels
,
205 // SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
206 kRGBtoYV12_vsFetch2Pixels
,
209 // The code snippets that together make up an entire fragment shader program.
210 const char* kFragmentShaderSourceCodeMap
[] = {
211 // SHADER_PROGRAM_BLIT
213 // SHADER_PROGRAM_SOLID_WHITE
216 // SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
217 kRGBtoYV12_fsConvertRGBtoY8UV44
,
218 // SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
219 kRGBtoYV12_fsConvertUV44toU2V2
,
222 GLuint
CompileShaderGLSL(ShaderProgram shader_program
, GLenum shader_type
) {
223 TRACE_EVENT2("gpu", "CompileShaderGLSL",
224 "program", shader_program
,
225 "type", shader_type
== GL_VERTEX_SHADER
? "vertex" : "fragment");
227 DCHECK_GE(shader_program
, 0);
228 DCHECK_LT(shader_program
, NUM_SHADER_PROGRAMS
);
230 const GLuint shader
= glCreateShader(shader_type
);
231 DCHECK_NE(shader
, 0u);
233 // Select and compile the shader program source code.
234 glShaderSource(shader
,
236 (shader_type
== GL_VERTEX_SHADER
? kVertexShaderSourceCodeMap
:
237 kFragmentShaderSourceCodeMap
) + shader_program
,
239 glCompileShader(shader
);
241 // Check for successful compilation. On error in debug builds, pull the info
242 // log and emit the compiler messages.
244 glGetShaderiv(shader
, GL_COMPILE_STATUS
, &error
);
245 if (error
!= GL_TRUE
) {
247 static const int kMaxInfoLogLength
= 8192;
248 scoped_ptr
<char[]> buffer(new char[kMaxInfoLogLength
]);
249 GLsizei length_returned
= 0;
250 glGetShaderInfoLog(shader
, kMaxInfoLogLength
- 1, &length_returned
,
252 buffer
[kMaxInfoLogLength
- 1] = '\0';
253 DLOG(ERROR
) << "Failed to compile "
254 << (shader_type
== GL_VERTEX_SHADER
? "vertex" : "fragment")
255 << " shader for program " << shader_program
<< ":\n"
257 << (length_returned
>= kMaxInfoLogLength
?
258 "\n*** TRUNCATED! ***" : "");
260 glDeleteShader(shader
);
268 GLuint
CompileAndLinkProgram(ShaderProgram which
) {
269 TRACE_EVENT1("gpu", "CompileAndLinkProgram", "program", which
);
271 // Compile and link a new shader program.
272 const GLuint vertex_shader
= CompileShaderGLSL(which
, GL_VERTEX_SHADER
);
273 const GLuint fragment_shader
= CompileShaderGLSL(which
, GL_FRAGMENT_SHADER
);
274 const GLuint program
= glCreateProgram();
275 DCHECK_NE(program
, 0u);
276 glAttachShader(program
, vertex_shader
);
277 glAttachShader(program
, fragment_shader
);
278 glLinkProgram(program
);
280 // Flag shaders for deletion so that they will be deleted automatically when
281 // the program is later deleted.
282 glDeleteShader(vertex_shader
);
283 glDeleteShader(fragment_shader
);
285 // Check that the program successfully linked.
286 GLint error
= GL_FALSE
;
287 glGetProgramiv(program
, GL_LINK_STATUS
, &error
);
288 if (error
!= GL_TRUE
) {
289 glDeleteProgram(program
);
298 CompositingIOSurfaceShaderPrograms::CompositingIOSurfaceShaderPrograms() {
299 COMPILE_ASSERT(kNumShaderPrograms
== NUM_SHADER_PROGRAMS
,
300 header_constant_disagrees_with_enum
);
301 COMPILE_ASSERT(arraysize(kVertexShaderSourceCodeMap
) == NUM_SHADER_PROGRAMS
,
302 vertex_shader_source_code_map_incorrect_size
);
303 COMPILE_ASSERT(arraysize(kFragmentShaderSourceCodeMap
) == NUM_SHADER_PROGRAMS
,
304 fragment_shader_source_code_map_incorrect_size
);
306 memset(shader_programs_
, 0, sizeof(shader_programs_
));
307 for (size_t i
= 0; i
< arraysize(texture_var_locations_
); ++i
)
308 texture_var_locations_
[i
] = -1;
309 for (size_t i
= 0; i
< arraysize(texel_scale_x_var_locations_
); ++i
)
310 texel_scale_x_var_locations_
[i
] = -1;
313 CompositingIOSurfaceShaderPrograms::~CompositingIOSurfaceShaderPrograms() {
315 for (size_t i
= 0; i
< arraysize(shader_programs_
); ++i
)
316 DCHECK_EQ(shader_programs_
[i
], 0u) << "Failed to call Reset().";
320 void CompositingIOSurfaceShaderPrograms::Reset() {
321 for (size_t i
= 0; i
< arraysize(shader_programs_
); ++i
) {
322 if (shader_programs_
[i
] != 0u) {
323 glDeleteProgram(shader_programs_
[i
]);
324 DCHECK(glGetError() == GL_NO_ERROR
)
325 << "when calling glDeleteProgram(shader_programs_[" << i
<< "])";
326 shader_programs_
[i
] = 0u;
329 for (size_t i
= 0; i
< arraysize(texture_var_locations_
); ++i
)
330 texture_var_locations_
[i
] = -1;
331 for (size_t i
= 0; i
< arraysize(texel_scale_x_var_locations_
); ++i
)
332 texel_scale_x_var_locations_
[i
] = -1;
335 bool CompositingIOSurfaceShaderPrograms::UseBlitProgram() {
336 const GLuint program
= GetShaderProgram(SHADER_PROGRAM_BLIT
);
339 glUseProgram(program
);
340 BindUniformTextureVariable(SHADER_PROGRAM_BLIT
, 0);
344 bool CompositingIOSurfaceShaderPrograms::UseSolidWhiteProgram() {
345 const GLuint program
= GetShaderProgram(SHADER_PROGRAM_SOLID_WHITE
);
348 glUseProgram(program
);
352 bool CompositingIOSurfaceShaderPrograms::UseRGBToYV12Program(
353 int pass_number
, float texel_scale_x
) {
354 const int which
= SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
+ pass_number
- 1;
355 DCHECK_GE(which
, SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
);
356 DCHECK_LE(which
, SHADER_PROGRAM_RGB_TO_YV12__2_OF_2
);
358 const GLuint program
= GetShaderProgram(which
);
361 glUseProgram(program
);
362 BindUniformTextureVariable(which
, 0);
363 if (which
== SHADER_PROGRAM_RGB_TO_YV12__1_OF_2
) {
364 BindUniformTexelScaleXVariable(which
, texel_scale_x
);
366 // The second pass doesn't have a texel_scale_x uniform variable since it's
367 // never supposed to be doing any scaling (i.e., outside of the usual
368 // 2x2-->1x1 that's already built into the process).
369 DCHECK_EQ(texel_scale_x
, 1.0f
);
374 GLuint
CompositingIOSurfaceShaderPrograms::GetShaderProgram(int which
) {
375 if (shader_programs_
[which
] == 0u) {
376 shader_programs_
[which
] =
377 CompileAndLinkProgram(static_cast<ShaderProgram
>(which
));
378 DCHECK_NE(shader_programs_
[which
], 0u)
379 << "Failed to create ShaderProgram " << which
;
381 return shader_programs_
[which
];
384 void CompositingIOSurfaceShaderPrograms::BindUniformTextureVariable(
385 int which
, int texture_unit_offset
) {
386 if (texture_var_locations_
[which
] == -1) {
387 texture_var_locations_
[which
] =
388 glGetUniformLocation(GetShaderProgram(which
), "texture_");
389 DCHECK_NE(texture_var_locations_
[which
], -1)
390 << "Failed to find location of uniform variable: texture_";
392 glUniform1i(texture_var_locations_
[which
], texture_unit_offset
);
395 void CompositingIOSurfaceShaderPrograms::BindUniformTexelScaleXVariable(
396 int which
, float texel_scale_x
) {
397 if (texel_scale_x_var_locations_
[which
] == -1) {
398 texel_scale_x_var_locations_
[which
] =
399 glGetUniformLocation(GetShaderProgram(which
), "texel_scale_x_");
400 DCHECK_NE(texel_scale_x_var_locations_
[which
], -1)
401 << "Failed to find location of uniform variable: texel_scale_x_";
403 glUniform1f(texel_scale_x_var_locations_
[which
], texel_scale_x
);
406 } // namespace content