diff options
author | Kyle Brenneman <[email protected]> | 2017-01-04 11:31:58 -0700 |
---|---|---|
committer | Emil Velikov <[email protected]> | 2017-04-17 13:03:58 +0100 |
commit | ce562f9e3fab769d64b0e5453ec2b4f8710a31ce (patch) | |
tree | 5c372e39ba2c33611f145b91224f304a53ab6dd0 /src/egl/main | |
parent | 370df207cadbc1ae60415b3b953f85088e6398d4 (diff) |
EGL: Implement the libglvnd interface for EGL (v3)
The new interface mostly just sits on top of the existing library.
The only change to the existing EGL code is to split the client
extension string into platform extensions and everything else. On
non-glvnd builds, eglQueryString will just concatenate the two strings.
The EGL dispatch stubs are all generated. The script is based on the one
used to generate entrypoints in libglvnd itself.
v2: [Kyle]
- Rebased against master.
- Reworked the EGL makefile to use separate libraries
- Made the EGL code generation scripts work with Python 2 and 3.
- Change gen_egl_dispatch.py to use argparse for the command line arguments.
- Assorted formatting and style cleanup in the Python scripts.
v3: [Emil Velikov]
- Rebase
- Remove separate glvnd glx/egl configure toggles
Signed-off-by: Emil Velikov <[email protected]>
Diffstat (limited to 'src/egl/main')
-rw-r--r-- | src/egl/main/50_mesa.json | 6 | ||||
-rw-r--r-- | src/egl/main/eglapi.c | 6 | ||||
-rw-r--r-- | src/egl/main/egldispatchstubs.c | 110 | ||||
-rw-r--r-- | src/egl/main/egldispatchstubs.h | 26 | ||||
-rw-r--r-- | src/egl/main/eglglobals.c | 47 | ||||
-rw-r--r-- | src/egl/main/eglglobals.h | 13 | ||||
-rw-r--r-- | src/egl/main/eglglvnd.c | 82 |
7 files changed, 285 insertions, 5 deletions
diff --git a/src/egl/main/50_mesa.json b/src/egl/main/50_mesa.json new file mode 100644 index 00000000000..8aaaa100ffa --- /dev/null +++ b/src/egl/main/50_mesa.json @@ -0,0 +1,6 @@ +{ + "file_format_version" : "1.0.0", + "ICD" : { + "library_path" : "libEGL_mesa.so.0" + } +} diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c index 5694b5a4ca3..fc243a58e8c 100644 --- a/src/egl/main/eglapi.c +++ b/src/egl/main/eglapi.c @@ -657,7 +657,11 @@ eglQueryString(EGLDisplay dpy, EGLint name) _EGLDriver *drv; if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) { - RETURN_EGL_SUCCESS(NULL, _eglGlobal.ClientExtensionString); + const char *ret = _eglGetClientExtensionString(); + if (ret != NULL) + RETURN_EGL_SUCCESS(NULL, ret); + else + RETURN_EGL_ERROR(NULL, EGL_BAD_ALLOC, NULL); } disp = _eglLockDisplay(dpy); diff --git a/src/egl/main/egldispatchstubs.c b/src/egl/main/egldispatchstubs.c new file mode 100644 index 00000000000..e02abd7a9e0 --- /dev/null +++ b/src/egl/main/egldispatchstubs.c @@ -0,0 +1,110 @@ +#include "egldispatchstubs.h" +#include "g_egldispatchstubs.h" + +#include <string.h> + +#include "eglcurrent.h" + +static const __EGLapiExports *exports; + +const int __EGL_DISPATCH_FUNC_COUNT = __EGL_DISPATCH_COUNT; +int __EGL_DISPATCH_FUNC_INDICES[__EGL_DISPATCH_COUNT + 1]; + +static int FindProcIndex(const char *name) +{ + unsigned first = 0; + unsigned last = __EGL_DISPATCH_COUNT - 1; + + while (first <= last) { + unsigned middle = (first + last) / 2; + int comp = strcmp(name, + __EGL_DISPATCH_FUNC_NAMES[middle]); + + if (comp > 0) + first = middle + 1; + else if (comp < 0) + last = middle - 1; + else + return middle; + } + + /* Just point to the dummy entry at the end of the respective table */ + return __EGL_DISPATCH_COUNT; +} + +void __eglInitDispatchStubs(const __EGLapiExports *exportsTable) +{ + int i; + exports = exportsTable; + for (i=0; i<__EGL_DISPATCH_FUNC_COUNT; i++) { + __EGL_DISPATCH_FUNC_INDICES[i] = -1; + } +} + +void __eglSetDispatchIndex(const char *name, int dispatchIndex) +{ + int index = FindProcIndex(name); + __EGL_DISPATCH_FUNC_INDICES[index] = dispatchIndex; +} + +void *__eglDispatchFindDispatchFunction(const char *name) +{ + int index = FindProcIndex(name); + return (void *) __EGL_DISPATCH_FUNCS[index]; +} + +static __eglMustCastToProperFunctionPointerType FetchVendorFunc(__EGLvendorInfo *vendor, + int index, EGLint errorCode) +{ + __eglMustCastToProperFunctionPointerType func = NULL; + + if (vendor != NULL) { + func = exports->fetchDispatchEntry(vendor, __EGL_DISPATCH_FUNC_INDICES[index]); + } + if (func == NULL) { + if (errorCode != EGL_SUCCESS) { + _eglError(errorCode, __EGL_DISPATCH_FUNC_NAMES[index]); + } + return NULL; + } + + if (!exports->setLastVendor(vendor)) { + // Don't bother trying to set an error code in libglvnd. If + // setLastVendor failed, then setEGLError would also fail. + _eglError(errorCode, __EGL_DISPATCH_FUNC_NAMES[index]); + return NULL; + } + + return func; +} + +__eglMustCastToProperFunctionPointerType __eglDispatchFetchByCurrent(int index) +{ + __EGLvendorInfo *vendor; + + // Note: This is only used for the eglWait* functions. For those, if + // there's no current context, then they're supposed to do nothing but + // return success. + exports->threadInit(); + vendor = exports->getCurrentVendor(); + return FetchVendorFunc(vendor, index, EGL_SUCCESS); +} + +__eglMustCastToProperFunctionPointerType __eglDispatchFetchByDisplay(EGLDisplay dpy, int index) +{ + __EGLvendorInfo *vendor; + + exports->threadInit(); + vendor = exports->getVendorFromDisplay(dpy); + return FetchVendorFunc(vendor, index, EGL_BAD_DISPLAY); +} + +__eglMustCastToProperFunctionPointerType __eglDispatchFetchByDevice(EGLDeviceEXT dev, int index) +{ + __EGLvendorInfo *vendor; + + exports->threadInit(); + vendor = exports->getVendorFromDevice(dev); + return FetchVendorFunc(vendor, index, EGL_BAD_DEVICE_EXT); +} + diff --git a/src/egl/main/egldispatchstubs.h b/src/egl/main/egldispatchstubs.h new file mode 100644 index 00000000000..7861ea5e61a --- /dev/null +++ b/src/egl/main/egldispatchstubs.h @@ -0,0 +1,26 @@ +#ifndef EGLDISPATCHSTUBS_H +#define EGLDISPATCHSTUBS_H + +#include "glvnd/libeglabi.h" + +// These variables are all generated along with the dispatch stubs. +extern const int __EGL_DISPATCH_FUNC_COUNT; +extern const char * const __EGL_DISPATCH_FUNC_NAMES[]; +extern int __EGL_DISPATCH_FUNC_INDICES[]; +extern const __eglMustCastToProperFunctionPointerType __EGL_DISPATCH_FUNCS[]; + +void __eglInitDispatchStubs(const __EGLapiExports *exportsTable); +void __eglSetDispatchIndex(const char *name, int index); + +/** + * Returns the dispatch function for the given name, or \c NULL if the function + * isn't supported. + */ +void *__eglDispatchFindDispatchFunction(const char *name); + +// Helper functions used by the generated stubs. +__eglMustCastToProperFunctionPointerType __eglDispatchFetchByDisplay(EGLDisplay dpy, int index); +__eglMustCastToProperFunctionPointerType __eglDispatchFetchByDevice(EGLDeviceEXT dpy, int index); +__eglMustCastToProperFunctionPointerType __eglDispatchFetchByCurrent(int index); + +#endif // EGLDISPATCHSTUBS_H diff --git a/src/egl/main/eglglobals.c b/src/egl/main/eglglobals.c index cb41063e321..baf96bb1ec5 100644 --- a/src/egl/main/eglglobals.c +++ b/src/egl/main/eglglobals.c @@ -29,6 +29,8 @@ #include <stdlib.h> +#include <stdio.h> +#include <string.h> #include <assert.h> #include "c11/threads.h" @@ -50,9 +52,13 @@ struct _egl_global _eglGlobal = _eglFiniDisplay }, - /* ClientExtensionString */ + /* ClientOnlyExtensionString */ "EGL_EXT_client_extensions" " EGL_EXT_platform_base" + " EGL_KHR_client_get_all_proc_addresses" + " EGL_KHR_debug", + + /* PlatformExtensionString */ #ifdef HAVE_WAYLAND_PLATFORM " EGL_EXT_platform_wayland" #endif @@ -65,8 +71,9 @@ struct _egl_global _eglGlobal = #ifdef HAVE_SURFACELESS_PLATFORM " EGL_MESA_platform_surfaceless" #endif - " EGL_KHR_client_get_all_proc_addresses" - " EGL_KHR_debug", + "", + + NULL, /* ClientExtensionsString */ NULL, /* debugCallback */ _EGL_DEBUG_BIT_CRITICAL | _EGL_DEBUG_BIT_ERROR, /* debugTypesEnabled */ @@ -101,3 +108,37 @@ _eglAddAtExitCall(void (*func)(void)) mtx_unlock(_eglGlobal.Mutex); } } + +const char * +_eglGetClientExtensionString(void) +{ + const char *ret; + + mtx_lock(_eglGlobal.Mutex); + + if (_eglGlobal.ClientExtensionString == NULL) { + size_t clientLen = strlen(_eglGlobal.ClientOnlyExtensionString); + size_t platformLen = strlen(_eglGlobal.PlatformExtensionString); + + _eglGlobal.ClientExtensionString = (char *) malloc(clientLen + platformLen + 1); + if (_eglGlobal.ClientExtensionString != NULL) { + char *ptr = _eglGlobal.ClientExtensionString; + + memcpy(ptr, _eglGlobal.ClientOnlyExtensionString, clientLen); + ptr += clientLen; + + if (platformLen > 0) { + // Note that if PlatformExtensionString is not empty, then it will + // already have a leading space. + assert(_eglGlobal.PlatformExtensionString[0] == ' '); + memcpy(ptr, _eglGlobal.PlatformExtensionString, platformLen); + ptr += platformLen; + } + *ptr = '\0'; + } + } + ret = _eglGlobal.ClientExtensionString; + + mtx_unlock(_eglGlobal.Mutex); + return ret; +} diff --git a/src/egl/main/eglglobals.h b/src/egl/main/eglglobals.h index ec4f3d04a36..c6ef59d4820 100644 --- a/src/egl/main/eglglobals.h +++ b/src/egl/main/eglglobals.h @@ -57,7 +57,15 @@ struct _egl_global EGLint NumAtExitCalls; void (*AtExitCalls[10])(void); - const char *ClientExtensionString; + /* + * Under libglvnd, the client extension string has to be split into two + * strings, one for platform extensions, and one for everything else. So, + * define separate strings for them. _eglGetClientExtensionString will + * concatenate them together for a non-libglvnd build. + */ + const char *ClientOnlyExtensionString; + const char *PlatformExtensionString; + char *ClientExtensionString; EGLDEBUGPROCKHR debugCallback; unsigned int debugTypesEnabled; @@ -76,4 +84,7 @@ static inline unsigned int DebugBitFromType(EGLenum type) return (1 << (type - EGL_DEBUG_MSG_CRITICAL_KHR)); } +extern const char * +_eglGetClientExtensionString(void); + #endif /* EGLGLOBALS_INCLUDED */ diff --git a/src/egl/main/eglglvnd.c b/src/egl/main/eglglvnd.c new file mode 100644 index 00000000000..6b984ed6c28 --- /dev/null +++ b/src/egl/main/eglglvnd.c @@ -0,0 +1,82 @@ +#include <string.h> +#include <assert.h> + +#include <glvnd/libeglabi.h> + +#include "eglcurrent.h" +#include "egldispatchstubs.h" +#include "eglglobals.h" + +static const __EGLapiExports *__eglGLVNDApiExports = NULL; + +static const char * EGLAPIENTRY +__eglGLVNDQueryString(EGLDisplay dpy, EGLenum name) +{ + // For client extensions, return the list of non-platform extensions. The + // platform extensions are returned by __eglGLVNDGetVendorString. + if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) + return _eglGlobal.ClientOnlyExtensionString; + + // For everything else, forward to the normal eglQueryString function. + return eglQueryString(dpy, name); +} + +static const char * +__eglGLVNDGetVendorString(int name) +{ + if (name == __EGL_VENDOR_STRING_PLATFORM_EXTENSIONS) { + const char *str = _eglGlobal.PlatformExtensionString; + // The platform extension string may have a leading space. If it does, + // then skip over it. + while (*str == ' ') { + str++; + } + return str; + } + + return NULL; +} + +static EGLDisplay +__eglGLVNDGetPlatformDisplay(EGLenum platform, void *native_display, + const EGLAttrib *attrib_list) +{ + if (platform == EGL_NONE) { + assert(native_display == (void *) EGL_DEFAULT_DISPLAY); + assert(attrib_list == NULL); + return eglGetDisplay((EGLNativeDisplayType) native_display); + } else { + return eglGetPlatformDisplay(platform, native_display, attrib_list); + } +} + +static void * +__eglGLVNDGetProcAddress(const char *procName) +{ + if (strcmp(procName, "eglQueryString") == 0) + return (void *) __eglGLVNDQueryString; + + return (void *) eglGetProcAddress(procName); +} + +EGLAPI EGLBoolean +__egl_Main(uint32_t version, const __EGLapiExports *exports, + __EGLvendorInfo *vendor, __EGLapiImports *imports) +{ + if (EGL_VENDOR_ABI_GET_MAJOR_VERSION(version) != + EGL_VENDOR_ABI_MAJOR_VERSION) + return EGL_FALSE; + + __eglGLVNDApiExports = exports; + __eglInitDispatchStubs(exports); + + imports->getPlatformDisplay = __eglGLVNDGetPlatformDisplay; + imports->getSupportsAPI = _eglIsApiValid; + imports->getVendorString = __eglGLVNDGetVendorString; + imports->getProcAddress = __eglGLVNDGetProcAddress; + imports->getDispatchAddress = __eglDispatchFindDispatchFunction; + imports->setDispatchIndex = __eglSetDispatchIndex; + + return EGL_TRUE; +} + |