| Index: ui/gfx/gl/generate_bindings.py
|
| diff --git a/ui/gfx/gl/generate_bindings.py b/ui/gfx/gl/generate_bindings.py
|
| index 9d5ecf820c370fcc6d2ff7463141c8c843896a38..6260a8eafcd7eb1314b3635b075d5c416f925679 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'],
|
| @@ -465,11 +466,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):
|
| @@ -489,7 +493,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
|
| @@ -519,7 +527,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.
|
| @@ -530,12 +538,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]))
|
|
|
| @@ -543,18 +555,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:
|
| @@ -597,19 +636,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):
|
| @@ -646,18 +697,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(' NOTREACHED();\n')
|
| + 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."""
|
| @@ -667,7 +835,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)
|
| @@ -675,7 +843,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')
|
|
|