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 <[email protected]> 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 <[email protected]> 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) {

