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') |