diff options
71 files changed, 8108 insertions, 6 deletions
diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..8e0cae2 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,67 @@ +version: 1.0.{build} + +image: Visual Studio 2015 + +configuration: Release + +# Configure both 32-bit and 64-bit builds +environment: + matrix: + - platform: x86 + config: Win32 + pout: x86 + - platform: x64 + config: x64 + pout: x64 + +shallow_clone: true + +# Download Meson and Ninja, create install directory +before_build: +- mkdir build +- mkdir libepoxy-shared-%pout% +- cd build +- curl -LsSO https://github.com/mesonbuild/meson/releases/download/0.47.1/meson-0.47.1.tar.gz +- 7z x meson-0.47.1.tar.gz +- move dist\meson-0.47.1.tar . +- 7z x meson-0.47.1.tar +- rmdir dist +- del meson-0.47.1.tar meson-0.47.1.tar.gz +- curl -LsSO https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-win.zip +- 7z x ninja-win.zip +- del ninja-win.zip +- cd .. + +# Build and install +build_script: +- cd build +- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PLATFORM% +- C:\Python36\python.exe meson-0.47.1\meson.py .. . --backend=ninja --prefix=%APPVEYOR_BUILD_FOLDER%\libepoxy-shared-%pout% +- ninja +- ninja install +- cd .. + +# Copy license into install directory and create .zip file +after_build: +- copy COPYING libepoxy-shared-%pout% +- dir libepoxy-shared-%pout% /s /b +- 7z a -tzip libepoxy-shared-%pout%.zip libepoxy-shared-%pout% + +artifacts: + - path: libepoxy-shared-%pout%.zip + name: libepoxy-shared-%pout% + +test: off + +# Upload .zip file to GitHub release +deploy: + release: $(APPVEYOR_REPO_TAG_NAME) + description: "Epoxy $(APPVEYOR_REPO_TAG_NAME)" + provider: GitHub + auth_token: + secure: X7Ro8Y2RWYo/M1AAn93f9X0dEQFvu7gPb6li2eKRtzPYLGj/JKm7MNWRw2cCcjm6 + artifact: libepoxy-shared-$(pout) + draft: false + prerelease: false + on: + appveyor_repo_tag: true # deploy on tag push only diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..7f3eee2 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,6 @@ +((nil + (indent-tabs-mode . nil) + (tab-width . 8) + (c-basic-offset . 4) + ) + ) diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ce6000d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,30 @@ +root = true + +[*] +charset = utf-8 + +[*.{c,h}] +indent_style = space +indent_size = 4 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[configure.ac] +indent_style = tab +indent_size = 8 + +[Makefile.am] +indent_style = tab +indent_size = 8 + +[.travis.yml] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[meson.build] +indent_style = space +indent_size = 8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75d444d --- /dev/null +++ b/.gitignore @@ -0,0 +1,94 @@ +# +# 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 +*.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 +test-driver + +gl_generated_dispatch.c +gl_generated.h + +glx_generated_dispatch.c +glx_generated.h + +egl_generated_dispatch.c +egl_generated.h + +wgl_generated_dispatch.c +wgl_generated.h diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..52c043e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,54 @@ +sudo: false + +branches: + except: + - debian + - khronos-registry + +os: + - linux + - osx + +compiler: + - gcc + - clang + +language: + - c + +services: + - docker + +matrix: + exclude: + - os: osx + compiler: gcc + +before_install: + - | + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update + brew install python@3 meson + # Use a Ninja with QuLogic's patch: https://github.com/ninja-build/ninja/issues/1219 + mkdir -p $HOME/tools; curl -L http://nirbheek.in/files/binaries/ninja/macos/ninja -o $HOME/tools/ninja; chmod +x $HOME/tools/ninja + fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull ebassi/epoxyci ; fi + +before_script: + - | + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + echo FROM ebassi/epoxyci > Dockerfile + echo ADD . /root >> Dockerfile + echo WORKDIR /root >> Dockerfile + docker build -t withgit . + fi + +env: + - BUILD_OPTS="" + - BUILD_OPTS="-Dglx=no" + - BUILD_OPTS="-Degl=no" + - BUILD_OPTS="-Dx11=false" + +script: + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run withgit /bin/sh -c "CC=$CC .travis/epoxy-ci-linux.sh $BUILD_OPTS" ; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then /bin/sh -c "CC=$CC .travis/epoxy-ci-osx.sh $BUILD_OPTS" ; fi diff --git a/.travis/Dockerfile b/.travis/Dockerfile new file mode 100644 index 0000000..bd9b40a --- /dev/null +++ b/.travis/Dockerfile @@ -0,0 +1,29 @@ +FROM debian:stretch-slim +MAINTAINER Emmanuele Bassi <ebassi@gmail.com> + +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -qq -y \ + ca-certificates \ + clang \ + gcc \ + libgl1-mesa-dev \ + libegl1-mesa-dev \ + libgles1-mesa-dev \ + libgles2-mesa-dev \ + libgl1-mesa-dri \ + locales \ + ninja-build \ + pkg-config \ + python3 \ + python3-pip \ + python3-setuptools \ + python3-wheel \ + xvfb && \ + rm -rf /usr/share/doc/* /usr/share/man/* + +RUN locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8 +ENV LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8 + +RUN pip3 install meson + +WORKDIR /root diff --git a/.travis/epoxy-ci-linux.sh b/.travis/epoxy-ci-linux.sh new file mode 100755 index 0000000..e95584f --- /dev/null +++ b/.travis/epoxy-ci-linux.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +dump_log_and_quit() { + local exitcode=$1 + + cat meson-logs/testlog.txt + + exit $exitcode +} + +# Start Xvfb +XVFB_WHD=${XVFB_WHD:-1280x720x16} + +Xvfb :99 -ac -screen 0 $XVFB_WHD -nolisten tcp & +xvfb=$! + +export DISPLAY=:99 + +srcdir=$( pwd ) +builddir=$( mktemp -d build_XXXXXX ) + +meson --prefix /usr "$@" $builddir $srcdir || exit $? + +cd $builddir + +ninja || exit $? +meson test || dump_log_and_quit $? + +cd .. + +# Stop Xvfb +kill -9 ${xvfb} diff --git a/.travis/epoxy-ci-osx.sh b/.travis/epoxy-ci-osx.sh new file mode 100755 index 0000000..1a062a1 --- /dev/null +++ b/.travis/epoxy-ci-osx.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +dump_log_and_quit() { + local exitcode=$1 + + cat meson-logs/testlog.txt + + exit $exitcode +} + +export SDKROOT=$( xcodebuild -version -sdk macosx Path ) +export CPPFLAGS=-I/usr/local/include +export LDFLAGS=-L/usr/local/lib +export OBJC=$CC +export PATH=$HOME/tools:$PATH + +srcdir=$( pwd ) +builddir=$( mktemp -d build_XXXXXX ) + +meson ${BUILDOPTS} $builddir $srcdir || exit $? + +cd $builddir + +ninja || exit $? +meson test || dump_log_and_quit $? + +cd .. diff --git a/.travis/run-docker.sh b/.travis/run-docker.sh new file mode 100755 index 0000000..4b3ecc7 --- /dev/null +++ b/.travis/run-docker.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -xe + +srcdir="$(pwd)/.." + +sudo docker build \ + --tag "epoxyci" \ + --file "Dockerfile" . +sudo docker run --rm \ + --volume "${srcdir}:/root/epoxy" \ + --tty --interactive "epoxyci" bash @@ -0,0 +1,50 @@ +The libepoxy project code is covered by the MIT license: + +/* + * Copyright © 2013-2014 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. + */ + +The generated code is derived from Khronos's xml files, which appear +under the following license: + +/* + * Copyright (c) 2013 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE 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 + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + */ diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8fead48 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,68 @@ + +# 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. + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = include/epoxy src + +SUBDIRS += test + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = epoxy.pc + +registry_files = \ + registry/egl.xml \ + registry/gl.xml \ + registry/glx.xml \ + registry/wgl.xml \ + $() + +meson_build_files = \ + meson_options.txt \ + meson.build \ + include/epoxy/meson.build \ + src/meson.build \ + test/meson.build \ + doc/meson.build \ + doc/Doxyfile.in \ + cross/fedora-mingw64.txt \ + $() + +EXTRA_DIST = \ + .dir-locals.el \ + README.md \ + autogen.sh \ + epoxy.pc.in \ + $(registry_files) \ + $(meson_build_files) \ + $() + +dist-hook: + @if test -d "$(top_srcdir)/.git"; then \ + echo Generating ChangeLog... ; \ + ( $(top_srcdir)/missing --run git log --stat ) > "$(top_srcdir)/ChangeLog.tmp" \ + && mv -f "$(top_srcdir)/ChangeLog.tmp" "$(top_distdir)/ChangeLog" \ + || ( rm -f "$(top_srcdir)/ChangeLog.tmp"; \ + echo Failed to generate ChangeLog >&2 ); \ + else \ + echo A git checkout is required to generate a ChangeLog >&2; \ + fi diff --git a/README.md b/README.md new file mode 100644 index 0000000..56b6a65 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +[![Build Status](https://travis-ci.org/anholt/libepoxy.svg?branch=master)](https://travis-ci.org/anholt/libepoxy) +[![Build status](https://ci.appveyor.com/api/projects/status/xv6y5jurt5v5ngjx/branch/master?svg=true)](https://ci.appveyor.com/project/ebassi/libepoxy/branch/master) + +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. They get to read 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. + +Features +-------- + + * Automatically initializes as new GL functions are used. + * GL 4.6 core and compatibility context support. + * GLES 1/2/3 context support. + * Knows about function aliases so (e.g.) `glBufferData()` can be + used with `GL_ARB_vertex_buffer_object` implementations, along + with GL 1.5+ implementations. + * EGL, GLX, and WGL support. + * Can be mixed with non-epoxy GL usage. + +Building +-------- + +```sh +mkdir _build && cd _build +meson +ninja +sudo ninja install +``` + +Dependencies for Debian: + + * meson + * libegl1-mesa-dev + +Dependencies for macOS (using MacPorts): + + * pkgconfig + * meson + +The test suite has additional dependencies depending on the platform. +(X11, EGL, a running X Server). + +Switching your code to using epoxy +---------------------------------- + +It should be as easy as replacing: + +```cpp +#include <GL/gl.h> +#include <GL/glx.h> +#include <GL/glext.h> +``` + +with: + +```cpp +#include <epoxy/gl.h> +#include <epoxy/glx.h> +``` + +As long as epoxy's headers appear first, you should be ready to go. +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? + +Why not use libGLEW? +-------------------- + +GLEW has several issues: + + * Doesn't know about aliases of functions (There are 5 providers of + `glPointParameterfv()`, for example, and you don't want to have to + choose which one to call when they're all the same). + * Doesn't support OpenGL ES. + * Has a hard-to-maintain parser of extension specification text + instead of using the old .spec file or the new .xml. + * Has significant startup time overhead when `glewInit()` + autodetects the world. + * User-visible multithreading support choice for win32. + +The motivation for this project came out of previous use of libGLEW in +[piglit](http://piglit.freedesktop.org/). Other GL dispatch code +generation projects had similar failures. Ideally, piglit wants to be +able to build a single binary for a test that can run on whatever +context or window system it chooses, not based on link time choices. + +We had to solve some of GLEW's problems for piglit and solving them +meant replacing every single piece of GLEW, so we built +piglit-dispatch from scratch. And since we wanted to reuse it in +other GL-related projects, this is the result. + +Known issues when running on Windows +------------------------------------ + +The automatic per-context symbol resolution for win32 requires that +epoxy knows when `wglMakeCurrent()` is called, because `wglGetProcAddress()` +returns values depend on the context's device and pixel format. If +`wglMakeCurrent()` is called from outside of epoxy (in a way that might +change the device or pixel format), then epoxy needs to be notified of +the change using the `epoxy_handle_external_wglMakeCurrent()` function. + +The win32 `wglMakeCurrent()` variants are slower than they should be, +because they should be caching the resolved dispatch tables instead of +resetting an entire thread-local dispatch table every time. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..02b81ef --- /dev/null +++ b/autogen.sh @@ -0,0 +1,16 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd "$srcdir" + +mkdir m4 || exit 1 + +autoreconf -v --install || exit $? +cd "$ORIGDIR" || exit $? + +if test -z "$NOCONFIGURE"; then + exec "$srcdir/configure" "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..b0bb452 --- /dev/null +++ b/configure.ac @@ -0,0 +1,275 @@ +# 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.5.4], + [https://github.com/anholt/libepoxy], + [libepoxy]) +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) + +# Initialize Automake +AM_INIT_AUTOMAKE([foreign -Wno-portability dist-xz no-dist-gzip tar-ustar 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_LIBTOOL_WIN32_DLL +AC_PROG_LIBTOOL +AC_SYS_LARGEFILE + +AC_CHECK_HEADER([KHR/khrplatform.h], + [AC_DEFINE([HAVE_KHRPLATFORM_H], [1], + [Define to 1 if you have <KHR/khrplatform.h> (used for tests)] + )] + ) + +# OS X defaults to having -Wint-conversion ("warn when passing +# uintptr_t to a void *") by default. Kill that. +XORG_TESTSET_CFLAG(CWARNFLAGS, [-Wno-int-conversion]) + +AC_ARG_ENABLE([x11], + [AC_HELP_STRING([--enable-x11=@<:@yes,no@:>@], [Enable X11 support @<:@default=yes@:>@])], + [enable_x11=$enableval], + [enable_x11=yes]) + +AC_ARG_ENABLE([glx], + [AC_HELP_STRING([--enable-glx=@<:@auto,yes,no@:>@], [Enable GLX support @<:@default=auto@:>@])], + [enable_glx=$enableval], + [enable_glx=auto]) + +# GLX can be used on different platforms, so we expose a +# configure time switch to enable or disable it; in case +# the "auto" default value is set, we only enable GLX +# support on Linux and Unix +AS_CASE([$enable_glx], + [auto], [ + AS_CASE([$host_os], + [mingw*], [build_glx=no], + [darwin*], [build_glx=no], + [android*], [build_glx=no], + [build_glx=yes]) + ], + + [yes], [ + build_glx=yes + ], + + [no], [ + build_glx=no + ], + + [AC_MSG_ERROR([Invalid value "$enable_glx" for option "--enable-glx"])] +]) + +AC_ARG_ENABLE([egl], + [AC_HELP_STRING([--enable-egl=@<:@auto,yes,no@:>@], [Enable EGL support @<:@default=auto@:>@])], + [enable_egl=$enableval], + [enable_egl=auto]) + +AS_CASE([$enable_egl], + [auto], [ + AS_CASE([$host_os], + [mingw*], [build_egl=no], + [darwin*], [build_egl=no], + [build_egl=yes]) + ], + + [yes], [ + build_egl=yes + ], + + [no], [ + build_egl=no + ], + + [AC_MSG_ERROR([Invalid value "$enable_egl" for option "--enable-egl"])] +]) + +# The remaining platform specific API are enabled depending on the +# platform we're building for +AS_CASE([$host_os], + [mingw*], [ + build_wgl=yes + has_znow=yes + # On windows, the DLL has to have all of its functions + # resolved at link time, so we have to link directly against + # opengl32.dll. But that's the only GL provider, anyway. + EPOXY_LINK_LIBS="-lopengl32" + + # Testing our built windows binaries requires that they be run + # under wine. Yeah, we should be nice and autodetect, but + # there's lots of missing autodetection for the testsuite + # (like checking for EGL and GLX libs in non-windows.). + AC_SUBST([LOG_COMPILER], [wine]) + ], + + [darwin*], [ + build_wgl=no + has_znow=no + EPOXY_LINK_LIBS="" + ], + + [ + build_wgl=no + has_znow=yes + # On platforms with dlopen, we load everything dynamically and + # don't link against a specific window system or GL implementation. + EPOXY_LINK_LIBS="" + ] +) + +AC_SUBST(EPOXY_LINK_LIBS) + +if test x$enable_x11 = xno; then + if test x$enable_glx = xyes; then + AC_MSG_ERROR([GLX support is explicitly enabled, but X11 was disabled]) + fi + build_glx=no +else + AC_DEFINE([ENABLE_X11], [1], [Whether X11 support is enabled]) +fi + +AM_CONDITIONAL(BUILD_EGL, test x$build_egl = xyes) +if test x$build_egl = xyes; then + PKG_CHECK_MODULES(EGL, [egl]) + AC_DEFINE([BUILD_EGL], [1], [build EGL tests]) + AC_DEFINE(ENABLE_EGL, [1], [Whether EGL support is enabled]) +fi + +AM_CONDITIONAL(BUILD_GLX, test x$build_glx = xyes) +if test x$build_glx = xyes; then + AC_DEFINE([BUILD_GLX], [1], [build GLX tests]) +fi + +AM_CONDITIONAL(BUILD_WGL, test x$build_wgl = xyes) +if test x$build_wgl = xyes; then + AC_DEFINE([BUILD_WGL], [1], [build WGL tests]) +fi + +AM_CONDITIONAL(HAS_ZNOW, test x$has_znow = xyes) + +AC_CHECK_LIB([GLESv1_CM], [glFlush], [has_gles1=yes], [has_gles1=no]) +AM_CONDITIONAL(HAS_GLES1, test x$has_gles1 = xyes) + +AC_CHECK_LIB([dl], [dlopen], [DLOPEN_LIBS="-ldl"]) +AC_SUBST([DLOPEN_LIBS]) + +savelibs=$LIBS +LIBS=$DLOPEN_LIBS +AC_CHECK_FUNCS([dlvsym], [have_dlvsym=1], [have_dlvsym=0]) +AM_CONDITIONAL(HAVE_DLVSYM, test $have_dlvsym = 1) +LIBS=$savelibs + +VISIBILITY_CFLAGS="" +AS_CASE(["$host"], + + [*-*-mingw*], [ + dnl on mingw32 we do -fvisibility=hidden and __declspec(dllexport) + AC_DEFINE([EPOXY_PUBLIC], + [__attribute__((visibility("default"))) __declspec(dllexport) extern], + [defines how to decorate public symbols while building]) + VISIBILITY_CFLAGS="-fvisibility=hidden" + ], + + [ + dnl on other compilers, check if we can do -fvisibility=hidden + SAVED_CFLAGS="${CFLAGS}" + CFLAGS="-fvisibility=hidden" + AC_MSG_CHECKING([for -fvisibility=hidden compiler flag]) + AC_TRY_COMPILE([], [int main (void) { return 0; }], [ + AC_MSG_RESULT(yes) + enable_fvisibility_hidden=yes + ], [ + AC_MSG_RESULT(no) + enable_fvisibility_hidden=no + ]) + CFLAGS="${SAVED_CFLAGS}" + + AS_IF([test "${enable_fvisibility_hidden}" = "yes"], [ + AC_DEFINE([EPOXY_PUBLIC], + [__attribute__((visibility("default"))) extern], + [defines how to decorate public symbols while building]) + VISIBILITY_CFLAGS="-fvisibility=hidden" + ]) + ] +) + +AC_SUBST([VISIBILITY_CFLAGS]) + +if test x$enable_x11 = xyes; then + PKG_CHECK_MODULES(X11, [x11], [x11=yes], [x11=no]) + if test x$x11 = xno -a x$build_glx = xyes; then + AC_MSG_ERROR([libX11 headers (libx11-dev) are required to build with GLX support]) + fi +else + x11=no +fi + +if test x$build_glx = xyes; then + AC_DEFINE(ENABLE_GLX, [1], [Whether GLX support is enabled]) +fi + +AM_CONDITIONAL(HAVE_X11, test x$x11 = xyes) + +PKG_CHECK_MODULES(GL, [gl], [gl=yes], [gl=no]) +PKG_CHECK_MODULES(EGL, [egl], [egl=yes], [egl=no]) + +GL_REQS="" +AS_IF([test x$gl = xyes], [GL_REQS="$GL_REQS gl"]) +AS_IF([test x$build_egl = xyes && test x$egl = xyes], [GL_REQS="$GL_REQS egl"]) +AC_SUBST(GL_REQS) + +# Variables for the pkg-config file; AC_SUBST does not do `test` substitutions, +# so we need to specify the boolean values here +AS_IF([test x$build_glx = xyes], [epoxy_has_glx=1], [epoxy_has_glx=0]) +AS_IF([test x$build_egl = xyes], [epoxy_has_egl=1], [epoxy_has_egl=0]) +AS_IF([test x$build_wgl = xyes], [epoxy_has_wgl=1], [epoxy_has_wgl=0]) +AC_SUBST(epoxy_has_glx) +AC_SUBST(epoxy_has_egl) +AC_SUBST(epoxy_has_wgl) + +AC_CONFIG_FILES([ + epoxy.pc + Makefile + include/epoxy/Makefile + src/Makefile + test/Makefile +]) +AC_OUTPUT + +echo " EGL: $build_egl" +echo " GLX: $build_glx" +echo " WGL: $build_wgl" +echo " PYTHON: $PYTHON" diff --git a/cross/fedora-mingw64.txt b/cross/fedora-mingw64.txt new file mode 100644 index 0000000..7c0eda7 --- /dev/null +++ b/cross/fedora-mingw64.txt @@ -0,0 +1,18 @@ +[binaries] +c = '/usr/bin/x86_64-w64-mingw32-gcc' +cpp = '/usr/bin/x86_64-w64-mingw32-cpp' +ar = '/usr/bin/x86_64-w64-mingw32-ar' +strip = '/usr/bin/x86_64-w64-mingw32-strip' +pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config' +exe_wrapper = 'wine' + +[properties] +root = '/usr/x86_64-w64-mingw32/sys-root/mingw' +c_args = [ '-pipe', '-Wp,-D_FORTIFY_SOURCE=2', '-fexceptions', '--param=ssp-buffer-size=4', '-I/usr/x86_64-w64-mingw32/sys-root/mingw/include' ] +c_link_args = [ '-L/usr/x86_64-w64-mingw32/sys-root/mingw/lib' ] + +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000..0f3c00d --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,241 @@ +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = @PACKAGE_NAME@ +PROJECT_NUMBER = @PACKAGE_VERSION@ +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = YES +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@top_srcdir@/include" "@top_builddir@/include" + +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = "newin{2}=\xrefitem since_\1_\2 \"Since @PACKAGE_NAME@ \1.\2\" \"New API in @PACKAGE_NAME@ \1.\2\"" +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 + +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = NO +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_IN_BODY_DOCS = YES +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = YES +SORT_GROUP_NAMES = YES +SORT_BY_SCOPE_NAME = YES +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = NO +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 2 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = NO +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = + +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = doc/doxygen.log + +INPUT = "@top_srcdir@/include/epoxy" "@top_srcdir@/src" +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = "*.h" "*.c" +RECURSIVE = NO +EXCLUDE = "@top_srcdir@/src/gen_dispatch.py" +EXCLUDE_SYMLINKS = YES +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = _* GLAPI* KHRONOS_* APIENTRY* GLX* wgl* EPOXY_CALLSPEC EPOXY_BEGIN_DECLS EPOXY_END_DECLS +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = NO + +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = "epoxy" + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 1 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = NO +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = + +GENERATE_LATEX = NO +GENERATE_RTF = NO +GENERATE_MAN = NO +GENERATE_XML = NO +GENERATE_DOCBOOK = NO +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_PATH = "@top_srcdir@/include" \ + "@top_builddir@/include" +INCLUDE_FILE_PATTERNS = *.h +PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ + "EPOXY_BEGIN_DECLS=" \ + "EPOXY_END_DECLS=" \ + "EPOXY_PUBLIC=" +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +ALLEXTERNALS = NO +EXTERNAL_GROUPS = NO +EXTERNAL_PAGES = NO + +HAVE_DOT = @HAVE_DOT@ +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Sans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = NO +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = YES +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..ef886da --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,25 @@ +doxyfile_conf = configuration_data() +doxyfile_conf.set('PACKAGE_NAME', meson.project_name()) +doxyfile_conf.set('PACKAGE_VERSION', meson.project_version()) +doxyfile_conf.set('top_srcdir', meson.source_root()) +doxyfile_conf.set('top_builddir', meson.build_root()) + +if find_program('dot', required: false).found() + doxyfile_conf.set('HAVE_DOT', 'YES') +else + doxyfile_conf.set('HAVE_DOT', 'NO') +endif + +doxyfile = configure_file(input: 'Doxyfile.in', + output: 'Doxyfile', + configuration: doxyfile_conf, + install: false) + +docdir = join_paths(epoxy_datadir, 'doc') + +html_target = custom_target('epoxy-docs', + input: [ doxyfile ], + output: [ 'html' ], + command: [ doxygen, doxyfile ], + install: true, + install_dir: join_paths(docdir, 'epoxy')) diff --git a/epoxy.pc.in b/epoxy.pc.in new file mode 100644 index 0000000..cdda8d9 --- /dev/null +++ b/epoxy.pc.in @@ -0,0 +1,16 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +epoxy_has_glx=@epoxy_has_glx@ +epoxy_has_egl=@epoxy_has_egl@ +epoxy_has_wgl=@epoxy_has_wgl@ + +Name: epoxy +Description: epoxy GL dispatch Library +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lepoxy +Libs.private: @DLOPEN_LIBS@ +Requires.private: @GL_REQS@ diff --git a/include/epoxy/Makefile.am b/include/epoxy/Makefile.am new file mode 100644 index 0000000..494c96e --- /dev/null +++ b/include/epoxy/Makefile.am @@ -0,0 +1,42 @@ +# 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. + +epoxyincludedir = $(includedir)/epoxy + +epoxyinclude_HEADERS = \ + common.h \ + gl.h \ + $(EGL_INCLUDES) \ + $(GLX_INCLUDES) \ + $(WGL_INCLUDES) \ + $() + +if BUILD_EGL +EGL_INCLUDES = egl.h +endif + +if BUILD_GLX +GLX_INCLUDES = glx.h +endif + +if BUILD_WGL +WGL_INCLUDES = wgl.h +endif diff --git a/include/epoxy/common.h b/include/epoxy/common.h new file mode 100644 index 0000000..cf58ab2 --- /dev/null +++ b/include/epoxy/common.h @@ -0,0 +1,63 @@ +/* + * Copyright 2017 Emmanuele Bassi + * + * 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 common.h + * + * A common header file, used to define macros and shared symbols. + */ + +#ifndef EPOXY_COMMON_H +#define EPOXY_COMMON_H + +#ifdef __cplusplus +# define EPOXY_BEGIN_DECLS extern "C" { +# define EPOXY_END_DECLS } +#else +# define EPOXY_BEGIN_DECLS +# define EPOXY_END_DECLS +#endif + +#ifndef EPOXY_PUBLIC +# if defined(_MSC_VER) +# define EPOXY_PUBLIC __declspec(dllimport) extern +# else +# define EPOXY_PUBLIC extern +# endif +#endif + +#if defined(_MSC_VER) && !defined(__bool_true_false_are_defined) && (_MSC_VER < 1800) +typedef unsigned char bool; +# define false 0 +# define true 1 +#else +# include <stdbool.h> +#endif + +EPOXY_BEGIN_DECLS + +EPOXY_PUBLIC bool epoxy_extension_in_string(const char *extension_list, + const char *ext); + +EPOXY_END_DECLS + +#endif /* EPOXY_COMMON_H */ diff --git a/include/epoxy/egl.h b/include/epoxy/egl.h new file mode 100644 index 0000000..b5f8ee0 --- /dev/null +++ b/include/epoxy/egl.h @@ -0,0 +1,54 @@ +/* + * 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 egl.h + * + * Provides an implementation of an EGL dispatch layer using global + * function pointers + * + * You should include `<epoxy/egl.h>` instead of `<EGL/egl.h>`. + */ + +#ifndef EPOXY_EGL_H +#define EPOXY_EGL_H + +#include "epoxy/common.h" + +#if defined(__egl_h_) || defined(__eglext_h_) +#error epoxy/egl.h must be included before (or in place of) GL/egl.h +#else +#define __egl_h_ +#define __eglext_h_ +#endif + +EPOXY_BEGIN_DECLS + +#include "epoxy/egl_generated.h" + +EPOXY_PUBLIC bool epoxy_has_egl_extension(EGLDisplay dpy, const char *extension); +EPOXY_PUBLIC int epoxy_egl_version(EGLDisplay dpy); +EPOXY_PUBLIC bool epoxy_has_egl(void); + +EPOXY_END_DECLS + +#endif /* EPOXY_EGL_H */ diff --git a/include/epoxy/gl.h b/include/epoxy/gl.h new file mode 100644 index 0000000..1fef4ba --- /dev/null +++ b/include/epoxy/gl.h @@ -0,0 +1,112 @@ +/* + * 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 either + * global function pointers or a hidden vtable. + * + * You should include `<epoxy/gl.h>` instead of `<GL/gl.h>` and `<GL/glext.h>`. + */ + +#ifndef EPOXY_GL_H +#define EPOXY_GL_H + +#include "epoxy/common.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 + +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +#define KHRONOS_APIATTRIBUTES + +#ifndef _WIN32 +/* APIENTRY and GLAPIENTRY are not used on Linux or Mac. */ +#define APIENTRY +#define GLAPIENTRY +#define EPOXY_CALLSPEC +#define GLAPI +#define KHRONOS_APIENTRY +#define KHRONOS_APICALL + +#else +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY APIENTRY +#endif + +#ifndef EPOXY_CALLSPEC +#define EPOXY_CALLSPEC __stdcall +#endif + +#ifndef GLAPI +#define GLAPI extern +#endif + +#define KHRONOS_APIENTRY __stdcall +#define KHRONOS_APICALL __declspec(dllimport) __stdcall + +#endif /* _WIN32 */ + +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +#ifndef GLAPIENTRYP +#define GLAPIENTRYP GLAPIENTRY * +#endif + +EPOXY_BEGIN_DECLS + +#include "epoxy/gl_generated.h" + +EPOXY_PUBLIC bool epoxy_has_gl_extension(const char *extension); +EPOXY_PUBLIC bool epoxy_is_desktop_gl(void); +EPOXY_PUBLIC int epoxy_gl_version(void); +EPOXY_PUBLIC int epoxy_glsl_version(void); + +/* + * the type of the stub function that the failure handler must return; + * this function will be called on subsequent calls to the same bogus + * function name + */ +typedef void (*epoxy_resolver_stub_t)(void); + +/* the type of the failure handler itself */ +typedef epoxy_resolver_stub_t +(*epoxy_resolver_failure_handler_t)(const char *name); + +EPOXY_PUBLIC epoxy_resolver_failure_handler_t +epoxy_set_resolver_failure_handler(epoxy_resolver_failure_handler_t handler); + +EPOXY_END_DECLS + +#endif /* EPOXY_GL_H */ diff --git a/include/epoxy/glx.h b/include/epoxy/glx.h new file mode 100644 index 0000000..8517063 --- /dev/null +++ b/include/epoxy/glx.h @@ -0,0 +1,57 @@ +/* + * 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 global + * function pointers. + * + * You should include `<epoxy/glx.h>` instead of `<GL/glx.h>`. + */ + +#ifndef EPOXY_GLX_H +#define EPOXY_GLX_H + +#include <epoxy/gl.h> +#include <X11/Xlib.h> +#include <X11/Xutil.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 GLX_H +#define __glx_h__ +#define __glxext_h_ +#endif + +EPOXY_BEGIN_DECLS + +#include "epoxy/glx_generated.h" + +EPOXY_PUBLIC bool epoxy_has_glx_extension(Display *dpy, int screen, const char *extension); +EPOXY_PUBLIC int epoxy_glx_version(Display *dpy, int screen); +EPOXY_PUBLIC bool epoxy_has_glx(Display *dpy); + +EPOXY_END_DECLS + +#endif /* EPOXY_GLX_H */ diff --git a/include/epoxy/meson.build b/include/epoxy/meson.build new file mode 100644 index 0000000..65f83be --- /dev/null +++ b/include/epoxy/meson.build @@ -0,0 +1,44 @@ +headers = [ 'common.h' ] + +# GL is always generated +generated_headers = [ [ 'gl.h', 'gl_generated.h', gl_registry ] ] + +if build_egl + generated_headers += [ [ 'egl.h', 'egl_generated.h', egl_registry ] ] +endif + +if build_glx + generated_headers += [ [ 'glx.h', 'glx_generated.h', glx_registry ] ] +endif + +if build_wgl + generated_headers += [ [ 'wgl.h', 'wgl_generated.h', wgl_registry ] ] +endif + +gen_headers = [] + +foreach g: generated_headers + header = g[0] + gen_header = g[1] + registry = g[2] + generated = custom_target(gen_header, + input: registry, + output: [ gen_header ], + command: [ + python, + gen_dispatch_py, + '--header', + '--no-source', + '--outputdir=@OUTDIR@', + '@INPUT@', + ], + install: true, + install_dir: join_paths(epoxy_includedir, 'epoxy')) + + gen_headers += [ generated ] + headers += [ header ] +endforeach + +epoxy_headers = files(headers) + gen_headers + +install_headers(headers, subdir: 'epoxy') diff --git a/include/epoxy/wgl.h b/include/epoxy/wgl.h new file mode 100644 index 0000000..7c85dec --- /dev/null +++ b/include/epoxy/wgl.h @@ -0,0 +1,61 @@ +/* + * 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 wgl.h + * + * Provides an implementation of a WGL dispatch layer using a hidden + * vtable. + */ + +#ifndef EPOXY_WGL_H +#define EPOXY_WGL_H + +#include <windows.h> + +#include "epoxy/common.h" + +#undef wglUseFontBitmaps +#undef wglUseFontOutlines + +#if defined(__wglxext_h_) +#error epoxy/wgl.h must be included before (or in place of) wgl.h +#else +#define __wglxext_h_ +#endif + +#ifdef UNICODE +#define wglUseFontBitmaps wglUseFontBitmapsW +#else +#define wglUseFontBitmaps wglUseFontBitmapsA +#endif + +EPOXY_BEGIN_DECLS + +#include "epoxy/wgl_generated.h" + +EPOXY_PUBLIC bool epoxy_has_wgl_extension(HDC hdc, const char *extension); +EPOXY_PUBLIC void epoxy_handle_external_wglMakeCurrent(void); + +EPOXY_END_DECLS + +#endif /* EPOXY_WGL_H */ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..91d9792 --- /dev/null +++ b/meson.build @@ -0,0 +1,232 @@ +project('libepoxy', 'c', version: '1.5.4', + default_options: [ + 'buildtype=debugoptimized', + 'c_std=gnu99', + 'warning_level=1', + ], + license: 'MIT', + meson_version: '>= 0.47.0') + +epoxy_version = meson.project_version().split('.') +epoxy_major_version = epoxy_version[0].to_int() +epoxy_minor_version = epoxy_version[1].to_int() +epoxy_micro_version = epoxy_version[2].to_int() + +epoxy_prefix = get_option('prefix') +epoxy_libdir = join_paths(epoxy_prefix, get_option('libdir')) +epoxy_datadir = join_paths(epoxy_prefix, get_option('datadir')) +epoxy_includedir = join_paths(epoxy_prefix, get_option('includedir')) + +cc = meson.get_compiler('c') +host_system = host_machine.system() + +conf = configuration_data() +conf.set_quoted('PACKAGE_NAME', meson.project_name()) +conf.set_quoted('PACKAGE_VERSION', meson.project_version()) +conf.set_quoted('PACKAGE_STRING', '@0@-@1@'.format(meson.project_name(), meson.project_version())) +conf.set_quoted('PACKAGE_DATADIR', join_paths(get_option('prefix'), get_option('datadir'))) +conf.set_quoted('PACKAGE_LIBDIR', join_paths(get_option('prefix'), get_option('libdir'))) +conf.set_quoted('PACKAGE_LOCALEDIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale')) +conf.set_quoted('PACKAGE_LIBEXECDIR', join_paths(get_option('prefix'), get_option('libexecdir'))) +conf.set('HAVE_KHRPLATFORM_H', cc.has_header('KHR/khrplatform.h')) + +# GLX can be used on different platforms, so we expose a +# configure time switch to enable or disable it; in case +# the "auto" default value is set, we only enable GLX +# support on Linux and Unix +enable_glx = get_option('glx') +if enable_glx == 'auto' + build_glx = not ['windows', 'darwin', 'android', 'haiku'].contains(host_system) +else + build_glx = enable_glx == 'yes' +endif + +enable_egl = get_option('egl') +if enable_egl == 'auto' + build_egl = not ['windows', 'darwin'].contains(host_system) +else + build_egl = enable_egl == 'yes' +endif + +enable_x11 = get_option('x11') +if not enable_x11 + if enable_glx == 'yes' + error('GLX support is explicitly enabled, but X11 was disabled') + endif + build_glx = false +endif + +# The remaining platform specific API for GL/GLES are enabled +# depending on the platform we're building for +if host_system == 'windows' + build_wgl = true + has_znow = true +elif host_system == 'darwin' + build_wgl = false + has_znow = false +else + build_wgl = false + has_znow = true +endif + +conf.set10('ENABLE_GLX', build_glx) +conf.set10('ENABLE_EGL', build_egl) +conf.set10('ENABLE_X11', enable_x11) + +# Compiler flags, taken from the Xorg macros +if cc.get_id() == 'msvc' + # Compiler options taken from msvc_recommended_pragmas.h + # in GLib, based on _Win32_Programming_ by Rector and Newcomer + test_cflags = [ + '-we4002', # too many actual parameters for macro + '-we4003', # not enough actual parameters for macro + '-w14010', # single-line comment contains line-continuation character + '-we4013', # 'function' undefined; assuming extern returning int + '-w14016', # no function return type; using int as default + '-we4020', # too many actual parameters + '-we4021', # too few actual parameters + '-we4027', # function declared without formal parameter list + '-we4029', # declared formal parameter list different from definition + '-we4033', # 'function' must return a value + '-we4035', # 'function' : no return value + '-we4045', # array bounds overflow + '-we4047', # different levels of indirection + '-we4049', # terminating line number emission + '-we4053', # an expression of type void was used as an operand + '-we4071', # no function prototype given + '-we4819', # the file contains a character that cannot be represented in the current code page + ] +elif cc.get_id() == 'gcc' or cc.get_id() == 'clang' + test_cflags = [ + '-Wpointer-arith', + '-Wmissing-declarations', + '-Wformat=2', + '-Wstrict-prototypes', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wbad-function-cast', + '-Wold-style-definition', + '-Wdeclaration-after-statement', + '-Wunused', + '-Wuninitialized', + '-Wshadow', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wlogical-op', + '-Werror=implicit', + '-Werror=nonnull', + '-Werror=init-self', + '-Werror=main', + '-Werror=missing-braces', + '-Werror=sequence-point', + '-Werror=return-type', + '-Werror=trigraphs', + '-Werror=array-bounds', + '-Werror=write-strings', + '-Werror=address', + '-Werror=int-to-pointer-cast', + '-Werror=pointer-to-int-cast', + '-fno-strict-aliasing', + '-Wno-int-conversion', + ] +else + test_cflags = [] +endif + +common_cflags = cc.get_supported_arguments(test_cflags) + +libtype = get_option('default_library') + +# Visibility compiler flags; we only use this for shared libraries +visibility_cflags = [] +if libtype == 'shared' + if host_system == 'windows' + conf.set('DLL_EXPORT', true) + conf.set('EPOXY_PUBLIC', '__declspec(dllexport) extern') + if cc.get_id() != 'msvc' + visibility_cflags += [ '-fvisibility=hidden' ] + endif + else + conf.set('EPOXY_PUBLIC', '__attribute__((visibility("default"))) extern') + visibility_cflags += [ '-fvisibility=hidden' ] + endif +endif + +# The inline keyword is available only for C++ in MSVC. +# So we need to use Microsoft specific __inline. +if host_system == 'windows' + if cc.get_id() == 'msvc' + conf.set('inline', '__inline') + endif +endif + +# Dependencies +dl_dep = cc.find_library('dl', required: false) +gl_dep = dependency('gl', required: false) +egl_dep = dependency('egl', required: false) + +# Optional dependencies for tests +x11_dep = dependency('x11', required: false) + +# GLES v2 and v1 may have pkg-config files, courtesy of downstream +# packagers; let's check those first, and fall back to find_library() +# if we fail +gles2_dep = dependency('glesv2', required: false) +if not gles2_dep.found() + gles2_dep = cc.find_library('libGLESv2', required: false) +endif + +gles1_dep = dependency('glesv1_cm', required: false) +if not gles1_dep.found() + gles1_dep = cc.find_library('libGLESv1_CM', required: false) +endif + +# On windows, the DLL has to have all of its functions +# resolved at link time, so we have to link directly against +# opengl32. But that's the only GL provider, anyway. +if host_system == 'windows' + opengl32_dep = cc.find_library('opengl32', required: true) + + # When building against static libraries, we need to control + # the order of the dependencies, and gdi32 provides symbols + # needed when using opengl32, like SetPixelFormat and + # ChoosePixelFormat. This is mostly a workaround for older + # versions of Meson. + gdi32_dep = cc.find_library('gdi32', required: true) +endif + +# Python +python = import('python3').find_python() +if not python.found() + python = find_program('python', required: true) +endif + +# Generates the dispatch tables +gen_dispatch_py = files('src/gen_dispatch.py') + +gl_registry = files('registry/gl.xml') +egl_registry = files('registry/egl.xml') +glx_registry = files('registry/glx.xml') +wgl_registry = files('registry/wgl.xml') + +libepoxy_inc = [ + include_directories('include'), + include_directories('src'), +] + +subdir('include/epoxy') +subdir('src') + +if get_option('tests') + subdir('test') +endif + +if get_option('docs') + doxygen = find_program('doxygen', required: false) + if doxygen.found() + subdir('doc') + else + message('Documentation disabled without doxygen') + endif +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..dc30e68 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,21 @@ +option('docs', + type: 'boolean', value: false, + description: 'Enable generating the Epoxy API reference (depends on Doxygen)') +option('glx', + type: 'combo', + choices: [ 'auto', 'yes', 'no' ], + value: 'auto', + description: 'Enable GLX support') +option('egl', + type: 'combo', + choices: [ 'auto', 'yes', 'no' ], + value: 'auto', + description: 'Enable EGL support') +option('x11', + type: 'boolean', + value: true, + description: 'Enable X11 support (GLX or EGL-X11)') +option('tests', + type: 'boolean', + value: true, + description: 'Build the test suite') diff --git a/registry/README.md b/registry/README.md new file mode 100644 index 0000000..a35d9d9 --- /dev/null +++ b/registry/README.md @@ -0,0 +1,13 @@ +## Updating the registry XML + +In order to update the registry XML files and retain the history you cannot +simply download the files the [Khronos website](https://khronos.org/registry/OpenGL/index_gl.php) +and copy them into this directory. You should follow these steps, instead: + + 1. check out the `khronos-registry` branch + 2. download the XML files from the Khronos repository + 3. copy them under the `registry` directory + 4. check the result for consistency and commit it + 5. check out the `master` branch and merge the `khronos-registry` branch + into it with the appropriate commit message + diff --git a/registry/gl.xml b/registry/gl.xml index 00846ff..84fd26b 100644 --- a/registry/gl.xml +++ b/registry/gl.xml @@ -772,6 +772,9 @@ typedef unsigned int GLhandleARB; <enum name="GL_MAX_EXT"/> <enum name="GL_MIN"/> <enum name="GL_MIN_EXT"/> + <enum name="GL_FUNC_ADD"/> + <enum name="GL_FUNC_REVERSE_SUBTRACT"/> + <enum name="GL_FUNC_SUBTRACT"/> </group> <group name="Boolean"> @@ -1060,6 +1063,12 @@ typedef unsigned int GLhandleARB; <enum name="GL_UNSIGNED_INT"/> </group> + <group name="DrawElementsType"> + <enum name="GL_UNSIGNED_BYTE"/> + <enum name="GL_UNSIGNED_SHORT"/> + <enum name="GL_UNSIGNED_INT"/> + </group> + <group name="EnableCap"> <enum name="GL_ALPHA_TEST"/> <enum name="GL_ASYNC_DRAW_PIXELS_SGIX"/> @@ -2088,6 +2097,8 @@ typedef unsigned int GLhandleARB; <enum name="GL_HISTOGRAM_EXT"/> <enum name="GL_PROXY_HISTOGRAM"/> <enum name="GL_PROXY_HISTOGRAM_EXT"/> + <enum name="GL_HISTOGRAM"/> + <enum name="GL_PROXY_HISTOGRAM"/> </group> <group name="IndexPointerType"> @@ -2229,6 +2240,9 @@ typedef unsigned int GLhandleARB; <enum name="GL_MAP_UNSYNCHRONIZED_BIT_EXT"/> <enum name="GL_MAP_WRITE_BIT"/> <enum name="GL_MAP_WRITE_BIT_EXT"/> + <enum name="GL_SPARSE_STORAGE_BIT_ARB"/> + <enum name="GL_LGPU_SEPARATE_STORAGE_BIT_NVX"/> + <enum name="GL_PER_GPU_STORAGE_BIT_NV"/> </group> <group name="MapTarget"> @@ -17737,7 +17751,7 @@ typedef unsigned int GLhandleARB; </command> <command> <proto>void <name>glGetDoublei_v</name></proto> - <param><ptype>GLenum</ptype> <name>target</name></param> + <param group="TypeEnum"><ptype>GLenum</ptype> <name>target</name></param> <param><ptype>GLuint</ptype> <name>index</name></param> <param len="COMPSIZE(target)"><ptype>GLdouble</ptype> *<name>data</name></param> </command> @@ -18057,7 +18071,7 @@ typedef unsigned int GLhandleARB; </command> <command> <proto>void <name>glGetInteger64i_v</name></proto> - <param><ptype>GLenum</ptype> <name>target</name></param> + <param group="TypeEnum"><ptype>GLenum</ptype> <name>target</name></param> <param><ptype>GLuint</ptype> <name>index</name></param> <param len="COMPSIZE(target)"><ptype>GLint64</ptype> *<name>data</name></param> </command> @@ -18082,13 +18096,13 @@ typedef unsigned int GLhandleARB; </command> <command> <proto>void <name>glGetIntegeri_v</name></proto> - <param><ptype>GLenum</ptype> <name>target</name></param> + <param group="TypeEnum"><ptype>GLenum</ptype> <name>target</name></param> <param><ptype>GLuint</ptype> <name>index</name></param> <param len="COMPSIZE(target)"><ptype>GLint</ptype> *<name>data</name></param> </command> <command> <proto>void <name>glGetIntegeri_vEXT</name></proto> - <param><ptype>GLenum</ptype> <name>target</name></param> + <param group="TypeEnum"><ptype>GLenum</ptype> <name>target</name></param> <param><ptype>GLuint</ptype> <name>index</name></param> <param><ptype>GLint</ptype> *<name>data</name></param> </command> @@ -18920,14 +18934,14 @@ typedef unsigned int GLhandleARB; </command> <command> <proto>void <name>glGetPixelTransformParameterfvEXT</name></proto> - <param><ptype>GLenum</ptype> <name>target</name></param> + <param group="TypeEnum"><ptype>GLenum</ptype> <name>target</name></param> <param><ptype>GLenum</ptype> <name>pname</name></param> <param len="COMPSIZE(pname)"><ptype>GLfloat</ptype> *<name>params</name></param> <glx type="vendor" opcode="2051"/> </command> <command> <proto>void <name>glGetPixelTransformParameterivEXT</name></proto> - <param><ptype>GLenum</ptype> <name>target</name></param> + <param group="TypeEnum"><ptype>GLenum</ptype> <name>target</name></param> <param><ptype>GLenum</ptype> <name>pname</name></param> <param len="COMPSIZE(pname)"><ptype>GLint</ptype> *<name>params</name></param> <glx type="vendor" opcode="2052"/> @@ -22356,6 +22370,16 @@ typedef unsigned int GLhandleARB; <alias name="glMultiDrawElementsIndirectCount"/> </command> <command> + <proto>void <name>glMultiDrawElementsIndirectCountARB</name></proto> + <param group="PrimitiveType"><ptype>GLenum</ptype> <name>mode</name></param> + <param group="DrawElementsType"><ptype>GLenum</ptype> <name>type</name></param> + <param>const void *<name>indirect</name></param> + <param><ptype>GLintptr</ptype> <name>drawcount</name></param> + <param><ptype>GLsizei</ptype> <name>maxdrawcount</name></param> + <param><ptype>GLsizei</ptype> <name>stride</name></param> + <alias name="glMultiDrawElementsIndirectCount"/> + </command> + <command> <proto>void <name>glMultiDrawElementsIndirectEXT</name></proto> <param group="PrimitiveType"><ptype>GLenum</ptype> <name>mode</name></param> <param group="DrawElementsType"><ptype>GLenum</ptype> <name>type</name></param> @@ -29034,6 +29058,17 @@ typedef unsigned int GLhandleARB; <param><ptype>GLsizei</ptype> <name>width</name></param> <param><ptype>GLsizei</ptype> <name>height</name></param> <param><ptype>GLsizei</ptype> <name>depth</name></param> + <param><ptype>GLboolean</ptype> <name>fixedSampleLocations</name></param> + <param><ptype>GLuint</ptype> <name>memory</name></param> + <param><ptype>GLuint64</ptype> <name>offset</name></param> + </command> + <command> + <proto>void <name>glTexStorageSparseAMD</name></proto> + <param group="TextureTarget"><ptype>GLenum</ptype> <name>target</name></param> + <param group="InternalFormat"><ptype>GLenum</ptype> <name>internalFormat</name></param> + <param><ptype>GLsizei</ptype> <name>width</name></param> + <param><ptype>GLsizei</ptype> <name>height</name></param> + <param><ptype>GLsizei</ptype> <name>depth</name></param> <param><ptype>GLsizei</ptype> <name>layers</name></param> <param group="TextureStorageMaskAMD"><ptype>GLbitfield</ptype> <name>flags</name></param> </command> diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..73f7435 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,184 @@ +# 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. + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + $() + +AM_CFLAGS = \ + $(CWARNFLAGS) \ + $(VISIBILITY_CFLAGS) \ + $(X11_CFLAGS) \ + $(EGL_CFLAGS) \ + $() + +epoxyincludedir = $(includedir)/epoxy +lib_LTLIBRARIES = libepoxy.la + +epoxyinclude_DATA = \ + $(GENERATED_GL_INCLUDES) \ + $(INSTALL_GLX_INCLUDES) \ + $(INSTALL_EGL_INCLUDES) \ + $(INSTALL_WGL_INCLUDES) \ + $() + +if BUILD_EGL +INSTALL_EGL_INCLUDES = $(GENERATED_EGL_INCLUDES) +endif + +if BUILD_GLX +INSTALL_GLX_INCLUDES = $(GENERATED_GLX_INCLUDES) +endif + +if BUILD_WGL +INSTALL_WGL_INCLUDES = $(GENERATED_WGL_INCLUDES) +endif + +GENERATED_GL_INCLUDES = \ + $(builddir)/../include/epoxy/gl_generated.h \ + $() + +GENERATED_GLX_INCLUDES = \ + $(builddir)/../include/epoxy/glx_generated.h \ + $() + +GENERATED_EGL_INCLUDES = \ + $(builddir)/../include/epoxy/egl_generated.h \ + $() + +GENERATED_WGL_INCLUDES = \ + $(builddir)/../include/epoxy/wgl_generated.h \ + $() + +GENERATED_GL_SOURCE = gl_generated_dispatch.c + +GENERATED_GL = \ + $(GENERATED_GL_SOURCE) \ + $(GENERATED_GL_INCLUDES) \ + $() + +GENERATED_GLX_SOURCE = glx_generated_dispatch.c + +GENERATED_GLX = \ + $(GENERATED_GLX_SOURCE) \ + $(GENERATED_GLX_INCLUDES) \ + $() + +GENERATED_EGL_SOURCE = egl_generated_dispatch.c + +GENERATED_EGL = \ + $(GENERATED_EGL_SOURCE) \ + $(GENERATED_EGL_INCLUDES) \ + $() + +GENERATED_WGL_SOURCE = wgl_generated_dispatch.c + +GENERATED_WGL = \ + $(GENERATED_WGL_SOURCE) \ + $(GENERATED_WGL_INCLUDES) \ + $() + +BUILT_SOURCES = \ + $(GENERATED_GL) \ + $(GENERATED_GLX) \ + $(GENERATED_EGL) \ + $(GENERATED_WGL) \ + $() +CLEANFILES = $(BUILT_SOURCES) + +libepoxy_la_SOURCES = \ + dispatch_common.c \ + dispatch_common.h \ + $(GENERATED_GL) \ + $(BUILD_EGL_CODE) \ + $(BUILD_GLX_CODE) \ + $(BUILD_WGL_CODE) \ + $() + +libepoxy_la_LDFLAGS = \ + -no-undefined \ + -Bsymbolic-functions \ + $() + +libepoxy_la_LIBADD = \ + $(EPOXY_LINK_LIBS) \ + $(DLOPEN_LIBS) \ + $() + +if BUILD_EGL +BUILD_EGL_CODE = \ + $(GENERATED_EGL) \ + dispatch_egl.c \ + $() +endif + +if BUILD_GLX +BUILD_GLX_CODE = \ + $(GENERATED_GLX) \ + dispatch_glx.c \ + $() +endif + +if BUILD_WGL +BUILD_WGL_CODE = \ + $(GENERATED_WGL) \ + dispatch_wgl.c \ + $() +endif + +# These are generated alongside the .c file. +$(GENERATED_GL_INCLUDES): $(GENERATED_GL_SOURCE) +$(GENERATED_GLX_INCLUDES): $(GENERATED_GLX_SOURCE) +$(GENERATED_EGL_INCLUDES): $(GENERATED_EGL_SOURCE) +$(GENERATED_WGL_INCLUDES): $(GENERATED_WGL_SOURCE) + +$(GENERATED_GL_SOURCE): $(srcdir)/gen_dispatch.py $(top_srcdir)/registry/gl.xml + @$(MKDIR_P) $(top_builddir)/include/epoxy + $(AM_V_GEN)$(PYTHON) $(srcdir)/gen_dispatch.py \ + --srcdir $(top_builddir)/src \ + --includedir $(top_builddir)/include/epoxy \ + $(top_srcdir)/registry/gl.xml + +$(GENERATED_GLX_SOURCE): $(srcdir)/gen_dispatch.py $(top_srcdir)/registry/glx.xml + @$(MKDIR_P) $(top_builddir)/include/epoxy + $(AM_V_GEN)$(PYTHON) $(srcdir)/gen_dispatch.py \ + --srcdir $(top_builddir)/src \ + --includedir $(top_builddir)/include/epoxy \ + $(top_srcdir)/registry/glx.xml + +$(GENERATED_EGL_SOURCE): $(srcdir)/gen_dispatch.py $(top_srcdir)/registry/egl.xml + @$(MKDIR_P) $(top_builddir)/include/epoxy + $(AM_V_GEN)$(PYTHON) $(srcdir)/gen_dispatch.py \ + --srcdir $(top_builddir)/src \ + --includedir $(top_builddir)/include/epoxy \ + $(top_srcdir)/registry/egl.xml + +$(GENERATED_WGL_SOURCE): $(srcdir)/gen_dispatch.py $(top_srcdir)/registry/wgl.xml + @$(MKDIR_P) $(top_builddir)/include/epoxy + $(AM_V_GEN)$(PYTHON) $(srcdir)/gen_dispatch.py \ + --srcdir $(top_builddir)/src \ + --includedir $(top_builddir)/include/epoxy \ + $(top_srcdir)/registry/wgl.xml + +EXTRA_DIST = \ + gen_dispatch.py \ + $() diff --git a/src/dispatch_common.c b/src/dispatch_common.c new file mode 100644 index 0000000..b3e4f5f --- /dev/null +++ b/src/dispatch_common.c @@ -0,0 +1,923 @@ +/* + * Copyright © 2013-2014 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. + */ + +/** + * \mainpage Epoxy + * + * \section intro_sec Introduction + * + * 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. They get to read 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. + * + * \section features_sec Features + * + * - Automatically initializes as new GL functions are used. + * - GL 4.6 core and compatibility context support. + * - GLES 1/2/3 context support. + * - Knows about function aliases so (e.g.) `glBufferData()` can be + * used with `GL_ARB_vertex_buffer_object` implementations, along + * with GL 1.5+ implementations. + * - EGL, GLX, and WGL support. + * - Can be mixed with non-epoxy GL usage. + * + * \section using_sec Using Epoxy + * + * Using Epoxy should be as easy as replacing: + * + * ```cpp + * #include <GL/gl.h> + * #include <GL/glx.h> + * #include <GL/glext.h> + * ``` + * + * with: + * + * ```cpp + * #include <epoxy/gl.h> + * #include <epoxy/glx.h> + * ``` + * + * \subsection using_include_sec Headers + * + * Epoxy comes with the following public headers: + * + * - `epoxy/gl.h` - For GL API + * - `epoxy/egl.h` - For EGL API + * - `epoxy/glx.h` - For GLX API + * - `epoxy/wgl.h` - For WGL API + * + * \section links_sec Additional links + * + * The latest version of the Epoxy code is available on [GitHub](https://github.com/anholt/libepoxy). + * + * For bug reports and enhancements, please use the [Issues](https://github.com/anholt/libepoxy/issues) + * link. + * + * The scope of this API reference does not include the documentation for + * OpenGL and OpenGL ES. For more information on those programming interfaces + * please visit: + * + * - [Khronos](https://www.khronos.org/) + * - [OpenGL page on Khronos.org](https://www.khronos.org/opengl/) + * - [OpenGL ES page on Khronos.org](https://www.khronos.org/opengles/) + * - [docs.GL](http://docs.gl/) + */ + +/** + * @file dispatch_common.c + * + * @brief 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> +#ifdef _WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#include <err.h> +#include <pthread.h> +#endif +#include <string.h> +#include <ctype.h> +#include <stdio.h> + +#include "dispatch_common.h" + +#if defined(__APPLE__) +#define GLX_LIB "/opt/X11/lib/libGL.1.dylib" +#define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" +#define GLES1_LIB "libGLESv1_CM.so" +#define GLES2_LIB "libGLESv2.so" +#elif defined(__ANDROID__) +#define GLX_LIB "libGLESv2.so" +#define EGL_LIB "libEGL.so" +#define GLES1_LIB "libGLESv1_CM.so" +#define GLES2_LIB "libGLESv2.so" +#elif defined(_WIN32) +#define EGL_LIB "libEGL.dll" +#define GLES1_LIB "libGLES_CM.dll" +#define GLES2_LIB "libGLESv2.dll" +#define OPENGL_LIB "OPENGL32" +#else +#define GLVND_GLX_LIB "libGLX.so.1" +#define GLX_LIB "libGL.so.1" +#define EGL_LIB "libEGL.so.1" +#define GLES1_LIB "libGLESv1_CM.so.1" +#define GLES2_LIB "libGLESv2.so.2" +#define OPENGL_LIB "libOpenGL.so.0" +#endif + +#ifdef __GNUC__ +#define CONSTRUCT(_func) static void _func (void) __attribute__((constructor)); +#define DESTRUCT(_func) static void _func (void) __attribute__((destructor)); +#elif defined (_MSC_VER) && (_MSC_VER >= 1500) +#define CONSTRUCT(_func) \ + static void _func(void); \ + static int _func ## _wrapper(void) { _func(); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper; + +#define DESTRUCT(_func) \ + static void _func(void); \ + static int _func ## _constructor(void) { atexit (_func); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; + +#else +#error "You will need constructor support for your compiler" +#endif + +struct api { +#ifndef _WIN32 + /* + * Locking for making sure we don't double-dlopen(). + */ + pthread_mutex_t mutex; +#endif + + /* + * dlopen() return value for the GLX API. This is libGLX.so.1 if the + * runtime is glvnd-enabled, else libGL.so.1 + */ + void *glx_handle; + + /* + * dlopen() return value for the desktop GL library. + * + * On Windows this is OPENGL32. On OSX this is classic libGL. On Linux + * this is either libOpenGL (if the runtime is glvnd-enabled) or + * classic libGL.so.1 + */ + void *gl_handle; + + /* dlopen() return value for libEGL.so.1 */ + void *egl_handle; + + /* dlopen() return value for libGLESv1_CM.so.1 */ + void *gles1_handle; + + /* dlopen() return value for libGLESv2.so.2 */ + void *gles2_handle; + + /* + * This value gets incremented when any thread is in + * glBegin()/glEnd() called through epoxy. + * + * We're not guaranteed to be called through our wrapper, so the + * conservative paths also try to handle the failure cases they'll + * see if begin_count didn't reflect reality. It's also a bit of + * a bug that the conservative paths might return success because + * some other thread was in epoxy glBegin/glEnd while our thread + * is trying to resolve, but given that it's basically just for + * informative error messages, we shouldn't need to care. + */ + long begin_count; +}; + +static struct api api = { +#ifndef _WIN32 + .mutex = PTHREAD_MUTEX_INITIALIZER, +#else + 0, +#endif +}; + +static bool library_initialized; + +static bool epoxy_current_context_is_glx(void); + +#if PLATFORM_HAS_EGL +static EGLenum +epoxy_egl_get_current_gl_context_api(void); +#endif + +CONSTRUCT (library_init) + +static void +library_init(void) +{ + library_initialized = true; +} + +static bool +get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail, bool load) +{ + if (*handle) + return true; + + if (!library_initialized) { + fputs("Attempting to dlopen() while in the dynamic linker.\n", stderr); + abort(); + } + +#ifdef _WIN32 + *handle = LoadLibraryA(lib_name); +#else + pthread_mutex_lock(&api.mutex); + if (!*handle) { + int flags = RTLD_LAZY | RTLD_LOCAL; + if (!load) + flags |= RTLD_NOLOAD; + + *handle = dlopen(lib_name, flags); + if (!*handle) { + if (exit_on_fail) { + fprintf(stderr, "Couldn't open %s: %s\n", lib_name, dlerror()); + abort(); + } else { + (void)dlerror(); + } + } + } + pthread_mutex_unlock(&api.mutex); +#endif + + return *handle != NULL; +} + +static void * +do_dlsym(void **handle, const char *name, bool exit_on_fail) +{ + void *result; + const char *error = ""; + +#ifdef _WIN32 + result = GetProcAddress(*handle, name); +#else + result = dlsym(*handle, name); + if (!result) + error = dlerror(); +#endif + if (!result && exit_on_fail) { + fprintf(stderr, "%s() not found: %s\n", name, error); + abort(); + } + + return result; +} + +/** + * @brief Checks whether we're using OpenGL or OpenGL ES + * + * @return `true` if we're using OpenGL + */ +bool +epoxy_is_desktop_gl(void) +{ + const char *es_prefix = "OpenGL ES"; + const char *version; + +#if PLATFORM_HAS_EGL + /* PowerVR's OpenGL ES implementation (and perhaps other) don't + * comply with the standard, which states that + * "glGetString(GL_VERSION)" should return a string starting with + * "OpenGL ES". Therefore, to distinguish desktop OpenGL from + * OpenGL ES, we must also check the context type through EGL (we + * can do that as PowerVR is only usable through EGL). + */ + if (!epoxy_current_context_is_glx()) { + switch (epoxy_egl_get_current_gl_context_api()) { + case EGL_OPENGL_API: return true; + case EGL_OPENGL_ES_API: return false; + case EGL_NONE: + default: break; + } + } +#endif + + if (api.begin_count) + return true; + + version = (const char *)glGetString(GL_VERSION); + + /* If we didn't get a version back, there are only two things that + * could have happened: either malloc failure (which basically + * doesn't exist), or we were called within a glBegin()/glEnd(). + * Assume the second, which only exists for desktop GL. + */ + if (!version) + return true; + + return strncmp(es_prefix, version, strlen(es_prefix)); +} + +static int +epoxy_internal_gl_version(GLenum version_string, int error_version) +{ + const char *version = (const char *)glGetString(version_string); + GLint major, minor, factor; + int scanf_count; + + if (!version) + return error_version; + + /* skip to version number */ + while (!isdigit(*version) && *version != '\0') + version++; + + /* Interpret version number */ + scanf_count = sscanf(version, "%i.%i", &major, &minor); + if (scanf_count != 2) { + fprintf(stderr, "Unable to interpret GL_VERSION string: %s\n", + version); + abort(); + } + + if (minor >= 10) + factor = 100; + else + factor = 10; + + return factor * major + minor; +} + +/** + * @brief Returns the version of OpenGL we are using + * + * The version is encoded as: + * + * ``` + * + * version = major * 10 + minor + * + * ``` + * + * So it can be easily used for version comparisons. + * + * @return The encoded version of OpenGL we are using + */ +int +epoxy_gl_version(void) +{ + return epoxy_internal_gl_version(GL_VERSION, 0); +} + +int +epoxy_conservative_gl_version(void) +{ + if (api.begin_count) + return 100; + + return epoxy_internal_gl_version(GL_VERSION, 100); +} + +/** + * @brief Returns the version of the GL Shading Language we are using + * + * The version is encoded as: + * + * ``` + * + * version = major * 100 + minor + * + * ``` + * + * So it can be easily used for version comparisons. + * + * @return The encoded version of the GL Shading Language we are using + */ +int +epoxy_glsl_version(void) +{ + if (epoxy_gl_version() >= 20 || + epoxy_has_gl_extension ("GL_ARB_shading_language_100")) + return epoxy_internal_gl_version(GL_SHADING_LANGUAGE_VERSION, 0); + + return 0; +} + +/** + * @brief Checks for the presence of an extension in an OpenGL extension string + * + * @param extension_list The string containing the list of extensions to check + * @param ext The name of the GL extension + * @return `true` if the extension is available' + * + * @note If you are looking to check whether a normal GL, EGL or GLX extension + * is supported by the client, this probably isn't the function you want. + * + * Some parts of the spec for OpenGL and friends will return an OpenGL formatted + * extension string that is separate from the usual extension strings for the + * spec. This function provides easy parsing of those strings. + * + * @see epoxy_has_gl_extension() + * @see epoxy_has_egl_extension() + * @see epoxy_has_glx_extension() + */ +bool +epoxy_extension_in_string(const char *extension_list, const char *ext) +{ + const char *ptr = extension_list; + int len; + + if (!ext) + return false; + + len = strlen(ext); + + if (extension_list == NULL || *extension_list == '\0') + return false; + + /* Make sure that don't just find an extension with our name as a prefix. */ + while (true) { + ptr = strstr(ptr, ext); + if (!ptr) + return false; + + if (ptr[len] == ' ' || ptr[len] == 0) + return true; + ptr += len; + } +} + +static bool +epoxy_internal_has_gl_extension(const char *ext, bool invalid_op_mode) +{ + if (epoxy_gl_version() < 30) { + const char *exts = (const char *)glGetString(GL_EXTENSIONS); + if (!exts) + return invalid_op_mode; + return epoxy_extension_in_string(exts, ext); + } else { + int num_extensions; + int i; + + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + if (num_extensions == 0) + return invalid_op_mode; + + for (i = 0; i < num_extensions; i++) { + const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i); + if (!gl_ext) + return false; + if (strcmp(ext, gl_ext) == 0) + return true; + } + + return false; + } +} + +bool +epoxy_load_glx(bool exit_if_fails, bool load) +{ +#if PLATFORM_HAS_GLX +# ifdef GLVND_GLX_LIB + /* prefer the glvnd library if it exists */ + if (!api.glx_handle) + get_dlopen_handle(&api.glx_handle, GLVND_GLX_LIB, false, load); +# endif + if (!api.glx_handle) + get_dlopen_handle(&api.glx_handle, GLX_LIB, exit_if_fails, load); +#endif + return api.glx_handle != NULL; +} + +void * +epoxy_conservative_glx_dlsym(const char *name, bool exit_if_fails) +{ +#if PLATFORM_HAS_GLX + if (epoxy_load_glx(exit_if_fails, exit_if_fails)) + return do_dlsym(&api.glx_handle, name, exit_if_fails); +#endif + return NULL; +} + +/** + * Tests whether the currently bound context is EGL or GLX, trying to + * avoid loading libraries unless necessary. + */ +static bool +epoxy_current_context_is_glx(void) +{ +#if !PLATFORM_HAS_GLX + return false; +#else + void *sym; + + sym = epoxy_conservative_glx_dlsym("glXGetCurrentContext", false); + if (sym) { + if (glXGetCurrentContext()) + return true; + } else { + (void)dlerror(); + } + +#if PLATFORM_HAS_EGL + sym = epoxy_conservative_egl_dlsym("eglGetCurrentContext", false); + if (sym) { + if (epoxy_egl_get_current_gl_context_api() != EGL_NONE) + return false; + } else { + (void)dlerror(); + } +#endif /* PLATFORM_HAS_EGL */ + + return false; +#endif /* PLATFORM_HAS_GLX */ +} + +/** + * @brief Returns true if the given GL extension is supported in the current context. + * + * @param ext The name of the GL extension + * @return `true` if the extension is available + * + * @note that this function can't be called from within `glBegin()` and `glEnd()`. + * + * @see epoxy_has_egl_extension() + * @see epoxy_has_glx_extension() + */ +bool +epoxy_has_gl_extension(const char *ext) +{ + return epoxy_internal_has_gl_extension(ext, false); +} + +bool +epoxy_conservative_has_gl_extension(const char *ext) +{ + if (api.begin_count) + return true; + + return epoxy_internal_has_gl_extension(ext, true); +} + +bool +epoxy_load_egl(bool exit_if_fails, bool load) +{ +#if PLATFORM_HAS_EGL + return get_dlopen_handle(&api.egl_handle, EGL_LIB, exit_if_fails, load); +#else + return false; +#endif +} + +void * +epoxy_conservative_egl_dlsym(const char *name, bool exit_if_fails) +{ +#if PLATFORM_HAS_EGL + if (epoxy_load_egl(exit_if_fails, exit_if_fails)) + return do_dlsym(&api.egl_handle, name, exit_if_fails); +#endif + return NULL; +} + +void * +epoxy_egl_dlsym(const char *name) +{ + return epoxy_conservative_egl_dlsym(name, true); +} + +void * +epoxy_glx_dlsym(const char *name) +{ + return epoxy_conservative_glx_dlsym(name, true); +} + +static void +epoxy_load_gl(void) +{ + if (api.gl_handle) + return; + +#if defined(_WIN32) || defined(__APPLE__) + get_dlopen_handle(&api.gl_handle, OPENGL_LIB, true, true); +#else + +#if defined(OPENGL_LIB) + if (!api.gl_handle) + get_dlopen_handle(&api.gl_handle, OPENGL_LIB, false, true); +#endif + + get_dlopen_handle(&api.glx_handle, GLX_LIB, true, true); + api.gl_handle = api.glx_handle; +#endif +} + +void * +epoxy_gl_dlsym(const char *name) +{ + epoxy_load_gl(); + + return do_dlsym(&api.gl_handle, name, true); +} + +void * +epoxy_gles1_dlsym(const char *name) +{ + if (epoxy_current_context_is_glx()) { + return epoxy_get_proc_address(name); + } else { + get_dlopen_handle(&api.gles1_handle, GLES1_LIB, true, true); + return do_dlsym(&api.gles1_handle, name, true); + } +} + +void * +epoxy_gles2_dlsym(const char *name) +{ + if (epoxy_current_context_is_glx()) { + return epoxy_get_proc_address(name); + } else { + get_dlopen_handle(&api.gles2_handle, GLES2_LIB, true, true); + return do_dlsym(&api.gles2_handle, name, true); + } +} + +/** + * Does the appropriate dlsym() or eglGetProcAddress() for GLES3 + * functions. + * + * Mesa interpreted GLES as intending that the GLES3 functions were + * available only through eglGetProcAddress() and not dlsym(), while + * ARM's Mali drivers interpreted GLES as intending that GLES3 + * functions were available only through dlsym() and not + * eglGetProcAddress(). Thanks, Khronos. + */ +void * +epoxy_gles3_dlsym(const char *name) +{ + if (epoxy_current_context_is_glx()) { + return epoxy_get_proc_address(name); + } else { + if (get_dlopen_handle(&api.gles2_handle, GLES2_LIB, false, true)) { + void *func = do_dlsym(&api.gles2_handle, name, false); + + if (func) + return func; + } + + return epoxy_get_proc_address(name); + } +} + +/** + * Performs either the dlsym or glXGetProcAddress()-equivalent for + * core functions in desktop GL. + */ +void * +epoxy_get_core_proc_address(const char *name, int core_version) +{ +#ifdef _WIN32 + int core_symbol_support = 11; +#elif defined(__ANDROID__) + /** + * All symbols must be resolved through eglGetProcAddress + * on Android + */ + int core_symbol_support = 0; +#else + int core_symbol_support = 12; +#endif + + if (core_version <= core_symbol_support) { + return epoxy_gl_dlsym(name); + } else { + return epoxy_get_proc_address(name); + } +} + +#if PLATFORM_HAS_EGL +static EGLenum +epoxy_egl_get_current_gl_context_api(void) +{ + EGLint curapi; + + if (eglQueryContext(eglGetCurrentDisplay(), eglGetCurrentContext(), + EGL_CONTEXT_CLIENT_TYPE, &curapi) == EGL_FALSE) { + (void)eglGetError(); + return EGL_NONE; + } + + return (EGLenum) curapi; +} +#endif /* PLATFORM_HAS_EGL */ + +/** + * Performs the dlsym() for the core GL 1.0 functions that we use for + * determining version and extension support for deciding on dlsym + * versus glXGetProcAddress() for all other functions. + * + * This needs to succeed on implementations without GLX (since + * glGetString() and glGetIntegerv() are both in GLES1/2 as well, and + * at call time we don't know for sure what API they're trying to use + * without inspecting contexts ourselves). + */ +void * +epoxy_get_bootstrap_proc_address(const char *name) +{ + /* If we already have a library that links to libglapi loaded, + * use that. + */ +#if PLATFORM_HAS_GLX + if (api.glx_handle && glXGetCurrentContext()) + return epoxy_gl_dlsym(name); +#endif + + /* If epoxy hasn't loaded any API-specific library yet, try to + * figure out what API the context is using and use that library, + * since future calls will also use that API (this prevents a + * non-X11 ES2 context from loading a bunch of X11 junk). + */ +#if PLATFORM_HAS_EGL + get_dlopen_handle(&api.egl_handle, EGL_LIB, false, true); + if (api.egl_handle) { + int version = 0; + switch (epoxy_egl_get_current_gl_context_api()) { + case EGL_OPENGL_API: + return epoxy_gl_dlsym(name); + case EGL_OPENGL_ES_API: + if (eglQueryContext(eglGetCurrentDisplay(), + eglGetCurrentContext(), + EGL_CONTEXT_CLIENT_VERSION, + &version)) { + if (version >= 2) + return epoxy_gles2_dlsym(name); + else + return epoxy_gles1_dlsym(name); + } + } + } +#endif /* PLATFORM_HAS_EGL */ + + /* Fall back to GLX */ + return epoxy_gl_dlsym(name); +} + +void * +epoxy_get_proc_address(const char *name) +{ +#if PLATFORM_HAS_EGL + GLenum egl_api = EGL_NONE; + + if (!epoxy_current_context_is_glx()) + egl_api = epoxy_egl_get_current_gl_context_api(); + + switch (egl_api) { + case EGL_OPENGL_API: + case EGL_OPENGL_ES_API: + return eglGetProcAddress(name); + case EGL_NONE: + break; + } +#endif + +#if defined(_WIN32) + return wglGetProcAddress(name); +#elif defined(__APPLE__) + return epoxy_gl_dlsym(name); +#elif PLATFORM_HAS_GLX + if (epoxy_current_context_is_glx()) + return glXGetProcAddressARB((const GLubyte *)name); + assert(0 && "Couldn't find current GLX or EGL context.\n"); +#endif + + return NULL; +} + +WRAPPER_VISIBILITY (void) +WRAPPER(epoxy_glBegin)(GLenum primtype) +{ +#ifdef _WIN32 + InterlockedIncrement(&api.begin_count); +#else + pthread_mutex_lock(&api.mutex); + api.begin_count++; + pthread_mutex_unlock(&api.mutex); +#endif + + epoxy_glBegin_unwrapped(primtype); +} + +WRAPPER_VISIBILITY (void) +WRAPPER(epoxy_glEnd)(void) +{ + epoxy_glEnd_unwrapped(); + +#ifdef _WIN32 + InterlockedDecrement(&api.begin_count); +#else + pthread_mutex_lock(&api.mutex); + api.begin_count--; + pthread_mutex_unlock(&api.mutex); +#endif +} + +PFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped; +PFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped; + +epoxy_resolver_failure_handler_t epoxy_resolver_failure_handler; + +/** + * Sets the function that will be called every time Epoxy fails to + * resolve a symbol. + * + * @param handler The new handler function + * @return The previous handler function + */ +epoxy_resolver_failure_handler_t +epoxy_set_resolver_failure_handler(epoxy_resolver_failure_handler_t handler) +{ +#ifdef _WIN32 + return InterlockedExchangePointer((void**)&epoxy_resolver_failure_handler, + handler); +#else + epoxy_resolver_failure_handler_t old; + pthread_mutex_lock(&api.mutex); + old = epoxy_resolver_failure_handler; + epoxy_resolver_failure_handler = handler; + pthread_mutex_unlock(&api.mutex); + return old; +#endif +} diff --git a/src/dispatch_common.h b/src/dispatch_common.h new file mode 100644 index 0000000..a361ccc --- /dev/null +++ b/src/dispatch_common.h @@ -0,0 +1,203 @@ +/* + * 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 "config.h" + +#ifdef _WIN32 +#define PLATFORM_HAS_EGL ENABLE_EGL +#define PLATFORM_HAS_GLX ENABLE_GLX +#define PLATFORM_HAS_WGL 1 +#elif defined(__APPLE__) +#define PLATFORM_HAS_EGL 0 +#define PLATFORM_HAS_GLX ENABLE_GLX +#define PLATFORM_HAS_WGL 0 +#elif defined(ANDROID) +#define PLATFORM_HAS_EGL ENABLE_EGL +#define PLATFORM_HAS_GLX 0 +#define PLATFORM_HAS_WGL 0 +#else +#define PLATFORM_HAS_EGL ENABLE_EGL +#define PLATFORM_HAS_GLX ENABLE_GLX +#define PLATFORM_HAS_WGL 0 +#endif + +#include "epoxy/gl.h" +#if PLATFORM_HAS_GLX +#include "epoxy/glx.h" +#endif +#if PLATFORM_HAS_EGL +# if !ENABLE_X11 +/* Mesa uses this symbol to avoid including X11 headers when including + * EGL.h; since X11 was explicitly disabled at configuration time, we + * should do the same + */ +# define MESA_EGL_NO_X11_HEADERS 1 +# endif +#include "epoxy/egl.h" +#endif +#if PLATFORM_HAS_WGL +#include "epoxy/wgl.h" +#endif + +#if defined(__GNUC__) +#define PACKED __attribute__((__packed__)) +#define ENDPACKED +#elif defined (_MSC_VER) +#define PACKED __pragma(pack(push,1)) +#define ENDPACKED __pragma(pack(pop)) +#else +#define PACKED +#define ENDPACKED +#endif + +/* On win32, we're going to need to keep a per-thread dispatch table, + * since the function pointers depend on the device and pixel format + * of the current context. + */ +#if defined(_WIN32) +#define USING_DISPATCH_TABLE 1 +#else +#define USING_DISPATCH_TABLE 0 +#endif + +#define UNWRAPPED_PROTO(x) (GLAPIENTRY *x) +#define WRAPPER_VISIBILITY(type) static type GLAPIENTRY +#define WRAPPER(x) x ## _wrapped + +#define GEN_GLOBAL_REWRITE_PTR(name, args, passthrough) \ + static void EPOXY_CALLSPEC \ + name##_global_rewrite_ptr args \ + { \ + if (name == (void *)name##_global_rewrite_ptr) \ + name = (void *)name##_resolver(); \ + name passthrough; \ + } + +#define GEN_GLOBAL_REWRITE_PTR_RET(ret, name, args, passthrough) \ + static ret EPOXY_CALLSPEC \ + name##_global_rewrite_ptr args \ + { \ + if (name == (void *)name##_global_rewrite_ptr) \ + name = (void *)name##_resolver(); \ + return name passthrough; \ + } + +#if USING_DISPATCH_TABLE +#define GEN_DISPATCH_TABLE_REWRITE_PTR(name, args, passthrough) \ + static void EPOXY_CALLSPEC \ + name##_dispatch_table_rewrite_ptr args \ + { \ + struct dispatch_table *dispatch_table = get_dispatch_table(); \ + \ + dispatch_table->name = (void *)name##_resolver(); \ + dispatch_table->name passthrough; \ + } + +#define GEN_DISPATCH_TABLE_REWRITE_PTR_RET(ret, name, args, passthrough) \ + static ret EPOXY_CALLSPEC \ + name##_dispatch_table_rewrite_ptr args \ + { \ + struct dispatch_table *dispatch_table = get_dispatch_table(); \ + \ + dispatch_table->name = (void *)name##_resolver(); \ + return dispatch_table->name passthrough; \ + } + +#define GEN_DISPATCH_TABLE_THUNK(name, args, passthrough) \ + static void EPOXY_CALLSPEC \ + name##_dispatch_table_thunk args \ + { \ + get_dispatch_table()->name passthrough; \ + } + +#define GEN_DISPATCH_TABLE_THUNK_RET(ret, name, args, passthrough) \ + static ret EPOXY_CALLSPEC \ + name##_dispatch_table_thunk args \ + { \ + return get_dispatch_table()->name passthrough; \ + } + +#else +#define GEN_DISPATCH_TABLE_REWRITE_PTR(name, args, passthrough) +#define GEN_DISPATCH_TABLE_REWRITE_PTR_RET(ret, name, args, passthrough) +#define GEN_DISPATCH_TABLE_THUNK(name, args, passthrough) +#define GEN_DISPATCH_TABLE_THUNK_RET(ret, name, args, passthrough) +#endif + +#define GEN_THUNKS(name, args, passthrough) \ + GEN_GLOBAL_REWRITE_PTR(name, args, passthrough) \ + GEN_DISPATCH_TABLE_REWRITE_PTR(name, args, passthrough) \ + GEN_DISPATCH_TABLE_THUNK(name, args, passthrough) + +#define GEN_THUNKS_RET(ret, name, args, passthrough) \ + GEN_GLOBAL_REWRITE_PTR_RET(ret, name, args, passthrough) \ + GEN_DISPATCH_TABLE_REWRITE_PTR_RET(ret, name, args, passthrough) \ + GEN_DISPATCH_TABLE_THUNK_RET(ret, name, args, passthrough) + +void *epoxy_egl_dlsym(const char *name); +void *epoxy_glx_dlsym(const char *name); +void *epoxy_gl_dlsym(const char *name); +void *epoxy_gles1_dlsym(const char *name); +void *epoxy_gles2_dlsym(const char *name); +void *epoxy_gles3_dlsym(const char *name); +void *epoxy_get_proc_address(const char *name); +void *epoxy_get_core_proc_address(const char *name, int core_version); +void *epoxy_get_bootstrap_proc_address(const char *name); + +int epoxy_conservative_gl_version(void); +bool epoxy_conservative_has_gl_extension(const char *name); +int epoxy_conservative_glx_version(void); +bool epoxy_conservative_has_glx_extension(const char *name); +int epoxy_conservative_egl_version(void); +bool epoxy_conservative_has_egl_extension(const char *name); +bool epoxy_conservative_has_wgl_extension(const char *name); +void *epoxy_conservative_egl_dlsym(const char *name, bool exit_if_fails); +void *epoxy_conservative_glx_dlsym(const char *name, bool exit_if_fails); + +bool epoxy_load_glx(bool exit_if_fails, bool load); +bool epoxy_load_egl(bool exit_if_fails, bool load); + +#define glBegin_unwrapped epoxy_glBegin_unwrapped +#define glEnd_unwrapped epoxy_glEnd_unwrapped +extern void UNWRAPPED_PROTO(glBegin_unwrapped)(GLenum primtype); +extern void UNWRAPPED_PROTO(glEnd_unwrapped)(void); + +extern epoxy_resolver_failure_handler_t epoxy_resolver_failure_handler; + +#if USING_DISPATCH_TABLE +void gl_init_dispatch_table(void); +void gl_switch_to_dispatch_table(void); +void wgl_init_dispatch_table(void); +void wgl_switch_to_dispatch_table(void); +extern uint32_t gl_tls_index, gl_tls_size; +extern uint32_t wgl_tls_index, wgl_tls_size; + +#define wglMakeCurrent_unwrapped epoxy_wglMakeCurrent_unwrapped +#define wglMakeContextCurrentARB_unwrapped epoxy_wglMakeContextCurrentARB_unwrapped +#define wglMakeContextCurrentEXT_unwrapped epoxy_wglMakeContextCurrentEXT_unwrapped +#define wglMakeAssociatedContextCurrentAMD_unwrapped epoxy_wglMakeAssociatedContextCurrentAMD_unwrapped +extern BOOL UNWRAPPED_PROTO(wglMakeCurrent_unwrapped)(HDC hdc, HGLRC hglrc); +extern BOOL UNWRAPPED_PROTO(wglMakeContextCurrentARB_unwrapped)(HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +extern BOOL UNWRAPPED_PROTO(wglMakeContextCurrentEXT_unwrapped)(HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +extern BOOL UNWRAPPED_PROTO(wglMakeAssociatedContextCurrentAMD_unwrapped)(HGLRC hglrc); +#endif /* _WIN32_ */ diff --git a/src/dispatch_egl.c b/src/dispatch_egl.c new file mode 100644 index 0000000..3f0c789 --- /dev/null +++ b/src/dispatch_egl.c @@ -0,0 +1,122 @@ +/* + * 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 <assert.h> +#include <string.h> +#include <stdio.h> + +#include "dispatch_common.h" + +int +epoxy_conservative_egl_version(void) +{ + EGLDisplay dpy = eglGetCurrentDisplay(); + + if (!dpy) + return 14; + + return epoxy_egl_version(dpy); +} + +/** + * @brief Returns the version of OpenGL we are using + * + * The version is encoded as: + * + * ``` + * + * version = major * 10 + minor + * + * ``` + * + * So it can be easily used for version comparisons. + * + * @param The EGL display + * + * @return The encoded version of EGL we are using + * + * @see epoxy_gl_version() + */ +int +epoxy_egl_version(EGLDisplay dpy) +{ + int major, minor; + const char *version_string; + int ret; + + version_string = eglQueryString(dpy, EGL_VERSION); + if (!version_string) + return 0; + + ret = sscanf(version_string, "%d.%d", &major, &minor); + assert(ret == 2); + return major * 10 + minor; +} + +bool +epoxy_conservative_has_egl_extension(const char *ext) +{ + return epoxy_has_egl_extension(eglGetCurrentDisplay(), ext); +} + +/** + * @brief Returns true if the given EGL extension is supported in the current context. + * + * @param dpy The EGL display + * @param extension The name of the EGL extension + * + * @return `true` if the extension is available + * + * @see epoxy_has_gl_extension() + * @see epoxy_has_glx_extension() + */ +bool +epoxy_has_egl_extension(EGLDisplay dpy, const char *ext) +{ + return epoxy_extension_in_string(eglQueryString(dpy, EGL_EXTENSIONS), ext) || epoxy_extension_in_string(eglQueryString(NULL, EGL_EXTENSIONS), ext); +} + +/** + * @brief Checks whether EGL is available. + * + * @return `true` if EGL is available + * + * @newin{1,4} + */ +bool +epoxy_has_egl(void) +{ +#if !PLATFORM_HAS_EGL + return false; +#else + if (epoxy_load_egl(false, true)) { + EGLDisplay* (* pf_eglGetCurrentDisplay) (void); + + pf_eglGetCurrentDisplay = epoxy_conservative_egl_dlsym("eglGetCurrentDisplay", false); + if (pf_eglGetCurrentDisplay) + return true; + } + + return false; +#endif /* PLATFORM_HAS_EGL */ +} diff --git a/src/dispatch_glx.c b/src/dispatch_glx.c new file mode 100644 index 0000000..e395564 --- /dev/null +++ b/src/dispatch_glx.c @@ -0,0 +1,172 @@ +/* + * 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 <assert.h> +#include <string.h> +#include <stdio.h> + +#include "dispatch_common.h" + +/** + * If we can determine the GLX version from the current context, then + * return that, otherwise return a version that will just send us on + * to dlsym() or get_proc_address(). + */ +int +epoxy_conservative_glx_version(void) +{ + Display *dpy = glXGetCurrentDisplay(); + GLXContext ctx = glXGetCurrentContext(); + int screen; + + if (!dpy || !ctx) + return 14; + + glXQueryContext(dpy, ctx, GLX_SCREEN, &screen); + + return epoxy_glx_version(dpy, screen); +} + + +/** + * @brief Returns the version of GLX we are using + * + * The version is encoded as: + * + * ``` + * + * version = major * 10 + minor + * + * ``` + * + * So it can be easily used for version comparisons. + * + * @param dpy The X11 display + * @param screen The X11 screen + * + * @return The encoded version of GLX we are using + * + * @see epoxy_gl_version() + */ +int +epoxy_glx_version(Display *dpy, int screen) +{ + int server_major, server_minor; + int client_major, client_minor; + int server, client; + const char *version_string; + int ret; + + version_string = glXQueryServerString(dpy, screen, GLX_VERSION); + if (!version_string) + return 0; + + ret = sscanf(version_string, "%d.%d", &server_major, &server_minor); + assert(ret == 2); + server = server_major * 10 + server_minor; + + version_string = glXGetClientString(dpy, GLX_VERSION); + if (!version_string) + return 0; + + ret = sscanf(version_string, "%d.%d", &client_major, &client_minor); + assert(ret == 2); + client = client_major * 10 + client_minor; + + if (client < server) + return client; + else + return server; +} + +/** + * If we can determine the GLX extension support from the current + * context, then return that, otherwise give the answer that will just + * send us on to get_proc_address(). + */ +bool +epoxy_conservative_has_glx_extension(const char *ext) +{ + Display *dpy = glXGetCurrentDisplay(); + GLXContext ctx = glXGetCurrentContext(); + int screen; + + if (!dpy || !ctx) + return true; + + glXQueryContext(dpy, ctx, GLX_SCREEN, &screen); + + return epoxy_has_glx_extension(dpy, screen, ext); +} + +/** + * @brief Returns true if the given GLX extension is supported in the current context. + * + * @param dpy The X11 display + * @param screen The X11 screen + * @param extension The name of the GLX extension + * + * @return `true` if the extension is available + * + * @see epoxy_has_gl_extension() + * @see epoxy_has_egl_extension() + */ +bool +epoxy_has_glx_extension(Display *dpy, int screen, const char *ext) +{ + /* 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() is what gives you the intersection + * of the two. + */ + return epoxy_extension_in_string(glXQueryExtensionsString(dpy, screen), ext); +} + +/** + * @brief Checks whether GLX is available. + * + * @param dpy The X11 display + * + * @return `true` if GLX is available + * + * @newin{1,4} + */ +bool +epoxy_has_glx(Display *dpy) +{ +#if !PLATFORM_HAS_GLX + return false; +#else + if (epoxy_load_glx(false, true)) { + Bool (* pf_glXQueryExtension) (Display *, int *, int *); + int error_base, event_base; + + pf_glXQueryExtension = epoxy_conservative_glx_dlsym("glXQueryExtension", false); + if (pf_glXQueryExtension && pf_glXQueryExtension(dpy, &error_base, &event_base)) + return true; + } + + return false; +#endif /* !PLATFORM_HAS_GLX */ +} diff --git a/src/dispatch_wgl.c b/src/dispatch_wgl.c new file mode 100644 index 0000000..7baf130 --- /dev/null +++ b/src/dispatch_wgl.c @@ -0,0 +1,196 @@ +/* + * 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 <assert.h> +#include <string.h> +#include <stdio.h> + +#include "dispatch_common.h" + +static bool first_context_current = false; +static bool already_switched_to_dispatch_table = false; + +/** + * If we can determine the WGL extension support from the current + * context, then return that, otherwise give the answer that will just + * send us on to get_proc_address(). + */ +bool +epoxy_conservative_has_wgl_extension(const char *ext) +{ + HDC hdc = wglGetCurrentDC(); + + if (!hdc) + return true; + + return epoxy_has_wgl_extension(hdc, ext); +} + +bool +epoxy_has_wgl_extension(HDC hdc, const char *ext) + { + PFNWGLGETEXTENSIONSSTRINGARBPROC getext; + + getext = (void *)wglGetProcAddress("wglGetExtensionsStringARB"); + if (!getext) { + fputs("Implementation unexpectedly missing " + "WGL_ARB_extensions_string. Probably a libepoxy bug.\n", + stderr); + return false; + } + + return epoxy_extension_in_string(getext(hdc), ext); +} + +/** + * Does the work necessary to update the win32 per-thread dispatch + * tables when wglMakeCurrent() is called. + * + * Right now, we use global function pointers until the second + * MakeCurrent occurs, at which point we switch to dispatch tables. + * This could be improved in the future to track a resolved dispatch + * table per context and reuse it when the context is made current + * again. + */ +void +epoxy_handle_external_wglMakeCurrent(void) +{ + if (!first_context_current) { + first_context_current = true; + } else { + if (!already_switched_to_dispatch_table) { + already_switched_to_dispatch_table = true; + gl_switch_to_dispatch_table(); + wgl_switch_to_dispatch_table(); + } + + gl_init_dispatch_table(); + wgl_init_dispatch_table(); + } +} + +/** + * This global symbol is apparently looked up by Windows when loading + * a DLL, but it doesn't declare the prototype. + */ +BOOL WINAPI +DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved); + +BOOL WINAPI +DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved) +{ + void *data; + + switch (reason) { + case DLL_PROCESS_ATTACH: + gl_tls_index = TlsAlloc(); + if (gl_tls_index == TLS_OUT_OF_INDEXES) + return FALSE; + wgl_tls_index = TlsAlloc(); + if (wgl_tls_index == TLS_OUT_OF_INDEXES) + return FALSE; + + first_context_current = false; + + /* FALLTHROUGH */ + + case DLL_THREAD_ATTACH: + data = LocalAlloc(LPTR, gl_tls_size); + TlsSetValue(gl_tls_index, data); + + data = LocalAlloc(LPTR, wgl_tls_size); + TlsSetValue(wgl_tls_index, data); + + break; + + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + data = TlsGetValue(gl_tls_index); + LocalFree(data); + + data = TlsGetValue(wgl_tls_index); + LocalFree(data); + + if (reason == DLL_PROCESS_DETACH) { + TlsFree(gl_tls_index); + TlsFree(wgl_tls_index); + } + break; + } + + return TRUE; +} + +WRAPPER_VISIBILITY (BOOL) +WRAPPER(epoxy_wglMakeCurrent)(HDC hdc, HGLRC hglrc) +{ + BOOL ret = epoxy_wglMakeCurrent_unwrapped(hdc, hglrc); + + epoxy_handle_external_wglMakeCurrent(); + + return ret; +} + + +WRAPPER_VISIBILITY (BOOL) +WRAPPER(epoxy_wglMakeContextCurrentARB)(HDC hDrawDC, + HDC hReadDC, + HGLRC hglrc) +{ + BOOL ret = epoxy_wglMakeContextCurrentARB_unwrapped(hDrawDC, hReadDC, + hglrc); + + epoxy_handle_external_wglMakeCurrent(); + + return ret; +} + + +WRAPPER_VISIBILITY (BOOL) +WRAPPER(epoxy_wglMakeContextCurrentEXT)(HDC hDrawDC, + HDC hReadDC, + HGLRC hglrc) +{ + BOOL ret = epoxy_wglMakeContextCurrentEXT_unwrapped(hDrawDC, hReadDC, + hglrc); + + epoxy_handle_external_wglMakeCurrent(); + + return ret; +} + + +WRAPPER_VISIBILITY (BOOL) +WRAPPER(epoxy_wglMakeAssociatedContextCurrentAMD)(HGLRC hglrc) +{ + BOOL ret = epoxy_wglMakeAssociatedContextCurrentAMD_unwrapped(hglrc); + + epoxy_handle_external_wglMakeCurrent(); + + return ret; +} + +PFNWGLMAKECURRENTPROC epoxy_wglMakeCurrent = epoxy_wglMakeCurrent_wrapped; +PFNWGLMAKECONTEXTCURRENTEXTPROC epoxy_wglMakeContextCurrentEXT = epoxy_wglMakeContextCurrentEXT_wrapped; +PFNWGLMAKECONTEXTCURRENTARBPROC epoxy_wglMakeContextCurrentARB = epoxy_wglMakeContextCurrentARB_wrapped; +PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC epoxy_wglMakeAssociatedContextCurrentEXT = epoxy_wglMakeAssociatedContextCurrentAMD_wrapped; diff --git a/src/gen_dispatch.py b/src/gen_dispatch.py new file mode 100755 index 0000000..f4d0f31 --- /dev/null +++ b/src/gen_dispatch.py @@ -0,0 +1,914 @@ +# -*- 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 GLProvider(object): + def __init__(self, condition, condition_name, loader, name): + # C code for determining if this function is available. + # (e.g. epoxy_is_desktop_gl() && epoxy_gl_version() >= 20 + self.condition = condition + + # A string (possibly with spaces) describing the condition. + self.condition_name = condition_name + + # The loader for getting the symbol -- either dlsym or + # getprocaddress. This is a python format string to generate + # C code, given self.name. + self.loader = loader + + # The name of the function to be loaded (possibly an + # ARB/EXT/whatever-decorated variant). + self.name = name + + # This is the C enum name we'll use for referring to this provider. + self.enum = condition_name + self.enum = self.enum.replace(' ', '_') + self.enum = self.enum.replace('\\"', '') + self.enum = self.enum.replace('.', '_') + self.enum = "PROVIDER_" + self.enum + +class GLFunction(object): + def __init__(self, ret_type, name): + self.name = name + self.ptr_type = 'PFN' + name.upper() + 'PROC' + self.ret_type = ret_type + self.providers = {} + self.args = [] + + # These are functions with hand-written wrapper code in + # dispatch_common.c. Their dispatch entries are replaced with + # non-public symbols with a "_unwrapped" suffix. + wrapped_functions = { + 'glBegin', + 'glEnd', + 'wglMakeCurrent', + 'wglMakeContextCurrentEXT', + 'wglMakeContextCurrentARB', + 'wglMakeAssociatedContextCurrentAMD', + } + + if name in wrapped_functions: + self.wrapped_name = name + '_unwrapped' + self.public = '' + else: + self.wrapped_name = name + self.public = 'EPOXY_PUBLIC ' + + # This is the string of C code for passing through the + # arguments to the function. + self.args_list = '' + + # This is the string of C code for declaring the arguments + # list. + self.args_decl = 'void' + + # This is the string name of the function that this is an + # alias of, or self.name. This initially comes from the + # registry, and may get updated if it turns out our alias is + # itself an alias (for example glFramebufferTextureEXT -> + # glFramebufferTextureARB -> glFramebufferTexture) + self.alias_name = name + + # After alias resolution, this is the function that this is an + # alias of. + self.alias_func = None + + # For the root of an alias tree, this lists the functions 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, arg_type, arg_name): + # Reword glDepthRange() arguments to avoid clashing with the + # "near" and "far" keywords on win32. + if arg_name == "near": + arg_name = "hither" + elif arg_name == "far": + arg_name = "yon" + + # Mac screwed up GLhandleARB and made it a void * instead of + # uint32_t, despite it being specced as only necessarily 32 + # bits wide, causing portability problems all over. There are + # prototype conflicts between things like + # glAttachShader(GLuint program, GLuint shader) and + # glAttachObjectARB(GLhandleARB container, GLhandleARB obj), + # even though they are marked as aliases in the XML (and being + # aliases in Mesa). + # + # We retain those aliases. In the x86_64 ABI, the first 6 + # args are stored in 64-bit registers, so the calls end up + # being the same despite the different types. We just need to + # add a cast to uintptr_t to shut up the compiler. + if arg_type == 'GLhandleARB': + assert len(self.args) < 6 + arg_list_name = '(uintptr_t)' + arg_name + else: + arg_list_name = arg_name + + self.args.append((arg_type, arg_name)) + if self.args_decl == 'void': + self.args_list = arg_list_name + self.args_decl = arg_type + ' ' + arg_name + else: + self.args_list += ', ' + arg_list_name + self.args_decl += ', ' + arg_type + ' ' + arg_name + + def add_provider(self, condition, loader, condition_name): + self.providers[condition_name] = GLProvider(condition, condition_name, + loader, self.name) + + def add_alias(self, ext): + assert self.alias_func is None + + self.alias_exts.append(ext) + ext.alias_func = self + +class Generator(object): + def __init__(self, target): + self.target = target + self.enums = {} + self.functions = {} + self.sorted_functions = [] + self.enum_string_offset = {} + self.max_enum_name_len = 1 + self.entrypoint_string_offset = {} + self.copyright_comment = None + self.typedefs = '' + self.out_file = None + + # GL versions named in the registry, which we should generate + # #defines for. + self.supported_versions = set() + + # Extensions named in the registry, which we should generate + # #defines for. + self.supported_extensions = set() + + # Dictionary mapping human-readable names of providers to a C + # enum token that will be used to reference those names, to + # reduce generated binary size. + self.provider_enum = {} + + # Dictionary mapping human-readable names of providers to C + # code to detect if it's present. + self.provider_condition = {} + + # Dictionary mapping human-readable names of providers to + # format strings for fetching the function pointer when + # provided the name of the symbol to be requested. + self.provider_loader = {} + + 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 + + # The gles1/gles2-specific types are redundant + # declarations, and the different types used for them (int + # vs int32_t) caused problems on win32 builds. + api = t.get('api') + if api: + continue + + if t.text is not None: + self.typedefs += t.text + + for child in t: + if child.tag == 'apientry': + self.typedefs += 'APIENTRY' + 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') + + # wgl.xml's 0xwhatever definitions end up colliding with + # wingdi.h's decimal definitions of these. + if name in ['WGL_SWAP_OVERLAY', 'WGL_SWAP_UNDERLAY', 'WGL_SWAP_MAIN_PLANE']: + continue + + 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: + # Note that some alias references appear before the + # target command is defined (glAttachObjectARB() -> + # glAttachShader(), for example). + func.alias_name = alias.get('name') + + 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 resolve_aliases(self): + for func in self.functions.values(): + # Find the root of the alias tree, and add ourselves to it. + if func.alias_name != func.name: + alias_func = func + while alias_func.alias_name != alias_func.name: + alias_func = self.functions[alias_func.alias_name] + func.alias_name = alias_func.name + func.alias_func = alias_func + alias_func.alias_exts.append(func) + + def prepare_provider_enum(self): + self.provider_enum = {} + + # We assume that for any given provider, all functions using + # it will have the same loader. This lets us generate a + # general C function for detecting conditions and calling the + # dlsym/getprocaddress, and have our many resolver stubs just + # call it with a table of values. + for func in self.functions.values(): + for provider in func.providers.values(): + if provider.condition_name in self.provider_enum: + assert self.provider_condition[provider.condition_name] == provider.condition + assert self.provider_loader[provider.condition_name] == provider.loader + continue + + self.provider_enum[provider.condition_name] = provider.enum + self.provider_condition[provider.condition_name] = provider.condition + self.provider_loader[provider.condition_name] = provider.loader + + def sort_functions(self): + self.sorted_functions = sorted(self.functions.values(), key=lambda func: func.name) + + def process_require_statements(self, feature, condition, loader, human_name): + for command in feature.findall('require/command'): + name = command.get('name') + + # wgl.xml describes 6 functions in WGL 1.0 that are in + # gdi32.dll instead of opengl32.dll, and we would need to + # change up our symbol loading to support that. Just + # don't wrap those functions. + if self.target == 'wgl' and 'wgl' not in name: + del self.functions[name] + continue + + func = self.functions[name] + func.add_provider(condition, loader, 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(r'([0-9])\.([0-9])', feature.get('number')) + version = int(m.group(1)) * 10 + int(m.group(2)) + + self.supported_versions.add(feature.get('name')) + + if api == 'gl': + human_name = 'Desktop OpenGL {0}'.format(feature.get('number')) + condition = 'epoxy_is_desktop_gl()' + + loader = 'epoxy_get_core_proc_address({0}, {1})'.format('{0}', version) + if version >= 11: + condition += ' && epoxy_conservative_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 = 'epoxy_gles2_dlsym({0})' + else: + loader = 'epoxy_gles3_dlsym({0})' + elif api == 'gles1': + human_name = 'OpenGL ES 1.0' + condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= 10 && epoxy_gl_version() < 20' + loader = 'epoxy_gles1_dlsym({0})' + elif api == 'glx': + human_name = 'GLX {0}'.format(version) + # We could just always use GPA for loading everything + # but glXGetProcAddress(), but dlsym() is a more + # efficient lookup. + if version > 13: + condition = 'epoxy_conservative_glx_version() >= {0}'.format(version) + loader = 'glXGetProcAddress((const GLubyte *){0})' + else: + condition = 'true' + loader = 'epoxy_glx_dlsym({0})' + elif api == 'egl': + human_name = 'EGL {0}'.format(version) + if version > 10: + condition = 'epoxy_conservative_egl_version() >= {0}'.format(version) + else: + condition = 'true' + # All EGL core entrypoints must be dlsym()ed out -- + # eglGetProcAdddress() will return NULL. + loader = 'epoxy_egl_dlsym({0})' + elif api == 'wgl': + human_name = 'WGL {0}'.format(version) + condition = 'true' + loader = 'epoxy_gl_dlsym({0})' + elif api == 'glsc2': + continue + 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') + cond_extname = "enum_string[enum_string_offsets[i]]" + + self.supported_extensions.add(extname) + + # '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: + condition = 'epoxy_conservative_has_glx_extension(provider_name)' + loader = 'glXGetProcAddress((const GLubyte *){0})' + self.process_require_statements(extension, condition, loader, extname) + if 'egl' in apis: + condition = 'epoxy_conservative_has_egl_extension(provider_name)' + loader = 'eglGetProcAddress({0})' + self.process_require_statements(extension, condition, loader, extname) + if 'wgl' in apis: + condition = 'epoxy_conservative_has_wgl_extension(provider_name)' + loader = 'wglGetProcAddress({0})' + self.process_require_statements(extension, condition, loader, extname) + if {'gl', 'gles1', 'gles2'}.intersection(apis): + condition = 'epoxy_conservative_has_gl_extension(provider_name)' + loader = 'epoxy_get_proc_address({0})' + self.process_require_statements(extension, condition, loader, extname) + + def fixup_bootstrap_function(self, name, loader): + # We handle glGetString(), glGetIntegerv(), and + # glXGetProcAddressARB() 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', loader, 'always present') + + def parse(self, xml_file): + reg = ET.parse(xml_file) + comment = reg.find('comment') + if comment is not None: + self.copyright_comment = 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 in sorted(self.supported_versions): + self.outln('#define {0} 1'.format(name)) + self.outln('') + + for name in sorted(self.supported_extensions): + self.outln('#define {0} 1'.format(name)) + self.outln('') + + # We want to sort by enum number (which puts a bunch of things + # in a logical order), then by name after that, so we do those + # sorts in reverse. This is still way uglier than doing some + # sort based on what version/extensions things are introduced + # in, but we haven't paid any attention to those attributes + # for enums yet. + sorted_by_name = sorted(self.enums.keys()) + sorted_by_number = sorted(sorted_by_name, key=lambda name: self.enums[name]) + for name in sorted_by_number: + self.outln('#define ' + name.ljust(self.max_enum_name_len + 3) + self.enums[name] + '') + + def write_function_ptr_typedefs(self): + for func in self.sorted_functions: + self.outln('typedef {0} (GLAPIENTRY *{1})({2});'.format(func.ret_type, + func.ptr_type, + func.args_decl)) + + def write_header_header(self, out_file): + self.out_file = open(out_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, out_file): + self.write_header_header(out_file) + + self.outln('#include "epoxy/common.h"') + + if self.target != "gl": + self.outln('#include "epoxy/gl.h"') + if self.target == "egl": + self.outln('#include "EGL/eglplatform.h"') + # Account for older eglplatform.h, which doesn't define + # the EGL_CAST macro. + self.outln('#ifndef EGL_CAST') + self.outln('#if defined(__cplusplus)') + self.outln('#define EGL_CAST(type, value) (static_cast<type>(value))') + self.outln('#else') + self.outln('#define EGL_CAST(type, value) ((type) (value))') + self.outln('#endif') + self.outln('#endif') + else: + # Add some ridiculous inttypes.h redefinitions that are + # from khrplatform.h and not included in the XML. We + # don't directly include khrplatform.h because it's not + # present on many systems, and coming up with #ifdefs to + # decide when it's not present would be hard. + self.outln('#define __khrplatform_h_ 1') + 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 long khronos_intptr_t;') + self.outln('typedef long khronos_ssize_t;') + self.outln('typedef unsigned long khronos_usize_t;') + self.outln('typedef uint64_t khronos_utime_nanoseconds_t;') + self.outln('typedef int64_t khronos_stime_nanoseconds_t;') + self.outln('#define KHRONOS_MAX_ENUM 0x7FFFFFFF') + self.outln('typedef enum {') + self.outln(' KHRONOS_FALSE = 0,') + self.outln(' KHRONOS_TRUE = 1,') + self.outln(' KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM') + self.outln('} khronos_boolean_enum_t;') + self.outln('typedef uintptr_t khronos_uintptr_t;') + + if self.target == "glx": + 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.sorted_functions: + self.outln('EPOXY_PUBLIC {0} (EPOXY_CALLSPEC *epoxy_{1})({2});'.format(func.ret_type, + func.name, + func.args_decl)) + self.outln('') + + for func in self.sorted_functions: + self.outln('#define {0} epoxy_{0}'.format(func.name)) + + def write_function_ptr_resolver(self, func): + self.outln('static {0}'.format(func.ptr_type)) + self.outln('epoxy_{0}_resolver(void)'.format(func.wrapped_name)) + self.outln('{') + + providers = [] + # Make a local list of all the providers for this alias group + alias_root = func + if func.alias_func: + alias_root = func.alias_func + for provider in alias_root.providers.values(): + providers.append(provider) + for alias_func in alias_root.alias_exts: + for provider in alias_func.providers.values(): + providers.append(provider) + + # Add some partial aliases of a few functions. These are ones + # that aren't quite aliases, because of some trivial behavior + # difference (like whether to produce an error for a + # non-Genned name), but where we'd like to fall back to the + # similar function if the proper one isn't present. + half_aliases = { + 'glBindVertexArray' : 'glBindVertexArrayAPPLE', + 'glBindVertexArrayAPPLE' : 'glBindVertexArray', + 'glBindFramebuffer' : 'glBindFramebufferEXT', + 'glBindFramebufferEXT' : 'glBindFramebuffer', + 'glBindRenderbuffer' : 'glBindRenderbufferEXT', + 'glBindRenderbufferEXT' : 'glBindRenderbuffer', + } + if func.name in half_aliases: + alias_func = self.functions[half_aliases[func.name]] + for provider in alias_func.providers.values(): + providers.append(provider) + + def provider_sort(provider): + return (provider.name != func.name, provider.name, provider.enum) + providers.sort(key=provider_sort) + + if len(providers) != 1: + self.outln(' static const enum {0}_provider providers[] = {{'.format(self.target)) + for provider in providers: + self.outln(' {0},'.format(provider.enum)) + self.outln(' {0}_provider_terminator'.format(self.target)) + self.outln(' };') + + self.outln(' static const uint32_t entrypoints[] = {') + if len(providers) > 1: + for provider in providers: + self.outln(' {0} /* "{1}" */,'.format(self.entrypoint_string_offset[provider.name], provider.name)) + else: + self.outln(' 0 /* None */,') + self.outln(' };') + + self.outln(' return {0}_provider_resolver(entrypoint_strings + {1} /* "{2}" */,'.format(self.target, + self.entrypoint_string_offset[func.name], + func.name)) + self.outln(' providers, entrypoints);') + else: + assert providers[0].name == func.name + self.outln(' return {0}_single_resolver({1}, {2} /* {3} */);'.format(self.target, + providers[0].enum, + self.entrypoint_string_offset[func.name], + func.name)) + self.outln('}') + self.outln('') + + def write_thunks(self, func): + # Writes out the function that's initially plugged into the + # global function pointer, which resolves, updates the global + # function pointer, and calls down to it. + # + # It also writes out the actual initialized global function + # pointer. + if func.ret_type == 'void': + self.outln('GEN_THUNKS({0}, ({1}), ({2}))'.format(func.wrapped_name, + func.args_decl, + func.args_list)) + else: + self.outln('GEN_THUNKS_RET({0}, {1}, ({2}), ({3}))'.format(func.ret_type, + func.wrapped_name, + func.args_decl, + func.args_list)) + + def write_function_pointer(self, func): + self.outln('{0} epoxy_{1} = epoxy_{1}_global_rewrite_ptr;'.format(func.ptr_type, func.wrapped_name)) + self.outln('') + + def write_provider_enums(self): + # Writes the enum declaration for the list of providers + # supported by gl_provider_resolver() + + self.outln('') + self.outln('enum {0}_provider {{'.format(self.target)) + + sorted_providers = sorted(self.provider_enum.keys()) + + # We always put a 0 enum first so that we can have a + # terminator in our arrays + self.outln(' {0}_provider_terminator = 0,'.format(self.target)) + + for human_name in sorted_providers: + enum = self.provider_enum[human_name] + self.outln(' {0},'.format(enum)) + self.outln('} PACKED;') + self.outln('ENDPACKED') + self.outln('') + + def write_provider_enum_strings(self): + # Writes the mapping from enums to the strings describing them + # for epoxy_print_failure_reasons(). + + sorted_providers = sorted(self.provider_enum.keys()) + + offset = 0 + self.outln('static const char *enum_string =') + for human_name in sorted_providers: + self.outln(' "{0}\\0"'.format(human_name)) + self.enum_string_offset[human_name] = offset + offset += len(human_name.replace('\\', '')) + 1 + self.outln(' ;') + self.outln('') + # We're using uint16_t for the offsets. + assert offset < 65536 + + self.outln('static const uint16_t enum_string_offsets[] = {') + self.outln(' -1, /* {0}_provider_terminator, unused */'.format(self.target)) + for human_name in sorted_providers: + enum = self.provider_enum[human_name] + self.outln(' {1}, /* {0} */'.format(human_name, self.enum_string_offset[human_name])) + self.outln('};') + self.outln('') + + def write_entrypoint_strings(self): + self.outln('static const char entrypoint_strings[] = {') + offset = 0 + for func in self.sorted_functions: + if func.name not in self.entrypoint_string_offset: + self.entrypoint_string_offset[func.name] = offset + offset += len(func.name) + 1 + for c in func.name: + self.outln(" '{0}',".format(c)) + self.outln(' 0, // {0}'.format(func.name)) + self.outln(' 0 };') + # We're using uint16_t for the offsets. + #assert(offset < 65536) + self.outln('') + + def write_provider_resolver(self): + self.outln('static void *{0}_provider_resolver(const char *name,'.format(self.target)) + self.outln(' const enum {0}_provider *providers,'.format(self.target)) + self.outln(' const uint32_t *entrypoints)') + self.outln('{') + self.outln(' int i;') + + self.outln(' for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target)) + self.outln(' const char *provider_name = enum_string + enum_string_offsets[providers[i]];') + self.outln(' switch (providers[i]) {') + self.outln('') + + for human_name in sorted(self.provider_enum.keys()): + enum = self.provider_enum[human_name] + self.outln(' case {0}:'.format(enum)) + self.outln(' if ({0})'.format(self.provider_condition[human_name])) + self.outln(' return {0};'.format(self.provider_loader[human_name]).format("entrypoint_strings + entrypoints[i]")) + self.outln(' break;') + + self.outln(' case {0}_provider_terminator:'.format(self.target)) + self.outln(' abort(); /* Not reached */') + self.outln(' }') + self.outln(' }') + self.outln('') + + self.outln(' if (epoxy_resolver_failure_handler)') + self.outln(' return epoxy_resolver_failure_handler(name);') + 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(' fprintf(stderr, "No provider of %s found. Requires one of:\\n", name);') + self.outln(' for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target)) + self.outln(' fprintf(stderr, " %s\\n", enum_string + enum_string_offsets[providers[i]]);') + self.outln(' }') + self.outln(' if (providers[0] == {0}_provider_terminator) {{'.format(self.target)) + self.outln(' fprintf(stderr, " No known providers. This is likely a bug "') + self.outln(' "in libepoxy code generation\\n");') + self.outln(' }') + self.outln(' abort();') + + self.outln('}') + self.outln('') + + single_resolver_proto = '{0}_single_resolver(enum {0}_provider provider, uint32_t entrypoint_offset)'.format(self.target) + self.outln('EPOXY_NOINLINE static void *') + self.outln('{0};'.format(single_resolver_proto)) + self.outln('') + self.outln('static void *') + self.outln('{0}'.format(single_resolver_proto)) + self.outln('{') + self.outln(' enum {0}_provider providers[] = {{'.format(self.target)) + self.outln(' provider,') + self.outln(' {0}_provider_terminator'.format(self.target)) + self.outln(' };') + self.outln(' return {0}_provider_resolver(entrypoint_strings + entrypoint_offset,'.format(self.target)) + self.outln(' providers, &entrypoint_offset);') + self.outln('}') + self.outln('') + + def write_source(self, f): + self.out_file = open(f, '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 "config.h"') + self.outln('') + self.outln('#include <stdlib.h>') + self.outln('#include <string.h>') + self.outln('#include <stdio.h>') + self.outln('') + self.outln('#include "dispatch_common.h"') + self.outln('#include "epoxy/{0}.h"'.format(self.target)) + self.outln('') + self.outln('#ifdef __GNUC__') + self.outln('#define EPOXY_NOINLINE __attribute__((noinline))') + self.outln('#elif defined (_MSC_VER)') + self.outln('#define EPOXY_NOINLINE __declspec(noinline)') + self.outln('#endif') + + self.outln('struct dispatch_table {') + for func in self.sorted_functions: + self.outln(' {0} epoxy_{1};'.format(func.ptr_type, func.wrapped_name)) + self.outln('};') + self.outln('') + + # Early declaration, so we can declare the real thing at the + # bottom. (I want the function_ptr_resolver as the first + # per-GL-call code, since it's the most interesting to see + # when you search for the implementation of a call) + self.outln('#if USING_DISPATCH_TABLE') + self.outln('static inline struct dispatch_table *') + self.outln('get_dispatch_table(void);') + self.outln('') + self.outln('#endif') + + self.write_provider_enums() + self.write_provider_enum_strings() + self.write_entrypoint_strings() + self.write_provider_resolver() + + for func in self.sorted_functions: + self.write_function_ptr_resolver(func) + + for func in self.sorted_functions: + self.write_thunks(func) + self.outln('') + + self.outln('#if USING_DISPATCH_TABLE') + + self.outln('static struct dispatch_table resolver_table = {') + for func in self.sorted_functions: + self.outln(' epoxy_{0}_dispatch_table_rewrite_ptr, /* {0} */'.format(func.wrapped_name)) + self.outln('};') + self.outln('') + + self.outln('uint32_t {0}_tls_index;'.format(self.target)) + self.outln('uint32_t {0}_tls_size = sizeof(struct dispatch_table);'.format(self.target)) + self.outln('') + + self.outln('static inline struct dispatch_table *') + self.outln('get_dispatch_table(void)') + self.outln('{') + self.outln(' return TlsGetValue({0}_tls_index);'.format(self.target)) + self.outln('}') + self.outln('') + + self.outln('void') + self.outln('{0}_init_dispatch_table(void)'.format(self.target)) + self.outln('{') + self.outln(' struct dispatch_table *dispatch_table = get_dispatch_table();') + self.outln(' memcpy(dispatch_table, &resolver_table, sizeof(resolver_table));') + self.outln('}') + self.outln('') + + self.outln('void') + self.outln('{0}_switch_to_dispatch_table(void)'.format(self.target)) + self.outln('{') + + for func in self.sorted_functions: + self.outln(' epoxy_{0} = epoxy_{0}_dispatch_table_thunk;'.format(func.wrapped_name)) + + self.outln('}') + self.outln('') + + self.outln('#endif /* !USING_DISPATCH_TABLE */') + + for func in self.sorted_functions: + self.write_function_pointer(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('--outputdir', metavar='dir', required=False, help='Destination directory for files (default to current dir)') +argparser.add_argument('--includedir', metavar='dir', required=False, help='Destination directory for headers') +argparser.add_argument('--srcdir', metavar='dir', required=False, help='Destination directory for source') +argparser.add_argument('--source', dest='source', action='store_true', required=False, help='Generate the source file') +argparser.add_argument('--no-source', dest='source', action='store_false', required=False, help='Do not generate the source file') +argparser.add_argument('--header', dest='header', action='store_true', required=False, help='Generate the header file') +argparser.add_argument('--no-header', dest='header', action='store_false', required=False, help='Do not generate the header file') +args = argparser.parse_args() + +if args.outputdir: + outputdir = args.outputdir +else: + outputdir = os.getcwd() + +if args.includedir: + includedir = args.includedir +else: + includedir = outputdir + +if args.srcdir: + srcdir = args.srcdir +else: + srcdir = outputdir + +build_source = args.source +build_header = args.header + +if not build_source and not build_header: + build_source = True + build_header = True + +for f in args.files: + name = os.path.basename(f).split('.xml')[0] + generator = Generator(name) + generator.parse(f) + + generator.drop_weird_glx_functions() + + # This is an ANSI vs Unicode function, handled specially by + # include/epoxy/wgl.h + if 'wglUseFontBitmaps' in generator.functions: + del generator.functions['wglUseFontBitmaps'] + + generator.sort_functions() + generator.resolve_aliases() + generator.fixup_bootstrap_function('glGetString', + 'epoxy_get_bootstrap_proc_address({0})') + generator.fixup_bootstrap_function('glGetIntegerv', + 'epoxy_get_bootstrap_proc_address({0})') + + # 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('glXGetProcAddress', + 'epoxy_glx_dlsym({0})') + + generator.prepare_provider_enum() + + if build_header: + generator.write_header(os.path.join(includedir, name + '_generated.h')) + if build_source: + generator.write_source(os.path.join(srcdir, name + '_generated_dispatch.c')) diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..fe092d1 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,112 @@ +# Configuration file +configure_file(output: 'config.h', configuration: conf) + +# List of generated sources: +# - name of the generated file +# - registry source file +# - additional sources +generated_sources = [ + [ 'gl_generated_dispatch.c', gl_registry, [ 'dispatch_common.c', 'dispatch_common.h' ] ] +] + +if build_egl + generated_sources += [ [ 'egl_generated_dispatch.c', egl_registry, 'dispatch_egl.c' ] ] +endif + +if build_glx + generated_sources += [ [ 'glx_generated_dispatch.c', glx_registry, 'dispatch_glx.c' ] ] +endif + +if build_wgl + generated_sources += [ [ 'wgl_generated_dispatch.c', wgl_registry, 'dispatch_wgl.c' ] ] +endif + +gen_sources = [ ] +sources = [ ] + +foreach g: generated_sources + gen_source = g[0] + registry = g[1] + source = g[2] + + generated = custom_target(gen_source, + input: registry, + output: [ gen_source ], + command: [ + python, + gen_dispatch_py, + '--source', + '--no-header', + '--outputdir=@OUTDIR@', + '@INPUT@', + ]) + + gen_sources += [ generated ] + sources += [ source ] +endforeach + +epoxy_sources = sources + gen_sources + +common_ldflags = [] + +if host_system == 'linux' and cc.get_id() == 'gcc' + common_ldflags += cc.get_supported_link_arguments([ '-Wl,-Bsymbolic-functions', '-Wl,-z,relro' ]) +endif + +# Maintain compatibility with autotools; see: https://github.com/anholt/libepoxy/issues/108 +if host_system == 'darwin' + common_ldflags += [ '-compatibility_version 1', '-current_version 1.0', ] +endif + +epoxy_deps = [ dl_dep, ] +if host_system == 'windows' + epoxy_deps += [ opengl32_dep, gdi32_dep ] +endif + +libepoxy = library( + 'epoxy', + sources: epoxy_sources + epoxy_headers, + version: '0.0.0', + install: true, + dependencies: epoxy_deps, + include_directories: libepoxy_inc, + c_args: common_cflags + visibility_cflags, + link_args: common_ldflags, +) + +libepoxy_dep = declare_dependency( + link_with: libepoxy, + include_directories: libepoxy_inc, + dependencies: epoxy_deps, + sources: epoxy_headers, +) + +epoxy_has_glx = build_glx ? '1' : '0' +epoxy_has_egl = build_egl ? '1' : '0' +epoxy_has_wgl = build_wgl ? '1' : '0' + +# We don't want to add these dependencies to the library, as they are +# not needed when building Epoxy; we do want to add them to the generated +# pkg-config file, for consumers of Epoxy +gl_reqs = [] +if gl_dep.found() and gl_dep.type_name() == 'pkgconfig' + gl_reqs += 'gl' +endif +if build_egl and egl_dep.found() and egl_dep.type_name() == 'pkgconfig' + gl_reqs += 'egl' +endif + +pkg = import('pkgconfig') +pkg.generate( + libraries: libepoxy, + name: 'epoxy', + description: 'GL dispatch library', + version: meson.project_version(), + variables: [ + 'epoxy_has_glx=@0@'.format(epoxy_has_glx), + 'epoxy_has_egl=@0@'.format(epoxy_has_egl), + 'epoxy_has_wgl=@0@'.format(epoxy_has_wgl), + ], + filebase: 'epoxy', + requires_private: ' '.join(gl_reqs), +) diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..78919f3 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,24 @@ +egl_and_glx_different_pointers_egl +egl_and_glx_different_pointers_egl_glx +egl_and_glx_different_pointers_glx +egl_has_extension_nocontext +egl_gl +egl_gles1_without_glx +egl_gles2_without_glx +glx_alias_prefer_same_name +glx_beginend +glx_gles2 +glx_glxgetprocaddress_nocontext +glx_has_extension_nocontext +glx_public_api +glx_public_api_core +glx_shared_znow +glx_static +headerguards +khronos_typedefs +miscdefines +wgl_core_and_exts +wgl_usefontbitmaps +wgl_usefontbitmaps_unicode +*.log +*.trs diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..fc3ffcd --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,192 @@ +# 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. + +EPOXY = $(builddir)/../src/libepoxy.la + +check_LTLIBRARIES = \ + $(EGL_UTIL_LIB) \ + $(GLX_UTIL_LIB) \ + $(WGL_UTIL_LIB) \ + $() + +libegl_common_la_SOURCES = \ + egl_common.c \ + egl_common.h + $() + +libglx_common_la_SOURCES = \ + glx_common.c \ + glx_common.h + $() + +libwgl_common_la_SOURCES = \ + wgl_common.c \ + wgl_common.h + $() +libwgl_common_la_LIBADD = $(EPOXY) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + $(X11_CFLAGS) \ + $(EGL_CFLAGS) \ + $() + +AM_CFLAGS = $(CWARNFLAGS) + +TESTS = \ + $(EGL_TESTS) \ + $(GLX_TESTS) \ + $(EGL_AND_GLX_TESTS) \ + $(WGL_TESTS) \ + gl_version$(EXEEXT) \ + headerguards$(EXEEXT) \ + miscdefines$(EXEEXT) \ + khronos_typedefs$(EXEEXT) \ + $() + +check_BINARIES = $(EGL_AND_GLX_BIN) + +XFAIL_TESTS = \ + $() + +check_PROGRAMS = $(TESTS) + +if BUILD_EGL +EGL_TESTS = \ + $() + +if HAVE_X11 +EGL_TESTS += \ + egl_has_extension_nocontext \ + egl_epoxy_api \ + egl_gles2_without_glx \ + $() + +if HAS_GLES1 +EGL_TESTS += egl_gles1_without_glx +endif + +EGL_UTIL_LIB = libegl_common.la +endif +endif + +if BUILD_GLX +if HAS_ZNOW +GLX_SHARED_ZNOW = glx_shared_znow +endif + +if BUILD_EGL +if BUILD_GLX +if HAVE_DLVSYM +EGL_AND_GLX_TESTS = \ + egl_gl \ + $() +endif +endif +endif + +if HAVE_DLVSYM +GLX_DLVSYM_TESTS = \ + glx_alias_prefer_same_name \ + glx_gles2 \ + $() +endif + +GLX_TESTS = \ + glx_beginend \ + glx_public_api \ + glx_public_api_core \ + glx_glxgetprocaddress_nocontext \ + glx_has_extension_nocontext \ + glx_static \ + $(GLX_SHARED_ZNOW) \ + $(GLX_DLVSYM_TESTS) \ + $() + +GLX_UTIL_LIB = libglx_common.la +endif + +if BUILD_WGL +WGL_TESTS = \ + wgl_core_and_exts$(EXEEXT) \ + wgl_per_context_funcptrs$(EXEEXT) \ + wgl_usefontbitmaps$(EXEEXT) \ + wgl_usefontbitmaps_unicode$(EXEEXT) \ + $() + +WGL_UTIL_LIB = libwgl_common.la +endif + +egl_has_extension_nocontext_LDADD = $(EPOXY) libegl_common.la $(X11_LIBS) + +egl_epoxy_api_LDADD = $(EPOXY) libegl_common.la $(X11_LIBS) + +egl_gl_LDADD = $(EPOXY) $(DLOPEN_LIBS) libegl_common.la $(X11_LIBS) + +egl_gles1_without_glx_CPPFLAGS = $(AM_CPPFLAGS) -DGLES_VERSION=1 +egl_gles1_without_glx_SOURCES = egl_without_glx.c +egl_gles1_without_glx_LDADD = $(EPOXY) $(DLOPEN_LIBS) libegl_common.la $(X11_LIBS) + +egl_gles2_without_glx_CPPFLAGS = $(AM_CPPFLAGS) -DGLES_VERSION=2 +egl_gles2_without_glx_SOURCES = egl_without_glx.c +egl_gles2_without_glx_LDADD = $(EPOXY) $(DLOPEN_LIBS) libegl_common.la $(X11_LIBS) + +glx_alias_prefer_same_name_SOURCES = glx_alias_prefer_same_name.c dlwrap.c dlwrap.h +glx_alias_prefer_same_name_LDFLAGS = -rdynamic +glx_alias_prefer_same_name_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) $(DLOPEN_LIBS) + +glx_beginend_LDADD = $(EPOXY) libglx_common.la $(GL_LIBS) $(X11_LIBS) + +glx_gles2_SOURCES = glx_gles2.c dlwrap.c dlwrap.h +glx_gles2_LDFLAGS = -rdynamic +glx_gles2_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) $(DLOPEN_LIBS) + +glx_public_api_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) + +glx_public_api_core_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) + +glx_glxgetprocaddress_nocontext_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) + +glx_has_extension_nocontext_LDADD = $(EPOXY) libglx_common.la $(X11_LIBS) + +glx_static_CFLAGS = -DNEEDS_TO_BE_STATIC +glx_static_LDADD = $(DLOPEN_LIBS) $(EPOXY) libglx_common.la $(X11_LIBS) +glx_static_LDFLAGS = -static + +glx_shared_znow_SOURCES = glx_static.c +glx_shared_znow_LDADD = $(DLOPEN_LIBS) $(EPOXY) libglx_common.la $(X11_LIBS) +glx_shared_znow_LDFLAGS = -Wl,-z,now + +khronos_typedefs_SOURCES = \ + khronos_typedefs.c \ + khronos_typedefs.h \ + khronos_typedefs_nonepoxy.c \ + $() + +wgl_core_and_exts_LDADD = $(EPOXY) libwgl_common.la -lgdi32 + +wgl_per_context_funcptrs_LDADD = $(EPOXY) libwgl_common.la -lgdi32 + +wgl_usefontbitmaps_LDADD = $(EPOXY) libwgl_common.la -lgdi32 +wgl_usefontbitmaps_unicode_SOURCES = wgl_usefontbitmaps.c +wgl_usefontbitmaps_unicode_LDADD = $(EPOXY) libwgl_common.la -lgdi32 +wgl_usefontbitmaps_unicode_CPPFLAGS = $(AM_CPPFLAGS) -DUNICODE diff --git a/test/cgl_core.c b/test/cgl_core.c new file mode 100644 index 0000000..9b56acf --- /dev/null +++ b/test/cgl_core.c @@ -0,0 +1,54 @@ +/* This is a copy of the test used by HomeBrew's libepoxy recipe, + * originally written by Mikko Lehtonen. + * + * The Homebrew recipe is released under the BSD 2-Clause license. + * + * Copyright (c) 2009-present, Homebrew contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <epoxy/gl.h> +#include <Carbon/Carbon.h> +#include <OpenGL/OpenGL.h> +#include <OpenGL/CGLTypes.h> +#include <OpenGL/CGLCurrent.h> +#include <OpenGL/CGLContext.h> + +int +main (void) +{ + CGLPixelFormatAttribute attribs[] = {0}; + CGLPixelFormatObj pix; + CGLContextObj ctx; + int npix; + + CGLChoosePixelFormat(attribs, &pix, &npix); + CGLCreateContext(pix, (void *) 0, &ctx); + + glClear(GL_COLOR_BUFFER_BIT); + + CGLReleaseContext(ctx); + CGLReleasePixelFormat(pix); + + return 0; +} diff --git a/test/cgl_epoxy_api.c b/test/cgl_epoxy_api.c new file mode 100644 index 0000000..e83222c --- /dev/null +++ b/test/cgl_epoxy_api.c @@ -0,0 +1,83 @@ +/* + * Copyright 2018 Emmanuele Bassi + * + * 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 cgl_epoxy_api.c + * + * Tests the Epoxy API using the CoreGraphics OpenGL framework. + */ + +#include <epoxy/gl.h> +#include <Carbon/Carbon.h> +#include <OpenGL/OpenGL.h> +#include <OpenGL/CGLTypes.h> +#include <OpenGL/CGLCurrent.h> +#include <OpenGL/CGLContext.h> + +int +main (void) +{ + CGLPixelFormatAttribute attribs[] = {0}; + CGLPixelFormatObj pix; + CGLContextObj ctx; + const char *string; + bool pass = true; + int npix; + GLint shader; + + CGLChoosePixelFormat(attribs, &pix, &npix); + CGLCreateContext(pix, (void *) 0, &ctx); + CGLSetCurrentContext(ctx); + + if (!epoxy_is_desktop_gl()) { + fputs("Claimed not to be desktop\n", stderr); + pass = false; + } + + if (epoxy_gl_version() < 20) { + fprintf(stderr, "Claimed to be GL version %d\n", + epoxy_gl_version()); + pass = false; + } + + if (epoxy_glsl_version() < 100) { + fprintf(stderr, "Claimed to have GLSL version %d\n", + epoxy_glsl_version()); + pass = false; + } + + string = (const char *)glGetString(GL_VERSION); + printf("GL version: %s - Epoxy: %d\n", string, epoxy_gl_version()); + + string = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION); + printf("GLSL version: %s - Epoxy: %d\n", string, epoxy_glsl_version()); + + shader = glCreateShader(GL_FRAGMENT_SHADER); + pass = glIsShader(shader); + + CGLSetCurrentContext(NULL); + CGLReleaseContext(ctx); + CGLReleasePixelFormat(pix); + + return pass != true; +} diff --git a/test/dlwrap.c b/test/dlwrap.c new file mode 100644 index 0000000..c0c24c2 --- /dev/null +++ b/test/dlwrap.c @@ -0,0 +1,325 @@ +/* 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 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 dlwrap.c + * + * Implements a wrapper for dlopen() and dlsym() so that epoxy will + * end up finding symbols from the testcases named + * "override_EGL_eglWhatever()" or "override_GLES2_glWhatever()" or + * "override_GL_glWhatever()" when it tries to dlopen() and dlsym() + * the real GL or EGL functions in question. + * + * This lets us simulate some target systems in the test suite, or + * just stub out GL functions so we can be sure of what's being + * called. + */ + +/* dladdr is a glibc extension */ +#define _GNU_SOURCE +#include <dlfcn.h> + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "dlwrap.h" + +#define STRNCMP_LITERAL(var, literal) \ + strncmp ((var), (literal), sizeof (literal) - 1) + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +void *libfips_handle; + +typedef void *(*fips_dlopen_t)(const char *filename, int flag); +typedef void *(*fips_dlsym_t)(void *handle, const char *symbol); + +void *override_EGL_eglGetProcAddress(const char *name); +void *override_GL_glXGetProcAddress(const char *name); +void *override_GL_glXGetProcAddressARB(const char *name); +void __dlclose(void *handle); + +static struct libwrap { + const char *filename; + const char *symbol_prefix; + void *handle; +} wrapped_libs[] = { + { "libGL.so", "GL", NULL }, + { "libEGL.so", "EGL", NULL }, + { "libGLESv2.so", "GLES2", NULL }, + { "libOpenGL.so", "GL", NULL}, +}; + +/* Match 'filename' against an internal list of libraries for which + * libfips has wrappers. + * + * Returns true and sets *index_ret if a match is found. + * Returns false if no match is found. */ +static struct libwrap * +find_wrapped_library(const char *filename) +{ + unsigned i; + + if (!filename) + return NULL; + + for (i = 0; i < ARRAY_SIZE(wrapped_libs); i++) { + if (strncmp(wrapped_libs[i].filename, filename, + strlen(wrapped_libs[i].filename)) == 0) { + return &wrapped_libs[i]; + } + } + + return NULL; +} + +/* Many (most?) OpenGL programs dlopen libGL.so.1 rather than linking + * against it directly, which means they would not be seeing our + * wrapped GL symbols via LD_PRELOAD. So we catch the dlopen in a + * wrapper here and redirect it to our library. + */ +void * +dlopen(const char *filename, int flag) +{ + void *ret; + struct libwrap *wrap; + + /* Before deciding whether to redirect this dlopen to our own + * library, we call the real dlopen. This assures that any + * expected side-effects from loading the intended library are + * resolved. Below, we may still return a handle pointing to + * our own library, and not what is opened here. */ + ret = dlwrap_real_dlopen(filename, flag); + + /* If filename is not a wrapped library, just return real dlopen */ + wrap = find_wrapped_library(filename); + if (!wrap) + return ret; + + wrap->handle = ret; + + /* We use wrapped_libs as our handles to libraries. */ + return wrap; +} + +/** + * Wraps dlclose to hide our faked handles from it. + */ +void +__dlclose(void *handle) +{ + struct libwrap *wrap = handle; + + if (wrap < wrapped_libs || + wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) { + void (*real_dlclose)(void *handle) = dlwrap_real_dlsym(RTLD_NEXT, "__dlclose"); + real_dlclose(handle); + } +} + +void * +dlwrap_real_dlopen(const char *filename, int flag) +{ + static fips_dlopen_t real_dlopen = NULL; + + if (!real_dlopen) { + real_dlopen = (fips_dlopen_t) dlwrap_real_dlsym(RTLD_NEXT, "dlopen"); + if (!real_dlopen) { + fputs("Error: Failed to find symbol for dlopen.\n", stderr); + exit(1); + } + } + + return real_dlopen(filename, flag); +} + +/** + * Return the dlsym() on the application's namespace for + * "override_<prefix>_<name>" + */ +static void * +wrapped_dlsym(const char *prefix, const char *name) +{ + char *wrap_name; + void *symbol; + + if (asprintf(&wrap_name, "override_%s_%s", prefix, name) < 0) { + fputs("Error: Failed to allocate memory.\n", stderr); + abort(); + } + + symbol = dlwrap_real_dlsym(RTLD_DEFAULT, wrap_name); + free(wrap_name); + return symbol; +} + +/* Since we redirect dlopens of libGL.so and libEGL.so to libfips we + * need to ensure that dlysm succeeds for all functions that might be + * defined in the real, underlying libGL library. But we're far too + * lazy to implement wrappers for function that would simply + * pass-through, so instead we also wrap dlysm and arrange for it to + * pass things through with RTLD_next if libfips does not have the + * function desired. */ +void * +dlsym(void *handle, const char *name) +{ + struct libwrap *wrap = handle; + + /* Make sure that handle is actually one of our wrapped libs. */ + if (wrap < wrapped_libs || + wrap >= wrapped_libs + ARRAY_SIZE(wrapped_libs)) { + wrap = NULL; + } + + /* Failing that, anything specifically requested from the + * libfips library should be redirected to a real GL + * library. */ + + if (wrap) { + void *symbol = wrapped_dlsym(wrap->symbol_prefix, name); + if (symbol) + return symbol; + else + return dlwrap_real_dlsym(wrap->handle, name); + } + + /* And anything else is some unrelated dlsym. Just pass it + * through. (This also covers the cases of lookups with + * special handles such as RTLD_DEFAULT or RTLD_NEXT.) + */ + return dlwrap_real_dlsym(handle, name); +} + +void * +dlwrap_real_dlsym(void *handle, const char *name) +{ + static fips_dlsym_t real_dlsym = NULL; + + if (!real_dlsym) { + /* FIXME: This brute-force, hard-coded searching for a versioned + * symbol is really ugly. The only reason I'm doing this is because + * I need some way to lookup the "dlsym" function in libdl, but + * I can't use 'dlsym' to do it. So dlvsym works, but forces me + * to guess what the right version is. + * + * Potential fixes here: + * + * 1. Use libelf to actually inspect libdl.so and + * find the right version, (finding the right + * libdl.so can be made easier with + * dl_iterate_phdr). + * + * 2. Use libelf to find the offset of the 'dlsym' + * symbol within libdl.so, (and then add this to + * the base address at which libdl.so is loaded + * as reported by dl_iterate_phdr). + * + * In the meantime, I'll just keep augmenting this + * hard-coded version list as people report bugs. */ + const char *version[] = { + "GLIBC_2.17", + "GLIBC_2.4", + "GLIBC_2.3", + "GLIBC_2.2.5", + "GLIBC_2.2", + "GLIBC_2.0", + "FBSD_1.0" + }; + int num_versions = sizeof(version) / sizeof(version[0]); + int i; + for (i = 0; i < num_versions; i++) { + real_dlsym = (fips_dlsym_t) dlvsym(RTLD_NEXT, "dlsym", version[i]); + if (real_dlsym) + break; + } + if (i == num_versions) { + fputs("Internal error: Failed to find real dlsym\n", stderr); + fputs("This may be a simple matter of fips not knowing about the version of GLIBC that\n" + "your program is using. Current known versions are:\n\n\t", + stderr); + for (i = 0; i < num_versions; i++) + fprintf(stderr, "%s ", version[i]); + fputs("\n\nYou can inspect your version by first finding libdl.so.2:\n" + "\n" + "\tldd <your-program> | grep libdl.so\n" + "\n" + "And then inspecting the version attached to the dlsym symbol:\n" + "\n" + "\treadelf -s /path/to/libdl.so.2 | grep dlsym\n" + "\n" + "And finally, adding the version to dlwrap.c:dlwrap_real_dlsym.\n", + stderr); + + exit(1); + } + } + + return real_dlsym(handle, name); +} + +void * +override_GL_glXGetProcAddress(const char *name) +{ + void *symbol; + + symbol = wrapped_dlsym("GL", name); + if (symbol) + return symbol; + + return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddress, + "glXGetProcAddress", (name)); +} + +void * +override_GL_glXGetProcAddressARB(const char *name) +{ + void *symbol; + + symbol = wrapped_dlsym("GL", name); + if (symbol) + return symbol; + + return DEFER_TO_GL("libGL.so.1", override_GL_glXGetProcAddressARB, + "glXGetProcAddressARB", (name)); +} + +void * +override_EGL_eglGetProcAddress(const char *name) +{ + void *symbol; + + if (!STRNCMP_LITERAL(name, "gl")) { + symbol = wrapped_dlsym("GLES2", name); + if (symbol) + return symbol; + } + + if (!STRNCMP_LITERAL(name, "egl")) { + symbol = wrapped_dlsym("EGL", name); + if (symbol) + return symbol; + } + + return DEFER_TO_GL("libEGL.so.1", override_EGL_eglGetProcAddress, + "eglGetProcAddress", (name)); +} diff --git a/test/dlwrap.h b/test/dlwrap.h new file mode 100644 index 0000000..39ec9ec --- /dev/null +++ b/test/dlwrap.h @@ -0,0 +1,67 @@ +/* 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 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. + */ + +#ifndef DLWRAP_H +#define DLWRAP_H + +#define _GNU_SOURCE +#include <dlfcn.h> + +/* Call the *real* dlopen. We have our own wrapper for dlopen that, of + * necessity must use claim the symbol 'dlopen'. So whenever anything + * internal needs to call the real, underlying dlopen function, the + * thing to call is dlwrap_real_dlopen. + */ +void * +dlwrap_real_dlopen(const char *filename, int flag); + +/* Perform a dlopen on the libfips library itself. + * + * Many places in fips need to lookup symbols within the libfips + * library itself, (and not in any other library). This function + * provides a reliable way to get a handle for performing such + * lookups. + * + * The returned handle can be passed to dlwrap_real_dlsym for the + * lookups. */ +void * +dlwrap_dlopen_libfips(void); + +/* Call the *real* dlsym. We have our own wrapper for dlsym that, of + * necessity must use claim the symbol 'dlsym'. So whenever anything + * internal needs to call the real, underlying dlysm function, the + * thing to call is dlwrap_real_dlsym. + */ +void * +dlwrap_real_dlsym(void *handle, const char *symbol); + +#define DEFER_TO_GL(library, func, name, args) \ +({ \ + void *lib = dlwrap_real_dlopen(library, RTLD_LAZY | RTLD_LOCAL); \ + typeof(&func) real_func = dlwrap_real_dlsym(lib, name); \ + /* gcc extension -- func's return value is the return value of \ + * the statement. \ + */ \ + real_func args; \ +}) + +#endif + diff --git a/test/egl_common.c b/test/egl_common.c new file mode 100644 index 0000000..c407912 --- /dev/null +++ b/test/egl_common.c @@ -0,0 +1,54 @@ +/* + * 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 <err.h> +#include <epoxy/egl.h> +#include <X11/Xlib.h> +#include "egl_common.h" + +/** + * Do whatever it takes to get us an EGL display for the system. + * + * This needs to be ported to other window systems. + */ +EGLDisplay * +get_egl_display_or_skip(void) +{ + Display *dpy = XOpenDisplay(NULL); + EGLint major, minor; + EGLDisplay *edpy; + bool ok; + + if (!dpy) + errx(77, "couldn't open display\n"); + + edpy = eglGetDisplay(dpy); + if (!edpy) + errx(1, "Couldn't get EGL display for X11 Display.\n"); + + ok = eglInitialize(edpy, &major, &minor); + if (!ok) + errx(1, "eglInitialize() failed\n"); + + return edpy; +} diff --git a/test/egl_common.h b/test/egl_common.h new file mode 100644 index 0000000..1c5963b --- /dev/null +++ b/test/egl_common.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + +EGLDisplay * +get_egl_display_or_skip(void); diff --git a/test/egl_epoxy_api.c b/test/egl_epoxy_api.c new file mode 100644 index 0000000..252b535 --- /dev/null +++ b/test/egl_epoxy_api.c @@ -0,0 +1,148 @@ +/* + * Copyright 2018 Emmanuele Bassi + * + * 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 epoxy_api.c + * + * Tests the Epoxy API using EGL. + */ + +#ifdef __sun +#define __EXTENSIONS__ +#else +#define _GNU_SOURCE +#endif +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/egl.h" + +#include "egl_common.h" + +static bool +make_egl_current_and_test(EGLDisplay *dpy, EGLContext ctx) +{ + const char *string; + GLuint shader; + bool pass = true; + + eglMakeCurrent(dpy, NULL, NULL, ctx); + + if (!epoxy_is_desktop_gl()) { + fputs("Claimed to be desktop\n", stderr); + pass = false; + } + + if (epoxy_gl_version() < 20) { + fprintf(stderr, "Claimed to be GL version %d\n", + epoxy_gl_version()); + pass = false; + } + + if (epoxy_glsl_version() < 100) { + fprintf(stderr, "Claimed to have GLSL version %d\n", + epoxy_glsl_version()); + pass = false; + } + + string = (const char *)glGetString(GL_VERSION); + printf("GL version: %s - Epoxy: %d\n", string, epoxy_gl_version()); + + string = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION); + printf("GLSL version: %s - Epoxy: %d\n", string, epoxy_glsl_version()); + + shader = glCreateShader(GL_FRAGMENT_SHADER); + pass = glIsShader(shader); + + return pass; +} + +static void +init_egl(EGLDisplay *dpy, EGLContext *out_ctx) +{ + static const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + EGLContext ctx; + EGLConfig cfg; + EGLint count; + + if (!epoxy_has_egl_extension(dpy, "EGL_KHR_surfaceless_context")) + errx(77, "Test requires EGL_KHR_surfaceless_context"); + + if (!eglBindAPI(EGL_OPENGL_API)) + errx(77, "Couldn't initialize EGL with desktop GL\n"); + + if (!eglChooseConfig(dpy, config_attribs, &cfg, 1, &count)) + errx(77, "Couldn't get an EGLConfig\n"); + + ctx = eglCreateContext(dpy, cfg, NULL, context_attribs); + if (!ctx) + errx(77, "Couldn't create a GL context\n"); + + *out_ctx = ctx; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + + EGLContext egl_ctx; + EGLDisplay *dpy = get_egl_display_or_skip(); + const char *extensions = eglQueryString(dpy, EGL_EXTENSIONS); + char *first_space; + char *an_extension; + + /* We don't have any extensions guaranteed by the ABI, so for the + * touch test we just check if the first one is reported to be there. + */ + first_space = strstr(extensions, " "); + if (first_space) { + an_extension = strndup(extensions, first_space - extensions); + } else { + an_extension = strdup(extensions); + } + + if (!epoxy_extension_in_string(extensions, an_extension)) + errx(1, "Implementation reported absence of %s", an_extension); + + free(an_extension); + + init_egl(dpy, &egl_ctx); + pass = make_egl_current_and_test(dpy, egl_ctx); + + return pass != true; +} diff --git a/test/egl_gl.c b/test/egl_gl.c new file mode 100644 index 0000000..1acc19e --- /dev/null +++ b/test/egl_gl.c @@ -0,0 +1,130 @@ +/* + * Copyright © 2014 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 egl_gl.c + * + * Tests that epoxy works with EGL using desktop OpenGL. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <err.h> +#include <dlfcn.h> +#include "epoxy/gl.h" +#include "epoxy/egl.h" +#include "epoxy/glx.h" + +#include "egl_common.h" +#include "glx_common.h" +#include "dlwrap.h" + +static bool +make_egl_current_and_test(EGLDisplay *dpy, EGLContext ctx) +{ + const char *string; + GLuint shader; + bool pass = true; + + eglMakeCurrent(dpy, NULL, NULL, ctx); + + if (!epoxy_is_desktop_gl()) { + fputs("Claimed to be desktop\n", stderr); + pass = false; + } + + if (epoxy_gl_version() < 20) { + fprintf(stderr, "Claimed to be GL version %d\n", + epoxy_gl_version()); + pass = false; + } + + string = (const char *)glGetString(GL_VERSION); + printf("GL version: %s\n", string); + + shader = glCreateShader(GL_FRAGMENT_SHADER); + pass = glIsShader(shader); + + return pass; +} + +static void +init_egl(EGLDisplay **out_dpy, EGLContext *out_ctx) +{ + EGLDisplay *dpy = get_egl_display_or_skip(); + static const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + EGLContext ctx; + EGLConfig cfg; + EGLint count; + + if (!epoxy_has_egl_extension(dpy, "EGL_KHR_surfaceless_context")) + errx(77, "Test requires EGL_KHR_surfaceless_context"); + + if (!eglBindAPI(EGL_OPENGL_API)) + errx(77, "Couldn't initialize EGL with desktop GL\n"); + + if (!eglChooseConfig(dpy, config_attribs, &cfg, 1, &count)) + errx(77, "Couldn't get an EGLConfig\n"); + + ctx = eglCreateContext(dpy, cfg, NULL, context_attribs); + if (!ctx) + errx(77, "Couldn't create a GL context\n"); + + *out_dpy = dpy; + *out_ctx = ctx; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + EGLDisplay *egl_dpy; + EGLContext egl_ctx; + + /* Force epoxy to have loaded both EGL and GLX libs already -- we + * can't assume anything about symbol resolution based on having + * EGL or GLX loaded. + */ + (void)glXGetCurrentContext(); + (void)eglGetCurrentContext(); + + init_egl(&egl_dpy, &egl_ctx); + pass = make_egl_current_and_test(egl_dpy, egl_ctx) && pass; + + return pass != true; +} diff --git a/test/egl_has_extension_nocontext.c b/test/egl_has_extension_nocontext.c new file mode 100644 index 0000000..4fa5f60 --- /dev/null +++ b/test/egl_has_extension_nocontext.c @@ -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. + */ + +/** + * @file egl_has_extension_nocontext.c + * + * Catches a bug in early development where eglGetProcAddress() with + * no context bound would fail out in dispatch. + */ + +#ifdef __sun +#define __EXTENSIONS__ +#else +#define _GNU_SOURCE +#endif +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/egl.h" + +#include "egl_common.h" + +int +main(int argc, char **argv) +{ + bool pass = true; + + EGLDisplay *dpy = get_egl_display_or_skip(); + const char *extensions = eglQueryString(dpy, EGL_EXTENSIONS); + char *first_space; + char *an_extension; + + /* We don't have any extensions guaranteed by the ABI, so for the + * touch test we just check if the first one is reported to be there. + */ + first_space = strstr(extensions, " "); + if (first_space) { + an_extension = strndup(extensions, first_space - extensions); + } else { + an_extension = strdup(extensions); + } + + if (!epoxy_has_egl_extension(dpy, an_extension)) + errx(1, "Implementation reported absence of %s", an_extension); + + free(an_extension); + + if (epoxy_has_egl_extension(dpy, "GLX_ARB_ham_sandwich")) + errx(1, "Implementation reported presence of GLX_ARB_ham_sandwich"); + + return pass != true; +} diff --git a/test/egl_without_glx.c b/test/egl_without_glx.c new file mode 100644 index 0000000..195ef41 --- /dev/null +++ b/test/egl_without_glx.c @@ -0,0 +1,165 @@ +/* + * Copyright © 2014 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 egl_without_glx.c + * + * Tries to test operation of the library on a GL stack with EGL and + * GLES but no GLX or desktop GL (such as Arm's Mali GLES3 drivers). + * This test is varied by the GLES_VERSION defined at compile time to + * test either a GLES1-only or a GLES2-only system. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <err.h> +#include <dlfcn.h> +#include "epoxy/gl.h" +#include "epoxy/egl.h" + +#include "egl_common.h" + +/** + * Wraps the system dlopen(), which libepoxy will end up calling when + * it tries to dlopen() the API libraries, and errors out the + * libraries we're trying to simulate not being installed on the + * system. + */ +void * +dlopen(const char *filename, int flag) +{ + void * (*dlopen_unwrapped)(const char *filename, int flag); + + if (filename) { + if (!strcmp(filename, "libGL.so.1")) + return NULL; +#if GLES_VERSION == 2 + if (!strcmp(filename, "libGLESv1_CM.so.1")) + return NULL; +#else + if (!strcmp(filename, "libGLESv2.so.2")) + return NULL; +#endif + } + + dlopen_unwrapped = dlsym(RTLD_NEXT, "dlopen"); + assert(dlopen_unwrapped); + + return dlopen_unwrapped(filename, flag); +} + + +static EGLenum last_api; +static EGLenum extra_error = EGL_SUCCESS; + +/** + * Override of the real libEGL's eglBindAPI to simulate the target + * system's eglBindAPI. + */ +static EGLBoolean +override_eglBindAPI(EGLenum api) +{ + void *egl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL); + EGLBoolean (*real_eglBindAPI)(EGLenum api) = dlsym(egl, "eglBindAPI"); + + last_api = api; + + if (api == EGL_OPENGL_API) { + extra_error = EGL_BAD_PARAMETER; + return EGL_FALSE; + } + + assert(real_eglBindAPI); + return real_eglBindAPI(api); +} + +/** + * Override of the real libEGL's eglGetError() to feed back the error + * that might have been generated by override_eglBindAPI(). + */ +static EGLint +override_eglGetError(void) +{ + void *egl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL); + EGLint (*real_eglGetError)(void) = dlsym(egl, "eglGetError"); + + if (extra_error != EGL_SUCCESS) { + EGLenum error = extra_error; + extra_error = EGL_SUCCESS; + return error; + } + + assert(real_eglGetError); + return real_eglGetError(); +} + +int +main(int argc, char **argv) +{ + bool pass = true; + EGLDisplay *dpy = get_egl_display_or_skip(); + EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, + EGL_NONE + }; + EGLConfig cfg; + EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + EGLint count; + EGLContext ctx; + const unsigned char *string; + + epoxy_eglBindAPI = override_eglBindAPI; + epoxy_eglGetError = override_eglGetError; + + if (!epoxy_has_egl_extension(dpy, "EGL_KHR_surfaceless_context")) + errx(77, "Test requires EGL_KHR_surfaceless_context"); + + eglBindAPI(EGL_OPENGL_ES_API); + + if (!eglChooseConfig(dpy, config_attribs, &cfg, 1, &count)) + errx(77, "Couldn't get an EGLConfig\n"); + + ctx = eglCreateContext(dpy, cfg, NULL, context_attribs); + if (!ctx) + errx(77, "Couldn't create a GLES%d context\n", GLES_VERSION); + + eglMakeCurrent(dpy, NULL, NULL, ctx); + + string = glGetString(GL_VERSION); + printf("GL_VERSION: %s\n", string); + + assert(eglGetError() == EGL_SUCCESS); + + return pass != true; +} diff --git a/test/gl_version.c b/test/gl_version.c new file mode 100644 index 0000000..9ab0080 --- /dev/null +++ b/test/gl_version.c @@ -0,0 +1,89 @@ +/* + * Copyright © 2018 Broadcom + * + * 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 <stdio.h> +#include <assert.h> +#include "epoxy/gl.h" + +GLenum mock_enum; +const char *mock_gl_version; +const char *mock_glsl_version; + +static const GLubyte *override_glGetString(GLenum name) +{ + switch (name) { + case GL_VERSION: + return (GLubyte *)mock_gl_version; + case GL_SHADING_LANGUAGE_VERSION: + return (GLubyte *)mock_glsl_version; + default: + assert(!"unexpected glGetString() enum"); + return 0; + } +} + +static bool +test_version(const char *gl_string, int gl_version, + const char *glsl_string, int glsl_version) +{ + int epoxy_version; + + mock_gl_version = gl_string; + mock_glsl_version = glsl_string; + + epoxy_version = epoxy_gl_version(); + if (epoxy_version != gl_version) { + fprintf(stderr, + "glGetString(GL_VERSION) = \"%s\" returned epoxy_gl_version() " + "%d instead of %d\n", gl_string, epoxy_version, gl_version); + return false; + } + + + epoxy_version = epoxy_glsl_version(); + if (epoxy_version != glsl_version) { + fprintf(stderr, + "glGetString() = \"%s\" returned epoxy_glsl_version() " + "%d instead of %d\n", glsl_string, epoxy_version, glsl_version); + return false; + } + + return true; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + + epoxy_glGetString = override_glGetString; + + pass = pass && test_version("3.0 Mesa 13.0.6", 30, + "1.30", 130); + pass = pass && test_version("OpenGL ES 3.2 Mesa 18.3.0-devel", 32, + "OpenGL ES GLSL ES 3.20", 320); + pass = pass && test_version("4.5.0 NVIDIA 384.130", 45, + "4.50", 450); + + return pass != true; +} diff --git a/test/glx_alias_prefer_same_name.c b/test/glx_alias_prefer_same_name.c new file mode 100644 index 0000000..cfc1344 --- /dev/null +++ b/test/glx_alias_prefer_same_name.c @@ -0,0 +1,82 @@ +/* + * 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_gles2.c + * + * Catches a bug where a GLES2 context using + * GLX_EXT_create_context_es2_profile would try to find the symbols in + * libGLESv2.so.2 instead of libGL.so.1. + */ + +#include <stdio.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; + +static int last_call; + +#define CORE_FUNC_VAL 100 +#define EXT_FUNC_VAL 101 + +void +override_GL_glBindTexture(GLenum target); +void +override_GL_glBindTextureEXT(GLenum target); + +void +override_GL_glBindTexture(GLenum target) +{ + last_call = CORE_FUNC_VAL; +} + +void +override_GL_glBindTextureEXT(GLenum target) +{ + last_call = EXT_FUNC_VAL; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + + dpy = get_display_or_skip(); + make_glx_context_current_or_skip(dpy); + + if (!epoxy_has_gl_extension("GL_EXT_texture_object")) + errx(77, "Test requires GL_EXT_texture_object"); + + glBindTexture(GL_TEXTURE_2D, 1); + pass = pass && last_call == CORE_FUNC_VAL; + glBindTextureEXT(GL_TEXTURE_2D, 1); + pass = pass && last_call == EXT_FUNC_VAL; + + return pass != true; +} diff --git a/test/glx_beginend.c b/test/glx_beginend.c new file mode 100644 index 0000000..c68f408 --- /dev/null +++ b/test/glx_beginend.c @@ -0,0 +1,107 @@ +/* + * 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 <stdio.h> +#include <assert.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; +static bool has_argb2101010; + +static bool +test_with_epoxy(void) +{ + glBegin(GL_TRIANGLES); + { + /* Hit a base entrypoint that won't call gl_version() */ + glVertex2f(0, 0); + + /* Hit an entrypoint that will call probably call gl_version() */ + glMultiTexCoord4f(GL_TEXTURE0, 0.0, 0.0, 0.0, 0.0); + + /* Hit an entrypoint that will probably call + * epoxy_conservative_has_extension(); + */ + if (has_argb2101010) { + glTexCoordP4ui(GL_UNSIGNED_INT_2_10_10_10_REV, 0); + } + } + glEnd(); + + /* No error should have been generated in the process. */ + return glGetError() == 0; +} + + + +#undef glBegin +#undef glEnd +extern void glBegin(GLenum primtype); +extern void glEnd(void); + +static bool +test_without_epoxy(void) +{ + glBegin(GL_TRIANGLES); + { + /* Hit a base entrypoint that won't call gl_version() */ + glVertex4f(0, 0, 0, 0); + + /* Hit an entrypoint that will call probably call gl_version() */ + glMultiTexCoord3f(GL_TEXTURE0, 0.0, 0.0, 0.0); + + /* Hit an entrypoint that will probably call + * epoxy_conservative_has_extension(); + */ + if (has_argb2101010) { + glTexCoordP3ui(GL_UNSIGNED_INT_2_10_10_10_REV, 0); + } + } + glEnd(); + + /* We can't make any assertions about error presence this time + * around. This test is just trying to catch segfaults. + */ + return true; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + + dpy = get_display_or_skip(); + make_glx_context_current_or_skip(dpy); + + has_argb2101010 = + epoxy_has_gl_extension("GL_ARB_vertex_type_2_10_10_10_rev"); + + pass = pass && test_with_epoxy(); + pass = pass && test_without_epoxy(); + + return pass != true; +} diff --git a/test/glx_common.c b/test/glx_common.c new file mode 100644 index 0000000..7f2fbe6 --- /dev/null +++ b/test/glx_common.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2009, 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 <stdio.h> +#include <stdlib.h> +#include <X11/Xlib.h> +#include "glx_common.h" + +Display * +get_display_or_skip(void) +{ + Display *dpy = XOpenDisplay(NULL); + + if (!dpy) { + fputs("couldn't open display\n", stderr); + exit(77); + } + + return dpy; +} + +XVisualInfo * +get_glx_visual(Display *dpy) +{ + XVisualInfo *visinfo; + int attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + None + }; + int screen = DefaultScreen(dpy); + + visinfo = glXChooseVisual(dpy, screen, attrib); + if (visinfo == NULL) { + fputs("Couldn't get an RGBA, double-buffered visual\n", stderr); + exit(1); + } + + return visinfo; +} + +Window +get_glx_window(Display *dpy, XVisualInfo *visinfo, bool map) +{ + XSetWindowAttributes window_attr; + unsigned long mask; + int screen = DefaultScreen(dpy); + Window root_win = RootWindow(dpy, screen); + Window win; + + window_attr.background_pixel = 0; + window_attr.border_pixel = 0; + window_attr.colormap = XCreateColormap(dpy, root_win, + visinfo->visual, AllocNone); + window_attr.event_mask = StructureNotifyMask | ExposureMask | + KeyPressMask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + win = XCreateWindow(dpy, root_win, 0, 0, + 10, 10, /* width, height */ + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &window_attr); + + return win; +} + +void +make_glx_context_current_or_skip(Display *dpy) +{ + GLXContext ctx; + XVisualInfo *visinfo = get_glx_visual(dpy); + Window win = get_glx_window(dpy, visinfo, false); + + ctx = glXCreateContext(dpy, visinfo, False, True); + if (ctx == None) { + fputs("glXCreateContext failed\n", stderr); + exit(1); + } + + glXMakeCurrent(dpy, win, ctx); +} + +GLXFBConfig +get_fbconfig_for_visinfo(Display *dpy, XVisualInfo *visinfo) +{ + int i, nconfigs; + GLXFBConfig ret = None, *configs; + + configs = glXGetFBConfigs(dpy, visinfo->screen, &nconfigs); + if (!configs) + return None; + + for (i = 0; i < nconfigs; i++) { + int v; + + if (glXGetFBConfigAttrib(dpy, configs[i], GLX_VISUAL_ID, &v)) + continue; + + if (v == visinfo->visualid) { + ret = configs[i]; + break; + } + } + + XFree(configs); + return ret; +} diff --git a/test/glx_common.h b/test/glx_common.h new file mode 100644 index 0000000..8b6c263 --- /dev/null +++ b/test/glx_common.h @@ -0,0 +1,39 @@ +/* + * 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 "epoxy/glx.h" + +Display * +get_display_or_skip(void); + +void +make_glx_context_current_or_skip(Display *dpy); + +GLXFBConfig +get_fbconfig_for_visinfo(Display *dpy, XVisualInfo *visinfo); + +XVisualInfo * +get_glx_visual(Display *dpy); + +Window +get_glx_window(Display *dpy, XVisualInfo *visinfo, bool map); diff --git a/test/glx_gles2.c b/test/glx_gles2.c new file mode 100644 index 0000000..969d26b --- /dev/null +++ b/test/glx_gles2.c @@ -0,0 +1,118 @@ +/* + * 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_gles2.c + * + * Catches a bug where a GLES2 context using + * GLX_EXT_create_context_es2_profile would try to find the symbols in + * libGLESv2.so.2 instead of libGL.so.1. + */ + +#include <stdio.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; + +GLuint +override_GLES2_glCreateShader(GLenum target); + +GLuint +override_GLES2_glCreateShader(GLenum target) +{ + return 0; +} + +void +override_GLES2_glGenQueries(GLsizei n, GLuint *ids); + +void +override_GLES2_glGenQueries(GLsizei n, GLuint *ids) +{ + int i; + for (i = 0; i < n; i++) + ids[i] = 0; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + XVisualInfo *vis; + Window win; + GLXContext ctx; + GLXFBConfig config; + int context_attribs[] = { + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, + GLX_CONTEXT_MAJOR_VERSION_ARB, 2, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + 0 + }; + GLuint shader; + + dpy = get_display_or_skip(); + + if (!epoxy_has_glx_extension(dpy, 0, "GLX_EXT_create_context_es2_profile")) + errx(77, "Test requires GLX_EXT_create_context_es2_profile"); + + vis = get_glx_visual(dpy); + config = get_fbconfig_for_visinfo(dpy, vis); + win = get_glx_window(dpy, vis, false); + + ctx = glXCreateContextAttribsARB(dpy, config, NULL, true, + context_attribs); + + glXMakeCurrent(dpy, win, ctx); + + if (epoxy_is_desktop_gl()) { + errx(1, "GLES2 context creation made a desktop context\n"); + } + + if (epoxy_gl_version() < 20) { + errx(1, "GLES2 context creation made a version %f context\n", + epoxy_gl_version() / 10.0f); + } + + /* Test using an entrypoint that's in GLES2, but not the desktop GL ABI. */ + shader = glCreateShader(GL_FRAGMENT_SHADER); + if (shader == 0) + errx(1, "glCreateShader() failed\n"); + glDeleteShader(shader); + + if (epoxy_gl_version() >= 30) { + GLuint q = 0; + + glGenQueries(1, &q); + if (!q) + errx(1, "glGenQueries() failed\n"); + glDeleteQueries(1, &q); + } + + return pass != true; +} diff --git a/test/glx_glxgetprocaddress_nocontext.c b/test/glx_glxgetprocaddress_nocontext.c new file mode 100644 index 0000000..2182215 --- /dev/null +++ b/test/glx_glxgetprocaddress_nocontext.c @@ -0,0 +1,57 @@ +/* + * 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_glxgetprocaddress_nocontext.c + * + * Catches a bug in early development where glXGetProcAddress() with + * no context bound would fail out in dispatch. + */ + +#include <stdio.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; + +int +main(int argc, char **argv) +{ + bool pass = true; + void *func; + + dpy = get_display_or_skip(); + if (epoxy_glx_version(dpy, 0) < 14) + errx(77, "GLX version 1.4 required for glXGetProcAddress().\n"); + + func = glXGetProcAddress((const GLubyte *)"glGetString"); + if (!func) + errx(1, "glXGetProcAddress() returned NULL\n"); + + return pass != true; +} diff --git a/test/glx_has_extension_nocontext.c b/test/glx_has_extension_nocontext.c new file mode 100644 index 0000000..2f87ac3 --- /dev/null +++ b/test/glx_has_extension_nocontext.c @@ -0,0 +1,56 @@ +/* + * 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_has_extension_nocontext.c + * + * Catches a bug in early development where glXGetProcAddress() with + * no context bound would fail out in dispatch. + */ + +#include <stdio.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; + +int +main(int argc, char **argv) +{ + bool pass = true; + + dpy = get_display_or_skip(); + + if (!epoxy_has_glx_extension(dpy, 0, "GLX_ARB_get_proc_address")) + errx(1, "Implementation reported absence of GLX_ARB_get_proc_address"); + + if (epoxy_has_glx_extension(dpy, 0, "GLX_ARB_ham_sandwich")) + errx(1, "Implementation reported presence of GLX_ARB_ham_sandwich"); + + return pass != true; +} diff --git a/test/glx_public_api.c b/test/glx_public_api.c new file mode 100644 index 0000000..aecdd2a --- /dev/null +++ b/test/glx_public_api.c @@ -0,0 +1,123 @@ +/* + * 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 <stdio.h> +#include <assert.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; + +static bool +test_gl_version(void) +{ + int version = epoxy_gl_version(); + if (version < 12) { + fprintf(stderr, + "Reported GL version %d, should be at least 12\n", + version); + return false; + } + + return true; +} + +static bool +test_glx_version(void) +{ + int version = epoxy_glx_version(dpy, 0); + const char *version_string; + int ret; + int server_major, server_minor; + int client_major, client_minor; + int server, client, expected; + + if (version < 13) { + fprintf(stderr, + "Reported GLX version %d, should be at least 13 " + "according to Linux GL ABI\n", + version); + return false; + } + + version_string = glXQueryServerString(dpy, 0, GLX_VERSION); + ret = sscanf(version_string, "%d.%d", &server_major, &server_minor); + assert(ret == 2); + server = server_major * 10 + server_minor; + + version_string = glXGetClientString(dpy, GLX_VERSION); + ret = sscanf(version_string, "%d.%d", &client_major, &client_minor); + assert(ret == 2); + client = client_major * 10 + client_minor; + + if (client < server) + expected = client; + else + expected = server; + + if (version != expected) { + fprintf(stderr, + "Reported GLX version %d, should be %d (%s)\n", + version, expected, version_string); + return false; + } + + return true; +} + +static bool +test_glx_extension_supported(void) +{ + if (!epoxy_has_glx_extension(dpy, 0, "GLX_ARB_get_proc_address")) { + fputs("Incorrectly reported no support for GLX_ARB_get_proc_address " + "(should always be present in Linux ABI)\n", + stderr); + return false; + } + + if (epoxy_has_glx_extension(dpy, 0, "GLX_EXT_ham_sandwich")) { + fputs("Incorrectly reported support for GLX_EXT_ham_sandwich\n", + stderr); + return false; + } + + return true; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + + dpy = get_display_or_skip(); + make_glx_context_current_or_skip(dpy); + + pass = test_gl_version() && pass; + pass = test_glx_version() && pass; + pass = test_glx_extension_supported() && pass; + + return pass != true; +} diff --git a/test/glx_public_api_core.c b/test/glx_public_api_core.c new file mode 100644 index 0000000..f5a4f04 --- /dev/null +++ b/test/glx_public_api_core.c @@ -0,0 +1,181 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <err.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> + +#include "glx_common.h" + +static Display *dpy; + +static bool +test_has_extensions(void) +{ + int num_extensions; + + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + + for (int i = 0; i < num_extensions; i++) { + char *ext = (char *)glGetStringi(GL_EXTENSIONS, i); + + if (!epoxy_has_gl_extension(ext)) { + fprintf(stderr, "GL implementation reported support for %s, " + "but epoxy didn't\n", ext); + return false; + } + } + + if (epoxy_has_gl_extension("GL_ARB_ham_sandwich")) { + fputs("epoxy implementation reported support for " + "GL_ARB_ham_sandwich, but it shouldn't\n", + stderr); + return false; + } + + return true; +} + +static bool +test_gl_version(void) +{ + int gl_version, epoxy_version; + int major, minor; + + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + gl_version = major * 10 + minor; + + if (gl_version < 32) { + fprintf(stderr, + "Implementation reported GL version %d, should be at least 32\n", + gl_version); + return false; + } + + epoxy_version = epoxy_gl_version(); + if (epoxy_version != gl_version) { + fprintf(stderr, + "Epoxy reported GL version %d, should be %d\n", + epoxy_version, gl_version); + return false; + } + + return true; +} + +static bool +test_glx_version(void) +{ + int version = epoxy_glx_version(dpy, 0); + const char *version_string; + int ret; + int server_major, server_minor; + int client_major, client_minor; + int server, client, expected; + + if (version < 13) { + fprintf(stderr, + "Reported GLX version %d, should be at least 13 " + "according to Linux GL ABI\n", + version); + return false; + } + + version_string = glXQueryServerString(dpy, 0, GLX_VERSION); + ret = sscanf(version_string, "%d.%d", &server_major, &server_minor); + assert(ret == 2); + server = server_major * 10 + server_minor; + + version_string = glXGetClientString(dpy, GLX_VERSION); + ret = sscanf(version_string, "%d.%d", &client_major, &client_minor); + assert(ret == 2); + client = client_major * 10 + client_minor; + + if (client < server) + expected = client; + else + expected = server; + + if (version != expected) { + fprintf(stderr, + "Reported GLX version %d, should be %d (%s)\n", + version, expected, version_string); + return false; + } + + return true; +} + +static int +error_handler(Display *d, XErrorEvent *ev) +{ + return 0; +} + +int +main(int argc, char **argv) +{ + bool pass = true; + XVisualInfo *visinfo; + Window win; + GLXFBConfig config; + static const int attribs[] = { + GLX_CONTEXT_PROFILE_MASK_ARB, + GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_MAJOR_VERSION_ARB, + 3, + GLX_CONTEXT_MINOR_VERSION_ARB, + 2, + None + }; + GLXContext ctx; + int (*old_handler)(Display *, XErrorEvent *); + + dpy = get_display_or_skip(); + + if (!epoxy_has_glx_extension(dpy, 0, "GLX_ARB_create_context_profile")) + errx(77, "Test requires GLX_ARB_create_context_profile"); + + visinfo = get_glx_visual(dpy); + win = get_glx_window(dpy, visinfo, false); + config = get_fbconfig_for_visinfo(dpy, visinfo); + + old_handler = XSetErrorHandler(error_handler); + ctx = glXCreateContextAttribsARB(dpy, config, NULL, True, attribs); + if (ctx == None) + errx(77, "glXCreateContext failed"); + XSetErrorHandler(old_handler); + + glXMakeCurrent(dpy, win, ctx); + + pass = test_gl_version() && pass; + pass = test_glx_version() && pass; + pass = test_has_extensions() && pass; + + return pass != true; +} diff --git a/test/glx_static.c b/test/glx_static.c new file mode 100644 index 0000000..1466f55 --- /dev/null +++ b/test/glx_static.c @@ -0,0 +1,70 @@ +/* + * 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_static.c + * + * Simple touch-test of using epoxy when linked statically. On Linux, + * the ifunc support we'd like to use has some significant behavior + * changes depending on whether it's a static build or shared library + * build. + * + * Note that if configured without --enable-static, this test will end + * up dynamically linked anyway, defeating the test. + */ + +#include <stdio.h> +#include <assert.h> +#include "epoxy/gl.h" +#include "epoxy/glx.h" +#include <X11/Xlib.h> +#include <dlfcn.h> + +#include "glx_common.h" + +int +main(int argc, char **argv) +{ + bool pass = true; + int val; + +#if NEEDS_TO_BE_STATIC + if (dlsym(NULL, "epoxy_glCompileShader")) { + fputs("glx_static requires epoxy built with --enable-static\n", stderr); + return 77; + } +#endif + + Display *dpy = get_display_or_skip(); + make_glx_context_current_or_skip(dpy); + + glEnable(GL_LIGHTING); + val = 0; + glGetIntegerv(GL_LIGHTING, &val); + if (!val) { + fputs("Enabling GL_LIGHTING didn't stick.\n", stderr); + pass = false; + } + + return pass != true; +} diff --git a/test/headerguards.c b/test/headerguards.c new file mode 100644 index 0000000..c5e5e69 --- /dev/null +++ b/test/headerguards.c @@ -0,0 +1,60 @@ +/* + * 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 "config.h" + +#include <epoxy/gl.h> + +#ifdef BUILD_EGL +#include <epoxy/egl.h> +#include <epoxy/egl.h> +#endif + +#ifdef BUILD_GLX +#include <epoxy/glx.h> +#include <epoxy/glx.h> +#endif + +#ifdef BUILD_EGL +#include <EGL/egl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <EGL/eglext.h> +#endif + +#ifdef BUILD_GLX +#ifdef __APPLE__ +#include <OpenGL/gl.h> +#include <OpenGL/glext.h> +#else +#include <GL/gl.h> +#include <GL/glext.h> +#endif +#include <GL/glx.h> +#include <GL/glxext.h> +#endif + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/test/khronos_typedefs.c b/test/khronos_typedefs.c new file mode 100644 index 0000000..f28fb67 --- /dev/null +++ b/test/khronos_typedefs.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2014 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 <stdio.h> +#include "khronos_typedefs.h" +#include "epoxy/gl.h" + +#define COMPARE_SIZE(type) \ + do { \ + if (sizeof(type) != system_sizes[type ## _slot]) { \ + fprintf(stderr, "system %s is size %d, epoxy is %d\n", \ + #type, \ + (int)system_sizes[type ## _slot], \ + (int)sizeof(type)); \ + error = true; \ + } \ +} while (0) + +int +main(int argc, char **argv) +{ + uint32_t system_sizes[khronos_typedef_count]; + bool error = false; + + get_system_typedef_sizes(system_sizes); + + COMPARE_SIZE(khronos_int8_t); + COMPARE_SIZE(khronos_uint8_t); + COMPARE_SIZE(khronos_int16_t); + COMPARE_SIZE(khronos_uint16_t); + COMPARE_SIZE(khronos_int32_t); + COMPARE_SIZE(khronos_uint32_t); + COMPARE_SIZE(khronos_int64_t); + COMPARE_SIZE(khronos_uint64_t); + COMPARE_SIZE(khronos_intptr_t); + COMPARE_SIZE(khronos_uintptr_t); + COMPARE_SIZE(khronos_ssize_t); + COMPARE_SIZE(khronos_usize_t); + COMPARE_SIZE(khronos_float_t); + COMPARE_SIZE(khronos_utime_nanoseconds_t); + COMPARE_SIZE(khronos_stime_nanoseconds_t); + COMPARE_SIZE(khronos_boolean_enum_t); + + return error; +} diff --git a/test/khronos_typedefs.h b/test/khronos_typedefs.h new file mode 100644 index 0000000..bcb4dab --- /dev/null +++ b/test/khronos_typedefs.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2014 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 <stdint.h> + +enum typedef_slot { + khronos_int8_t_slot, + khronos_uint8_t_slot, + khronos_int16_t_slot, + khronos_uint16_t_slot, + khronos_int32_t_slot, + khronos_uint32_t_slot, + khronos_int64_t_slot, + khronos_uint64_t_slot, + khronos_intptr_t_slot, + khronos_uintptr_t_slot, + khronos_ssize_t_slot, + khronos_usize_t_slot, + khronos_float_t_slot, + /* khrplatform.h claims it defines khronos_time_ns_t, but it doesn't. */ + khronos_utime_nanoseconds_t_slot, + khronos_stime_nanoseconds_t_slot, + khronos_boolean_enum_t_slot, + khronos_typedef_count +}; + +void get_system_typedef_sizes(uint32_t *sizes); diff --git a/test/khronos_typedefs_nonepoxy.c b/test/khronos_typedefs_nonepoxy.c new file mode 100644 index 0000000..d249545 --- /dev/null +++ b/test/khronos_typedefs_nonepoxy.c @@ -0,0 +1,69 @@ +/* + * Copyright © 2014 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 <stdio.h> +#include <stdlib.h> + +#include "config.h" +#include "khronos_typedefs.h" + +#ifdef HAVE_KHRPLATFORM_H + +#include <KHR/khrplatform.h> + +#define GET_SIZE(type) sizes[type ## _slot] = sizeof(type) + +void +get_system_typedef_sizes(uint32_t *sizes) +{ + GET_SIZE(khronos_int8_t); + GET_SIZE(khronos_uint8_t); + GET_SIZE(khronos_int16_t); + GET_SIZE(khronos_uint16_t); + GET_SIZE(khronos_int32_t); + GET_SIZE(khronos_uint32_t); + GET_SIZE(khronos_int64_t); + GET_SIZE(khronos_uint64_t); + GET_SIZE(khronos_intptr_t); + GET_SIZE(khronos_uintptr_t); + GET_SIZE(khronos_ssize_t); + GET_SIZE(khronos_usize_t); + GET_SIZE(khronos_float_t); + GET_SIZE(khronos_utime_nanoseconds_t); + GET_SIZE(khronos_stime_nanoseconds_t); + GET_SIZE(khronos_boolean_enum_t); +} + +#else /* !HAVE_KHRPLATFORM_H */ + +/* Don't care -- this is a conditional case in test code. */ +#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" + +void +get_system_typedef_sizes(uint32_t *sizes) +{ + fputs("./configure failed to find khrplatform.h\n", stderr); + exit(77); +} + +#endif diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 0000000..862d57a --- /dev/null +++ b/test/meson.build @@ -0,0 +1,184 @@ +dl_dep = cc.find_library('dl', required: false) +has_dlvsym = cc.has_function('dlvsym', dependencies: dl_dep) + +has_gles1 = gles1_dep.found() +has_gles2 = gles2_dep.found() +build_x11_tests = enable_x11 and x11_dep.found() + +test_cflags = common_cflags +if not has_dlvsym +test_cflags += [ + '-D_XOPEN_SOURCE', + '-D_POSIX_C_SOURCE=200809L', +] +endif + +# Unconditionally built tests +test('header_guards', + executable('header guards', 'headerguards.c', + c_args: common_cflags, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc)) +test('misc_defines', + executable('misc defines', 'miscdefines.c', + c_args: common_cflags, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc)) +test('khronos_typedefs', + executable('khronos typedefs', [ + 'khronos_typedefs.c', + 'khronos_typedefs.h', + 'khronos_typedefs_nonepoxy.c', + ], + c_args: common_cflags, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc)) +test('gl_version', + executable('gl_version', + 'gl_version.c', + c_args: common_cflags, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc)) + +if build_egl and build_x11_tests + egl_common_sources = [ 'egl_common.h', 'egl_common.c', ] + egl_common_lib = static_library('egl_common', + sources: egl_common_sources, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc, + c_args: common_cflags, + install: false) + + egl_tests = [ + [ 'egl_has_extension_nocontext', [], [ 'egl_has_extension_nocontext.c' ], true, ], + [ 'egl_epoxy_api', [], [ 'egl_epoxy_api.c' ], true ], + [ 'egl_gles1_without_glx', [ '-DGLES_VERSION=1', ], [ 'egl_without_glx.c' ], has_gles1, ], + [ 'egl_gles2_without_glx', [ '-DGLES_VERSION=2', ], [ 'egl_without_glx.c' ], has_gles2, ], + ] + + if build_glx + egl_tests += [ + [ 'egl_gl', [], [ 'egl_gl.c' ], true, ], + ] + endif + + foreach test: egl_tests + test_name = test[0] + test_source = test[2] + test_args = test[1] + test_run = test[3] + + if test_run + test_bin = executable(test_name, test_source, + c_args: test_cflags + test_args, + include_directories: libepoxy_inc, + dependencies: [ libepoxy_dep, x11_dep, egl_dep, dl_dep ], + link_with: egl_common_lib, + link_args: '-rdynamic') + test(test_name, test_bin) + endif + endforeach +endif + +if build_glx and build_x11_tests + glx_common_sources = [ 'glx_common.h', 'glx_common.c', ] + glx_common_lib = static_library('glx_common', + sources: glx_common_sources, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc, + c_args: common_cflags, + install: false) + + # glx_beginend links directly with the GL library, so we need to check it + # separately + test('glx_beginend', executable('glx_beginend', 'glx_beginend.c', + c_args: test_cflags, + include_directories: libepoxy_inc, + dependencies: [ libepoxy_dep, x11_dep, gl_dep, dl_dep ], + link_with: glx_common_lib)) + + glx_tests = [ + [ 'glx_public_api', [ 'glx_public_api.c' ], [], [], true ], + [ 'glx_public_api_core', [ 'glx_public_api_core.c' ], [], [], true ], + [ 'glx_glxgetprocaddress_nocontext', [ 'glx_glxgetprocaddress_nocontext.c' ], [], [], true ], + [ 'glx_has_extension_nocontext', [ 'glx_has_extension_nocontext.c' ], [], [], true ], + [ 'glx_static', [ 'glx_static.c' ], [ '-DNEEDS_TO_BE_STATIC'], [ '-static' ], libtype == 'static' ], + [ 'glx_shared_znow', [ 'glx_static.c', ], [], [ '-Wl,-z,now' ], has_znow ], + [ 'glx_alias_prefer_same_name', [ 'glx_alias_prefer_same_name.c', 'dlwrap.c', 'dlwrap.h' ], [], [ '-rdynamic' ], has_dlvsym ], + [ 'glx_gles2', [ 'glx_gles2.c', 'dlwrap.c', 'dlwrap.h' ], [], [ '-rdynamic' ], has_dlvsym ], + ] + + foreach test: glx_tests + test_name = test[0] + test_source = test[1] + test_c_args = test[2] + test_link_args = test[3] + test_run = test[4] + + if test_run + test_bin = executable(test_name, test_source, + c_args: test_cflags + test_c_args, + include_directories: libepoxy_inc, + dependencies: [ libepoxy_dep, x11_dep, dl_dep ], + link_with: glx_common_lib, + link_args: test_link_args) + test(test_name, test_bin) + endif + endforeach +endif + +# WGL +if build_wgl + wgl_common_sources = [ 'wgl_common.h', 'wgl_common.c', ] + wgl_common_lib = static_library('wgl_common', + sources: wgl_common_sources, + dependencies: libepoxy_dep, + include_directories: libepoxy_inc, + c_args: common_cflags, + install: false) + + wgl_tests = [ + [ 'wgl_core_and_exts', [ 'wgl_core_and_exts.c' ], [], ], + [ 'wgl_per_context_funcptrs', [ 'wgl_per_context_funcptrs.c' ], [], ], + [ 'wgl_usefontbitmaps', [ 'wgl_usefontbitmaps.c'], [], ], + [ 'wgl_usefontbitmaps_unicode', [ 'wgl_usefontbitmaps.c' ], [ '-DUNICODE' ], ], + ] + + foreach test: wgl_tests + test_name = test[0] + test_source = test[1] + test_c_args = test[2] + + test_bin = executable(test_name, test_source, + c_args: test_cflags + test_c_args, + include_directories: libepoxy_inc, + dependencies: [ libepoxy_dep ], + link_with: wgl_common_lib) + + test(test_name, test_bin) + endforeach +endif + +# Apple +if host_machine.system().contains('darwin') + opengl_dep = dependency('appleframeworks', modules: ['OpenGL', 'Carbon'], required: true) + + cgl_tests = [ + [ 'cgl_core', [ 'cgl_core.c' ] ], + [ 'cgl_epoxy_api', [ 'cgl_epoxy_api.c' ] ], + ] + + foreach t: cgl_tests + test_name = t[0] + test_sources = t[1] + + test(test_name, + executable( + test_name, test_sources, + c_args: test_cflags, + include_directories: libepoxy_inc, + dependencies: [ libepoxy_dep, opengl_dep ], + ), + ) + endforeach +endif diff --git a/test/miscdefines.c b/test/miscdefines.c new file mode 100644 index 0000000..e4bc79a --- /dev/null +++ b/test/miscdefines.c @@ -0,0 +1,67 @@ +/* + * 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 <epoxy/gl.h> + +#ifdef BUILD_EGL +#include <epoxy/egl.h> +#endif + +#ifdef BUILD_GLX +#include <epoxy/glx.h> +#endif + +#if GL_VERSION_3_2 != 1 +#error bad GL_VERSION_3_2 +#endif + +#if GL_ARB_ES2_compatibility != 1 +#error bad GL_ARB_ES2_compatibility +#endif + +#ifndef GLAPI +#error missing GLAPI +#endif + +#ifndef GLAPIENTRY +#error missing GLAPIENTRY +#endif + +#ifndef GLAPIENTRYP +#error missing GLAPIENTRYP +#endif + +#ifndef APIENTRY +#error missing APIENTRY +#endif + +#ifndef APIENTRYP +#error missing APIENTRYP +#endif + +/* Do we want to export GL_GLEXT_VERSION? */ + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/test/wgl_common.c b/test/wgl_common.c new file mode 100644 index 0000000..e8d9c26 --- /dev/null +++ b/test/wgl_common.c @@ -0,0 +1,128 @@ +/* + * 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 <stdio.h> +#include <epoxy/wgl.h> +#include "wgl_common.h" + +static int (*test_callback)(HDC hdc); + +static void +setup_pixel_format(HDC hdc) +{ + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW | + PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 32, + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 16, + 0, + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0, + }; + int pixel_format; + + pixel_format = ChoosePixelFormat(hdc, &pfd); + if (!pixel_format) { + fputs("ChoosePixelFormat failed.\n", stderr); + exit(1); + } + + if (SetPixelFormat(hdc, pixel_format, &pfd) != TRUE) { + fputs("SetPixelFormat() failed.\n", stderr); + exit(1); + } +} + +static LRESULT CALLBACK +window_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + HDC hdc = GetDC(hwnd); + int ret; + + switch (message) { + case WM_CREATE: + setup_pixel_format(hdc); + ret = test_callback(hdc); + ReleaseDC(hwnd, hdc); + exit(ret); + return 0; + default: + return DefWindowProc(hwnd, message, wparam, lparam); + } +} + +void +make_window_and_test(int (*callback)(HDC hdc)) +{ + const char *class_name = "epoxy"; + const char *window_name = "epoxy"; + int width = 150; + int height = 150; + HWND hwnd; + HINSTANCE hcurrentinst = NULL; + WNDCLASS window_class; + MSG msg; + + test_callback = callback; + + memset(&window_class, 0, sizeof(window_class)); + window_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = window_proc; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = hcurrentinst; + window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + window_class.hCursor = LoadCursor(NULL, IDC_ARROW); + window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + window_class.lpszMenuName = NULL; + window_class.lpszClassName = class_name; + if (!RegisterClass(&window_class)) { + fputs("Failed to register window class\n", stderr); + exit(1); + } + + /* create window */ + hwnd = CreateWindow(class_name, window_name, + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 0, 0, width, height, + NULL, NULL, hcurrentinst, NULL); + + ShowWindow(hwnd, SW_SHOWDEFAULT); + UpdateWindow(hwnd); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} diff --git a/test/wgl_common.h b/test/wgl_common.h new file mode 100644 index 0000000..8b13d96 --- /dev/null +++ b/test/wgl_common.h @@ -0,0 +1,27 @@ +/* + * 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 <epoxy/wgl.h> + +void +make_window_and_test(int (*callback)(HDC hdc)); diff --git a/test/wgl_core_and_exts.c b/test/wgl_core_and_exts.c new file mode 100644 index 0000000..7d22acc --- /dev/null +++ b/test/wgl_core_and_exts.c @@ -0,0 +1,80 @@ +/* + * 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 <stdio.h> + +#include "wgl_common.h" +#include <epoxy/gl.h> + +static int +test_function(HDC hdc) +{ + bool pass = true; + int val; + HGLRC ctx; + + ctx = wglCreateContext(hdc); + if (!ctx) { + fputs("Failed to create wgl context\n", stderr); + return 1; + } + if (!wglMakeCurrent(hdc, ctx)) { + fputs("Failed to make context current\n", stderr); + return 1; + } + + /* GL 1.0 APIs are available as symbols in opengl32.dll. */ + glEnable(GL_LIGHTING); + val = 0; + glGetIntegerv(GL_LIGHTING, &val); + if (!val) { + fputs("Enabling GL_LIGHTING didn't stick.\n", stderr); + pass = false; + } + + if (epoxy_gl_version() >= 15 || + epoxy_has_gl_extension("GL_ARB_vertex_buffer_object")) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 1234); + + val = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &val); + if (val != 1234) { + printf("GL_ELEMENT_ARRAY_BUFFER_BINDING didn't stick: %d\n", val); + pass = false; + } + } + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(ctx); + + return !pass; +} + +int +main(int argc, char **argv) +{ + make_window_and_test(test_function); + + /* UNREACHED */ + return 1; +} diff --git a/test/wgl_per_context_funcptrs.c b/test/wgl_per_context_funcptrs.c new file mode 100644 index 0000000..2cf0dcb --- /dev/null +++ b/test/wgl_per_context_funcptrs.c @@ -0,0 +1,165 @@ +/* + * 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 wgl_per_context_funcptrs.c + * + * Tests that epoxy works correctly when wglGetProcAddress() returns + * different function pointers for different contexts. + * + * wgl allows that to be the case when the device or pixel format are + * different. We don't know if the underlying implementation actually + * *will* return different function pointers, so force the issue by + * overriding wglGetProcAddress() to return our function pointers with + * magic behavior. This way we can test epoxy's implementation + * regardless. + */ + +#include <stdio.h> +#include <assert.h> + +#include "wgl_common.h" +#include <epoxy/gl.h> + +#define CREATESHADER_CTX1_VAL 1001 +#define CREATESHADER_CTX2_VAL 1002 + +static HGLRC ctx1, ctx2, current_context; +static bool pass = true; + +#define OVERRIDE_API(type) __declspec(dllexport) type __stdcall + +OVERRIDE_API (GLuint) override_glCreateShader_ctx1(GLenum target); +OVERRIDE_API (GLuint) override_glCreateShader_ctx2(GLenum target); +OVERRIDE_API (PROC) override_wglGetProcAddress(LPCSTR name); + +OVERRIDE_API (GLuint) +override_glCreateShader_ctx1(GLenum target) +{ + if (current_context != ctx1) { + fputs("ctx1 called while other context current\n", stderr); + pass = false; + } + return CREATESHADER_CTX1_VAL; +} + +OVERRIDE_API (GLuint) +override_glCreateShader_ctx2(GLenum target) +{ + if (current_context != ctx2) { + fputs("ctx2 called while other context current\n", stderr); + pass = false; + } + return CREATESHADER_CTX2_VAL; +} + +OVERRIDE_API (PROC) +override_wglGetProcAddress(LPCSTR name) +{ + assert(strcmp(name, "glCreateShader") == 0); + + if (current_context == ctx1) { + return (PROC)override_glCreateShader_ctx1; + } else { + assert(current_context == ctx2); + return (PROC)override_glCreateShader_ctx2; + } +} + +static void +test_createshader(HDC hdc, HGLRC ctx) +{ + GLuint shader, expected; + int ctxnum; + + wglMakeCurrent(hdc, ctx); + current_context = ctx; + + /* Install our GPA override so we can force per-context function + * pointers. + */ + wglGetProcAddress = override_wglGetProcAddress; + + if (ctx == ctx1) { + expected = CREATESHADER_CTX1_VAL; + ctxnum = 1; + } else { + assert(ctx == ctx2); + expected = CREATESHADER_CTX2_VAL; + ctxnum = 2; + } + + shader = glCreateShader(GL_FRAGMENT_SHADER); + printf("ctx%d: Returned %d\n", ctxnum, shader); + if (shader != expected) { + fprintf(stderr, " expected %d\n", expected); + pass = false; + } +} + +static int +test_function(HDC hdc) +{ + ctx1 = wglCreateContext(hdc); + ctx2 = wglCreateContext(hdc); + if (!ctx1 || !ctx2) { + fputs("Failed to create wgl contexts\n", stderr); + return 1; + } + + if (!wglMakeCurrent(hdc, ctx1)) { + fputs("Failed to make context current\n", stderr); + return 1; + } + + if (epoxy_gl_version() < 20) { + /* We could possibly do a 1.3 entrypoint or something instead. */ + fputs("Test relies on overriding a GL 2.0 entrypoint\n", stderr); + return 77; + } + + /* Force resolving epoxy_wglGetProcAddress. */ + wglGetProcAddress("glCreateShader"); + + test_createshader(hdc, ctx1); + test_createshader(hdc, ctx1); + test_createshader(hdc, ctx2); + test_createshader(hdc, ctx2); + test_createshader(hdc, ctx1); + test_createshader(hdc, ctx2); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(ctx1); + wglDeleteContext(ctx2); + + return !pass; +} + +int +main(int argc, char **argv) +{ + make_window_and_test(test_function); + + /* UNREACHED */ + return 1; +} diff --git a/test/wgl_usefontbitmaps.c b/test/wgl_usefontbitmaps.c new file mode 100644 index 0000000..d54e1ee --- /dev/null +++ b/test/wgl_usefontbitmaps.c @@ -0,0 +1,74 @@ +/* + * 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 <stdio.h> + +#include "wgl_common.h" +#include <epoxy/gl.h> + +static int +test_function(HDC hdc) +{ + bool pass = true; + HGLRC ctx; + GLuint dlist[2] = {100, 101}; + const char *string = "some string"; + + ctx = wglCreateContext(hdc); + if (!ctx) { + fputs("Failed to create wgl context\n", stderr); + return 1; + } + if (!wglMakeCurrent(hdc, ctx)) { + fputs("Failed to make context current\n", stderr); + return 1; + } + + /* First, use the #ifdeffed variant of the function */ + wglUseFontBitmaps(hdc, 0, 255, dlist[0]); + glListBase(dlist[1]); + glCallLists(strlen(string), GL_UNSIGNED_BYTE, string); + + /* Now, use the specific version, manually. */ +#ifdef UNICODE + wglUseFontBitmapsW(hdc, 0, 255, dlist[0]); +#else + wglUseFontBitmapsA(hdc, 0, 255, dlist[0]); +#endif + glListBase(dlist[1]); + glCallLists(strlen(string), GL_UNSIGNED_BYTE, string); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(ctx); + + return !pass; +} + +int +main(int argc, char **argv) +{ + make_window_and_test(test_function); + + /* UNREACHED */ + return 1; +} |