Chromium Code Reviews| Index: ui/gfx/gl/generate_bindings.py |
| diff --git a/ui/gfx/gl/generate_bindings.py b/ui/gfx/gl/generate_bindings.py |
| index b3f52fbe93fbe80c19f6c83c0ae188cade18e5b4..cf45e43037e89306b777229f02c3e9d9aff93ad2 100644 |
| --- a/ui/gfx/gl/generate_bindings.py |
| +++ b/ui/gfx/gl/generate_bindings.py |
| @@ -7,6 +7,7 @@ |
| """code generator for GL/GLES extension wrangler.""" |
| import os |
| +import collections |
| import re |
| import sys |
| @@ -19,7 +20,7 @@ GL_FUNCTIONS = [ |
| ['void', ['glBindBuffer'], 'GLenum target, GLuint buffer'], |
| ['void', ['glBindFragDataLocation'], |
| 'GLuint program, GLuint colorNumber, const char* name'], |
| -['void', ['glBindFragDataLocationIndexedARB'], |
| +['void', ['glBindFragDataLocationIndexed'], |
| 'GLuint program, GLuint colorNumber, GLuint index, const char* name'], |
| ['void', ['glBindFramebufferEXT', 'glBindFramebuffer'], |
| 'GLenum target, GLuint framebuffer'], |
| @@ -33,11 +34,11 @@ GL_FUNCTIONS = [ |
| ['void', ['glBlendFunc'], 'GLenum sfactor, GLenum dfactor'], |
| ['void', ['glBlendFuncSeparate'], |
| 'GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha'], |
| -['void', ['glBlitFramebufferEXT', 'BlitFramebuffer'], |
| +['void', ['glBlitFramebufferEXT', 'glBlitFramebuffer'], |
| 'GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, ' |
| 'GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, ' |
| 'GLbitfield mask, GLenum filter'], |
| -['void', ['glBlitFramebufferANGLE', 'BlitFramebuffer'], |
| +['void', ['glBlitFramebufferANGLE', 'glBlitFramebuffer'], |
| 'GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, ' |
| 'GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, ' |
| 'GLbitfield mask, GLenum filter'], |
| @@ -181,7 +182,7 @@ GL_FUNCTIONS = [ |
| ['GLboolean', ['glIsTexture'], 'GLuint texture'], |
| ['void', ['glLineWidth'], 'GLfloat width'], |
| ['void', ['glLinkProgram'], 'GLuint program'], |
| -['void*', ['glMapBuffer'], 'GLenum target, GLenum access'], |
| +['void*', ['glMapBuffer', 'glMapBufferOES'], 'GLenum target, GLenum access'], |
| ['void', ['glPixelStorei'], 'GLenum pname, GLint param'], |
| ['void', ['glPolygonOffset'], 'GLfloat factor, GLfloat units'], |
| ['void', ['glQueryCounter'], 'GLuint id, GLenum target'], |
| @@ -251,7 +252,7 @@ GL_FUNCTIONS = [ |
| 'GLint location, GLsizei count, GLboolean transpose, const GLfloat* value'], |
| ['void', ['glUniformMatrix4fv'], |
| 'GLint location, GLsizei count, GLboolean transpose, const GLfloat* value'], |
| -['GLboolean', ['glUnmapBuffer'], 'GLenum target'], |
| +['GLboolean', ['glUnmapBuffer', 'glUnmapBufferOES'], 'GLenum target'], |
| ['void', ['glUseProgram'], 'GLuint program'], |
| ['void', ['glValidateProgram'], 'GLuint program'], |
| ['void', ['glVertexAttrib1f'], 'GLuint indx, GLfloat x'], |
| @@ -463,11 +464,14 @@ GLX_FUNCTIONS = [ |
| ] |
| FUNCTION_SETS = [ |
| - [GL_FUNCTIONS, 'gl'], |
| - [OSMESA_FUNCTIONS, 'osmesa'], |
| - [EGL_FUNCTIONS, 'egl'], |
| - [WGL_FUNCTIONS, 'wgl'], |
| - [GLX_FUNCTIONS, 'glx'], |
| + [GL_FUNCTIONS, 'gl', ['../../../third_party/mesa/MesaLib/include/GL/glext.h', |
| + '../../../gpu/GLES2/gl2ext.h']], |
| + [OSMESA_FUNCTIONS, 'osmesa', []], |
| + [EGL_FUNCTIONS, 'egl', ['../../../gpu/EGL/eglext.h']], |
| + [WGL_FUNCTIONS, 'wgl', [ |
| + '../../../third_party/mesa/MesaLib/include/GL/wglext.h']], |
| + [GLX_FUNCTIONS, 'glx', [ |
| + '../../../third_party/mesa/MesaLib/include/GL/glxext.h']], |
| ] |
| def GenerateHeader(file, functions, set_name): |
| @@ -487,7 +491,11 @@ def GenerateHeader(file, functions, set_name): |
| file.write('\n') |
| file.write('namespace gfx {\n') |
| file.write('\n') |
| + file.write('class GLContext;\n') |
| + file.write('\n') |
| file.write('void InitializeGLBindings%s();\n' % set_name.upper()) |
| + file.write('void InitializeGLExtensionBindings%s(GLContext* context);\n' % |
| + set_name.upper()) |
| file.write('void InitializeDebugGLBindings%s();\n' % set_name.upper()) |
| # Write typedefs for function pointer types. Always use the GL name for the |
| @@ -517,7 +525,7 @@ def GenerateHeader(file, functions, set_name): |
| set_name.upper()) |
| -def GenerateSource(file, functions, set_name): |
| +def GenerateSource(file, functions, set_name, used_extension_functions): |
| """Generates gl_binding_autogen_x.cc""" |
| # Write file header. |
| @@ -528,12 +536,16 @@ def GenerateSource(file, functions, set_name): |
| file.write('// This file is automatically generated.\n') |
| file.write('\n') |
| file.write('#include "ui/gfx/gl/gl_bindings.h"\n') |
| + file.write('#include "ui/gfx/gl/gl_context.h"\n') |
| file.write('#include "ui/gfx/gl/gl_implementation.h"\n') |
| # Write definitions of function pointers. |
| file.write('\n') |
| file.write('namespace gfx {\n') |
| file.write('\n') |
| + file.write('static bool g_debugBindingsInitialized;\n') |
| + file.write('static void UpdateDebugGLExtensionBindings();\n') |
| + file.write('\n') |
| for [return_type, names, arguments] in functions: |
| file.write('%sProc g_%s;\n' % (names[0], names[0])) |
| @@ -541,18 +553,45 @@ def GenerateSource(file, functions, set_name): |
| for [return_type, names, arguments] in functions: |
| file.write('static %sProc g_debug_%s;\n' % (names[0], names[0])) |
| - # Write function to initialize the function pointers. |
| + # Write function to initialize the core function pointers. The code assumes |
| + # any non-NULL pointer returned by GetGLCoreProcAddress() is valid, although |
| + # it may be overwritten by an extension function pointer later. |
| file.write('\n') |
| file.write('void InitializeGLBindings%s() {\n' % set_name.upper()) |
| for [return_type, names, arguments] in functions: |
| - for name in names: |
| - file.write(' if (!g_%s)\n' % names[0]) |
| + for i, name in enumerate(names): |
| + if i: |
| + file.write(' if (!g_%s)\n ' % names[0]) |
| file.write( |
| - ' g_%s = reinterpret_cast<%sProc>(GetGLProcAddress("%s"));\n' % |
| + ' g_%s = reinterpret_cast<%sProc>(GetGLCoreProcAddress("%s"));\n' % |
| (names[0], names[0], name)) |
| file.write('}\n') |
| file.write('\n') |
| + # Write function to initialize the extension function pointers. This function |
| + # uses a current context to query which extensions are actually supported. |
| + file.write('void InitializeGLExtensionBindings%s(GLContext* context) {\n' % |
| + set_name.upper()) |
| + file.write(' DCHECK(context && context->IsCurrent(NULL));\n') |
| + for extension, ext_functions in used_extension_functions: |
| + file.write(' if (context->HasExtension("%s")) {\n' % extension) |
| + queried_entry_points = set() |
| + for entry_point_name, function_name in ext_functions: |
| + # Replace the pointer unconditionally unless this extension has several |
| + # alternatives for the same entry point (e.g., |
| + # GL_ARB_blend_func_extended). |
| + if entry_point_name in queried_entry_points: |
| + file.write(' if (!g_%s)\n ' % entry_point_name) |
| + file.write( |
| + ' g_%s = reinterpret_cast<%sProc>(GetGLProcAddress("%s"));\n' % |
| + (entry_point_name, entry_point_name, function_name)) |
| + queried_entry_points.add(entry_point_name) |
| + file.write(' }\n') |
| + file.write(' if (g_debugBindingsInitialized)\n') |
| + file.write(' UpdateDebugGLExtensionBindings();\n') |
| + file.write('}\n') |
| + file.write('\n') |
| + |
| # Write logging wrappers for each function. |
| file.write('extern "C" {\n') |
| for [return_type, names, arguments] in functions: |
| @@ -595,19 +634,31 @@ def GenerateSource(file, functions, set_name): |
| file.write('}\n') |
| file.write('} // extern "C"\n') |
| - # Write function to initialize the function pointers. |
| + # Write function to initialize the debug function pointers. |
| file.write('\n') |
| file.write('void InitializeDebugGLBindings%s() {\n' % set_name.upper()) |
| for [return_type, names, arguments] in functions: |
| - for name in names: |
| - file.write(' if (!g_debug_%s) {\n' % names[0]) |
| - file.write(' g_debug_%s = g_%s;\n' % (names[0], names[0])) |
| - file.write(' g_%s = Debug_%s;\n' % (names[0], names[0])) |
| - file.write(' }\n') |
| + file.write(' if (!g_debug_%s) {\n' % names[0]) |
| + file.write(' g_debug_%s = g_%s;\n' % (names[0], names[0])) |
| + file.write(' g_%s = Debug_%s;\n' % (names[0], names[0])) |
| + file.write(' }\n') |
| + file.write(' g_debugBindingsInitialized = true;\n') |
| file.write('}\n') |
| + |
| + # Write function to update the debug function pointers to extension functions |
| + # after the extensions have been initialized. |
| file.write('\n') |
| + file.write('static void UpdateDebugGLExtensionBindings() {\n') |
| + for extension, ext_functions in used_extension_functions: |
| + for name, _ in ext_functions: |
| + file.write(' if (g_debug_%s != g_%s &&\n' % (name, name)) |
| + file.write(' g_%s != Debug_%s) {\n' % (name, name)) |
| + file.write(' g_debug_%s = g_%s;\n' % (name, name)) |
| + file.write(' g_%s = Debug_%s;\n' % (name, name)) |
| + file.write(' }\n') |
| + file.write('}\n') |
| - file.write( '} // namespace gfx\n') |
| + file.write('} // namespace gfx\n') |
| def GenerateMockSource(file, functions): |
| @@ -644,18 +695,135 @@ def GenerateMockSource(file, functions): |
| (function_name, argument_names)) |
| file.write('}\n') |
| + # Write an 'invalid' function to catch code calling through uninitialized |
| + # function pointers or trying to interpret the return value of |
| + # GLProcAddress(). |
| + file.write('\n') |
| + file.write('static void MockInvalidFunction() {\n') |
| + file.write(' DCHECK(0);\n') |
|
apatrick_chromium
2011/10/26 18:29:11
This could be NOTREACHED()
Sami
2011/10/27 13:46:06
Done.
|
| + file.write('}\n') |
| + |
| # Write a function to lookup a mock GL function based on its name. |
| file.write('\n') |
| file.write('void* GL_BINDING_CALL GetMockGLProcAddress(const char* name) {\n') |
| for [return_type, names, arguments] in functions: |
| file.write(' if (strcmp(name, "%s") == 0)\n' % names[0]) |
| file.write(' return reinterpret_cast<void*>(Mock_%s);\n' % names[0]) |
| - file.write(' return NULL;\n') |
| + # Always return a non-NULL pointer like some EGL implementations do. |
| + file.write(' return reinterpret_cast<void*>(&MockInvalidFunction);\n') |
| file.write('}\n'); |
| file.write('\n') |
| file.write('} // namespace gfx\n') |
| +def ParseExtensionFunctionsFromHeader(header_file): |
| + """Parse a C extension header file and return a map from extension names to |
| + a list of functions. |
| + |
| + Args: |
| + header_file: Line-iterable C header file. |
| + Returns: |
| + Map of extension name => functions. |
| + """ |
| + extension_start = re.compile(r'#define ([A-Z]+_[A-Z]+_[a-zA-Z]\w+) 1') |
| + extension_function = re.compile(r'.+\s+([a-z]+\w+)\s*\(.+\);') |
| + typedef = re.compile(r'typedef .*') |
| + macro_start = re.compile(r'^#(if|ifdef|ifndef).*') |
| + macro_end = re.compile(r'^#endif.*') |
| + macro_depth = 0 |
| + current_extension = None |
| + current_extension_depth = 0 |
| + extensions = collections.defaultdict(lambda: []) |
| + for line in header_file: |
| + if macro_start.match(line): |
| + macro_depth += 1 |
| + elif macro_end.match(line): |
| + macro_depth -= 1 |
| + if macro_depth < current_extension_depth: |
| + current_extension = None |
| + match = extension_start.match(line) |
| + if match: |
| + current_extension = match.group(1) |
| + current_extension_depth = macro_depth |
| + assert current_extension not in extensions, \ |
| + "Duplicate extension: " + current_extension |
| + match = extension_function.match(line) |
| + if match and current_extension and not typedef.match(line): |
| + extensions[current_extension].append(match.group(1)) |
| + return extensions |
| + |
| +def GetExtensionFunctions(extension_headers): |
| + """Parse extension functions from a list of header files. |
| + |
| + Args: |
| + extension_headers: List of header file names. |
| + Returns: |
| + Map of extension name => list of functions. |
| + """ |
| + extensions = {} |
| + for header in extension_headers: |
| + extensions.update(ParseExtensionFunctionsFromHeader(open(header))) |
| + return extensions |
| + |
| +def GetFunctionToExtensionMap(extensions): |
| + """Construct map from a function names to extensions which define the |
| + function. |
| + |
| + Args: |
| + extensions: Map of extension name => functions. |
| + Returns: |
| + Map of function name => extension name. |
| + """ |
| + function_to_extension = {} |
| + for extension, functions in extensions.items(): |
| + for function in functions: |
| + assert function not in function_to_extension, \ |
| + "Duplicate function: " + function |
| + function_to_extension[function] = extension |
| + return function_to_extension |
| + |
| +def LooksLikeExtensionFunction(function): |
| + """Heuristic to see if a function name is consistent with extension function |
| + naming.""" |
| + vendor = re.match(r'\w+?([A-Z][A-Z]+)$', function) |
| + return vendor is not None and not vendor.group(1) in ['GL', 'API', 'DC'] |
| + |
| +def GetUsedExtensionFunctions(functions, extension_headers): |
| + """Determine which functions belong to extensions. |
| + |
| + Args: |
| + functions: List of (return type, function names, arguments). |
| + extension_headers: List of header file names. |
| + Returns: |
| + List of (extension name, [function name alternatives]) sorted with least |
| + preferred extensions first. |
| + """ |
| + # Parse known extensions. |
| + extensions = GetExtensionFunctions(extension_headers) |
| + functions_to_extensions = GetFunctionToExtensionMap(extensions) |
| + |
| + # Collect all used extension functions. |
| + used_extension_functions = collections.defaultdict(lambda: []) |
| + for [return_type, names, arguments] in functions: |
| + for name in names: |
| + # Make sure we know about all extension functions. |
| + if (LooksLikeExtensionFunction(name) and |
| + not name in functions_to_extensions): |
| + raise RuntimeError('%s looks like an extension function but does not ' |
| + 'belong to any of the known extensions.' % name) |
| + if name in functions_to_extensions: |
| + extension = functions_to_extensions[name] |
| + used_extension_functions[extension].append((names[0], name)) |
| + |
| + def ExtensionSortKey(name): |
| + # Prefer ratified extensions and EXTs. |
| + preferences = ['_ARB_', '_OES_', '_EXT_', ''] |
| + for i, category in enumerate(preferences): |
| + if category in name: |
| + return -i |
| + used_extension_functions = sorted(used_extension_functions.items(), |
| + key = lambda item: ExtensionSortKey(item[0])) |
| + return used_extension_functions |
| def main(argv): |
| """This is the main function.""" |
| @@ -665,7 +833,7 @@ def main(argv): |
| else: |
| dir = '.' |
| - for [functions, set_name] in FUNCTION_SETS: |
| + for [functions, set_name, extension_headers] in FUNCTION_SETS: |
| header_file = open( |
| os.path.join(dir, 'gl_bindings_autogen_%s.h' % set_name), 'wb') |
| GenerateHeader(header_file, functions, set_name) |
| @@ -673,7 +841,9 @@ def main(argv): |
| source_file = open( |
| os.path.join(dir, 'gl_bindings_autogen_%s.cc' % set_name), 'wb') |
| - GenerateSource(source_file, functions, set_name) |
| + used_extension_functions = GetUsedExtensionFunctions( |
| + functions, extension_headers) |
| + GenerateSource(source_file, functions, set_name, used_extension_functions) |
| source_file.close() |
| source_file = open(os.path.join(dir, 'gl_bindings_autogen_mock.cc'), 'wb') |