Chromium Code Reviews| Index: ui/gl/generate_bindings.py |
| diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py |
| index f054baa08cc79d87d6bf41f57164c8d0b09e6665..d5ce40d7ba5768fa705401cf3bea4a0b109e8de9 100755 |
| --- a/ui/gl/generate_bindings.py |
| +++ b/ui/gl/generate_bindings.py |
| @@ -11,6 +11,26 @@ import collections |
| import re |
| import sys |
| +"""In case there are multiple versions of the same function, one that's listed |
| +first takes priority if its conditions are met. If the function is an extension |
| +function, finding the extension from the extension string is a condition for |
| +binding it. The last version of the function is treated as a fallback option in |
| +case no other versions were bound, so a non-null function pointer in the |
| +bindings does not guarantee that the function is supported. |
| + |
| +Function binding conditions can be specified manually by supplying a versions |
| +array instead of the names array. Each version has the following keys: |
| + name: Mandatory. Name of the function. Multiple versions can have the same |
| + name but different conditions. |
| + gl_versions: List of GL versions where the function is found. |
| + extensions: Extensions where the function is found. If not specified, the |
| + extensions are determined based on GL header files. |
| + If the function exists in an extension header, you may specify |
| + an empty array to prevent making that a condition for binding. |
| + |
| +By default, the function gets its name from the first name in its names or |
| +versions array. This can be overridden by supplying a 'known_as' key. |
| +""" |
| GL_FUNCTIONS = [ |
| { 'return_type': 'void', |
| 'names': ['glActiveTexture'], |
| @@ -313,11 +333,13 @@ GL_FUNCTIONS = [ |
| 'names': ['glGetIntegerv'], |
| 'arguments': 'GLenum pname, GLint* params', }, |
| { 'return_type': 'void', |
| - 'names': ['glGetProgramBinary', 'glGetProgramBinaryOES'], |
| + 'known_as': 'glGetProgramBinary', |
| + 'versions': [{ 'name': 'glGetProgramBinaryOES' }, |
| + { 'name': 'glGetProgramBinary', |
| + 'extensions': ['GL_ARB_get_program_binary'] }, |
| + { 'name': 'glGetProgramBinary' }], |
| 'arguments': 'GLuint program, GLsizei bufSize, GLsizei* length, ' |
| - 'GLenum* binaryFormat, GLvoid* binary', |
| - 'other_extensions': ['ARB_get_program_binary', |
| - 'OES_get_program_binary'] }, |
| + 'GLenum* binaryFormat, GLvoid* binary' }, |
| { 'return_type': 'void', |
| 'names': ['glGetProgramiv'], |
| 'arguments': 'GLuint program, GLenum pname, GLint* params', }, |
| @@ -435,7 +457,8 @@ GL_FUNCTIONS = [ |
| 'names': ['glLinkProgram'], |
| 'arguments': 'GLuint program', }, |
| { 'return_type': 'void*', |
| - 'names': ['glMapBuffer', 'glMapBufferOES'], |
| + 'known_as': 'glMapBuffer', |
| + 'names': ['glMapBufferOES', 'glMapBuffer'], |
| 'arguments': 'GLenum target, GLenum access', }, |
| { 'return_type': 'void*', |
| 'names': ['glMapBufferRange'], |
| @@ -454,15 +477,18 @@ GL_FUNCTIONS = [ |
| 'names': ['glPolygonOffset'], |
| 'arguments': 'GLfloat factor, GLfloat units', }, |
| { 'return_type': 'void', |
| - 'names': ['glProgramBinary', 'glProgramBinaryOES'], |
| + 'known_as': 'glProgramBinary', |
| + 'versions': [{ 'name': 'glProgramBinaryOES' }, |
| + { 'name': 'glProgramBinary', |
| + 'extensions': ['GL_ARB_get_program_binary'] }, |
| + { 'name': 'glProgramBinary' }], |
| 'arguments': 'GLuint program, GLenum binaryFormat, ' |
| - 'const GLvoid* binary, GLsizei length', |
| - 'other_extensions': ['ARB_get_program_binary', |
| - 'OES_get_program_binary'] }, |
| + 'const GLvoid* binary, GLsizei length' }, |
| { 'return_type': 'void', |
| - 'names': ['glProgramParameteri'], |
| - 'arguments': 'GLuint program, GLenum pname, GLint value', |
| - 'other_extensions': ['ARB_get_program_binary'] }, |
| + 'versions': [{ 'name': 'glProgramParameteri', |
| + 'extensions': ['GL_ARB_get_program_binary'] }, |
| + { 'name': 'glProgramParameteri' }], |
| + 'arguments': 'GLuint program, GLenum pname, GLint value' }, |
| { 'return_type': 'void', |
| 'names': ['glQueryCounter'], |
| 'arguments': 'GLuint id, GLenum target', }, |
| @@ -477,10 +503,27 @@ GL_FUNCTIONS = [ |
| { 'return_type': 'void', |
| 'names': ['glReleaseShaderCompiler'], |
| 'arguments': 'void', }, |
| +# Multisampling API is different in different GL versions, some require an |
| +# explicit resolve step for renderbuffers and/or FBO texture attachments and |
| +# some do not. Multiple alternatives might be present in a single |
| +# implementation, which require different use of the API and may have |
| +# different performance (explicit resolve performing worse, for example). |
| +# So even though the function signature is the same across versions, we split |
| +# their definitions so that the function to use can be chosen correctly at a |
| +# higher level. |
| +# TODO(oetuaho@nvidia.com): Some of these might still be possible to combine. |
| +# This could also fix weirdness in the mock bindings that's caused by the same |
| +# function name appearing multiple times. |
| +# This is the ES3 function, which requires explicit resolve: |
| { 'return_type': 'void', |
| 'names': ['glRenderbufferStorageMultisample'], |
| 'arguments': 'GLenum target, GLsizei samples, GLenum internalformat, ' |
| 'GLsizei width, GLsizei height', }, |
| +# In desktop GL, EXT and core versions both have an explicit resolve step, |
| +# though desktop core GL implicitly resolves when drawing to a window. |
| +# TODO(oetuaho@nvidia.com): Right now this function also doubles as ES2 EXT |
| +# function, which has implicit resolve, and for which the fallback is wrong. |
| +# Fix this. |
| { 'return_type': 'void', |
| 'names': ['glRenderbufferStorageMultisampleEXT', |
| 'glRenderbufferStorageMultisample'], |
| @@ -511,8 +554,8 @@ GL_FUNCTIONS = [ |
| 'const void* binary, GLsizei length', }, |
| { 'return_type': 'void', |
| 'names': ['glShaderSource'], |
| - 'arguments': |
| - 'GLuint shader, GLsizei count, const char* const* str, const GLint* length', |
| + 'arguments': 'GLuint shader, GLsizei count, const char* const* str, ' |
| + 'const GLint* length', |
| 'logging_code': """ |
| GL_SERVICE_LOG_CODE_BLOCK({ |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| @@ -636,7 +679,8 @@ GL_FUNCTIONS = [ |
| 'arguments': 'GLint location, GLsizei count, ' |
| 'GLboolean transpose, const GLfloat* value', }, |
| { 'return_type': 'GLboolean', |
| - 'names': ['glUnmapBuffer', 'glUnmapBufferOES'], |
| + 'known_as': 'glUnmapBuffer', |
| + 'names': ['glUnmapBufferOES', 'glUnmapBuffer'], |
| 'arguments': 'GLenum target', }, |
| { 'return_type': 'void', |
| 'names': ['glUseProgram'], |
| @@ -712,51 +756,67 @@ GL_FUNCTIONS = [ |
| 'arguments': |
| 'GLsync sync, GLbitfield flags, GLuint64 timeout', }, |
| { 'return_type': 'void', |
| - 'names': ['glDrawArraysInstancedANGLE', 'glDrawArraysInstancedARB'], |
| + 'known_as': 'glDrawArraysInstancedANGLE', |
| + 'names': ['glDrawArraysInstancedARB', 'glDrawArraysInstancedANGLE'], |
| 'arguments': 'GLenum mode, GLint first, GLsizei count, GLsizei primcount', }, |
| { 'return_type': 'void', |
| - 'names': ['glDrawElementsInstancedANGLE', 'glDrawElementsInstancedARB'], |
| + 'known_as': 'glDrawElementsInstancedANGLE', |
| + 'names': ['glDrawElementsInstancedARB', 'glDrawElementsInstancedANGLE'], |
| 'arguments': |
| 'GLenum mode, GLsizei count, GLenum type, const void* indices, ' |
| 'GLsizei primcount', }, |
| { 'return_type': 'void', |
| - 'names': ['glVertexAttribDivisorANGLE', 'glVertexAttribDivisorARB'], |
| + 'known_as': 'glVertexAttribDivisorANGLE', |
| + 'names': ['glVertexAttribDivisorARB', 'glVertexAttribDivisorANGLE'], |
| 'arguments': |
| 'GLuint index, GLuint divisor', }, |
| { 'return_type': 'void', |
| - 'names': ['glGenVertexArraysOES', |
| - 'glGenVertexArraysAPPLE', |
| - 'glGenVertexArrays'], |
| - 'arguments': 'GLsizei n, GLuint* arrays', |
| - 'other_extensions': ['OES_vertex_array_object', |
| - 'APPLE_vertex_array_object', |
| - 'ARB_vertex_array_object'] }, |
| -{ 'return_type': 'void', |
| - 'names': ['glDeleteVertexArraysOES', |
| - 'glDeleteVertexArraysAPPLE', |
| - 'glDeleteVertexArrays'], |
| - 'arguments': 'GLsizei n, const GLuint* arrays', |
| - 'other_extensions': ['OES_vertex_array_object', |
| - 'APPLE_vertex_array_object', |
| - 'ARB_vertex_array_object'] }, |
| -{ 'return_type': 'void', |
| - 'names': ['glBindVertexArrayOES', |
| - 'glBindVertexArrayAPPLE', |
| - 'glBindVertexArray'], |
| - 'arguments': 'GLuint array', |
| - 'other_extensions': ['OES_vertex_array_object', |
| - 'APPLE_vertex_array_object', |
| - 'ARB_vertex_array_object'] }, |
| + 'known_as': 'glGenVertexArraysOES', |
| + 'versions': [{ 'name': 'glGenVertexArrays', |
| + 'gl_versions': ['gl3', 'gl4'] }, |
| + { 'name': 'glGenVertexArrays', |
| + 'extensions': ['GL_ARB_vertex_array_object'] }, |
| + { 'name': 'glGenVertexArraysOES' }, |
| + { 'name': 'glGenVertexArraysAPPLE', |
| + 'extensions': ['GL_APPLE_vertex_array_object'] }], |
| + 'arguments': 'GLsizei n, GLuint* arrays', }, |
| +{ 'return_type': 'void', |
| + 'known_as': 'glDeleteVertexArraysOES', |
| + 'versions': [{ 'name': 'glDeleteVertexArrays', |
| + 'gl_versions': ['gl3', 'gl4'] }, |
| + { 'name': 'glDeleteVertexArrays', |
| + 'extensions': ['GL_ARB_vertex_array_object'] }, |
| + { 'name': 'glDeleteVertexArraysOES' }, |
| + { 'name': 'glDeleteVertexArraysAPPLE', |
| + 'extensions': ['GL_APPLE_vertex_array_object'] }], |
| + 'arguments': 'GLsizei n, const GLuint* arrays' }, |
| +{ 'return_type': 'void', |
| + 'known_as': 'glBindVertexArrayOES', |
| + 'versions': [{ 'name': 'glBindVertexArray', |
| + 'gl_versions': ['gl3', 'gl4'] }, |
| + { 'name': 'glBindVertexArray', |
| + 'extensions': ['GL_ARB_vertex_array_object'] }, |
| + { 'name': 'glBindVertexArrayOES' }, |
| + { 'name': 'glBindVertexArrayAPPLE', |
| + 'extensions': ['GL_APPLE_vertex_array_object'] }], |
| + 'arguments': 'GLuint array' }, |
| { 'return_type': 'GLboolean', |
| - 'names': ['glIsVertexArrayOES', |
| - 'glIsVertexArrayAPPLE', |
| - 'glIsVertexArray'], |
| - 'arguments': 'GLuint array', |
| - 'other_extensions': ['OES_vertex_array_object', |
| - 'APPLE_vertex_array_object', |
| - 'ARB_vertex_array_object'] }, |
| -{ 'return_type': 'void', |
| - 'names': ['glDiscardFramebufferEXT', 'glInvalidateFramebuffer'], |
| + 'known_as': 'glIsVertexArrayOES', |
| + 'versions': [{ 'name': 'glIsVertexArray', |
| + 'gl_versions': ['gl3', 'gl4'] }, |
| + { 'name': 'glIsVertexArray', |
| + 'extensions': ['GL_ARB_vertex_array_object'] }, |
| + { 'name': 'glIsVertexArrayOES' }, |
| + { 'name': 'glIsVertexArrayAPPLE', |
| + 'extensions': ['GL_APPLE_vertex_array_object'] }], |
| + 'arguments': 'GLuint array' }, |
| +{ 'return_type': 'void', |
| + 'known_as': 'glDiscardFramebufferEXT', |
| + 'versions': [{ 'name': 'glInvalidateFramebuffer', |
| + 'gl_versions': ['es3'], |
| + 'extensions': [] }, |
| + { 'name': 'glDiscardFramebufferEXT', |
| + 'gl_versions': ['es1', 'es2'] }], |
| 'arguments': 'GLenum target, GLsizei numAttachments, ' |
| 'const GLenum* attachments' }, |
| ] |
| @@ -845,15 +905,15 @@ EGL_FUNCTIONS = [ |
| 'arguments': |
| 'EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value', }, |
| { 'return_type': 'EGLImageKHR', |
| - 'names': ['eglCreateImageKHR'], |
| + 'versions': [{ 'name': 'eglCreateImageKHR', |
| + 'extensions': ['EGL_KHR_image_base'] }], |
| 'arguments': |
| 'EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, ' |
| - 'const EGLint* attrib_list', |
| - 'other_extensions': ['EGL_KHR_image_base'] }, |
| + 'const EGLint* attrib_list' }, |
| { 'return_type': 'EGLBoolean', |
| - 'names': ['eglDestroyImageKHR'], |
| - 'arguments': 'EGLDisplay dpy, EGLImageKHR image', |
| - 'other_extensions': ['EGL_KHR_image_base'] }, |
| + 'versions': [{ 'name' : 'eglDestroyImageKHR', |
| + 'extensions': ['EGL_KHR_image_base'] }], |
| + 'arguments': 'EGLDisplay dpy, EGLImageKHR image' }, |
| { 'return_type': 'EGLSurface', |
| 'names': ['eglCreateWindowSurface'], |
| 'arguments': 'EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, ' |
| @@ -951,23 +1011,23 @@ EGL_FUNCTIONS = [ |
| 'arguments': |
| 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value', }, |
| { 'return_type': 'EGLSyncKHR', |
| - 'names': ['eglCreateSyncKHR'], |
| - 'arguments': 'EGLDisplay dpy, EGLenum type, const EGLint* attrib_list', |
| - 'other_extensions': ['EGL_KHR_fence_sync'] }, |
| + 'versions': [{ 'name': 'eglCreateSyncKHR', |
| + 'extensions': ['EGL_KHR_fence_sync'] }], |
| + 'arguments': 'EGLDisplay dpy, EGLenum type, const EGLint* attrib_list' }, |
| { 'return_type': 'EGLint', |
| - 'names': ['eglClientWaitSyncKHR'], |
| + 'versions': [{ 'name': 'eglClientWaitSyncKHR', |
| + 'extensions': ['EGL_KHR_fence_sync'] }], |
| 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, ' |
| - 'EGLTimeKHR timeout', |
| - 'other_extensions': ['EGL_KHR_fence_sync'] }, |
| + 'EGLTimeKHR timeout' }, |
| { 'return_type': 'EGLBoolean', |
| - 'names': ['eglGetSyncAttribKHR'], |
| + 'versions': [{ 'name': 'eglGetSyncAttribKHR', |
| + 'extensions': ['EGL_KHR_fence_sync'] }], |
| 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, ' |
| - 'EGLint* value', |
| - 'other_extensions': ['EGL_KHR_fence_sync'] }, |
| + 'EGLint* value' }, |
| { 'return_type': 'EGLBoolean', |
| - 'names': ['eglDestroySyncKHR'], |
| - 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync', |
| - 'other_extensions': ['EGL_KHR_fence_sync'] }, |
| + 'versions': [{ 'name': 'eglDestroySyncKHR', |
| + 'extensions': ['EGL_KHR_fence_sync'] }], |
| + 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync' }, |
| { 'return_type': 'EGLBoolean', |
| 'names': ['eglGetSyncValuesCHROMIUM'], |
| 'arguments': |
| @@ -975,9 +1035,9 @@ EGL_FUNCTIONS = [ |
| 'EGLuint64CHROMIUM* ust, EGLuint64CHROMIUM* msc, ' |
| 'EGLuint64CHROMIUM* sbc', }, |
| { 'return_type': 'EGLint', |
| - 'names': ['eglWaitSyncKHR'], |
| - 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags', |
| - 'other_extensions': ['EGL_KHR_wait_sync'] }, |
| + 'versions': [{ 'name': 'eglWaitSyncKHR', |
| + 'extensions': ['EGL_KHR_fence_sync'] }], |
| + 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags' } |
| ] |
| WGL_FUNCTIONS = [ |
| @@ -1227,7 +1287,7 @@ FUNCTION_SETS = [ |
| [GLX_FUNCTIONS, [], 'glx', ['GL/glx.h', 'GL/glxext.h'], []], |
| ] |
| -def GenerateHeader(file, functions, set_name, used_extension_functions): |
| +def GenerateHeader(file, functions, set_name, used_extensions): |
| """Generates gl_bindings_autogen_x.h""" |
| # Write file header. |
| @@ -1252,12 +1312,12 @@ class GLContext; |
| file.write('\n') |
| for func in functions: |
| file.write('typedef %s (GL_BINDING_CALL *%sProc)(%s);\n' % |
| - (func['return_type'], func['names'][0], func['arguments'])) |
| + (func['return_type'], func['known_as'], func['arguments'])) |
| # Write declarations for booleans indicating which extensions are available. |
| file.write('\n') |
| file.write("struct Extensions%s {\n" % set_name.upper()) |
| - for extension, ext_functions in used_extension_functions: |
| + for extension in sorted(used_extensions): |
| file.write(' bool b_%s;\n' % extension) |
| file.write('};\n') |
| file.write('\n') |
| @@ -1265,7 +1325,7 @@ class GLContext; |
| # Write Procs struct. |
| file.write("struct Procs%s {\n" % set_name.upper()) |
| for func in functions: |
| - file.write(' %sProc %sFn;\n' % (func['names'][0], func['names'][0])) |
| + file.write(' %sProc %sFn;\n' % (func['known_as'], func['known_as'])) |
| file.write('};\n') |
| file.write('\n') |
| @@ -1279,7 +1339,7 @@ class GLContext; |
| """ % {'name': set_name.upper()}) |
| for func in functions: |
| file.write(' virtual %s %sFn(%s) = 0;\n' % |
| - (func['return_type'], func['names'][0], func['arguments'])) |
| + (func['return_type'], func['known_as'], func['arguments'])) |
| file.write('};\n') |
| file.write('\n') |
| @@ -1290,14 +1350,14 @@ class GLContext; |
| file.write('\n') |
| for func in functions: |
| file.write('#define %s ::gfx::g_current_%s_context->%sFn\n' % |
| - (func['names'][0], set_name.lower(), func['names'][0])) |
| + (func['known_as'], set_name.lower(), func['known_as'])) |
| file.write('\n') |
| file.write('#endif // UI_GFX_GL_GL_BINDINGS_AUTOGEN_%s_H_\n' % |
| set_name.upper()) |
| -def GenerateAPIHeader(file, functions, set_name, used_extension_functions): |
| +def GenerateAPIHeader(file, functions, set_name): |
| """Generates gl_bindings_api_autogen_x.h""" |
| # Write file header. |
| @@ -1313,12 +1373,12 @@ def GenerateAPIHeader(file, functions, set_name, used_extension_functions): |
| # Write API declaration. |
| for func in functions: |
| file.write(' virtual %s %sFn(%s) OVERRIDE;\n' % |
| - (func['return_type'], func['names'][0], func['arguments'])) |
| + (func['return_type'], func['known_as'], func['arguments'])) |
| file.write('\n') |
| -def GenerateMockHeader(file, functions, set_name, used_extension_functions): |
| +def GenerateMockHeader(file, functions, set_name): |
| """Generates gl_mock_autogen_x.h""" |
| # Write file header. |
| @@ -1340,13 +1400,12 @@ def GenerateMockHeader(file, functions, set_name, used_extension_functions): |
| if len(args): |
| arg_count = func['arguments'].count(',') + 1 |
| file.write(' MOCK_METHOD%d(%s, %s(%s));\n' % |
| - (arg_count, func['names'][0][2:], func['return_type'], args)) |
| + (arg_count, func['known_as'][2:], func['return_type'], args)) |
| file.write('\n') |
| -def GenerateInterfaceHeader( |
| - file, functions, set_name, used_extension_functions): |
| +def GenerateInterfaceHeader(file, functions, set_name): |
| """Generates gl_interface_autogen_x.h""" |
| # Write file header. |
| @@ -1365,13 +1424,13 @@ def GenerateInterfaceHeader( |
| if args == 'void': |
| args = '' |
| file.write(' virtual %s %s(%s) = 0;\n' % |
| - (func['return_type'], func['names'][0][2:], args)) |
| + (func['return_type'], func['known_as'][2:], args)) |
| file.write('\n') |
| def GenerateSource( |
| - file, functions, nulldraw_functions, set_name, used_extension_functions): |
| + file, functions, nulldraw_functions, set_name, used_extensions): |
| """Generates gl_bindings_autogen_x.cc""" |
| # Write file header. |
| @@ -1388,6 +1447,7 @@ def GenerateSource( |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_implementation.h" |
| +#include "ui/gl/gl_version_info.h" |
| #include "ui/gl/gl_%s_api_implementation.h" |
| using gpu::gles2::GLES2Util; |
| @@ -1395,78 +1455,161 @@ using gpu::gles2::GLES2Util; |
| namespace gfx { |
| """ % set_name.lower()) |
| - # Write definitions of function pointers. |
| file.write('\n') |
| file.write('static bool g_debugBindingsInitialized;\n') |
| file.write('Driver%s g_driver_%s;\n' % (set_name.upper(), set_name.lower())) |
| file.write('\n') |
| - # 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. |
| + # Write stub functions that take the place of some functions before a context |
| + # is initialized. This is done to provide clear asserts on debug build and to |
| + # avoid crashing in case of a bug on release build. |
| + file.write('\n') |
| + for func in functions: |
| + unique_names = set([version['name'] for version in func['versions']]) |
| + if len(unique_names) > 1: |
| + file.write('%s %sNotBound(%s) {\n' % |
| + (func['return_type'], func['known_as'], func['arguments'])) |
| + file.write(' NOTREACHED();\n') |
| + return_type = func['return_type'].lower() |
| + # Returning 0 works for booleans, integers and pointers. |
| + if return_type != 'void': |
| + file.write(' return 0;\n') |
| + file.write('}\n') |
| + |
| + # Write function to initialize the function pointers that are always the same |
| + # and to initialize bindings where choice of the function depends on the |
| + # extension string or the GL version to point to stub functions. |
| file.write('\n') |
| - file.write('void Driver%s::InitializeBindings() {\n' % |
| + file.write('void Driver%s::InitializeStaticBindings() {\n' % |
| set_name.upper()) |
| + |
| + def WriteFuncBinding(file, known_as, version_name): |
| + file.write( |
| + ' fn.%sFn = reinterpret_cast<%sProc>(GetGLProcAddress("%s"));\n' % |
| + (known_as, known_as, version_name)) |
| + |
| for func in functions: |
| - first_name = func['names'][0] |
| - for i, name in enumerate(func['names']): |
| - if i: |
| - file.write(' if (!fn.%sFn)\n ' % first_name) |
| - file.write( |
| - ' fn.%sFn = reinterpret_cast<%sProc>(' |
| - 'GetGLCoreProcAddress("%s"));\n' % |
| - (first_name, first_name, name)) |
| + unique_names = set([version['name'] for version in func['versions']]) |
| + if len(unique_names) == 1: |
| + WriteFuncBinding(file, func['known_as'], func['known_as']) |
| + else: |
| + file.write(' fn.%sFn = reinterpret_cast<%sProc>(%sNotBound);\n' % |
| + (func['known_as'], func['known_as'], func['known_as'])) |
| + |
| 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 Driver%s::InitializeExtensionBindings( |
| - GLContext* context) { |
| + # Write function to initialize bindings where choice of the function depends |
| + # on the extension string or the GL version. |
| + file.write("""void Driver%s::InitializeDynamicBindings(GLContext* context) { |
| + DCHECK(context && context->IsCurrent(NULL)); |
| + const GLVersionInfo* ver ALLOW_UNUSED = context->GetVersionInfo(); |
| + std::string extensions ALLOW_UNUSED = context->GetExtensions(); |
| + extensions += " "; |
| + |
| """ % set_name.upper()) |
| - file.write(' DCHECK(context && context->IsCurrent(NULL));\n') |
| - for extension, ext_functions in used_extension_functions: |
| - file.write(' ext.b_%s = context->HasExtension("%s");\n' % |
| + for extension in sorted(used_extensions): |
| + # Extra space at the end of the extension name is intentional, it is used |
| + # as a separator |
| + file.write(' ext.b_%s = extensions.find("%s ") != std::string::npos;\n' % |
| (extension, extension)) |
| - file.write(' if (ext.b_%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 (!fn.%sFn)\n ' % entry_point_name) |
| - file.write( |
| - ' fn.%sFn = reinterpret_cast<%sProc>(GetGLProcAddress("%s"));\n' % |
| - (entry_point_name, entry_point_name, function_name)) |
| - queried_entry_points.add(entry_point_name) |
| - file.write(' }\n') |
| + |
| + def WrapOr(cond): |
| + if ' || ' in cond: |
| + return '(%s)' % cond |
| + return cond |
| + |
| + def WrapAnd(cond): |
| + if ' && ' in cond: |
| + return '(%s)' % cond |
| + return cond |
| + |
| + def VersionCondition(version): |
| + conditions = [] |
| + if 'gl_versions' in version: |
| + gl_versions = version['gl_versions'] |
| + version_cond = ' || '.join(['ver->is_%s' % gl for gl in gl_versions]) |
| + conditions.append(WrapOr(version_cond)) |
| + if 'extensions' in version and version['extensions']: |
| + ext_cond = ' || '.join(['ext.b_%s' % e for e in version['extensions']]) |
| + conditions.append(WrapOr(ext_cond)) |
| + return ' && '.join(conditions) |
| + |
| + def WriteConditionalFuncBinding(file, func): |
| + # Functions with only one version are always bound unconditionally |
| + assert len(func['versions']) > 1 |
| + known_as = func['known_as'] |
| + i = 0 |
| + first_version = True |
| + while i < len(func['versions']): |
| + version = func['versions'][i] |
| + cond = VersionCondition(version) |
| + combined_conditions = [WrapAnd(cond)] |
| + last_version = i + 1 == len(func['versions']) |
| + while not last_version and \ |
| + func['versions'][i + 1]['name'] == version['name']: |
| + i += 1 |
| + combinable_cond = VersionCondition(func['versions'][i]) |
| + combined_conditions.append(WrapAnd(combinable_cond)) |
| + last_version = i + 1 == len(func['versions']) |
| + if len(combined_conditions) > 1: |
| + if [1 for cond in combined_conditions if cond == '']: |
| + cond = '' |
| + else: |
| + cond = ' || '.join(combined_conditions) |
| + # Don't make the last possible binding conditional on anything else but |
| + # that the function isn't already bound to avoid verbose specification |
| + # of functions which have both ARB and core versions with the same name, |
| + # and to be able to bind to mock extension functions in unit tests which |
| + # call InitializeDynamicGLBindings with a stub context that doesn't have |
| + # extensions in its extension string. |
| + # TODO(oetuaho@nvidia.com): Get rid of the fallback. |
| + # http://crbug.com/325668 |
| + if cond != '' and not last_version: |
| + if not first_version: |
| + file.write(' if (!fn.%sFn && (%s))\n ' % (known_as, cond)) |
| + else: |
| + file.write(' if (%s)\n ' % cond) |
| + elif not first_version: |
| + file.write(' if (!fn.%sFn)\n ' % known_as) |
| + WriteFuncBinding(file, known_as, version['name']) |
| + i += 1 |
| + first_version = False |
| + |
| + for func in functions: |
| + unique_names = set([version['name'] for version in func['versions']]) |
| + if len(unique_names) > 1: |
| + file.write('\n') |
| + file.write(' fn.%sFn = 0;\n' % func['known_as']) |
| + file.write(' debug_fn.%sFn = 0;\n' % func['known_as']) |
| + WriteConditionalFuncBinding(file, func) |
| + |
| + # Some new function pointers have been added, so update them in debug bindings |
| + file.write('\n') |
| file.write(' if (g_debugBindingsInitialized)\n') |
| - file.write(' UpdateDebugExtensionBindings();\n') |
| + file.write(' InitializeDebugBindings();\n') |
| file.write('}\n') |
| file.write('\n') |
| # Write empty stubs for functions that want one. |
| file.write('extern "C" {\n') |
| for func in nulldraw_functions: |
| - names = func['names'] |
| + known_as = func['known_as'] |
| return_type = func['return_type'] |
| arguments = func['arguments'] |
| file.write('\n') |
| file.write('static %s GL_BINDING_CALL Stub_%s(%s) {}\n' % |
| - (return_type, names[0], arguments)) |
| + (return_type, known_as, arguments)) |
| file.write('} // extern "C"\n') |
| # Write logging wrappers for each function. |
| file.write('extern "C" {\n') |
| for func in functions: |
| - names = func['names'] |
| return_type = func['return_type'] |
| arguments = func['arguments'] |
| file.write('\n') |
| file.write('static %s GL_BINDING_CALL Debug_%s(%s) {\n' % |
| - (return_type, names[0], arguments)) |
| + (return_type, func['known_as'], arguments)) |
| argument_names = re.sub( |
| r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', arguments) |
| argument_names = re.sub( |
| @@ -1488,9 +1631,9 @@ namespace gfx { |
| log_argument_names) |
| log_argument_names = re.sub( |
| r'CONSTVOID_([a-zA-Z0-9_]+)', |
| - r'static_cast<const void*>(\1)', log_argument_names); |
| + r'static_cast<const void*>(\1)', log_argument_names) |
| log_argument_names = re.sub( |
| - r'CONSTCHAR_([a-zA-Z0-9_]+)', r'\1', log_argument_names); |
| + r'CONSTCHAR_([a-zA-Z0-9_]+)', r'\1', log_argument_names) |
| log_argument_names = re.sub( |
| r'GLenum_([a-zA-Z0-9_]+)', r'GLES2Util::GetStringEnum(\1)', |
| log_argument_names) |
| @@ -1503,7 +1646,7 @@ namespace gfx { |
| log_argument_names = '' |
| else: |
| log_argument_names = " << " + log_argument_names |
| - function_name = names[0] |
| + function_name = func['known_as'] |
| if return_type == 'void': |
| file.write(' GL_SERVICE_LOG("%s" << "(" %s << ")");\n' % |
| (function_name, log_argument_names)) |
| @@ -1519,7 +1662,7 @@ namespace gfx { |
| if 'logging_code' in func: |
| file.write("%s\n" % func['logging_code']) |
| else: |
| - file.write(' GL_SERVICE_LOG("GL_RESULT: " << result);\n'); |
| + file.write(' GL_SERVICE_LOG("GL_RESULT: " << result);\n') |
| file.write(' return result;\n') |
| file.write('}\n') |
| file.write('} // extern "C"\n') |
| @@ -1529,7 +1672,7 @@ namespace gfx { |
| file.write('void Driver%s::InitializeDebugBindings() {\n' % |
| set_name.upper()) |
| for func in functions: |
| - first_name = func['names'][0] |
| + first_name = func['known_as'] |
| file.write(' if (!debug_fn.%sFn) {\n' % first_name) |
| file.write(' debug_fn.%sFn = fn.%sFn;\n' % (first_name, first_name)) |
| file.write(' fn.%sFn = Debug_%s;\n' % (first_name, first_name)) |
| @@ -1544,24 +1687,10 @@ namespace gfx { |
| set_name.upper()) |
| for func in nulldraw_functions: |
| - first_name = func['names'][0] |
| + first_name = func['known_as'] |
| file.write(' fn.%sFn = Stub_%s;\n' % (first_name, first_name)) |
| 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('void Driver%s::UpdateDebugExtensionBindings() {\n' % |
| - set_name.upper()) |
| - for extension, ext_functions in used_extension_functions: |
| - for name, _ in ext_functions: |
| - file.write(' if (debug_fn.%sFn != fn.%sFn &&\n' % (name, name)) |
| - file.write(' fn.%sFn != Debug_%s) {\n' % (name, name)) |
| - file.write(' debug_fn.%sFn = fn.%sFn;\n' % (name, name)) |
| - file.write(' fn.%sFn = Debug_%s;\n' % (name, name)) |
| - file.write(' }\n') |
| - file.write('}\n') |
| - |
| # Write function to clear all function pointers. |
| file.write('\n') |
| file.write("""void Driver%s::ClearBindings() { |
| @@ -1571,19 +1700,18 @@ namespace gfx { |
| # Write GLApiBase functions |
| for func in functions: |
| - names = func['names'] |
| + function_name = func['known_as'] |
| return_type = func['return_type'] |
| arguments = func['arguments'] |
| file.write('\n') |
| file.write('%s %sApiBase::%sFn(%s) {\n' % |
| - (return_type, set_name.upper(), names[0], arguments)) |
| + (return_type, set_name.upper(), function_name, arguments)) |
| argument_names = re.sub( |
| r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', arguments) |
| argument_names = re.sub( |
| r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', argument_names) |
| if argument_names == 'void' or argument_names == '': |
| argument_names = '' |
| - function_name = names[0] |
| if return_type == 'void': |
| file.write(' driver_->fn.%sFn(%s);\n' % |
| (function_name, argument_names)) |
| @@ -1594,19 +1722,18 @@ namespace gfx { |
| # Write TraceGLApi functions |
| for func in functions: |
| - names = func['names'] |
| + function_name = func['known_as'] |
| return_type = func['return_type'] |
| arguments = func['arguments'] |
| file.write('\n') |
| file.write('%s Trace%sApi::%sFn(%s) {\n' % |
| - (return_type, set_name.upper(), names[0], arguments)) |
| + (return_type, set_name.upper(), function_name, arguments)) |
| argument_names = re.sub( |
| r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', arguments) |
| argument_names = re.sub( |
| r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', argument_names) |
| if argument_names == 'void' or argument_names == '': |
| argument_names = '' |
| - function_name = names[0] |
| file.write(' TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::%s")\n' % |
| function_name) |
| if return_type == 'void': |
| @@ -1641,12 +1768,12 @@ namespace gfx { |
| for func in functions: |
| file.write('\n') |
| file.write('%s GL_BINDING_CALL Mock_%s(%s) {\n' % |
| - (func['return_type'], func['names'][0], func['arguments'])) |
| - argument_names = re.sub(r'(const )?[a-zA-Z0-9]+((\s*const\s*)?\*)* ([a-zA-Z0-9]+)', r'\4', |
| - func['arguments']) |
| + (func['return_type'], func['known_as'], func['arguments'])) |
| + arg_re = r'(const )?[a-zA-Z0-9]+((\s*const\s*)?\*)* ([a-zA-Z0-9]+)' |
| + argument_names = re.sub(arg_re, r'\4', func['arguments']) |
| if argument_names == 'void': |
| argument_names = '' |
| - function_name = func['names'][0][2:] |
| + function_name = func['known_as'][2:] |
| if func['return_type'] == 'void': |
| file.write(' GLInterface::GetGLInterface()->%s(%s);\n' % |
| (function_name, argument_names)) |
| @@ -1667,12 +1794,14 @@ namespace gfx { |
| file.write('\n') |
| file.write('void* GL_BINDING_CALL GetMockGLProcAddress(const char* name) {\n') |
| for func in functions: |
| - first_name = func['names'][0] |
| - file.write(' if (strcmp(name, "%s") == 0)\n' % first_name) |
| - file.write(' return reinterpret_cast<void*>(Mock_%s);\n' % first_name) |
| + unique_names = set([version['name'] for version in func['versions']]) |
| + for name in unique_names: |
| + file.write(' if (strcmp(name, "%s") == 0)\n' % name) |
| + file.write( |
| + ' return reinterpret_cast<void*>(Mock_%s);\n' % func['known_as']) |
| # 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('\n') |
| file.write('} // namespace gfx\n') |
| @@ -1755,49 +1884,45 @@ def LooksLikeExtensionFunction(function): |
| return vendor is not None and not vendor.group(1) in ['GL', 'API', 'DC'] |
| -def GetUsedExtensionFunctions(functions, extension_headers, extra_extensions): |
| - """Determine which functions belong to extensions. |
| +def FillExtensionsFromHeaders(functions, extension_headers, extra_extensions): |
| + """Determine which functions belong to extensions based on extension headers, |
| + and fill in this information to the functions table for functions that don't |
| + already have the information. |
| Args: |
| - functions: List of (return type, function names, arguments). |
| + functions: List of (return type, function versions, arguments). |
| extension_headers: List of header file names. |
| + extra_extensions: Extensions to add to the list. |
| Returns: |
| - List of (extension name, [function name alternatives]) sorted with least |
| - preferred extensions first. |
| + Set of used extensions. |
| """ |
| # Parse known extensions. |
| extensions = GetExtensionFunctions(extension_headers) |
| functions_to_extensions = GetFunctionToExtensionMap(extensions) |
| - # Collect all used extension functions. |
| - used_extension_functions = collections.defaultdict(lambda: []) |
| + # Fill in the extension information. |
| + used_extensions = set() |
| for func in functions: |
| - for name in func['names']: |
| - # Make sure we know about all extension functions. |
| - if (LooksLikeExtensionFunction(name) and |
| - not name in functions_to_extensions): |
| + for version in func['versions']: |
| + name = version['name'] |
| + # Make sure we know about all extensions and extension functions. |
| + if 'extensions' in version: |
| + used_extensions.update(version['extensions']) |
| + elif name in functions_to_extensions: |
| + # If there are multiple versions with the same name, assume that they |
| + # already have all the correct conditions, we can't just blindly add |
| + # the same extension conditions to all of them |
| + if len([v for v in func['versions'] if v['name'] == name]) == 1: |
| + version['extensions'] = functions_to_extensions[name] |
| + used_extensions.update(version['extensions']) |
| + elif LooksLikeExtensionFunction(name): |
| 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: |
| - extensions = functions_to_extensions[name][:] |
| - if 'other_extensions' in func: |
| - extensions.extend(func['other_extensions']) |
| - for extension in extensions: |
| - used_extension_functions[extension].append((func['names'][0], name)) |
| # Add extensions that do not have any functions. |
| - used_extension_functions.update(dict( |
| - [(e, []) for e in extra_extensions if e not in used_extension_functions])) |
| + used_extensions.update(extra_extensions) |
| - 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 |
| + return used_extensions |
| def ResolveHeader(header, header_paths): |
| @@ -1837,54 +1962,63 @@ def main(argv): |
| print ResolveHeader(header, options.header_paths) |
| return 0 |
| + directory = '.' |
| if len(args) >= 1: |
| - dir = args[0] |
| - else: |
| - dir = '.' |
| + directory = args[0] |
| for [functions, |
| nulldraw_functions, |
| set_name, |
| extension_headers, |
| extensions] in FUNCTION_SETS: |
| + # Function names can be specified in two ways (list of unique names or list |
| + # of versions with different binding conditions). Fill in the data to the |
| + # versions list in case it is missing, so that can be used from here on: |
| + for func in functions + nulldraw_functions: |
| + assert 'versions' in func or 'names' in func, 'Function with no names' |
| + if 'versions' not in func: |
| + func['versions'] = [{'name': n} for n in func['names']] |
| + # Use the first version's name unless otherwise specified |
| + if 'known_as' not in func: |
| + func['known_as'] = func['versions'][0]['name'] |
|
no sievers
2014/01/14 23:16:01
Should we null out func['names'] here somewhere to
oetuaho-nv
2014/01/15 15:50:25
That should typically make the build fail when run
|
| + |
| extension_headers = [ResolveHeader(h, options.header_paths) |
| for h in extension_headers] |
| - used_extension_functions = GetUsedExtensionFunctions( |
| + used_extensions = FillExtensionsFromHeaders( |
| functions, extension_headers, extensions) |
| header_file = open( |
| - os.path.join(dir, 'gl_bindings_autogen_%s.h' % set_name), 'wb') |
| - GenerateHeader(header_file, functions, set_name, used_extension_functions) |
| + os.path.join(directory, 'gl_bindings_autogen_%s.h' % set_name), 'wb') |
| + GenerateHeader(header_file, functions, set_name, used_extensions) |
| header_file.close() |
| header_file = open( |
| - os.path.join(dir, 'gl_bindings_api_autogen_%s.h' % set_name), 'wb') |
| - GenerateAPIHeader( |
| - header_file, functions, set_name, used_extension_functions) |
| + os.path.join(directory, 'gl_bindings_api_autogen_%s.h' % set_name), |
| + 'wb') |
| + GenerateAPIHeader(header_file, functions, set_name) |
| header_file.close() |
| source_file = open( |
| - os.path.join(dir, 'gl_bindings_autogen_%s.cc' % set_name), 'wb') |
| + os.path.join(directory, 'gl_bindings_autogen_%s.cc' % set_name), 'wb') |
| GenerateSource(source_file, |
| functions, |
| nulldraw_functions, |
| set_name, |
| - used_extension_functions) |
| + used_extensions) |
| source_file.close() |
| header_file = open( |
| - os.path.join(dir, 'gl_interface_autogen_%s.h' % set_name), 'wb') |
| - GenerateInterfaceHeader( |
| - header_file, functions, set_name, used_extension_functions) |
| + os.path.join(directory, 'gl_interface_autogen_%s.h' % set_name), 'wb') |
| + GenerateInterfaceHeader(header_file, functions, set_name) |
| header_file.close() |
| header_file = open( |
| - os.path.join(dir, 'gl_mock_autogen_%s.h' % set_name), 'wb') |
| - GenerateMockHeader( |
| - header_file, functions, set_name, used_extension_functions) |
| + os.path.join(directory, 'gl_mock_autogen_%s.h' % set_name), 'wb') |
| + GenerateMockHeader(header_file, functions, set_name) |
| header_file.close() |
| - source_file = open(os.path.join(dir, 'gl_bindings_autogen_mock.cc'), 'wb') |
| + source_file = open(os.path.join(directory, 'gl_bindings_autogen_mock.cc'), |
| + 'wb') |
| GenerateMockSource(source_file, GL_FUNCTIONS) |
| source_file.close() |
| return 0 |