Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / compositing_iosurface_shader_programs_mac.cc
blob753c180dbf5466eaed5bf30cb76c821318f8ee13
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"
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"
18 namespace content {
20 namespace {
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;
39 void main() {
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;
49 void main() {
50 gl_FragColor = vec4(texture2DRect(texture_, texture_coord).rgb, 1.0);
55 // Only calculates position.
56 const char kvsSolidWhite[] = GLSL_PROGRAM_AS_STRING(
57 void main() {
58 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
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);
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.
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
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;
109 void main() {
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;
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;
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;
170 void main() {
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;
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;
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
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,
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
219 kfsBlit,
220 // SHADER_PROGRAM_SOLID_WHITE
221 kfsSolidWhite,
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[] = {
244 kVersionDirective,
245 kVertexShaderSourceCodeMap[shader_program],
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],
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.
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;
282 // Success!
283 return 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);
310 return 0;
312 return program;
315 } // namespace
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();
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;
346 DVLOG(1) << "Using RGBToYV12 fragment shader output format: "
347 << (rgb_to_yv12_output_format_ == GL_BGRA ? "BGRA" : "RGBA");
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
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);
372 if (program == 0u)
373 return false;
374 glUseProgram(program);
375 BindUniformTextureVariable(SHADER_PROGRAM_BLIT, 0);
376 return true;
379 bool CompositingIOSurfaceShaderPrograms::UseSolidWhiteProgram() {
380 const GLuint program = GetShaderProgram(SHADER_PROGRAM_SOLID_WHITE);
381 if (program == 0u)
382 return false;
383 glUseProgram(program);
384 return true;
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);
394 if (program == 0u)
395 return false;
396 glUseProgram(program);
397 BindUniformTextureVariable(which, 0);
398 if (which == SHADER_PROGRAM_RGB_TO_YV12__1_OF_2) {
399 BindUniformTexelScaleXVariable(which, texel_scale_x);
400 } else {
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);
406 return true;
409 void CompositingIOSurfaceShaderPrograms::SetOutputFormatForTesting(
410 GLenum format) {
411 rgb_to_yv12_output_format_ = format;
412 Reset();
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