libglvnd: Changes to 'upstream-unstable'
README.md | 26
configure.ac | 35
include/EGL/egl.h | 303 +++
include/EGL/eglext.h | 913 ++++++++++
include/EGL/eglplatform.h | 125 +
include/KHR/khrplatform.h | 282 +++
include/Makefile.am | 3
include/glvnd/GLdispatchABI.h | 7
include/glvnd/libeglabi.h | 436 ++++
libglvnd.pc.in | 2
src/EGL/Makefile.am | 105 +
src/EGL/egldispatchstubs.c | 113 +
src/EGL/egldispatchstubs.h | 27
src/EGL/libegl.c | 1221 +++++++++++++
src/EGL/libeglabipriv.h | 105 +
src/EGL/libeglcurrent.c | 201 ++
src/EGL/libeglcurrent.h | 126 +
src/EGL/libeglerror.c | 257 ++
src/EGL/libeglerror.h | 78
src/EGL/libeglmapping.c | 366 ++++
src/EGL/libeglmapping.h | 128 +
src/EGL/libeglvendor.c | 525 +++++
src/EGL/libeglvendor.h | 50
src/GLX/Makefile.am | 2
src/GLX/libglx.c | 125 -
src/GLX/libglxmapping.c | 29
src/GLX/libglxstring.c | 63
src/GLX/libglxstring.h | 44
src/GLdispatch/GLdispatch.c | 43
src/GLdispatch/GLdispatch.h | 9
src/GLdispatch/export_list.sym | 1
src/GLdispatch/vnd-glapi/entry_aarch64_tsd.c | 207 ++
src/GLdispatch/vnd-glapi/entry_armv7_tsd.c | 47
src/GLdispatch/vnd-glapi/entry_files.mk | 5
src/Makefile.am | 1
src/generate/eglFunctionList.py | 148 +
src/generate/genCommon.py | 2
src/generate/gen_egl_dispatch.py | 223 ++
src/generate/gen_gldispatch_mapi.py | 6
src/generate/gl_inittable.py | 2
src/generate/xml/egl.xml | 2412 +++++++++++++++++++++++++++
src/util/Makefile.am | 8
src/util/cJSON.c | 750 ++++++++
src/util/cJSON.h | 149 +
src/util/cJSON/LICENSE | 20
src/util/cJSON/README | 247 ++
src/util/cJSON/test.c | 162 +
src/util/cJSON/tests/test1 | 22
src/util/cJSON/tests/test2 | 11
src/util/cJSON/tests/test3 | 26
src/util/cJSON/tests/test4 | 88
src/util/cJSON/tests/test5 | 27
src/util/glvnd_genentry.c | 44
src/util/glvnd_pthread.c | 47
src/util/glvnd_pthread.h | 3
src/util/utils_misc.c | 169 +
src/util/utils_misc.h | 89
tests/GLX_dummy/GLX_dummy.c | 827 ---------
tests/GLX_dummy/GLX_dummy.h | 84
tests/GLX_dummy/Makefile.am | 34
tests/GLX_dummy/README | 3
tests/Makefile.am | 101 -
tests/dummy/EGL_dummy.c | 811 +++++++++
tests/dummy/EGL_dummy.h | 118 +
tests/dummy/GLX_dummy.c | 677 +++++++
tests/dummy/GLX_dummy.h | 84
tests/dummy/Makefile.am | 58
tests/dummy/README | 3
tests/dummy/patchentrypoints.c | 210 ++
tests/dummy/patchentrypoints.h | 47
tests/egl_test_utils.c | 48
tests/egl_test_utils.h | 71
tests/eglenv.sh | 4
tests/fini_test_env.sh | 16
tests/glxenv.sh | 4
tests/init_test_env.sh | 20
tests/json/10_egldummy0.json | 6
tests/json/20_egldummy1.json | 6
tests/testegldebug.c | 276 +++
tests/testegldebug.sh | 5
tests/testegldevice.c | 139 +
tests/testegldevice.sh | 5
tests/testegldisplay.c | 128 +
tests/testegldisplay.sh | 5
tests/testeglerror.c | 109 +
tests/testeglerror.sh | 5
tests/testeglgetprocaddress.c | 140 +
tests/testeglgetprocaddress.sh | 5
tests/testeglmakecurrent.c | 241 ++
tests/testeglmakecurrent.sh | 5
tests/testglxgetclientstr.sh | 3
tests/testglxgetprocaddress.c | 90 -
tests/testglxgetprocaddress.sh | 3
tests/testglxgetprocaddress_genentry.sh | 11
tests/testglxmakecurrent.c | 2
tests/testglxmcbasic.sh | 3
tests/testglxmclate.sh | 4
tests/testglxmcloop.sh | 3
tests/testglxmcoldlink.sh | 3
tests/testglxmcthreads.sh | 3
tests/testglxnscreens.c | 308 ---
tests/testglxnscreens.sh | 10
tests/testglxnscrthreads.sh | 13
tests/testglxqueryversion.c | 9
tests/testglxqueryversion.sh | 3
tests/testpatchentrypoints.c | 15
tests/testpatchentrypoints.sh | 3
tests/testpatchentrypoints_gldispatch.c | 185 ++
tests/testpatchentrypoints_gldispatch.sh | 4
tests/testx11glvndproto.c | 86
tests/testx11glvndproto.sh | 7
tests/xorg.2screens.conf | 115 -
tests/xorg.conf | 60
113 files changed, 13974 insertions(+), 1929 deletions(-)
New commits:
commit 522c6017999b87f1662a669b487078c83504f946
Author: Kyle Brenneman <kbrenneman@nvidia.com>
Date: Thu May 26 11:52:37 2016 -0600
Add support for aarch64.
Add assembly code and stub generation for TSD stubs on aarch64.
diff --git a/configure.ac b/configure.ac
index 7227f5a..73cafe8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,6 +70,9 @@ if test "x$enable_asm" = xyes; then
armv7l)
asm_arch=armv7l
;;
+ aarch64)
+ asm_arch=aarch64
+ ;;
esac
case "$asm_arch" in
@@ -85,6 +88,10 @@ if test "x$enable_asm" = xyes; then
DEFINES="$DEFINES -DUSE_ARMV7_ASM"
AC_MSG_RESULT([yes, armv7l])
;;
+ aarch64)
+ DEFINES="$DEFINES -DUSE_AARCH64_ASM"
+ AC_MSG_RESULT([yes, aarch64])
+ ;;
*)
AC_MSG_RESULT([no, platform not supported])
;;
@@ -153,6 +160,11 @@ xarmv7l)
gldispatch_entry_type=armv7_tsd
gldispatch_use_tls=no
;;
+xaarch64)
+ # For aarch64, only the TSD stubs have been implemented yet.
+ gldispatch_entry_type=aarch64_tsd
+ gldispatch_use_tls=no
+ ;;
*)
# The C stubs will work with either TLS or TSD.
gldispatch_entry_type=pure_c
@@ -170,6 +182,7 @@ AM_CONDITIONAL([GLDISPATCH_TYPE_X86_TSD], [test "x$gldispatch_entry_type" = "xx8
AM_CONDITIONAL([GLDISPATCH_TYPE_X86_64_TLS], [test "x$gldispatch_entry_type" = "xx86_64_tls"])
AM_CONDITIONAL([GLDISPATCH_TYPE_X86_64_TSD], [test "x$gldispatch_entry_type" = "xx86_64_tsd"])
AM_CONDITIONAL([GLDISPATCH_TYPE_ARMV7_TSD], [test "x$gldispatch_entry_type" = "xarmv7_tsd"])
+AM_CONDITIONAL([GLDISPATCH_TYPE_AARCH64_TSD], [test "x$gldispatch_entry_type" = "xaarch64_tsd"])
AM_CONDITIONAL([GLDISPATCH_TYPE_PURE_C], [test "x$gldispatch_entry_type" = "xpure_c"])
diff --git a/include/glvnd/GLdispatchABI.h b/include/glvnd/GLdispatchABI.h
index 25d593a..9b4c43d 100644
--- a/include/glvnd/GLdispatchABI.h
+++ b/include/glvnd/GLdispatchABI.h
@@ -82,7 +82,12 @@ enum {
/*!
* Used for stubs on ARMv7, using the normal ARM instruction set.
*/
- __GLDISPATCH_STUB_ARMV7_ARM
+ __GLDISPATCH_STUB_ARMV7_ARM,
+
+ /*!
+ * Used for stubs on ARMv8/aarch64.
+ */
+ __GLDISPATCH_STUB_AARCH64,
};
/*!
diff --git a/src/GLdispatch/vnd-glapi/entry_aarch64_tsd.c b/src/GLdispatch/vnd-glapi/entry_aarch64_tsd.c
new file mode 100644
index 0000000..7fbcb39
--- /dev/null
+++ b/src/GLdispatch/vnd-glapi/entry_aarch64_tsd.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * 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
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * 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.
+ */
+
+#include "entry.h"
+#include "entry_common.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "glapi.h"
+#include "u_macros.h"
+#include "u_current.h"
+#include "utils_misc.h"
+#include "glvnd/GLdispatchABI.h"
+
+/*
+ * See: https://sourceware.org/binutils/docs/as/ARM-Directives.html
+ */
+
+/*
+ * u_execmem_alloc() allocates 128 bytes per stub.
+ */
+#define AARCH64_ENTRY_SIZE 128
+
+#define STUB_ASM_ENTRY(func) \
+ ".balign " U_STRINGIFY(AARCH64_ENTRY_SIZE) "\n\t" \
+ ".global " func "\n\t" \
+ ".type " func ", %function\n\t" \
+ func ":\n\t"
+
+/*
+ * Looks up the current dispatch table, finds the stub address at the given slot
+ * then jumps to it.
+ *
+ * First tries to find a dispatch table in _glapi_Current[GLAPI_CURRENT_DISPATCH],
+ * if not found then it jumps to the 'lookup_dispatch' and calls
+ * _glapi_get_current() then jumps back to the 'found_dispatch' label.
+ *
+ * The 'found_dispatch' section computes the correct offset in the dispatch
+ * table then does a branch without link to the function address.
+ */
+#define STUB_ASM_CODE(slot) \
+ "stp x1, x0, [sp, #-16]!\n\t" \
+ "adrp x0, :got:_glapi_Current\n\t" \
+ "ldr x0, [x0, #:got_lo12:_glapi_Current]\n\t" \
+ "ldr x0, [x0]\n\t" \
+ "cbz x0, 10f\n\t" \
+ "11:\n\t" /* found dispatch */ \
+ "ldr x1, 3f\n\t" \
+ "ldr x16, [x0, x1]\n\t" \
+ "ldp x1, x0, [sp], #16\n\t" \
+ "br x16\n\t" \
+ "10:\n\t" /* lookup dispatch */ \
+ "str x30, [sp, #-16]!\n\t" \
+ "stp x7, x6, [sp, #-16]!\n\t" \
+ "stp x5, x4, [sp, #-16]!\n\t" \
+ "stp x3, x2, [sp, #-16]!\n\t" \
+ "adrp x0, :got:_glapi_get_current\n\t" \
+ "ldr x0, [x0, #:got_lo12:_glapi_get_current]\n\t" \
+ "blr x0\n\t" \
+ "ldp x3, x2, [sp], #16\n\t" \
+ "ldp x5, x4, [sp], #16\n\t" \
+ "ldp x7, x6, [sp], #16\n\t" \
+ "ldr x30, [sp], #16\n\t" \
+ "b 11b\n\t" \
+ "3:\n\t" \
+ ".xword " slot " * 8\n\t" /* size of (void *) */
+
+/*
+ * Bytecode for STUB_ASM_CODE()
+ */
+static const uint32_t BYTECODE_TEMPLATE[] =
+{
+ 0xa9bf03e1, // <ENTRY>: stp x1, x0, [sp,#-16]!
+ 0x58000240, // <ENTRY+4>: ldr x0, <ENTRY+76>
+ 0xf9400000, // <ENTRY+8>: ldr x0, [x0]
+ 0xb40000a0, // <ENTRY+12>: cbz x0, <ENTRY+32>
+ 0x58000261, // <ENTRY+16>: ldr x1, <ENTRY+92>
+ 0xf8616810, // <ENTRY+20>: ldr x16, [x0,x1]
+ 0xa8c103e1, // <ENTRY+24>: ldp x1, x0, [sp],#16
+ 0xd61f0200, // <ENTRY+28>: br x16
+ 0xf81f0ffe, // <ENTRY+32>: str x30, [sp,#-16]!
+ 0xa9bf1be7, // <ENTRY+36>: stp x7, x6, [sp,#-16]!
+ 0xa9bf13e5, // <ENTRY+40>: stp x5, x4, [sp,#-16]!
+ 0xa9bf0be3, // <ENTRY+44>: stp x3, x2, [sp,#-16]!
+ 0x58000120, // <ENTRY+48>: ldr x0, <ENTRY+84>
+ 0xd63f0000, // <ENTRY+52>: blr x0
+ 0xa8c10be3, // <ENTRY+56>: ldp x3, x2, [sp],#16
+ 0xa8c113e5, // <ENTRY+60>: ldp x5, x4, [sp],#16
+ 0xa8c11be7, // <ENTRY+64>: ldp x7, x6, [sp],#16
+ 0xf84107fe, // <ENTRY+68>: ldr x30, [sp],#16
+ 0x17fffff2, // <ENTRY+72>: b <ENTRY+16>
+
+ // Offsets that need to be patched
+ 0x00000000, 0x00000000, // <ENTRY+76>: _glapi_Current
+ 0x00000000, 0x00000000, // <ENTRY+84>: _glapi_get_current
+ 0x00000000, 0x00000000, // <ENTRY+92>: slot * sizeof(void*)
+};
+
+#define AARCH64_BYTECODE_SIZE sizeof(BYTECODE_TEMPLATE)
+
+__asm__(".section wtext,\"ax\"\n"
+ ".balign 4096\n"
+ ".globl public_entry_start\n"
+ ".hidden public_entry_start\n"
+ "public_entry_start:\n");
+
+#define MAPI_TMP_STUB_ASM_GCC
+#include "mapi_tmp.h"
+
+__asm__(".balign 4096\n"
+ ".globl public_entry_end\n"
+ ".hidden public_entry_end\n"
+ "public_entry_end:\n"
+ ".text\n\t");
+
+const int entry_type = __GLDISPATCH_STUB_AARCH64;
+const int entry_stub_size = AARCH64_ENTRY_SIZE;
+
+// The offsets in BYTECODE_TEMPLATE that need to be patched.
+static const int TEMPLATE_OFFSET_CURRENT_TABLE = AARCH64_BYTECODE_SIZE - 3*8;
+static const int TEMPLATE_OFFSET_CURRENT_TABLE_GET = AARCH64_BYTECODE_SIZE - 2*8;
+static const int TEMPLATE_OFFSET_SLOT = AARCH64_BYTECODE_SIZE - 8;
+
+void
+entry_init_public(void)
+{
+ STATIC_ASSERT(AARCH64_BYTECODE_SIZE <= AARCH64_ENTRY_SIZE);
+}
+
+void entry_generate_default_code(char *entry, int slot)
+{
+ char *writeEntry;
+
+ // Get the pointer to the writable mapping.
+ writeEntry = (char *) u_execmem_get_writable(entry);
+
+ memcpy(writeEntry, BYTECODE_TEMPLATE, AARCH64_BYTECODE_SIZE);
+
+ // Patch the slot number and whatever addresses need to be patched.
+ *((uint64_t *)(writeEntry + TEMPLATE_OFFSET_SLOT)) = (uint64_t)(slot * sizeof(mapi_func));
+ *((uint64_t *)(writeEntry + TEMPLATE_OFFSET_CURRENT_TABLE)) =
+ (uint64_t)_glapi_Current;
+ *((uint64_t *)(writeEntry + TEMPLATE_OFFSET_CURRENT_TABLE_GET)) =
+ (uint64_t)_glapi_get_current;
+
+ // See http://community.arm.com/groups/processors/blog/2010/02/17/caches-and-self-modifying-code
+ __builtin___clear_cache(writeEntry, writeEntry + AARCH64_BYTECODE_SIZE);
+}
+
+// Note: The rest of these functions could also be used for aarch64 TLS stubs,
+// once those are implemented.
+
+mapi_func
+entry_get_public(int index)
+{
+ return (mapi_func)(public_entry_start + (index * entry_stub_size));
+}
+
+void entry_get_patch_addresses(mapi_func entry, void **writePtr, const void **execPtr)
+{
+ // Get the actual beginning of the stub allocation
+ *execPtr = (const void *) entry;
+ *writePtr = u_execmem_get_writable((void *) entry);
+}
+
+#if !defined(STATIC_DISPATCH_ONLY)
+mapi_func entry_generate(int slot)
+{
+ void *code = u_execmem_alloc(entry_stub_size);
+ if (!code) {
+ return NULL;
+ }
+
+ entry_generate_default_code(code, slot);
+
+ return (mapi_func) code;
+}
+#endif // !defined(STATIC_DISPATCH_ONLY)
diff --git a/src/GLdispatch/vnd-glapi/entry_files.mk b/src/GLdispatch/vnd-glapi/entry_files.mk
index 628c2cd..38b6bf3 100644
--- a/src/GLdispatch/vnd-glapi/entry_files.mk
+++ b/src/GLdispatch/vnd-glapi/entry_files.mk
@@ -30,6 +30,11 @@ MAPI_GLDISPATCH_ENTRY_FILES = entry_armv7_tsd.c
MAPI_GLDISPATCH_ENTRY_FILES += entry_common.c
endif
+if GLDISPATCH_TYPE_AARCH64_TSD
+MAPI_GLDISPATCH_ENTRY_FILES = entry_aarch64_tsd.c
+MAPI_GLDISPATCH_ENTRY_FILES += entry_common.c
+endif
+
if GLDISPATCH_TYPE_PURE_C
MAPI_GLDISPATCH_ENTRY_FILES = entry_pure_c.c
endif
diff --git a/src/util/glvnd_genentry.c b/src/util/glvnd_genentry.c
index d0483ca..4763633 100644
--- a/src/util/glvnd_genentry.c
+++ b/src/util/glvnd_genentry.c
@@ -39,7 +39,8 @@
#define USE_ASM (defined(USE_X86_ASM) || \
defined(USE_X86_64_ASM) || \
- defined(USE_ARMV7_ASM))
+ defined(USE_ARMV7_ASM) || \
+ defined(USE_AARCH64_ASM))
#if defined(__GNUC__) && USE_ASM
@@ -47,7 +48,7 @@
#define GENERATED_ENTRYPOINT_MAX 4096
/// The size of each generated entrypoint.
-static const int STUB_ENTRY_SIZE = 16;
+static const int STUB_ENTRY_SIZE = 32;
#if defined(USE_X86_ASM)
/// A template used to generate an entrypoint.
@@ -87,6 +88,23 @@ static const uint16_t STUB_TEMPLATE[] =
static const int DISPATCH_FUNC_OFFSET = 8;
+#elif defined(USE_AARCH64_ASM)
+
+static const uint32_t STUB_TEMPLATE[] =
+{
+ // ldr x16, 1f
+ 0x58000070,
+ // br x16
+ 0xd61f0200,
+ // nop
+ 0xd503201f,
+ // Offset that needs to be patched
+ // 1:
+ 0x00000000, 0x00000000,
+};
+
+static const int DISPATCH_FUNC_OFFSET = 12;
+
#else
#error "Can't happen -- not implemented"
#endif
@@ -263,6 +281,12 @@ void SetDispatchFuncPointer(GLVNDGenEntrypoint *entry,
// See http://community.arm.com/groups/processors/blog/2010/02/17/caches-and-self-modifying-code
__builtin___clear_cache((char *)entry->entrypointExec - 1,
(char *)entry->entrypointExec - 1 + sizeof(STUB_TEMPLATE));
+#elif defined(USE_AARCH64_ASM)
+ *((uintptr_t *)(code + DISPATCH_FUNC_OFFSET)) = (uintptr_t)dispatch;
+
+ // See http://community.arm.com/groups/processors/blog/2010/02/17/caches-and-self-modifying-code
+ __builtin___clear_cache((char *)entry->entrypointExec - 1,
+ (char *)entry->entrypointExec - 1 + sizeof(STUB_TEMPLATE));
#else
#error "Can't happen -- not implemented"
#endif
diff --git a/tests/dummy/patchentrypoints.c b/tests/dummy/patchentrypoints.c
index a8cf348..a822cc7 100644
--- a/tests/dummy/patchentrypoints.c
+++ b/tests/dummy/patchentrypoints.c
@@ -128,12 +128,47 @@ static void patch_armv7_thumb(char *writeEntry, const char *execEntry,
#endif
}
+static void patch_aarch64(char *writeEntry, const char *execEntry,
+ int stubSize, void *incrementPtr)
+{
+#if defined(__aarch64__)
+ const uint32_t tmpl[] = {
+ // ldr x0, 1f
+ 0x580000a0,
+ // ldr x1, [x0]
+ 0xf9400001,
+ // add x1, x1, #1
+ 0x91000421,
+ // str x1, [x0]
+ 0xf9000001,
+ // br x30
+ 0xd61f03c0,
+ // 1:
+ 0x00000000, 0x00000000,
+ };
+
+ static const int offsetAddr = sizeof(tmpl) - 8;
+
+ if (stubSize < sizeof(tmpl)) {
+ return;
+ }
+
+ memcpy(writeEntry, tmpl, sizeof(tmpl));
+ *((uint64_t *)(writeEntry + offsetAddr)) = (uint64_t) incrementPtr;
+
+ __builtin___clear_cache((char *) execEntry, (char *) (execEntry + sizeof(tmpl)));
+#else
+ assert(0); // Should not be calling this
+#endif
+}
+
GLboolean dummyCheckPatchSupported(int type, int stubSize)
{
switch (type) {
case __GLDISPATCH_STUB_X86_64:
case __GLDISPATCH_STUB_X86:
case __GLDISPATCH_STUB_ARMV7_THUMB:
+ case __GLDISPATCH_STUB_AARCH64:
return GL_TRUE;
default:
return GL_FALSE;
@@ -162,6 +197,9 @@ GLboolean commonInitiatePatch(int type, int stubSize,
case __GLDISPATCH_STUB_ARMV7_THUMB:
patch_armv7_thumb(writeAddr, execAddr, stubSize, incrementPtr);
break;
+ case __GLDISPATCH_STUB_AARCH64:
+ patch_aarch64(writeAddr, execAddr, stubSize, incrementPtr);
+ break;
default:
assert(0);
}
commit 260a54606c584183052f2496fb4d5f6335e59d26
Author: Kyle Brenneman <kbrenneman@nvidia.com>
Date: Wed Oct 26 13:59:10 2016 -0600
tests: Add a test for EGL_KHR_debug.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5a3bacc..29eab01 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -53,7 +53,8 @@ TESTS = \
testegldevice.sh \
testeglgetprocaddress.sh \
testeglmakecurrent.sh \
- testeglerror.sh
+ testeglerror.sh \
+ testegldebug.sh
EXTRA_DIST = $(TESTS) \
glxenv.sh \
@@ -72,7 +73,8 @@ check_PROGRAMS = \
testegldevice \
testeglgetprocaddress \
testeglmakecurrent \
- testeglerror
+ testeglerror \
+ testegldebug
# The *_oldlink variant tests that linking against legacy libGL.so works
@@ -163,3 +165,8 @@ testeglerror_SOURCES = \
egl_test_utils.c
testeglerror_LDADD = $(top_builddir)/src/EGL/libEGL.la
testeglerror_LDADD += $(top_builddir)/src/OpenGL/libOpenGL.la
+
+testegldebug_SOURCES = \
+ testegldebug.c \
+ egl_test_utils.c
+testegldebug_LDADD = $(top_builddir)/src/EGL/libEGL.la
diff --git a/tests/dummy/EGL_dummy.c b/tests/dummy/EGL_dummy.c
index 18c6211..372e0a0 100644
--- a/tests/dummy/EGL_dummy.c
+++ b/tests/dummy/EGL_dummy.c
@@ -61,6 +61,7 @@ static const EGLint DUMMY_EGL_CONFIG_COUNT = 2;
typedef struct DummyEGLDisplayRec {
EGLenum platform;
void *native_display;
+ EGLLabelKHR label;
struct glvnd_list entry;
} DummyEGLDisplay;
@@ -68,6 +69,7 @@ typedef struct DummyEGLDisplayRec {
typedef struct DummyThreadStateRec {
EGLint lastError;
EGLContext currentContext;
+ EGLLabelKHR label;
} DummyThreadState;
static const __EGLapiExports *apiExports = NULL;
@@ -75,6 +77,9 @@ static glvnd_key_t threadStateKey;
static struct glvnd_list displayList;
static EGLint failNextMakeCurrentError = EGL_NONE;
+static EGLDEBUGPROCKHR debugCallbackFunc = NULL;
+static EGLBoolean debugCallbackEnabled = EGL_TRUE;
+
static DummyThreadState *GetThreadState(void)
{
DummyThreadState *thr = (DummyThreadState *)
@@ -102,10 +107,16 @@ static void CommonEntrypoint(void)
thr->lastError = EGL_SUCCESS;
}
-static void SetLastError(EGLint error)
+static void SetLastError(const char *command, EGLLabelKHR label, EGLint error)
{
DummyThreadState *thr = GetThreadState();
+
thr->lastError = error;
+
+ if (error != EGL_SUCCESS && debugCallbackFunc != NULL && debugCallbackEnabled) {
+ debugCallbackFunc(error, command, EGL_DEBUG_MSG_ERROR_KHR, thr->label,
+ label, DUMMY_VENDOR_NAME);
+ }
}
static DummyEGLDisplay *LookupEGLDisplay(EGLDisplay dpy)
@@ -189,7 +200,7 @@ static EGLDisplay dummyGetPlatformDisplay(EGLenum platform, void *native_display
}
} else {
// We don't support this platform.
- SetLastError(EGL_BAD_PARAMETER);
+ SetLastError("eglGetPlatformDisplay", NULL, EGL_BAD_PARAMETER);
return EGL_NO_DISPLAY;
}
@@ -258,15 +269,16 @@ static EGLContext EGLAPIENTRY dummy_eglCreateContext(EGLDisplay dpy,
EGLConfig config, EGLContext share_context, const EGLint *attrib_list)
{
DummyEGLContext *dctx;
+ DummyEGLDisplay *disp;
CommonEntrypoint();
- LookupEGLDisplay(dpy);
+ disp = LookupEGLDisplay(dpy);
if (attrib_list != NULL) {
int i;
for (i=0; attrib_list[i] != EGL_NONE; i += 2) {
if (attrib_list[i] == EGL_CREATE_CONTEXT_FAIL) {
- SetLastError(attrib_list[i + 1]);
+ SetLastError("eglCreateContext", disp->label, attrib_list[i + 1]);
return EGL_NO_CONTEXT;
} else {
printf("Invalid attribute 0x%04x in eglCreateContext\n", attrib_list[i]);
@@ -355,7 +367,7 @@ static EGLBoolean EGLAPIENTRY dummy_eglMakeCurrent(EGLDisplay dpy, EGLSurface dr
LookupEGLDisplay(dpy);
if (failNextMakeCurrentError != EGL_NONE) {
- SetLastError(failNextMakeCurrentError);
+ SetLastError("eglMakeCurrent", NULL, failNextMakeCurrentError);
failNextMakeCurrentError = EGL_NONE;
return EGL_FALSE;
}
@@ -495,6 +507,51 @@ static EGLBoolean EGLAPIENTRY dummy_eglQueryDevicesEXT(EGLint max_devices, EGLDe
return EGL_TRUE;
}
+static EGLint EGLAPIENTRY dummy_eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, const EGLAttrib *attrib_list)
+{
+ CommonEntrypoint();
+
+ if (callback != NULL) {
+ if (attrib_list != NULL) {
+ int i;
+ for (i=0; attrib_list[i] != EGL_NONE; i += 2) {
+ if (EGL_DEBUG_MSG_ERROR_KHR) {
+ debugCallbackEnabled = (attrib_list[i + 1] != 0);
+ }
+ }
+ }
+ } else {
+ debugCallbackEnabled = EGL_TRUE;
+ }
+ debugCallbackFunc = callback;
+
+ return EGL_SUCCESS;
+}
+
+static EGLBoolean EGLAPIENTRY dummy_eglQueryDebugKHR(EGLint attribute, EGLAttrib *value)
+{
+ // eglQueryDebugKHR should never be called, because libEGL keeps track of
+ // all of the debug state.
+ printf("eglQueryDebugKHR should never be called\n");
+ abort();
+ return EGL_FALSE;
+}
+
+static EGLint EGLAPIENTRY dummy_eglLabelObjectKHR(EGLDisplay dpy,
+ EGLenum objectType, EGLObjectKHR object, EGLLabelKHR label)
+{
+ CommonEntrypoint();
+
+ if (objectType == EGL_OBJECT_THREAD_KHR) {
+ DummyThreadState *thr = GetThreadState();
+ thr->label = label;
+ } else if (objectType == EGL_OBJECT_DISPLAY_KHR) {
+ DummyEGLDisplay *disp = LookupEGLDisplay(dpy);
+ disp->label = label;
+ }
+ return EGL_SUCCESS;
+}
+
static const GLubyte *dummy_glGetString(GLenum name)
{
if (name == GL_VENDOR) {
@@ -666,6 +723,9 @@ static const struct {
PROC_ENTRY(eglGetError),
PROC_ENTRY(eglQueryDevicesEXT),
+ PROC_ENTRY(eglDebugMessageControlKHR),
+ PROC_ENTRY(eglQueryDebugKHR),
+ PROC_ENTRY(eglLabelObjectKHR),
PROC_ENTRY(glGetString),
#undef PROC_ENTRY
diff --git a/tests/egl_test_utils.c b/tests/egl_test_utils.c
index 583e4ed..cfc32e1 100644
--- a/tests/egl_test_utils.c
+++ b/tests/egl_test_utils.c
@@ -9,6 +9,9 @@ const char *DUMMY_VENDOR_NAMES[DUMMY_VENDOR_COUNT] = {
};
PFNEGLQUERYDEVICESEXTPROC ptr_eglQueryDevicesEXT;
+PFNEGLDEBUGMESSAGECONTROLKHRPROC ptr_eglDebugMessageControlKHR;
+PFNEGLQUERYDEBUGKHRPROC ptr_eglQueryDebugKHR;
+PFNEGLLABELOBJECTKHRPROC ptr_eglLabelObjectKHR;
pfn_eglTestDispatchDisplay ptr_eglTestDispatchDisplay;
pfn_eglTestDispatchDevice ptr_eglTestDispatchDevice;
@@ -28,6 +31,12 @@ void loadEGLExtensions(void)
{
ptr_eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)
loadEGLFunction("eglQueryDevicesEXT");
+ ptr_eglDebugMessageControlKHR = (PFNEGLDEBUGMESSAGECONTROLKHRPROC)
+ loadEGLFunction("eglDebugMessageControlKHR");
+ ptr_eglQueryDebugKHR = (PFNEGLQUERYDEBUGKHRPROC)
+ loadEGLFunction("eglQueryDebugKHR");
+ ptr_eglLabelObjectKHR = (PFNEGLLABELOBJECTKHRPROC)
+ loadEGLFunction("eglLabelObjectKHR");
ptr_eglTestDispatchDisplay = (pfn_eglTestDispatchDisplay)
loadEGLFunction("eglTestDispatchDisplay");
diff --git a/tests/egl_test_utils.h b/tests/egl_test_utils.h
index aad4c71..9703994 100644
--- a/tests/egl_test_utils.h
+++ b/tests/egl_test_utils.h
@@ -48,6 +48,9 @@
extern const char *DUMMY_VENDOR_NAMES[DUMMY_VENDOR_COUNT];
extern PFNEGLQUERYDEVICESEXTPROC ptr_eglQueryDevicesEXT;
+extern PFNEGLDEBUGMESSAGECONTROLKHRPROC ptr_eglDebugMessageControlKHR;
+extern PFNEGLQUERYDEBUGKHRPROC ptr_eglQueryDebugKHR;
+extern PFNEGLLABELOBJECTKHRPROC ptr_eglLabelObjectKHR;
extern pfn_eglTestDispatchDisplay ptr_eglTestDispatchDisplay;
extern pfn_eglTestDispatchDevice ptr_eglTestDispatchDevice;
diff --git a/tests/testegldebug.c b/tests/testegldebug.c
new file mode 100644
index 0000000..729cde3
--- /dev/null
+++ b/tests/testegldebug.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * 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
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * 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.
+ */
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GL/gl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "dummy/EGL_dummy.h"
+#include "egl_test_utils.h"
+
+/**
+ * \file
+ *
+ * Tests for EGL_KHR_debug.
+ *
+ * This test works by recording the parameters that we expect the debug
+ * callback function to get, and then calling an EGL function that generates an
+ * error.
+ *
+ * The debug callback checks its parameters against the expected values, and
+ * exits if any of them don't match.
+ */
+
+static const EGLLabelKHR THREAD_LABEL = (EGLLabelKHR) "THREAD_LABEL";
+static const EGLLabelKHR DISPLAY_LABEL = (EGLLabelKHR) "DISPLAY_LABEL";
+
+static void testCallback(EGLDisplay dpy, EGLBoolean callbackEnabled);
+
+static void EGLAPIENTRY debugCallback(EGLenum error, const char *command,
+ EGLint messageType, EGLLabelKHR threadLabel, EGLLabelKHR objectLabel,
+ const char *message);
+
+/**
+ * Records the expected parameters for the next call to the debug callback.
+ */
+static void setCallbackExpected(const char *command, EGLenum error,
+ EGLLabelKHR objectLabel, const char *message);
+static void setCallbackNotExpected(void);
+static void checkError(EGLint expectedError);
+
+/**
+ * True if the debug callback has been called since the last call to
+ * \c setCallbackExpected. This is used to make sure that the debug callback
+ * is called exactly once when a function generates an error.
+ */
+static EGLBoolean callbackWasCalled = EGL_FALSE;
+
+// These are the expected values for the next call to the debug callback, set
+// from setCallbackExpected and setCallbackNotExpected.
+static EGLBoolean shouldExpectCallback = EGL_FALSE;
+static const char *nextExpectedCommand = NULL;
+static EGLint nextExpectedError = EGL_NONE;
+static EGLLabelKHR nextExpectedObject = NULL;
+static const char *nextExpectedMessage = NULL;
+
+int main(int argc, char **argv)
+{
+ EGLDisplay dpy;
+ EGLAttrib callbackAttribs[] = {
+ EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE,
+ EGL_NONE
+ };
+
+ // We shouldn't get a callback for anything yet.
+ setCallbackNotExpected();
+
+ loadEGLExtensions();
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ checkError(EGL_SUCCESS);
+
+ ptr_eglLabelObjectKHR(EGL_NO_DISPLAY, EGL_OBJECT_THREAD_KHR, NULL, THREAD_LABEL);
+ ptr_eglLabelObjectKHR(dpy, EGL_OBJECT_DISPLAY_KHR, (EGLObjectKHR) dpy, DISPLAY_LABEL);
+
+ // Start by enabling the callback and generating some EGL errors. Make sure
+ // that the callback gets called with the correct parameters.
+ printf("Testing with callback\n");
+ ptr_eglDebugMessageControlKHR(debugCallback, NULL);
+ testCallback(dpy, EGL_TRUE);
+
+ // Disable the callback and try again. This time, the callback should not
+ // be called, but we should still get the same errors from eglGetError.
+ printf("Testing with no callback\n");
+ ptr_eglDebugMessageControlKHR(NULL, NULL);
+ testCallback(dpy, EGL_FALSE);
+
+ // Set a callback, but disable error messages. Again, the callback should
+ // not be called.
+ printf("Testing with callback and error messages disabled\n");
+ callbackAttribs[1] = EGL_FALSE;
+ ptr_eglDebugMessageControlKHR(debugCallback, callbackAttribs);
+ testCallback(dpy, EGL_FALSE);
+
+ return 0;
+}
+
+void testCallback(EGLDisplay dpy, EGLBoolean callbackEnabled)
+{
+ static const EGLint ERROR_ATTRIBS[] = {
+ EGL_CREATE_CONTEXT_FAIL, EGL_BAD_MATCH,
+ EGL_NONE
+ };
+
+ if (!callbackEnabled) {
+ setCallbackNotExpected();
+ }
+
+ // Generate an error from libEGL.so.
+ printf("Checking eglGetCurrentSurface\n");
+ if (callbackEnabled) {
+ setCallbackExpected("eglGetCurrentSurface", EGL_BAD_PARAMETER,
+ THREAD_LABEL, NULL);
+ }
+ eglGetCurrentSurface(EGL_NONE);
+ checkError(EGL_BAD_PARAMETER);
+
+ // Generate an error from a dispatch stub that expects a display. This
+ // should go through the same error reporting as eglGetCurrentSurface did.
+ printf("Checking eglCreateContext with invalid display\n");
+ if (callbackEnabled) {
+ setCallbackExpected("eglCreateContext", EGL_BAD_DISPLAY,
+ NULL, NULL);
+ }
+ eglCreateContext(EGL_NO_DISPLAY, NULL, EGL_NO_CONTEXT, NULL);
+ checkError(EGL_BAD_DISPLAY);
+
+ // Generate an error from the vendor library, to make sure that all of the
+ // EGL_KHR_debug calls got passed through correctly. The vendor library
+ // should pass the display label to the callback, and it uses the vendor
+ // name as the message.
+ printf("Checking eglCreateContext with valid display\n");
+ if (callbackEnabled) {
+ setCallbackExpected("eglCreateContext", EGL_BAD_MATCH,
+ DISPLAY_LABEL, DUMMY_VENDOR_NAMES[0]);
+ }
+ eglCreateContext(dpy, NULL, EGL_NO_CONTEXT, ERROR_ATTRIBS);
+ checkError(EGL_BAD_MATCH);
+}
+
+void setCallbackExpected(const char *command, EGLenum error,
+ EGLLabelKHR objectLabel, const char *message)
+{
+ shouldExpectCallback = EGL_TRUE;
+ nextExpectedCommand = command;
+ nextExpectedError = error;
+ nextExpectedObject = objectLabel;
+ nextExpectedMessage = message;
+ callbackWasCalled = EGL_FALSE;
+}
+
+void setCallbackNotExpected(void)
+{
+ shouldExpectCallback = EGL_FALSE;
+ callbackWasCalled = EGL_FALSE;
+}
+
+void EGLAPIENTRY debugCallback(EGLenum error, const char *command,
+ EGLint messageType, EGLLabelKHR threadLabel, EGLLabelKHR objectLabel,
+ const char *message)
+{
+ // First, make sure the debug callback was supposed to be called at all.
+ if (!shouldExpectCallback) {
+ printf("Unexpected callback from \"%s\"\n", command);
+ exit(1);
+ }
+
+ // Make sure the callback only gets called once.
+ if (callbackWasCalled) {
+ printf("Callback called multiple times from \"%s\"\n", command);
+ exit(1);
+ }
+ callbackWasCalled = EGL_TRUE;
+
+ if (messageType != EGL_DEBUG_MSG_ERROR_KHR) {
+ printf("Unexpected callback type: Expected 0x%04x, got 0x%04x\n",
+ EGL_DEBUG_MSG_ERROR_KHR, messageType);
+ exit(1);
+ }
+
+ if (error != nextExpectedError) {
+ printf("Unexpected callback error: Expected 0x%04x, got 0x%04x\n",
+ nextExpectedError, error);
+ exit(1);
+ }
+
+ if (command == NULL) {
+ printf("Command is NULL\n");
+ exit(1);
+ }
+
+ if (nextExpectedCommand != NULL) {
+ if (strcmp(nextExpectedCommand, command) != 0) {
+ printf("Unexpected command: Expected \"%s\", got \"%s\"\n",
+ nextExpectedCommand, command);
+ exit(1);
+ }
+ }
+
+ if (nextExpectedMessage != NULL) {
Reply to: