diff options
author | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-27 18:30:44 +0000 |
---|---|---|
committer | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-27 18:30:44 +0000 |
commit | 6494823b83620eb43763364d4bb775e36f4d452e (patch) | |
tree | 0f620ad317811b22585100818a23a9ed839ac6f0 /ui/gfx/gl/generate_bindings.py | |
parent | 87a8296b236bc96bd87d05f57180a35300159ed1 (diff) | |
download | chromium_src-6494823b83620eb43763364d4bb775e36f4d452e.zip chromium_src-6494823b83620eb43763364d4bb775e36f4d452e.tar.gz chromium_src-6494823b83620eb43763364d4bb775e36f4d452e.tar.bz2 |
Split GL binding init into core and extension part
This change splits the GL binding initialization into to phases: core
functions and extension functions. This is motivated by two
peculiarities of the EGL window binding layer:
1. Pointers to core GL entry points may not be queried through
eglGetProcAddress().
2. The return value of eglGetProcAddress() is not specified to be NULL
for unsupported functions.
In light of these limitations, the current strategy of blindly querying
all GL entry points from eglGetProcAddress() and falling back to dlsym()
results in bogus function pointers on some EGL implementations.
This patch fixes the problem as follows:
1. Add GetGLCoreProcAddress() that only queries dlsym() and use it to
initialize pointers to all entry points. On EGL based platforms
pointers to extension functions will most likely be NULL at this
point.
2. When a context is first made current, look up the set of supported
extensions and use GetGLProcAddress() to populate the remaining
extension function pointers.
The reworked GL bindings generator uses the standard Khronos headers to
recognize the extension functions and produce appropriate initialization
code.
Note that the patch also updates gl2ext.h to match the upstream Khronos
version and corrects some minor errors in the downstream changes.
BUG=5427391
TEST=
Review URL: http://codereview.chromium.org/8381001
Patch from Sami Kyostila <skyostil@chromium.org>.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107602 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/gfx/gl/generate_bindings.py')
-rw-r--r-- | ui/gfx/gl/generate_bindings.py | 220 |
1 files changed, 195 insertions, 25 deletions
diff --git a/ui/gfx/gl/generate_bindings.py b/ui/gfx/gl/generate_bindings.py index 9d5ecf8..6260a8e 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') |