From a909eb4a229ebbea2d60717e748df3f0a2f69cc4 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 19 Sep 2013 09:50:49 -0700 Subject: [PATCH] Add the generator and build infrastructure. Not actually working yet, but it's a snapshot to start from. --- .gitignore | 90 ++++++++ Makefile.am | 36 +++ README.md | 61 +++++ autogen.sh | 14 ++ configure.ac | 75 ++++++ epoxy.pc.in | 10 + include/epoxy/gl.h | 52 +++++ include/epoxy/gl_common.h | 38 ++++ include/epoxy/glx.h | 51 +++++ include/epoxy/glx_common.h | 38 ++++ src/Makefile.am | 84 +++++++ src/dispatch_common.c | 217 ++++++++++++++++++ src/dispatch_common.h | 55 +++++ src/gen_dispatch.py | 456 +++++++++++++++++++++++++++++++++++++ 14 files changed, 1277 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.am create mode 100644 README.md create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 epoxy.pc.in create mode 100644 include/epoxy/gl.h create mode 100644 include/epoxy/gl_common.h create mode 100644 include/epoxy/glx.h create mode 100644 include/epoxy/glx_common.h create mode 100644 src/Makefile.am create mode 100644 src/dispatch_common.c create mode 100644 src/dispatch_common.h create mode 100755 src/gen_dispatch.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35b14ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +# +# X.Org module default exclusion patterns +# The next section if for module specific patterns +# +# Do not edit the following section +# GNU Build System (Autotools) +aclocal.m4 +autom4te.cache/ +autoscan.log +ChangeLog +compile +config.guess +config.h +config.h.in +config.log +config-ml.in +config.py +config.status +config.status.lineno +config.sub +configure +configure.scan +depcomp +.deps/ +INSTALL +install-sh +.libs/ +libtool +libtool.m4 +ltmain.sh +lt~obsolete.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +Makefile +Makefile.in +mdate-sh +missing +mkinstalldirs +*.pc +py-compile +stamp-h? +symlink-tree +texinfo.tex +ylwrap +src/sna/git_version.h +src/sna/brw/brw_test + +# Do not edit the following section +# Edit Compile Debug Document Distribute +*~ +*.[0-9] +*.[0-9]x +*.bak +*.bin +core +*.dll +*.exe +*-ISO*.bdf +*-JIS*.bdf +*-KOI8*.bdf +*.kld +*.ko +*.ko.cmd +*.lai +*.l[oa] +*.[oa] +*.obj +*.patch +*.so +*.pcf.gz +*.pdb +*.tar.bz2 +*.tar.gz +# +# Add & Override patterns for gldispatch +# +# Edit the following section as needed +# For example, !report.pc overrides *.pc. See 'man gitignore' +# +configure.lineno +.dirstamp + +gl_generated_dispatch.c +gl_generated.h +gl_generated_vtable_defines.h + +glx_generated_dispatch.c +glx_generated.h +glx_generated_vtable_defines.h diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3d8179b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,36 @@ +# 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. + +SUBDIRS = src + +#if HAVE_X11 +#SUBDIRS += test +#endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = epoxy.pc + +EXTRA_DIST = \ + epoxy.pc.in \ + registry/egl.xml \ + registry/gl.xml \ + registry/glx.xml \ + registry/wgl.xml \ + $() diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5f36b2 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +Epoxy is a library for handling OpenGL function pointer management for +you. + +It hides the complexity of dlopen(), dlsym(), glXGetProcAddress(), +eglGetProcAddress(), etc. from the app developer, with very little +knowledge needed on their part. Just read your GL specs and write +code using undecorated function names like glCompileShader(). + +Don't forget to check for your extensions or versions being present +before you use them, just like before! We'll tell you what you forgot +to check for instead of just segfaulting, though. + +Why does this library exist? +---------------------------- + +OpenGL on Linux (and other platforms) made some ABI decisions back in +the days when symbol versioning and dlsym() weren't as widely +available, that resulted in window-systems-specific APIs that looked +kind of like dlsym. They allowed you to build an app that required +OpenGL 1.2, but could optionally use features of OpenGL 1.4 if the +implementation made those available through the glXGetProcAddress() +(or other similarly-named) mechanism. + +The downside is that the fixed OpenGL 1.2 ABI means that application +developers have to GetProcAddress() out every modern GL entrypoint +they want to use and stash that function pointer somewhere. Sometimes +this is done in a pretty way (like libGLEW), sometimes it is done in +an ad-hoc way (like most applications I've seen), but it's never done +as well as we think it could be done. + +Additionally, the proliferation of OpenGL ABIs (desktop GL, GLESv1, +GLESv2) and window systems (GLX, AGL, WGL, all versus EGL) means that + +Switching your code to using epoxy +---------------------------------- + +It should be as easy as replacing: + +> #include +> #include +> #include + +with: + +> #include +> #include + +Additionally, some new helpers become available, so you don't have to +write them: + +int epoxy_gl_version() returns the GL version: + +12 for GL 1.2 +20 for GL 2.0 +44 for GL 4.4 + +bool epoxy_has_gl_extension() returns whether a GL extension is +available ("GL_ARB_texture_buffer_object", for example). + +Note that this is not terribly fast, so keep it out of your hot paths, +ok? diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..fc34bd5 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,14 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +autoreconf -v --install || exit 1 +cd $ORIGDIR || exit $? + +if test -z "$NOCONFIGURE"; then + $srcdir/configure "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..f2f137f --- /dev/null +++ b/configure.ac @@ -0,0 +1,75 @@ +# 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. + +# Initialize Autoconf +AC_PREREQ([2.60]) +AC_INIT([libepoxy], + [1.0], + [https://people.freedesktop.org/~anholt/libepoxy], + [libepoxy]) +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CONFIG_HEADERS([config.h]) + +# Initialize Automake +AM_INIT_AUTOMAKE([foreign dist-bzip2 subdir-objects]) + +# Require X.Org macros 1.8 or later for MAN_SUBSTS set by XORG_MANPAGE_SECTIONS +m4_ifndef([XORG_MACROS_VERSION], + [m4_fatal([must install xorg-macros 1.8 or later before running autoconf/autogen. + Hint: either install from source, git://anongit.freedesktop.org/xorg/util/macros or, + depending on you distribution, try package 'xutils-dev' or 'xorg-x11-util-macros'])]) + +XORG_MACROS_VERSION(1.8) +XORG_DEFAULT_OPTIONS + +AC_CHECK_PROGS([PYTHON], [python3 python2 python]) + +# Initialize libtool +AC_DISABLE_STATIC +AC_PROG_LIBTOOL +AC_SYS_LARGEFILE + +if test "x$GCC" = xyes; then + save_CFLAGS="$CFLAGS" + AC_MSG_CHECKING([whether $CC supports -fvisibility=hidden]) + VISIBILITY_CFLAGS="-fvisibility=hidden" + CFLAGS="$CFLAGS $VISIBILITY_CFLAGS" + AC_LINK_IFELSE([AC_LANG_PROGRAM()], AC_MSG_RESULT([yes]), + [VISIBILITY_CFLAGS=""; AC_MSG_RESULT([no])]); + + # Restore CFLAGS; VISIBILITY_CFLAGS are added to it where needed. + CFLAGS=$save_CFLAGS +fi + +AC_SUBST([VISIBILITY_CFLAGS]) + +PKG_CHECK_MODULES(X11, [x11], [x11=yes], [x11=no]) +AM_CONDITIONAL(HAVE_X11, test x$x11 = xyes) + + +AC_CONFIG_FILES([ + epoxy.pc + Makefile + src/Makefile +]) +AC_OUTPUT + +echo " PYTHON: $PYTHON" diff --git a/epoxy.pc.in b/epoxy.pc.in new file mode 100644 index 0000000..6d484d1 --- /dev/null +++ b/epoxy.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: epoxy +Description: epoxy GL dispatch Library +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lepoxy diff --git a/include/epoxy/gl.h b/include/epoxy/gl.h new file mode 100644 index 0000000..2b3bbaf --- /dev/null +++ b/include/epoxy/gl.h @@ -0,0 +1,52 @@ +/* + * 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 gl.h + * + * Provides an implementation of a GL dispatch layer using a hidden + * vtable. + * + * This is a lower performance path than ifuncs when they are + * available, but it is required if you might have multiple return + * values for GetProcAddress/dlsym()ed functions. That is the case if + * you're using WGL (which can return different function pointers per + * context), or if you dlclose() and re-dlopen() libGL (which means + * you'll get different dynamically allocated dispatch stubs). + */ + +#ifndef __EPOXY_GL_H +#define __EPOXY_GL_H + +#if defined(__gl_h_) || defined(__glext_h_) +#error epoxy/gl.h must be included before (or in place of) GL/gl.h +#else +#define __gl_h_ +#define __glext_h_ +#endif + + +#include "epoxy/gl_common.h" +#include "epoxy/gl_generated.h" +#include "epoxy/gl_generated_vtable_defines.h" + +#endif /* __EPOXY_GL_H */ diff --git a/include/epoxy/gl_common.h b/include/epoxy/gl_common.h new file mode 100644 index 0000000..991fe98 --- /dev/null +++ b/include/epoxy/gl_common.h @@ -0,0 +1,38 @@ +/* + * 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 gl_common.h + * + * Provides prototypes for epoxy GL functions independent of dispatch mechanism. + */ + +#ifndef EPOXY_GL_COMMON_H +#define EPOXY_GL_COMMON_H + +#include + +bool epoxy_has_gl_extension(const char *extension); +int epoxy_gl_version(void); + +#endif /* EPOXY_GL_COMMON_H */ + diff --git a/include/epoxy/glx.h b/include/epoxy/glx.h new file mode 100644 index 0000000..215cd2a --- /dev/null +++ b/include/epoxy/glx.h @@ -0,0 +1,51 @@ +/* + * 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 glx.h + * + * Provides an implementation of a GLX dispatch layer using a hidden + * vtable. + * + * This is a lower performance path than ifuncs when they are + * available, but it is required if you might have multiple return + * values for GetProcAddress/dlsym()ed functions (for example, if you + * unload libGL.so.1). + */ + +#ifndef __EPOXY_GLX_H +#define __EPOXY_GLX_H + +#if defined(__glx_h_) || defined(__glxext_h_) +#error epoxy/glx.h must be included before (or in place of) GL/glx.h +#else +#define __glext_h_ +#define __glxext_h_ +#endif + +#pragma once + +#include "epoxy/glx_common.h" +#include "epoxy/glx_generated.h" +#include "epoxy/glx_generated_vtable_defines.h" + +#endif /* __EPOXY_GLX_H */ diff --git a/include/epoxy/glx_common.h b/include/epoxy/glx_common.h new file mode 100644 index 0000000..cee9f51 --- /dev/null +++ b/include/epoxy/glx_common.h @@ -0,0 +1,38 @@ +/* + * 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 gl_common.h + * + * Provides prototypes for epoxy GL functions independent of dispatch mechanism. + */ + +#ifndef EPOXY_GLX_COMMON_H +#define EPOXY_GLX_COMMON_H + +#include + +bool epoxy_has_glx_extension(const char *extension); +int epoxy_glx_version(void); + +#endif /* EPOXY_GLX_COMMON_H */ + 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 +#include +#include +#include +#include +#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 +#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 ') + self.outln('#include ') + 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 ') + self.outln('#include ') + + 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 ') + self.outln('#include ') + 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')