Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / renderer_host / compositing_iosurface_shader_programs_mac.cc
blob12988a1d4ac3ee31f1aa12f0bd56d77971a7a7d3
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"
7 #include <string>
8 #include <OpenGL/gl.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"
15 namespace content {
17 namespace {
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;
30 void main() {
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;
40 void main() {
41 gl_FragColor = vec4(texture2DRect(texture_, texture_coord).rgb, 1.0);
46 // Only calculates position.
47 const char kvsSolidWhite[] = GLSL_PROGRAM_AS_STRING(
48 void main() {
49 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
53 // Always white.
54 const char kfsSolidWhite[] = GLSL_PROGRAM_AS_STRING(
55 void main() {
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.
66 // (original)
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
73 // |
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
81 // |
82 // | (u plane) (v plane)
83 // Second | UUUU VVVV
84 // pass +--> { UUUU + VVVV }
85 // 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;
100 void main() {
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;
123 void main() {
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;
162 void main() {
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;
175 void main() {
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;
188 enum ShaderProgram {
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,
193 NUM_SHADER_PROGRAMS
196 // The code snippets that together make up an entire vertex shader program.
197 const char* kVertexShaderSourceCodeMap[] = {
198 // SHADER_PROGRAM_BLIT
199 kvsBlit,
200 // SHADER_PROGRAM_SOLID_WHITE
201 kvsSolidWhite,
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
212 kfsBlit,
213 // SHADER_PROGRAM_SOLID_WHITE
214 kfsSolidWhite,
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,
238 NULL);
239 glCompileShader(shader);
241 // Check for successful compilation. On error in debug builds, pull the info
242 // log and emit the compiler messages.
243 GLint error;
244 glGetShaderiv(shader, GL_COMPILE_STATUS, &error);
245 if (error != GL_TRUE) {
246 #ifndef NDEBUG
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,
251 buffer.get());
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"
256 << buffer.get()
257 << (length_returned >= kMaxInfoLogLength ?
258 "\n*** TRUNCATED! ***" : "");
259 #endif
260 glDeleteShader(shader);
261 return 0;
264 // Success!
265 return 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);
290 return 0;
292 return program;
295 } // namespace
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() {
314 #ifndef NDEBUG
315 for (size_t i = 0; i < arraysize(shader_programs_); ++i)
316 DCHECK_EQ(shader_programs_[i], 0u) << "Failed to call Reset().";
317 #endif
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);
337 if (program == 0u)
338 return false;
339 glUseProgram(program);
340 BindUniformTextureVariable(SHADER_PROGRAM_BLIT, 0);
341 return true;
344 bool CompositingIOSurfaceShaderPrograms::UseSolidWhiteProgram() {
345 const GLuint program = GetShaderProgram(SHADER_PROGRAM_SOLID_WHITE);
346 if (program == 0u)
347 return false;
348 glUseProgram(program);
349 return true;
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);
359 if (program == 0u)
360 return false;
361 glUseProgram(program);
362 BindUniformTextureVariable(which, 0);
363 if (which == SHADER_PROGRAM_RGB_TO_YV12__1_OF_2) {
364 BindUniformTexelScaleXVariable(which, texel_scale_x);
365 } else {
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);
371 return true;
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