summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2013-09-19 09:50:49 -0700
committerEric Anholt <eric@anholt.net>2013-10-16 11:43:11 -0700
commita909eb4a229ebbea2d60717e748df3f0a2f69cc4 (patch)
treeb2b265d8609bd52369b4b6345ebc8d07f72d1e0e /src
parentdb667aa8b847aa9718bce8f371c65a739ea922ef (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.am84
-rw-r--r--src/dispatch_common.c217
-rw-r--r--src/dispatch_common.h55
-rwxr-xr-xsrc/gen_dispatch.py456
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')