diff options
author | Eric Anholt <eric@anholt.net> | 2013-09-19 09:50:49 -0700 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2013-10-16 11:43:11 -0700 |
commit | a909eb4a229ebbea2d60717e748df3f0a2f69cc4 (patch) | |
tree | b2b265d8609bd52369b4b6345ebc8d07f72d1e0e /src | |
parent | db667aa8b847aa9718bce8f371c65a739ea922ef (diff) |
Add the generator and build infrastructure.
Not actually working yet, but it's a snapshot to start from.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 84 | ||||
-rw-r--r-- | src/dispatch_common.c | 217 | ||||
-rw-r--r-- | src/dispatch_common.h | 55 | ||||
-rwxr-xr-x | src/gen_dispatch.py | 456 |
4 files changed, 812 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..6719397 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,84 @@ +# Copyright 2005 Adam Jackson. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + $() + +AM_CFLAGS = $(CWARNFLAGS) $(VISIBILITY_CFLAGS) + +epoxyincludedir = $(includedir)/epoxy +lib_LTLIBRARIES = libepoxy.la + +epoxyinclude_DATA = \ + $(GL_INCLUDES) \ + $(GLX_INCLUDES) \ + $() + +GL_INCLUDES = \ + ../include/epoxy/gl.h \ + ../include/epoxy/gl_common.h \ + $(GENERATED_GL_INCLUDES) \ + $() + +GLX_INCLUDES = \ + ../include/epoxy/glx.h \ + ../include/epoxy/glx_common.h \ + $(GENERATED_GLX_INCLUDES) \ + $() + +GENERATED_GL_INCLUDES = \ + $(builddir)/../include/epoxy/gl_generated.h \ + $(builddir)/../include/epoxy/gl_generated_vtable_defines.h \ + $() + +GENERATED_GLX_INCLUDES = \ + $(builddir)/../include/epoxy/glx_generated.h \ + $(builddir)/../include/epoxy/glx_generated_vtable_defines.h \ + $() + +GENERATED_GL = \ + $(builddir)/gl_generated_dispatch.c \ + $(GENERATED_GL_INCLUDES) \ + $() + +GENERATED_GLX = \ + $(builddir)/glx_generated_dispatch.c \ + $(GENERATED_GLX_INCLUDES) \ + $() + +BUILT_SOURCES = \ + $(GENERATED_GL) \ + $(GENERATED_GLX) \ + $() +CLEANFILES = $(BUILT_SOURCES) + +libepoxy_la_SOURCES = \ + dispatch_common.c \ + dispatch_common.h \ + $(BUILT_SOURCES) + $() + +$(GENERATED_GL): gen_dispatch.py ../registry/gl.xml + $(AM_V_GEN)$(PYTHON) gen_dispatch.py --dir $(top_builddir) ../registry/gl.xml + +$(GENERATED_GLX): gen_dispatch.py ../registry/glx.xml + $(AM_V_GEN)$(PYTHON) gen_dispatch.py --dir $(top_builddir) ../registry/glx.xml diff --git a/src/dispatch_common.c b/src/dispatch_common.c new file mode 100644 index 0000000..1b45389 --- /dev/null +++ b/src/dispatch_common.c @@ -0,0 +1,217 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * @file dispatch_common.c + * + * Implements common code shared by the generated GL/EGL/GLX dispatch code. + * + * A collection of some important specs on getting GL function pointers. + * + * From the linux GL ABI (http://www.opengl.org/registry/ABI/): + * + * "3.4. The libraries must export all OpenGL 1.2, GLU 1.3, GLX 1.3, and + * ARB_multitexture entry points statically. + * + * 3.5. Because non-ARB extensions vary so widely and are constantly + * increasing in number, it's infeasible to require that they all be + * supported, and extensions can always be added to hardware drivers + * after the base link libraries are released. These drivers are + * dynamically loaded by libGL, so extensions not in the base + * library must also be obtained dynamically. + * + * 3.6. To perform the dynamic query, libGL also must export an entry + * point called + * + * void (*glXGetProcAddressARB(const GLubyte *))(); + * + * The full specification of this function is available separately. It + * takes the string name of a GL or GLX entry point and returns a pointer + * to a function implementing that entry point. It is functionally + * identical to the wglGetProcAddress query defined by the Windows OpenGL + * library, except that the function pointers returned are context + * independent, unlike the WGL query." + * + * From the EGL 1.4 spec: + * + * "Client API function pointers returned by eglGetProcAddress are + * independent of the display and the currently bound client API context, + * and may be used by any client API context which supports the extension. + * + * eglGetProcAddress may be queried for all of the following functions: + * + * • All EGL and client API extension functions supported by the + * implementation (whether those extensions are supported by the current + * client API context or not). This includes any mandatory OpenGL ES + * extensions. + * + * eglGetProcAddress may not be queried for core (non-extension) functions + * in EGL or client APIs 20 . + * + * For functions that are queryable with eglGetProcAddress, + * implementations may choose to also export those functions statically + * from the object libraries im- plementing those functions. However, + * portable clients cannot rely on this behavior. + * + * From the GLX 1.4 spec: + * + * "glXGetProcAddress may be queried for all of the following functions: + * + * • All GL and GLX extension functions supported by the implementation + * (whether those extensions are supported by the current context or + * not). + * + * • All core (non-extension) functions in GL and GLX from version 1.0 up + * to and including the versions of those specifications supported by + * the implementation, as determined by glGetString(GL VERSION) and + * glXQueryVersion queries." + */ + +#include <assert.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <string.h> +#include <stdio.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include "dispatch_common.h" + +/* XXX: Make this thread local */ +struct api local_api; + +struct api *api = &local_api; + +bool +epoxy_is_desktop_gl(void) +{ + const char *es_prefix = "OpenGL ES "; + const char *version = (const char *)glGetString(GL_VERSION); + + printf("VERSION %s\n", version); + return !strncmp(es_prefix, version, strlen(es_prefix)); +} + +PUBLIC int +epoxy_gl_version(void) +{ + GLint major, minor; + + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MAJOR_VERSION, &minor); + + return major * 10 + minor; +} + +bool +epoxy_is_glx(void) +{ + return true; /* XXX */ +} + +int +epoxy_glx_version(void) +{ + return 14; /* XXX */ +} + +static bool +epoxy_extension_in_string(const char *extension_list, const char *ext) +{ + const char *ptr = extension_list; + int len = strlen(ext); + + /* Make sure that don't just find an extension with our name as a prefix. */ + do { + ptr = strstr(ptr, ext); + } while (ptr && (ptr[len] != ' ' && ptr[len] != 0)); + + return ptr != NULL; +} + +PUBLIC bool +epoxy_has_gl_extension(const char *ext) +{ + return epoxy_extension_in_string((const char *)glGetString(GL_EXTENSIONS), + ext); +} + +#if 0 +PUBLIC bool +epoxy_has_egl_extension(const char *ext) +{ + return epoxy_extension_in_string(eglQueryString(EGL_EXTENSIONS), ext); +} +#endif + +PUBLIC bool +epoxy_has_glx_extension(const char *ext) +{ + Display *dpy = glXGetCurrentDisplay(); + int screen = 0; + + if (!dpy) { + fprintf(stderr, "waffle needs a display!"); /* XXX */ + return false; + } + + /* No, you can't just use glXGetClientString or glXGetServerString() here. + * Those each tell you about one half of what's needed for an extension to + * be supported, and glXQueryExtensionsString(). + */ + return epoxy_extension_in_string(glXQueryExtensionsString(dpy, screen), ext); +} + +void * +epoxy_dlsym(const char *name) +{ + assert(api->gl_handle); + return dlsym(api->gl_handle, name); +} + +void * +epoxy_get_proc_address(const char *name) +{ + return glXGetProcAddress((const GLubyte *)name); +} + +void +epoxy_glx_autoinit(void) +{ + if (api->gl_handle) + return; + + api->gl_handle = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL); + if (!api->gl_handle) { + fprintf(stderr, "Couldn't open libGL.so.1: %s", dlerror()); + exit(1); + } + + api->winsys_handle = api->gl_handle; +} + +void +epoxy_platform_autoinit(void) +{ + epoxy_glx_autoinit(); +} + diff --git a/src/dispatch_common.h b/src/dispatch_common.h new file mode 100644 index 0000000..0113e8f --- /dev/null +++ b/src/dispatch_common.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdbool.h> +#include "epoxy/gl_common.h" +#include "epoxy/glx_common.h" + +#ifndef PUBLIC +# if (defined(__GNUC__) && __GNUC__ >= 4) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) +# define PUBLIC __attribute__((visibility("default"))) +# else +# define PUBLIC +# endif +#endif + +struct api { + bool is_glx; + bool is_gl; + bool is_gles1; + bool is_gles2; /**< Also GLES3 */ + + /** dlopen() return value for libGL.so.1, libGLESv2.so.1, etc. */ + void *gl_handle; + + /** dlopen() return value for libGL.so.1 or libEGL.so.1 */ + void *winsys_handle; +}; + +bool epoxy_is_desktop_gl(void); +bool epoxy_is_glx(void); + +void *epoxy_get_proc_address(const char *name); +void *epoxy_dlsym(const char *name); +void epoxy_glx_autoinit(void); +void epoxy_platform_autoinit(void); diff --git a/src/gen_dispatch.py b/src/gen_dispatch.py new file mode 100755 index 0000000..5adc57d --- /dev/null +++ b/src/gen_dispatch.py @@ -0,0 +1,456 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright © 2013 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import sys +import argparse +import xml.etree.ElementTree as ET +import re +import os + +class GLFunction(object): + def __init__(self, ret_type, name): + self.name = name + self.ptr_type = 'PFN' + name.upper() + self.ret_type = ret_type + self.providers = [] + self.args = [] + self.args_list = '' + self.args_decl = 'void' + + # For an alias group, alias_func is the function they are all + # marked as the alias of. + self.alias_func = None + # For an alias group, the shared alias_func has this list of + # functions (of different names) that are marked as aliases of + # it, so that it can write a resolver for all of them. + self.alias_exts = [] + + def add_arg(self, type, name): + self.args.append((type, name)) + if self.args_decl == 'void': + self.args_list = name + self.args_decl = type + ' ' + name + else: + self.args_list += ', ' + name + self.args_decl += ', ' + type + ' ' + name + + def add_provider(self, condition, loader, human_name): + self.providers.append((condition, loader, human_name)) + + def add_alias(self, ext): + # We don't support transitivity of aliases. + assert not ext.alias_exts + assert self.alias_func is None + + self.alias_exts.append(ext) + ext.alias_func = self + +class Generator(object): + def __init__(self): + self.enums = {} + self.functions = {} + self.max_enum_name_len = 1 + self.copyright_comment = None + self.typedefs = '' + self.out_file = None + + self.dlsym_loader = 'epoxy_dlsym("{0}")' + self.gpa_loader = 'epoxy_get_proc_address("{0}")' + + def all_text_until_element_name(self, element, element_name): + text = '' + + if element.text is not None: + text += element.text + + for child in element: + if child.tag == element_name: + break + if child.text: + text += child.text + if child.tail: + text += child.tail + return text + + def out(self, text): + self.out_file.write(text) + + def outln(self, text): + self.out_file.write(text + '\n') + + def parse_typedefs(self, reg): + for t in reg.findall('types/type'): + if 'name' in t.attrib and t.attrib['name'] not in {'GLhandleARB'}: + continue + + if t.text is not None: + self.typedefs += t.text + + for child in t: + if child.text: + self.typedefs += child.text + if child.tail: + self.typedefs += child.tail + self.typedefs += '\n' + + def parse_enums(self, reg): + for enum in reg.findall('enums/enum'): + name = enum.get('name') + self.max_enum_name_len = max(self.max_enum_name_len, len(name)) + self.enums[name] = enum.get('value') + + def get_function_return_type(self, proto): + # Everything up to the start of the name element is the return type. + return self.all_text_until_element_name(proto, 'name').strip() + + def parse_function_definitions(self, reg): + for command in reg.findall('commands/command'): + proto = command.find('proto') + name = proto.find('name').text + ret_type = self.get_function_return_type(proto) + + func = GLFunction(ret_type, name) + + for arg in command.findall('param'): + func.add_arg(self.all_text_until_element_name(arg, 'name').strip(), + arg.find('name').text) + + alias = command.find('alias') + if alias is not None: + alias_func = self.functions[alias.get('name')] + alias_func.add_alias(func) + + self.functions[name] = func + + def drop_weird_glx_functions(self): + # Drop a few ancient SGIX GLX extensions that use types not defined + # anywhere in Xlib. In glxext.h, they're protected by #ifdefs for the + # headers that defined them. + weird_functions = [name for name, func in self.functions.items() + if 'VLServer' in func.args_decl + or 'DMparams' in func.args_decl] + + for name in weird_functions: + del self.functions[name] + + def process_require_statements(self, feature, condition, loader, human_name): + for command in feature.findall('require/command'): + name = command.get('name') + func = self.functions[name] + func.add_provider(condition, loader.format(name), human_name) + + def parse_function_providers(self, reg): + for feature in reg.findall('feature'): + api = feature.get('api') # string gl, gles1, gles2, glx + m = re.match('([0-9])\.([0-9])', feature.get('number')) + version = int(m.group(1)) * 10 + int(m.group(2)) + + if api == 'gl': + human_name = 'Desktop OpenGL {0}'.format(feature.get('number')) + condition = 'epoxy_is_desktop_gl()' + + # Everything in GL 1.2 is guaranteed to be present as + # public symbols in the Linux libGL ABI. Everything + # else is supposed to not be present, so you have to + # glXGetProcAddress() it. + if version <= 12: + loader = self.dlsym_loader + else: + loader = self.gpa_loader + condition += ' && epoxy_gl_version() >= {0}'.format(version) + elif api == 'gles2': + human_name = 'OpenGL ES {0}'.format(feature.get('number')) + condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= {0}'.format(version) + + if version <= 20: + loader = self.dlsym_loader + else: + loader = self.gpa_loader + elif api == 'gles1': + human_name = 'OpenGL ES 1.0' + condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() == 10' + + if version <= 20: + loader = self.dlsym_loader + else: + loader = self.gpa_loader + elif api == 'glx': + human_name = 'GLX {0}'.format(version) + condition = 'epoxy_glx_version() >= {0}'.format(version) + loader = self.dlsym_loader + + if version <= 13: + loader = self.dlsym_loader + else: + loader = self.gpa_loader + else: + sys.exit('unknown API: "{0}"'.format(api)) + + self.process_require_statements(feature, condition, loader, human_name) + + for extension in reg.findall('extensions/extension'): + extname = extension.get('name') + # 'supported' is a set of strings like gl, gles1, gles2, or glx, which are + # separated by '|' + apis = extension.get('supported').split('|') + if 'glx' in apis: + human_name = 'GLX extension \\"{0}\\"'.format(extname) + condition = 'epoxy_has_glx_extension("{0}")'.format(extname) + loader = 'epoxy_get_proc_address("{0}")' + self.process_require_statements(extension, condition, loader, human_name) + if 'gl' in apis: + human_name = 'GL extension \\"{0}\\"'.format(extname) + condition = 'epoxy_has_gl_extension("{0}")'.format(extname) + loader = 'epoxy_get_proc_address("{0}")' + self.process_require_statements(extension, condition, loader, human_name) + + def fixup_bootstrap_function(self, name): + # We handle glGetString() and glGetIntegerv() specially, because we + # need to use them in the process of deciding on loaders for + # resolving, and the naive code generation would result in their + # resolvers calling their own resolvers. + if name not in self.functions: + return + + func = self.functions[name] + func.providers = [] + func.add_provider('true', self.dlsym_loader.format(func.name), + 'always present') + + def parse(self, file): + reg = ET.parse(file) + if reg.find('comment') != None: + self.copyright_comment = reg.find('comment').text + else: + self.copyright_comment = '' + self.parse_typedefs(reg) + self.parse_enums(reg) + self.parse_function_definitions(reg) + self.parse_function_providers(reg) + + def write_copyright_comment_body(self): + for line in self.copyright_comment.splitlines(): + if '-----' in line: + break + self.outln(' * ' + line) + + def write_enums(self): + for name, value in self.enums.items(): + self.outln('#define ' + name.ljust(self.max_enum_name_len + 3) + value + '') + + def write_function_ptr_typedefs(self): + for func in self.functions.values(): + self.outln('typedef {0} (*{1})({2});'.format(func.ret_type, func.ptr_type, + func.args_decl)) + + def write_header_header(self, file): + self.out_file = open(file, 'w') + + self.outln('/* GL dispatch header.') + self.outln(' * This is code-generated from the GL API XML files from Khronos.') + self.write_copyright_comment_body() + self.outln(' */') + self.outln('') + + self.outln('#pragma once') + + self.outln('#include <inttypes.h>') + self.outln('#include <stddef.h>') + self.outln('') + + def write_header(self, file): + self.write_header_header(file) + + if 'gl_' not in file: + self.outln('#include "epoxy/gl_generated.h"') + else: + # Add some ridiculous inttypes.h redefinitions that are from + # khrplatform.h and not included in the XML. + self.outln('typedef int8_t khronos_int8_t;') + self.outln('typedef int16_t khronos_int16_t;') + self.outln('typedef int32_t khronos_int32_t;') + self.outln('typedef int64_t khronos_int64_t;') + self.outln('typedef uint8_t khronos_uint8_t;') + self.outln('typedef uint16_t khronos_uint16_t;') + self.outln('typedef uint32_t khronos_uint32_t;') + self.outln('typedef uint64_t khronos_uint64_t;') + self.outln('typedef float khronos_float_t;') + self.outln('typedef intptr_t khronos_intptr_t;') + self.outln('typedef ptrdiff_t khronos_ssize_t;') + + if 'glx_' in file: + self.outln('#include <X11/Xlib.h>') + self.outln('#include <X11/Xutil.h>') + + self.out(self.typedefs) + self.outln('') + self.write_enums() + self.outln('') + self.write_function_ptr_typedefs() + + for func in self.functions.values(): + self.outln('{0} epoxy_{1}({2});'.format(func.ret_type, func.name, + func.args_decl)) + self.outln('') + + def write_proto_define_header(self, file, style): + self.write_header_header(file) + + for func in self.functions.values(): + self.outln('#define {0} epoxy_{1}{0}'.format(func.name, style)) + + def write_function_ptr_resolver(self, func): + self.outln('static {0}'.format(func.ptr_type)) + self.outln('epoxy_{0}_resolver(void)'.format(func.name)) + self.outln('{') + if 'glX' in func.name: + self.outln(' epoxy_glx_autoinit();') + else: + self.outln(' epoxy_platform_autoinit();') + self.outln('') + + providers = [] + # Make a local list of all the providers for this alias group + for provider in func.providers: + providers.append(provider) + for alias_func in func.alias_exts: + for provider in alias_func.providers: + providers.append(provider) + + # Check if there's any alias of this that's built into our + # ABI, and just use that one if available. This is important + # for avoiding loops of + # glXGetProcAddress(glXGetProcAddress()), or + # glGetIntegerv(glGetIntegerv()). + global_loader = None + for condition, loader, human_name in providers: + if condition == 'true': + global_loader = loader + + if global_loader: + self.outln(' return {0};'.format(loader)) + else: + for condition, loader, human_name in providers: + self.outln(' if ({0})'.format(condition)) + self.outln(' return {0};'.format(loader)) + self.outln('') + + # If the function isn't provided by any known extension, print + # something useful for the poor application developer before + # aborting. (In non-epoxy GL usage, the app developer would + # call into some blank stub function and segfault). + self.outln(' printf("No provider of \\"{0}()\\" found. Requires one of:\\n");'.format(func.name)) + if len(func.providers) == 0: + self.outln(' printf(" unknown\\n");') + else: + for provider in func.providers: + self.outln(' printf(" {0}\\n");'.format(provider[2])) + + self.outln(' abort();') + + self.outln('}') + self.outln('') + + def write_dispatch_table_stub(self, func): + dispatch_table_entry = 'dispatch_table->p{0}'.format(func.name) + + # Use the same resolver for all the aliases of a particular + # function. + alias_func = func + if func.alias_func: + alias_func = func + + self.outln('PUBLIC {0}'.format(func.ret_type)) + self.outln('epoxy_{0}({1})'.format(func.name, func.args_decl)) + self.outln('{') + self.outln(' if (!{0})'.format(dispatch_table_entry)) + self.outln(' {0} = epoxy_{1}_resolver();'.format(dispatch_table_entry, + alias_func.name)) + self.outln('') + if func.ret_type == 'void': + self.outln(' {0}({1});'.format(dispatch_table_entry, func.args_list)) + else: + self.outln(' return {0}({1});'.format(dispatch_table_entry, func.args_list)) + self.outln('}') + self.outln('') + + def write_source(self, file): + self.out_file = open(file, 'w') + + self.outln('/* GL dispatch code.') + self.outln(' * This is code-generated from the GL API XML files from Khronos.') + self.write_copyright_comment_body() + self.outln(' */') + self.outln('') + self.outln('#include <stdlib.h>') + self.outln('#include <stdio.h>') + self.outln('') + self.outln('#include "dispatch_common.h"') + if 'glx_' in file: + self.outln('#include "epoxy/glx_generated.h"') + else: + self.outln('#include "epoxy/gl_generated.h"') + self.outln('') + + self.outln('struct dispatch_table {') + for func in self.functions.values(): + self.outln(' {0} p{1};'.format(func.ptr_type, func.name)) + self.outln('};') + self.outln('') + + self.outln('/* XXX: Make this thread-local and swapped on makecurrent. */') + self.outln('static struct dispatch_table local_dispatch_table;') + self.outln('static struct dispatch_table *dispatch_table = &local_dispatch_table;') + self.outln('') + + for func in self.functions.values(): + if not func.alias_func: + self.write_function_ptr_resolver(func) + + self.write_dispatch_table_stub(func) + +argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.') +argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed') +argparser.add_argument('--dir', metavar='dir', required=True, help='Destination directory') +args = argparser.parse_args() + +srcdir = args.dir + '/src/' +incdir = args.dir + '/include/epoxy/' + +for file in args.files: + name = os.path.basename(file).split('.xml')[0] + generator = Generator() + generator.parse(file) + generator.drop_weird_glx_functions() + generator.fixup_bootstrap_function('glGetString') + generator.fixup_bootstrap_function('glGetIntegerv') + + # While this is technically exposed as a GLX extension, it's + # required to be present as a public symbol by the Linux OpenGL + # ABI. + generator.fixup_bootstrap_function('glXGetProcAddressARB') + + generator.write_header(incdir + name + '_generated.h') + generator.write_proto_define_header(incdir + name + '_generated_vtable_defines.h', '') + generator.write_source(srcdir + name + '_generated_dispatch.c') |