diff options
Diffstat (limited to 'opengl/libs')
34 files changed, 4836 insertions, 2561 deletions
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 75dda6dc61..abc7a72716 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -140,16 +140,22 @@ cc_library_shared { "EGL/egl_cache.cpp", "EGL/egl_display.cpp", "EGL/egl_object.cpp", + "EGL/egl_layers.cpp", "EGL/egl.cpp", "EGL/eglApi.cpp", + "EGL/egl_platform_entries.cpp", "EGL/Loader.cpp", + "EGL/egl_angle_platform.cpp", ], shared_libs: [ "libvndksupport", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", + "libbase", "libhidlbase", "libhidltransport", + "libnativebridge_lazy", + "libnativeloader_lazy", "libutils", ], static_libs: [ @@ -191,6 +197,7 @@ cc_library_shared { defaults: ["gles_libs_defaults"], srcs: ["GLES_CM/gl.cpp"], cflags: ["-DLOG_TAG=\"libGLESv1\""], + version_script: "libGLESv1_CM.map.txt", } //############################################################################## diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index b3752f5bcc..74c4d7d07a 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -79,7 +79,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, } std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, NULL); + CacheEntry dummyEntry(dummyKey, nullptr); while (true) { auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); @@ -139,7 +139,7 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, return 0; } std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, NULL); + CacheEntry dummyEntry(dummyKey, nullptr); auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); if (index == mCacheEntries.end() || dummyEntry < *index) { ALOGV("get: no cache entry found for key of size %zu", keySize); @@ -308,7 +308,7 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) { - if (data != NULL && copyData) { + if (data != nullptr && copyData) { memcpy(const_cast<void*>(mData), data, size); } } diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h index 1f5d5357e6..e5c5e5bc25 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -97,6 +97,10 @@ public: // int unflatten(void const* buffer, size_t size); + // clear flushes out all contents of the cache then the BlobCache, leaving + // it in an empty state. + void clear() { mCacheEntries.clear(); } + protected: // mMaxTotalSize is the maximum size that all cache entries can occupy. This // includes space for both keys and values. When a call to BlobCache::set diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp index edbaaf090d..cf67cf443b 100644 --- a/opengl/libs/EGL/BlobCache_test.cpp +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -97,7 +97,7 @@ TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0)); } TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { @@ -169,7 +169,7 @@ TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { } mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); - ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0)); } TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { @@ -219,7 +219,7 @@ TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { } mBC->set(key, MAX_KEY_SIZE, buf, bufSize); - ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); + ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0)); } TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { @@ -237,7 +237,7 @@ TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { int numCached = 0; for (int i = 0; i < 256; i++) { uint8_t k = i; - if (mBC->get(&k, 1, NULL, 0) == 1) { + if (mBC->get(&k, 1, nullptr, 0) == 1) { numCached++; } } @@ -260,7 +260,7 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { int numCached = 0; for (int i = 0; i < maxEntries+1; i++) { uint8_t k = i; - if (mBC->get(&k, 1, NULL, 0) == 1) { + if (mBC->get(&k, 1, nullptr, 0) == 1) { numCached++; } } diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp index 79237159bf..cc42ac7fef 100644 --- a/opengl/libs/EGL/FileBlobCache.cpp +++ b/opengl/libs/EGL/FileBlobCache.cpp @@ -77,7 +77,7 @@ FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxT return; } - uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0)); if (buf == MAP_FAILED) { ALOGE("error mmaping cache file: %s (%d)", strerror(errno), diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md new file mode 100644 index 0000000000..bfc44dbb69 --- /dev/null +++ b/opengl/libs/EGL/GLES_layers.md @@ -0,0 +1,258 @@ +# GLES Layers + +## EGL Loader Initialization +After standard entrypoints have all been populated unmodified, a GLES LayerLoader will be instantiated. If debug layers are enabled, the LayerLoader will scan specified directories for layers, just like the Vulkan loader does. + +If layering is enabled, the loader will search for and enumerate a specified layer list. The layer list will be specified by colon separated filenames (see [Enabling layers](#Enabling-layers) below). + +The layers will be traversed in the order they are specified, so the first layer will be directly below the application. For each layer, it will track two entrypoints from the layer. `AndroidGLESLayer_Initialize` and `AndroidGLESLayer_GetProcAddress`. +```cpp +typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); +void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address)) +``` + +`AndroidGLESLayer_Initialize` is a new function that provides an identifier for the layer to use (layer_id) and an entrypoint that can be called to look up functions below the layer. The entrypoint can be used like so: +```cpp +const char* func = "eglFoo"; +void* gpa = get_next_layer_proc_address(layer_id, func); +``` + +Note that only GLES2+ entrypoints will be provided. If a layer tries to make independent GLES 1.x calls, they will be routed to GLES2+ libraries, which may not behave as expected. Application calls to 1.x will not be affected. + +AndroidGLESLayer_GetProcAddress is a new function designed for this layering system. It takes the address of the next call in the chain that the layer should call when finished. If there is only one layer, next will point directly to the driver for most functions. +```cpp +void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next) +``` + +For each layer found, the GLES LayerLoader will call `AndroidGLESLayer_Initialize`, and then walk libEGL’s function lists and call `AndroidGLESLayer_GetProcAddress` for all known functions. The layer can track that next address with any means it wants. If the layer does not intercept the function, `AndroidGLESLayer_GetProcAddress` must return the same function address it was passed. The LayerLoader will then update the function hook list to point to the layer’s entrypoint. + +The layers are not required to do anything with the info provided by `AndroidGLESLayer_Initialize` or get_next_layer_proc_address, but providing them makes it easier for existing layers (like GAPID and RenderDoc) to support Android. That way a layer can look up functions independently (i.e. not wait for calls to `AndroidGLESLayer_GetProcAddress`). Layers must be sure to use gen_next_layer_proc_address if they look up function calls instead of eglGetProcAddress or they will not get an accurate answer. eglGetProcAddress must be passed down the chain to the platform. + +## Placing layers + +Where layers can be found, in order of priority + 1. System location for root + This requires root access + ```bash + adb root + adb disable-verity + adb reboot + adb root + adb shell setenforce 0 + adb shell mkdir -p /data/local/debug/gles + adb push <layer>.so /data/local/debug/gles/ + ``` + + 2. Application's base directory + Target application must be debuggable, or you must have root access: + ```bash + adb push libGLTrace.so /data/local/tmp + adb shell run-as com.android.gl2jni cp /data/local/tmp/libGLTrace.so . + adb shell run-as com.android.gl2jni ls | grep libGLTrace + libGLTrace.so + ``` + + 3. External APK + Determine the ABI of your target application, then install an APK containing the layers you wish to load: + ```bash + adb install --abi armeabi-v7a layers.apk + ``` + + 4. In the target application's APK + +## Enabling layers + +### Per application +Note these settings will persist across reboots: +```bash +# Enable layers +adb shell settings put global enable_gpu_debug_layers 1 + +# Specify target application +adb shell settings put global gpu_debug_app <package_name> + +# Specify layer list (from top to bottom) +adb shell settings put global gpu_debug_layers_gles <layer1:layer2:layerN> + +# Specify a package to search for layers +adb shell settings put global gpu_debug_layer_app <layer_package> +``` +To disable the per-app layers: +``` +adb shell settings delete global enable_gpu_debug_layers +adb shell settings delete global gpu_debug_app +adb shell settings delete global gpu_debug_layers_gles +adb shell settings delete global gpu_debug_layer_app +``` + +### Globally +These will be cleared on reboot: +```bash +# This will attempt to load layers for all applications, including native executables +adb shell setprop debug.gles.layers <layer1:layer2:layerN> +``` + + +## Creating a layer + +Layers must expose the following two functions described above: +```cpp +AndroidGLESLayer_Initialize +AndroidGLESLayer_GetProcAddress +``` + +For a simple layer that just wants to intercept a handful of functions, a passively initialized layer is the way to go. It can simply wait for the EGL Loader to initialize the function it cares about. See below for an example of creating a passive layer. + +For more formalized layers that need to fully initialize up front, or layers that needs to look up extensions not known to the EGL loader, active layer initialization is the way to go. The layer can utilize get_next_layer_proc_address provided by `AndroidGLESLayer_Initialize` to look up a function at any time. The layer must still respond to `AndroidGLESLayer_GetProcAddress` requests from the loader so the platform knows where to route calls. See below for an example of creating an active layer. + +### Example Passive Layer Initialization +```cpp +namespace { + +std::unordered_map<std::string, EGLFuncPointer> funcMap; + +EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig ( + EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, + EGLint *num_config) { + + EGLFuncPointer entry = funcMap["eglChooseConfig"]; + + typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)( + EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); + + PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry); + + return next(dpy, attrib_list, configs, config_size, num_config); +} + +EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) { + + #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \ + return (EGLFuncPointer)glesLayer_##func; } + + GETPROCADDR(eglChooseConfig); + + // Don't return anything for unrecognized functions + return nullptr; +} + +EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer( + void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { + // This function is purposefully empty, since this layer does not proactively + // look up any entrypoints + } + +EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress( + const char* funcName, EGLFuncPointer next) { + EGLFuncPointer entry = eglGPA(funcName); + if (entry != nullptr) { + funcMap[std::string(funcName)] = next; + return entry; + } + return next; +} + +} // namespace + +extern "C" { + __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize( + void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { + return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address); + } + __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres( + const char *funcName, EGLFuncPointer next) { + return (void*)glesLayer_GetLayerProcAddress(funcName, next); + } +} +``` + +### Example Active Layer Initialization +```cpp +namespace { + +std::unordered_map<std::string, EGLFuncPointer> funcMap; + +EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig ( + EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, + EGLint *num_config) { + + EGLFuncPointer entry = funcMap["eglChooseConfig"]; + + typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)( + EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); + + PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry); + + return next(dpy, attrib_list, configs, config_size, num_config); +} + +EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) { + + #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \ + return (EGLFuncPointer)glesLayer_##func; } + + GETPROCADDR(eglChooseConfig); + + // Don't return anything for unrecognized functions + return nullptr; +} + +EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer( + void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { + + // Note: This is where the layer would populate its function map with all the + // functions it cares about + const char* func = “eglChooseConfig”; + funcMap[func] = get_next_layer_proc_address(layer_id, func); +} + +EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress( + const char* funcName, EGLFuncPointer next) { + EGLFuncPointer entry = eglGPA(funcName); + if (entry != nullptr) { + return entry; + } + + return next; +} + +} // namespace + +extern "C" { + __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize( + void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { + return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address); + } + __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres( + const char *funcName, EGLFuncPointer next) { + return (void*)glesLayer_GetLayerProcAddress(funcName, next); + } +} +``` + +## Caveats +Only supports GLES 2.0+. + +When layering is enabled, GLES 1.x exclusive functions will continue to route to GLES 1.x drivers. But functions shared with GLES 2.0+ (like glGetString) will be routed to 2.0+ drivers, which can cause confusion. + +## FAQ + - Who can use layers? + - GLES Layers can be loaded by any debuggable application, or for any application if you have root access + - How do we know if layers are working on a device? + - This feature is backed by Android CTS, so you can run `atest CtsGpuToolsHostTestCases` + - How does a app determine if this feature is supported? + - There are two ways. First you can check against the version of Android. + ```bash + # Q is the first that will support this, so look for `Q` or 10 for release + adb shell getprop ro.build.version.sdk + # Or look for the SDK version, which should be 29 for Q + adb shell getprop ro.build.version.sdk + ``` + - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system: + ```cpp + std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos) + { + // Layers are supported! + } + ``` diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 6f050bf831..038a432337 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -17,7 +17,7 @@ //#define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include "Loader.h" +#include <EGL/Loader.h> #include <string> @@ -27,23 +27,19 @@ #include <android/dlext.h> #include <cutils/properties.h> #include <log/log.h> +#include <utils/Timers.h> #ifndef __ANDROID_VNDK__ #include <graphicsenv/GraphicsEnv.h> #endif #include <vndksupport/linker.h> +#include "egl_platform_entries.h" #include "egl_trace.h" #include "egldefs.h" +#include <EGL/eglext_angle.h> -extern "C" { - android_namespace_t* android_get_exported_namespace(const char*); -} - -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- - /* * EGL userspace drivers must be provided either: @@ -73,41 +69,6 @@ Loader& Loader::getInstance() { return loader; } -/* This function is called to check whether we run inside the emulator, - * and if this is the case whether GLES GPU emulation is supported. - * - * Returned values are: - * -1 -> not running inside the emulator - * 0 -> running inside the emulator, but GPU emulation not supported - * 1 -> running inside the emulator, GPU emulation is supported - * through the "emulation" host-side OpenGL ES implementation. - * 2 -> running inside the emulator, GPU emulation is supported - * through a guest-side vendor driver's OpenGL ES implementation. - */ -static int -checkGlesEmulationStatus(void) -{ - /* We're going to check for the following kernel parameters: - * - * qemu=1 -> tells us that we run inside the emulator - * android.qemu.gles=<number> -> tells us the GLES GPU emulation status - * - * Note that we will return <number> if we find it. This let us support - * more additionnal emulation modes in the future. - */ - char prop[PROPERTY_VALUE_MAX]; - int result = -1; - - /* First, check for qemu=1 */ - property_get("ro.kernel.qemu",prop,"0"); - if (atoi(prop) != 1) - return -1; - - /* We are in the emulator, get GPU status value */ - property_get("qemu.gles",prop,"0"); - return atoi(prop); -} - static void* do_dlopen(const char* path, int mode) { ATRACE_CALL(); return dlopen(path, mode); @@ -123,13 +84,16 @@ static void* do_android_load_sphal_library(const char* path, int mode) { return android_load_sphal_library(path, mode); } -// ---------------------------------------------------------------------------- +static int do_android_unload_sphal_library(void* dso) { + ATRACE_CALL(); + return android_unload_sphal_library(dso); +} Loader::driver_t::driver_t(void* gles) { dso[0] = gles; for (size_t i=1 ; i<NELEM(dso) ; i++) - dso[i] = 0; + dso[i] = nullptr; } Loader::driver_t::~driver_t() @@ -137,7 +101,7 @@ Loader::driver_t::~driver_t() for (size_t i=0 ; i<NELEM(dso) ; i++) { if (dso[i]) { dlclose(dso[i]); - dso[i] = 0; + dso[i] = nullptr; } } } @@ -160,10 +124,8 @@ int Loader::driver_t::set(void* hnd, int32_t api) return 0; } -// ---------------------------------------------------------------------------- - Loader::Loader() - : getProcAddress(NULL) + : getProcAddress(nullptr) { } @@ -216,33 +178,161 @@ static void setEmulatorGlesValue(void) { } } +static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl"; + +static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = { + DRIVER_SUFFIX_PROPERTY, + "ro.board.platform", +}; + +static bool should_unload_system_driver(egl_connection_t* cnx) { + // Return false if the system driver has been unloaded once. + if (cnx->systemDriverUnloaded) { + return false; + } + + // Return true if Angle namespace is set. + android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); + if (ns) { + return true; + } + +#ifndef __ANDROID_VNDK__ + // Return true if updated driver namespace is set. + ns = android::GraphicsEnv::getInstance().getDriverNamespace(); + if (ns) { + return true; + } +#endif + + return false; +} + +static void uninit_api(char const* const* api, __eglMustCastToProperFunctionPointerType* curr) { + while (*api) { + *curr++ = nullptr; + api++; + } +} + +void Loader::unload_system_driver(egl_connection_t* cnx) { + ATRACE_CALL(); + + uninit_api(gl_names, + (__eglMustCastToProperFunctionPointerType*)&cnx + ->hooks[egl_connection_t::GLESv2_INDEX] + ->gl); + uninit_api(gl_names, + (__eglMustCastToProperFunctionPointerType*)&cnx + ->hooks[egl_connection_t::GLESv1_INDEX] + ->gl); + uninit_api(egl_names, (__eglMustCastToProperFunctionPointerType*)&cnx->egl); + + if (cnx->dso) { + ALOGD("Unload system gl driver."); + driver_t* hnd = (driver_t*)cnx->dso; + if (hnd->dso[2]) { + do_android_unload_sphal_library(hnd->dso[2]); + } + if (hnd->dso[1]) { + do_android_unload_sphal_library(hnd->dso[1]); + } + if (hnd->dso[0]) { + do_android_unload_sphal_library(hnd->dso[0]); + } + cnx->dso = nullptr; + } + + cnx->systemDriverUnloaded = true; +} + void* Loader::open(egl_connection_t* cnx) { ATRACE_CALL(); + const nsecs_t openTime = systemTime(); - void* dso; - driver_t* hnd = 0; + if (should_unload_system_driver(cnx)) { + unload_system_driver(cnx); + } + + // If a driver has been loaded, return the driver directly. + if (cnx->dso) { + return cnx->dso; + } setEmulatorGlesValue(); - dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2); - if (dso) { - hnd = new driver_t(dso); + // Check if we should use ANGLE early, so loading each driver doesn't require repeated queries. + if (android::GraphicsEnv::getInstance().shouldUseAngle()) { + cnx->shouldUseAngle = true; } else { - // Always load EGL first - dso = load_driver("EGL", cnx, EGL); - if (dso) { - hnd = new driver_t(dso); - hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM ); - hnd->set( load_driver("GLESv2", cnx, GLESv2), GLESv2 ); + cnx->shouldUseAngle = false; + } + + // Firstly, try to load ANGLE driver. + driver_t* hnd = attempt_to_load_angle(cnx); + if (!hnd) { + // Secondly, try to load from driver apk. + hnd = attempt_to_load_updated_driver(cnx); + } + + bool failToLoadFromDriverSuffixProperty = false; + if (!hnd) { + // Finally, try to load system driver, start by searching for the library name appended by + // the system properties of the GLES userspace driver in both locations. + // i.e.: + // libGLES_${prop}.so, or: + // libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so + char prop[PROPERTY_VALUE_MAX + 1]; + for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { + if (property_get(key, prop, nullptr) <= 0) { + continue; + } + hnd = attempt_to_load_system_driver(cnx, prop, true); + if (hnd) { + break; + } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) { + failToLoadFromDriverSuffixProperty = true; + } } } - LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation"); + if (!hnd) { + // Can't find graphics driver by appending system properties, now search for the exact name + // without any suffix of the GLES userspace driver in both locations. + // i.e.: + // libGLES.so, or: + // libEGL.so, libGLESv1_CM.so, libGLESv2.so + hnd = attempt_to_load_system_driver(cnx, nullptr, true); + } + + if (!hnd && !failToLoadFromDriverSuffixProperty) { + hnd = attempt_to_load_system_driver(cnx, nullptr, false); + } + + if (!hnd) { + android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, + false, systemTime() - openTime); + } + + LOG_ALWAYS_FATAL_IF(!hnd, + "couldn't find an OpenGL ES implementation, make sure you set %s or %s", + HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1]); - cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so"); - cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so"); - cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so"); + if (!cnx->libEgl) { + cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so"); + } + if (!cnx->libGles1) { + cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so"); + } + if (!cnx->libGles2) { + cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so"); + } + + if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) { + android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, + false, systemTime() - openTime); + } LOG_ALWAYS_FATAL_IF(!cnx->libEgl, "couldn't load system EGL wrapper libraries"); @@ -250,13 +340,26 @@ void* Loader::open(egl_connection_t* cnx) LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1, "couldn't load system OpenGL ES wrapper libraries"); + android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, true, + systemTime() - openTime); + return (void*)hnd; } -void Loader::close(void* driver) +void Loader::close(egl_connection_t* cnx) { - driver_t* hnd = (driver_t*)driver; + driver_t* hnd = (driver_t*) cnx->dso; delete hnd; + cnx->dso = nullptr; + + cnx->shouldUseAngle = false; + cnx->angleDecided = false; + cnx->useAngle = false; + + if (cnx->vendorEGL) { + dlclose(cnx->vendorEGL); + cnx->vendorEGL = nullptr; + } } void Loader::init_api(void* dso, @@ -282,11 +385,11 @@ void Loader::init_api(void* dso, __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); - if (f == NULL) { + if (f == nullptr) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); } - if (f == NULL) { + if (f == nullptr) { // Try without the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) { @@ -296,7 +399,7 @@ void Loader::init_api(void* dso, //ALOGD_IF(f, "found <%s> instead", scrap); } } - if (f == NULL) { + if (f == nullptr) { // Try with the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; if (index>0 && strcmp(name+index, "OES")) { @@ -305,7 +408,7 @@ void Loader::init_api(void* dso, //ALOGD_IF(f, "found <%s> instead", scrap); } } - if (f == NULL) { + if (f == nullptr) { //ALOGD("%s", name); f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; @@ -328,43 +431,11 @@ void Loader::init_api(void* dso, } } -static void* load_system_driver(const char* kind) { +static void* load_system_driver(const char* kind, const char* suffix, const bool exact) { ATRACE_CALL(); class MatchFile { public: - static std::string find(const char* kind) { - std::string result; - int emulationStatus = checkGlesEmulationStatus(); - switch (emulationStatus) { - case 0: -#if defined(__LP64__) - result = "/vendor/lib64/egl/libGLES_android.so"; -#else - result = "/vendor/lib/egl/libGLES_android.so"; -#endif - return result; - case 1: - // Use host-side OpenGL through the "emulation" library -#if defined(__LP64__) - result = std::string("/vendor/lib64/egl/lib") + kind + "_emulation.so"; -#else - result = std::string("/vendor/lib/egl/lib") + kind + "_emulation.so"; -#endif - return result; - case 2: - // Use guest side swiftshader library -#if defined(__LP64__) - result = std::string("/vendor/lib64/egl/lib") + kind + "_swiftshader.so"; -#else - result = std::string("/vendor/lib/egl/lib") + kind + "_swiftshader.so"; -#endif - return result; - default: - // Not in emulator, or use other guest-side implementation - break; - } - - std::string pattern = std::string("lib") + kind; + static std::string find(const char* libraryName, const bool exact) { const char* const searchPaths[] = { #if defined(__LP64__) "/vendor/lib64/egl", @@ -375,35 +446,16 @@ static void* load_system_driver(const char* kind) { #endif }; - // first, we search for the exact name of the GLES userspace - // driver in both locations. - // i.e.: - // libGLES.so, or: - // libEGL.so, libGLESv1_CM.so, libGLESv2.so - - for (size_t i=0 ; i<NELEM(searchPaths) ; i++) { - if (find(result, pattern, searchPaths[i], true)) { - return result; - } - } - - // for compatibility with the old "egl.cfg" naming convention - // we look for files that match: - // libGLES_*.so, or: - // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so - - pattern.append("_"); - for (size_t i=0 ; i<NELEM(searchPaths) ; i++) { - if (find(result, pattern, searchPaths[i], false)) { - return result; + for (auto dir : searchPaths) { + std::string absolutePath; + if (find(absolutePath, libraryName, dir, exact)) { + return absolutePath; } } - // we didn't find the driver. gah. - result.clear(); - return result; + // Driver not found. gah. + return std::string(); } - private: static bool find(std::string& result, const std::string& pattern, const char* const search, bool exact) { @@ -417,9 +469,9 @@ static void* load_system_driver(const char* kind) { } DIR* d = opendir(search); - if (d != NULL) { + if (d != nullptr) { struct dirent* e; - while ((e = readdir(d)) != NULL) { + while ((e = readdir(d)) != nullptr) { if (e->d_type == DT_DIR) { continue; } @@ -441,11 +493,19 @@ static void* load_system_driver(const char* kind) { } }; - - std::string absolutePath = MatchFile::find(kind); + std::string libraryName = std::string("lib") + kind; + if (suffix) { + libraryName += std::string("_") + suffix; + } else if (!exact) { + // Deprecated: we look for files that match + // libGLES_*.so, or: + // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so + libraryName += std::string("_"); + } + std::string absolutePath = MatchFile::find(libraryName.c_str(), exact); if (absolutePath.empty()) { // this happens often, we don't want to log an error - return 0; + return nullptr; } const char* const driver_absolute_path = absolutePath.c_str(); @@ -455,10 +515,10 @@ static void* load_system_driver(const char* kind) { // sphal namespace. void* dso = do_android_load_sphal_library(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); - if (dso == 0) { + if (dso == nullptr) { const char* err = dlerror(); ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown"); - return 0; + return nullptr; } ALOGD("loaded %s", driver_absolute_path); @@ -466,10 +526,88 @@ static void* load_system_driver(const char* kind) { return dso; } -static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = { - "ro.hardware.egl", - "ro.board.platform", -}; +static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + + std::string name = std::string("lib") + kind + "_angle.so"; + + void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + + if (so) { + ALOGD("dlopen_ext from APK (%s) success at %p", name.c_str(), so); + return so; + } else { + ALOGE("dlopen_ext(\"%s\") failed: %s", name.c_str(), dlerror()); + } + + return nullptr; +} + +static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) { + void* so = nullptr; + + if ((cnx->shouldUseAngle) || android::GraphicsEnv::getInstance().shouldUseAngle()) { + so = load_angle_from_namespace(kind, ns); + cnx->shouldUseAngle = true; + } else { + cnx->shouldUseAngle = false; + } + + if (so) { + ALOGV("Loaded ANGLE %s library for '%s' (instead of native)", kind, + android::GraphicsEnv::getInstance().getAngleAppName().c_str()); + cnx->useAngle = true; + + char prop[PROPERTY_VALUE_MAX]; + + property_get("debug.hwui.renderer", prop, "UNSET"); + ALOGV("Skia's renderer set to %s", prop); + + EGLint angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; + property_get("debug.angle.backend", prop, "0"); + switch (atoi(prop)) { + case 1: + ALOGV("%s: Requesting OpenGLES back-end", __FUNCTION__); + angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; + break; + case 2: + ALOGV("%s: Requesting Vulkan back-end", __FUNCTION__); + angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; + break; + default: + break; + } + + cnx->angleBackend = angleBackendDefault; + if (!cnx->vendorEGL && (cnx->angleBackend == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) { + // Find and load vendor libEGL for ANGLE's GL back-end to use. + char prop[PROPERTY_VALUE_MAX + 1]; + for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { + if (property_get(key, prop, nullptr) <= 0) { + continue; + } + void* dso = load_system_driver("EGL", prop, true); + if (dso) { + cnx->vendorEGL = dso; + break; + } + } + if (!cnx->vendorEGL) { + cnx->vendorEGL = load_system_driver("EGL", nullptr, true); + } + } + } else { + ALOGV("Loaded native %s library for '%s' (instead of ANGLE)", kind, + android::GraphicsEnv::getInstance().getAngleAppName().c_str()); + cnx->useAngle = false; + } + cnx->angleDecided = true; + + return so; +} static void* load_updated_driver(const char* kind, android_namespace_t* ns) { ATRACE_CALL(); @@ -480,35 +618,110 @@ static void* load_updated_driver(const char* kind, android_namespace_t* ns) { void* so = nullptr; char prop[PROPERTY_VALUE_MAX + 1]; for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { - if (property_get(key, prop, nullptr) > 0) { - std::string name = std::string("lib") + kind + "_" + prop + ".so"; - so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); - if (so) { - return so; - } + if (property_get(key, prop, nullptr) <= 0) { + continue; + } + std::string name = std::string("lib") + kind + "_" + prop + ".so"; + so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + if (so) { + return so; } } return nullptr; } -void *Loader::load_driver(const char* kind, - egl_connection_t* cnx, uint32_t mask) -{ +Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { ATRACE_CALL(); + android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); + if (!ns) { + return nullptr; + } + + android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::ANGLE); + driver_t* hnd = nullptr; + + // ANGLE doesn't ship with GLES library, and thus we skip GLES driver. + void* dso = load_angle("EGL", ns, cnx); + if (dso) { + initialize_api(dso, cnx, EGL); + hnd = new driver_t(dso); + + dso = load_angle("GLESv1_CM", ns, cnx); + initialize_api(dso, cnx, GLESv1_CM); + hnd->set(dso, GLESv1_CM); - void* dso = nullptr; + dso = load_angle("GLESv2", ns, cnx); + initialize_api(dso, cnx, GLESv2); + hnd->set(dso, GLESv2); + } + return hnd; +} + +Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) { + ATRACE_CALL(); #ifndef __ANDROID_VNDK__ - android_namespace_t* ns = android_getDriverNamespace(); - if (ns) { - dso = load_updated_driver(kind, ns); + android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace(); + if (!ns) { + return nullptr; + } + + ALOGD("Load updated gl driver."); + android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL_UPDATED); + driver_t* hnd = nullptr; + void* dso = load_updated_driver("GLES", ns); + if (dso) { + initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2); + hnd = new driver_t(dso); + return hnd; + } + + dso = load_updated_driver("EGL", ns); + if (dso) { + initialize_api(dso, cnx, EGL); + hnd = new driver_t(dso); + + dso = load_updated_driver("GLESv1_CM", ns); + initialize_api(dso, cnx, GLESv1_CM); + hnd->set(dso, GLESv1_CM); + + dso = load_updated_driver("GLESv2", ns); + initialize_api(dso, cnx, GLESv2); + hnd->set(dso, GLESv2); } + return hnd; +#else + return nullptr; #endif - if (!dso) { - dso = load_system_driver(kind); - if (!dso) - return NULL; +} + +Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, + const bool exact) { + ATRACE_CALL(); + android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL); + driver_t* hnd = nullptr; + void* dso = load_system_driver("GLES", suffix, exact); + if (dso) { + initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2); + hnd = new driver_t(dso); + return hnd; + } + dso = load_system_driver("EGL", suffix, exact); + if (dso) { + initialize_api(dso, cnx, EGL); + hnd = new driver_t(dso); + + dso = load_system_driver("GLESv1_CM", suffix, exact); + initialize_api(dso, cnx, GLESv1_CM); + hnd->set(dso, GLESv1_CM); + + dso = load_system_driver("GLESv2", suffix, exact); + initialize_api(dso, cnx, GLESv2); + hnd->set(dso, GLESv2); } + return hnd; +} +void Loader::initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask) { if (mask & EGL) { getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); @@ -523,11 +736,11 @@ void *Loader::load_driver(const char* kind, char const * name = *api; __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); - if (f == NULL) { + if (f == nullptr) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); - if (f == NULL) { - f = (__eglMustCastToProperFunctionPointerType)0; + if (f == nullptr) { + f = (__eglMustCastToProperFunctionPointerType)nullptr; } } *curr++ = f; @@ -548,10 +761,6 @@ void *Loader::load_driver(const char* kind, &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, getProcAddress); } - - return dso; } -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- +} // namespace android diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h index e88d1a285e..6f31ab4741 100644 --- a/opengl/libs/EGL/Loader.h +++ b/opengl/libs/EGL/Loader.h @@ -33,7 +33,8 @@ class Loader { enum { EGL = 0x01, GLESv1_CM = 0x02, - GLESv2 = 0x04 + GLESv2 = 0x04, + PLATFORM = 0x08 }; struct driver_t { explicit driver_t(void* gles); @@ -50,11 +51,15 @@ public: ~Loader(); void* open(egl_connection_t* cnx); - void close(void* driver); + void close(egl_connection_t* cnx); private: Loader(); - void *load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask); + driver_t* attempt_to_load_angle(egl_connection_t* cnx); + driver_t* attempt_to_load_updated_driver(egl_connection_t* cnx); + driver_t* attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact); + void unload_system_driver(egl_connection_t* cnx); + void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask); static __attribute__((noinline)) void init_api(void* dso, diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 213c8023d9..25b1009ba2 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -30,11 +30,10 @@ #include "egl_tls.h" #include "egl_display.h" #include "egl_object.h" +#include "egl_layers.h" #include "CallStack.h" #include "Loader.h" -typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; - // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -89,22 +88,22 @@ static int sEarlyInitState = pthread_once(&once_control, &early_egl_init); egl_display_ptr validate_display(EGLDisplay dpy) { egl_display_ptr dp = get_display(dpy); if (!dp) - return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL)); + return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr)); if (!dp->isReady()) - return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL)); + return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr)); return dp; } egl_display_ptr validate_display_connection(EGLDisplay dpy, egl_connection_t*& cnx) { - cnx = NULL; + cnx = nullptr; egl_display_ptr dp = validate_display(dpy); if (!dp) return dp; cnx = &gEGLImpl; - if (cnx->dso == 0) { - return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL)); + if (cnx->dso == nullptr) { + return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr)); } return dp; } @@ -117,14 +116,14 @@ const GLubyte * egl_get_string_for_current_context(GLenum name) { EGLContext context = egl_tls_t::getContext(); if (context == EGL_NO_CONTEXT) - return NULL; + return nullptr; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction - return NULL; + if (c == nullptr) // this should never happen, by construction + return nullptr; if (name != GL_EXTENSIONS) - return NULL; + return nullptr; return (const GLubyte *)c->gl_extensions.c_str(); } @@ -135,19 +134,19 @@ const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) { EGLContext context = egl_tls_t::getContext(); if (context == EGL_NO_CONTEXT) - return NULL; + return nullptr; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction - return NULL; + if (c == nullptr) // this should never happen, by construction + return nullptr; if (name != GL_EXTENSIONS) - return NULL; + return nullptr; // if index is out of bounds, assume it will be in the default // implementation too, so we don't have to generate a GL error here if (index >= c->tokenized_gl_extensions.size()) - return NULL; + return nullptr; return (const GLubyte *)c->tokenized_gl_extensions[index].c_str(); } @@ -161,12 +160,16 @@ GLint egl_get_num_extensions_for_current_context() { return -1; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction + if (c == nullptr) // this should never happen, by construction return -1; return (GLint)c->tokenized_gl_extensions.size(); } +egl_connection_t* egl_get_connection() { + return &gEGLImpl; +} + // ---------------------------------------------------------------------------- // this mutex protects: @@ -184,12 +187,16 @@ static EGLBoolean egl_init_drivers_locked() { // dynamically load our EGL implementation egl_connection_t* cnx = &gEGLImpl; - if (cnx->dso == 0) { - cnx->hooks[egl_connection_t::GLESv1_INDEX] = - &gHooks[egl_connection_t::GLESv1_INDEX]; - cnx->hooks[egl_connection_t::GLESv2_INDEX] = - &gHooks[egl_connection_t::GLESv2_INDEX]; - cnx->dso = loader.open(cnx); + cnx->hooks[egl_connection_t::GLESv1_INDEX] = &gHooks[egl_connection_t::GLESv1_INDEX]; + cnx->hooks[egl_connection_t::GLESv2_INDEX] = &gHooks[egl_connection_t::GLESv2_INDEX]; + cnx->dso = loader.open(cnx); + + // Check to see if any layers are enabled and route functions through them + if (cnx->dso) { + // Layers can be enabled long after the drivers have been loaded. + // They will only be initialized once. + LayerLoader& layer_loader(LayerLoader::getInstance()); + layer_loader.InitLayers(cnx); } return cnx->dso ? EGL_TRUE : EGL_FALSE; @@ -249,7 +256,7 @@ void setGlThreadSpecific(gl_hooks_t const *value) { char const * const gl_names[] = { #include "../entries.in" - NULL + nullptr }; char const * const gl_names_1[] = { @@ -259,7 +266,12 @@ char const * const gl_names_1[] = { char const * const egl_names[] = { #include "egl_entries.in" - NULL + nullptr +}; + +char const * const platform_names[] = { + #include "platform_entries.in" + nullptr }; #undef GL_ENTRY diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 36deedcb85..29a966d348 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -1,5 +1,5 @@ /* - ** Copyright 2007, The Android Open Source Project + ** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -16,1170 +16,225 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <ctype.h> -#include <dlfcn.h> -#include <stdlib.h> -#include <string.h> - -#include <hardware/gralloc1.h> - #include <EGL/egl.h> #include <EGL/eglext.h> -#include <android/hardware_buffer.h> -#include <private/android/AHardwareBufferHelpers.h> - -#include <cutils/compiler.h> -#include <cutils/properties.h> -#include <log/log.h> - -#include <condition_variable> -#include <deque> -#include <mutex> -#include <unordered_map> -#include <string> -#include <thread> - #include "../egl_impl.h" -#include "egl_display.h" -#include "egl_object.h" +#include "egl_layers.h" +#include "egl_platform_entries.h" #include "egl_tls.h" #include "egl_trace.h" using namespace android; -// ---------------------------------------------------------------------------- - namespace android { -using nsecs_t = int64_t; - -struct extention_map_t { - const char* name; - __eglMustCastToProperFunctionPointerType address; -}; - -/* - * This is the list of EGL extensions exposed to applications. - * - * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL - * wrapper and are always available. - * - * The rest (gExtensionString) depend on support in the EGL driver, and are - * only available if the driver supports them. However, some of these must be - * supported because they are used by the Android system itself; these are - * listed as mandatory below and are required by the CDD. The system *assumes* - * the mandatory extensions are present and may not function properly if some - * are missing. - * - * NOTE: Both strings MUST have a single space as the last character. - */ - -extern char const * const gBuiltinExtensionString; -extern char const * const gExtensionString; - -// clang-format off -// Extensions implemented by the EGL wrapper. -char const * const gBuiltinExtensionString = - "EGL_KHR_get_all_proc_addresses " - "EGL_ANDROID_presentation_time " - "EGL_KHR_swap_buffers_with_damage " - "EGL_ANDROID_get_native_client_buffer " - "EGL_ANDROID_front_buffer_auto_refresh " - "EGL_ANDROID_get_frame_timestamps " - "EGL_EXT_surface_SMPTE2086_metadata " - "EGL_EXT_surface_CTA861_3_metadata " - ; - -// Whitelist of extensions exposed to applications if implemented in the vendor driver. -char const * const gExtensionString = - "EGL_KHR_image " // mandatory - "EGL_KHR_image_base " // mandatory - "EGL_EXT_image_gl_colorspace " - "EGL_KHR_image_pixmap " - "EGL_KHR_lock_surface " - "EGL_KHR_gl_colorspace " - "EGL_KHR_gl_texture_2D_image " - "EGL_KHR_gl_texture_3D_image " - "EGL_KHR_gl_texture_cubemap_image " - "EGL_KHR_gl_renderbuffer_image " - "EGL_KHR_reusable_sync " - "EGL_KHR_fence_sync " - "EGL_KHR_create_context " - "EGL_KHR_config_attribs " - "EGL_KHR_surfaceless_context " - "EGL_KHR_stream " - "EGL_KHR_stream_fifo " - "EGL_KHR_stream_producer_eglsurface " - "EGL_KHR_stream_consumer_gltexture " - "EGL_KHR_stream_cross_process_fd " - "EGL_EXT_create_context_robustness " - "EGL_NV_system_time " - "EGL_ANDROID_image_native_buffer " // mandatory - "EGL_KHR_wait_sync " // strongly recommended - "EGL_ANDROID_recordable " // mandatory - "EGL_KHR_partial_update " // strongly recommended - "EGL_EXT_pixel_format_float " - "EGL_EXT_buffer_age " // strongly recommended with partial_update - "EGL_KHR_create_context_no_error " - "EGL_KHR_mutable_render_buffer " - "EGL_EXT_yuv_surface " - "EGL_EXT_protected_content " - "EGL_IMG_context_priority " - "EGL_KHR_no_config_context " - ; -// clang-format on - -// extensions not exposed to applications but used by the ANDROID system -// "EGL_ANDROID_blob_cache " // strongly recommended -// "EGL_IMG_hibernate_process " // optional -// "EGL_ANDROID_native_fence_sync " // strongly recommended -// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 -// "EGL_ANDROID_image_crop " // optional - -/* - * EGL Extensions entry-points exposed to 3rd party applications - * (keep in sync with gExtensionString above) - * - */ -static const extention_map_t sExtensionMap[] = { - // EGL_KHR_lock_surface - { "eglLockSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, - { "eglUnlockSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, - - // EGL_KHR_image, EGL_KHR_image_base - { "eglCreateImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, - { "eglDestroyImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, - - // EGL_KHR_reusable_sync, EGL_KHR_fence_sync - { "eglCreateSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, - { "eglDestroySyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, - { "eglClientWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, - { "eglSignalSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, - { "eglGetSyncAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, - - // EGL_NV_system_time - { "eglGetSystemTimeFrequencyNV", - (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, - { "eglGetSystemTimeNV", - (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, - - // EGL_KHR_wait_sync - { "eglWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, - - // EGL_ANDROID_presentation_time - { "eglPresentationTimeANDROID", - (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, - - // EGL_KHR_swap_buffers_with_damage - { "eglSwapBuffersWithDamageKHR", - (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, - - // EGL_ANDROID_get_native_client_buffer - { "eglGetNativeClientBufferANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, - - // EGL_KHR_partial_update - { "eglSetDamageRegionKHR", - (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, - - { "eglCreateStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, - { "eglDestroyStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, - { "eglStreamAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, - { "eglQueryStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, - { "eglQueryStreamu64KHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, - { "eglQueryStreamTimeKHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, - { "eglCreateStreamProducerSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, - { "eglStreamConsumerGLTextureExternalKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, - { "eglStreamConsumerAcquireKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, - { "eglStreamConsumerReleaseKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, - { "eglGetStreamFileDescriptorKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, - { "eglCreateStreamFromFileDescriptorKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, - - // EGL_ANDROID_get_frame_timestamps - { "eglGetNextFrameIdANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, - { "eglGetCompositorTimingANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, - { "eglGetCompositorTimingSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, - { "eglGetFrameTimestampsANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, - { "eglGetFrameTimestampSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, - - // EGL_ANDROID_native_fence_sync - { "eglDupNativeFenceFDANDROID", - (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, -}; - -/* - * These extensions entry-points should not be exposed to applications. - * They're used internally by the Android EGL layer. - */ -#define FILTER_EXTENSIONS(procname) \ - (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \ - !strcmp((procname), "eglHibernateProcessIMG") || \ - !strcmp((procname), "eglAwakenProcessIMG")) - -// accesses protected by sExtensionMapMutex -static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; - -static int sGLExtentionSlot = 0; -static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; - -static void(*findProcAddress(const char* name, - const extention_map_t* map, size_t n))() { - for (uint32_t i=0 ; i<n ; i++) { - if (!strcmp(name, map[i].name)) { - return map[i].address; - } - } - return NULL; -} - -// ---------------------------------------------------------------------------- - -extern void setGLHooksThreadSpecific(gl_hooks_t const *value); extern EGLBoolean egl_init_drivers(); -extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; -extern gl_hooks_t gHooksTrace; - -} // namespace android; +} // namespace android -// ---------------------------------------------------------------------------- - -static inline void clearError() { egl_tls_t::clearError(); } -static inline EGLContext getContext() { return egl_tls_t::getContext(); } - -// ---------------------------------------------------------------------------- +static inline void clearError() { + egl_tls_t::clearError(); +} -EGLDisplay eglGetDisplay(EGLNativeDisplayType display) -{ +EGLDisplay eglGetDisplay(EGLNativeDisplayType display) { ATRACE_CALL(); clearError(); - uintptr_t index = reinterpret_cast<uintptr_t>(display); - if (index >= NUM_DISPLAYS) { - return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); - } - if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); } - EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display); - return dpy; + // Call down the chain, which usually points directly to the impl + // but may also be routed through layers + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetDisplay(display); } -// ---------------------------------------------------------------------------- -// Initialization -// ---------------------------------------------------------------------------- - -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) -{ +EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { + ATRACE_CALL(); clearError(); - egl_display_ptr dp = get_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - EGLBoolean res = dp->initialize(major, minor); + if (egl_init_drivers() == EGL_FALSE) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } - return res; + // Call down the chain, which usually points directly to the impl + // but may also be routed through layers + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetPlatformDisplay(platform, display, attrib_list); } -EGLBoolean eglTerminate(EGLDisplay dpy) -{ - // NOTE: don't unload the drivers b/c some APIs can be called - // after eglTerminate() has been called. eglTerminate() only - // terminates an EGLDisplay, not a EGL itself. - +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { clearError(); - egl_display_ptr dp = get_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - EGLBoolean res = dp->terminate(); - - return res; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglInitialize(dpy, major, minor); } -// ---------------------------------------------------------------------------- -// configuration -// ---------------------------------------------------------------------------- - -EGLBoolean eglGetConfigs( EGLDisplay dpy, - EGLConfig *configs, - EGLint config_size, EGLint *num_config) -{ +EGLBoolean eglTerminate(EGLDisplay dpy) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - if (num_config==0) { - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - - EGLBoolean res = EGL_FALSE; - *num_config = 0; - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - res = cnx->egl.eglGetConfigs( - dp->disp.dpy, configs, config_size, num_config); - } - - return res; + return cnx->platform.eglTerminate(dpy); } -EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) -{ +EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, + EGLint* num_config) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - if (num_config==0) { - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - - EGLBoolean res = EGL_FALSE; - *num_config = 0; - - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - if (attrib_list) { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.egl.force_msaa", value, "false"); - - if (!strcmp(value, "true")) { - size_t attribCount = 0; - EGLint attrib = attrib_list[0]; - - // Only enable MSAA if the context is OpenGL ES 2.0 and - // if no caveat is requested - const EGLint *attribRendererable = NULL; - const EGLint *attribCaveat = NULL; - - // Count the number of attributes and look for - // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT - while (attrib != EGL_NONE) { - attrib = attrib_list[attribCount]; - switch (attrib) { - case EGL_RENDERABLE_TYPE: - attribRendererable = &attrib_list[attribCount]; - break; - case EGL_CONFIG_CAVEAT: - attribCaveat = &attrib_list[attribCount]; - break; - default: - break; - } - attribCount++; - } - - if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && - (!attribCaveat || attribCaveat[1] != EGL_NONE)) { - - // Insert 2 extra attributes to force-enable MSAA 4x - EGLint aaAttribs[attribCount + 4]; - aaAttribs[0] = EGL_SAMPLE_BUFFERS; - aaAttribs[1] = 1; - aaAttribs[2] = EGL_SAMPLES; - aaAttribs[3] = 4; - - memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); - - EGLint numConfigAA; - EGLBoolean resAA = cnx->egl.eglChooseConfig( - dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); - - if (resAA == EGL_TRUE && numConfigAA > 0) { - ALOGD("Enabling MSAA 4x"); - *num_config = numConfigAA; - return resAA; - } - } - } - } - - res = cnx->egl.eglChooseConfig( - dp->disp.dpy, attrib_list, configs, config_size, num_config); - } - return res; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config); } -EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, + EGLint config_size, EGLint* num_config) { clearError(); - egl_connection_t* cnx = NULL; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (!dp) return EGL_FALSE; - - return cnx->egl.eglGetConfigAttrib( - dp->disp.dpy, config, attribute, value); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config); } -// ---------------------------------------------------------------------------- -// surfaces -// ---------------------------------------------------------------------------- +EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) { + clearError(); -// Translates EGL color spaces to Android data spaces. -static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { - if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { - return HAL_DATASPACE_UNKNOWN; - } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { - return HAL_DATASPACE_SRGB; - } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { - return HAL_DATASPACE_DISPLAY_P3; - } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) { - return HAL_DATASPACE_DISPLAY_P3_LINEAR; - } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) { - return HAL_DATASPACE_V0_SCRGB; - } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) { - return HAL_DATASPACE_V0_SCRGB_LINEAR; - } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) { - return HAL_DATASPACE_BT2020_LINEAR; - } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) { - return HAL_DATASPACE_BT2020_PQ; - } - return HAL_DATASPACE_UNKNOWN; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value); } -// Get the colorspace value that should be reported from queries. When the colorspace -// is unknown (no attribute passed), default to reporting LINEAR. -static EGLint getReportedColorSpace(EGLint colorspace) { - return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace; -} +EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window, + const EGLint* attrib_list) { + clearError(); -// Returns a list of color spaces understood by the vendor EGL driver. -static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { - std::vector<EGLint> colorSpaces; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list); +} - // sRGB and linear are always supported when color space support is present. - colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR); - colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR); +EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { + clearError(); - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_display_p3")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_scrgb")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_scrgb_linear")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_bt2020_linear")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_bt2020_pq")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_display_p3_linear")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); - } - return colorSpaces; -} - -// Cleans up color space related parameters that the driver does not understand. -// If there is no color space attribute in attrib_list, colorSpace is left -// unmodified. -static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window, - const EGLint* attrib_list, EGLint* colorSpace, - std::vector<EGLint>* strippedAttribList) { - for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { - bool copyAttribute = true; - if (attr[0] == EGL_GL_COLORSPACE_KHR) { - switch (attr[1]) { - case EGL_GL_COLORSPACE_LINEAR_KHR: - case EGL_GL_COLORSPACE_SRGB_KHR: - case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: - case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: - case EGL_GL_COLORSPACE_SCRGB_EXT: - case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: - case EGL_GL_COLORSPACE_BT2020_PQ_EXT: - case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT: - // Fail immediately if the driver doesn't have color space support at all. - if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - break; - default: - // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_* - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - } - *colorSpace = attr[1]; - - // Strip the attribute if the driver doesn't understand it. - copyAttribute = false; - std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp); - for (auto driverColorSpace : driverColorSpaces) { - if (attr[1] == driverColorSpace) { - copyAttribute = true; - break; - } - } - - // If the driver doesn't understand it, we should map sRGB-encoded P3 to - // sRGB rather than just dropping the colorspace on the floor. - // For this format, the driver is expected to apply the sRGB - // transfer function during framebuffer operations. - if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { - strippedAttribList->push_back(attr[0]); - strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR); - } - } - if (copyAttribute) { - strippedAttribList->push_back(attr[0]); - strippedAttribList->push_back(attr[1]); - } - } - // Terminate the attribute list. - strippedAttribList->push_back(EGL_NONE); - - // If the passed color space has wide color gamut, check whether the target native window - // supports wide color. - const bool colorSpaceIsNarrow = - *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR || - *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || - *colorSpace == EGL_UNKNOWN; - if (window && !colorSpaceIsNarrow) { - bool windowSupportsWideColor = true; - // Ordinarily we'd put a call to native_window_get_wide_color_support - // at the beginning of the function so that we'll have the - // result when needed elsewhere in the function. - // However, because eglCreateWindowSurface is called by SurfaceFlinger and - // SurfaceFlinger is required to answer the call below we would - // end up in a deadlock situation. By moving the call to only happen - // if the application has specifically asked for wide-color we avoid - // the deadlock with SurfaceFlinger since it will not ask for a - // wide-color surface. - int err = native_window_get_wide_color_support(window, &windowSupportsWideColor); - - if (err) { - ALOGE("processAttributes: invalid window (win=%p) " - "failed (%#x) (already connected to another API?)", - window, err); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE); - } - if (!windowSupportsWideColor) { - // Application has asked for a wide-color colorspace but - // wide-color support isn't available on the display the window is on. - return setError(EGL_BAD_MATCH, EGL_FALSE); - } - } - return true; -} - -// Gets the native pixel format corrsponding to the passed EGLConfig. -void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, - android_pixel_format* format) { - // Set the native window's buffers format to match what this config requests. - // Whether to use sRGB gamma is not part of the EGLconfig, but is part - // of our native format. So if sRGB gamma is requested, we have to - // modify the EGLconfig's format before setting the native window's - // format. - - EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType); - - EGLint a = 0; - EGLint r, g, b; - r = g = b = 0; - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r); - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g); - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b); - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a); - EGLint colorDepth = r + g + b; - - // Today, the driver only understands sRGB and linear on 888X - // formats. Strip other colorspaces from the attribute list and - // only use them to set the dataspace via - // native_window_set_buffers_dataspace - // if pixel format is RGBX 8888 - // TBD: Can test for future extensions that indicate that driver - // handles requested color space and we can let it through. - // allow SRGB and LINEAR. All others need to be stripped. - // else if 565, 4444 - // TBD: Can we assume these are supported if 8888 is? - // else if FP16 or 1010102 - // strip colorspace from attribs. - // endif - if (a == 0) { - if (colorDepth <= 16) { - *format = HAL_PIXEL_FORMAT_RGB_565; - } else { - if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { - if (colorDepth > 24) { - *format = HAL_PIXEL_FORMAT_RGBA_1010102; - } else { - *format = HAL_PIXEL_FORMAT_RGBX_8888; - } - } else { - *format = HAL_PIXEL_FORMAT_RGBA_FP16; - } - } - } else { - if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { - if (colorDepth > 24) { - *format = HAL_PIXEL_FORMAT_RGBA_1010102; - } else { - *format = HAL_PIXEL_FORMAT_RGBA_8888; - } - } else { - *format = HAL_PIXEL_FORMAT_RGBA_FP16; - } - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list); } -EGLBoolean sendSurfaceMetadata(egl_surface_t* s) { - android_smpte2086_metadata smpteMetadata; - if (s->getSmpte2086Metadata(smpteMetadata)) { - int err = - native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata); - s->resetSmpte2086Metadata(); - if (err != 0) { - ALOGE("error setting native window smpte2086 metadata: %s (%d)", - strerror(-err), err); - return EGL_FALSE; - } - } - android_cta861_3_metadata cta8613Metadata; - if (s->getCta8613Metadata(cta8613Metadata)) { - int err = - native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata); - s->resetCta8613Metadata(); - if (err != 0) { - ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", - strerror(-err), err); - return EGL_FALSE; - } - } - return EGL_TRUE; -} - -EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, - NativeWindowType window, - const EGLint *attrib_list) -{ - const EGLint *origAttribList = attrib_list; - clearError(); - - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - if (!window) { - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - int value = 0; - window->query(window, NATIVE_WINDOW_IS_VALID, &value); - if (!value) { - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); - if (result < 0) { - ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " - "failed (%#x) (already connected to another API?)", - window, result); - return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - } - - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // now select correct colorspace and dataspace based on user's attribute list - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, window, attrib_list, &colorSpace, - &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); - - { - int err = native_window_set_buffers_format(window, format); - if (err != 0) { - ALOGE("error setting native window pixel format: %s (%d)", - strerror(-err), err); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - } - - android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace); - // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. HAL_DATASPACE_UNKNOWN - // is the default value, but it may have changed at this point. - int err = native_window_set_buffers_data_space(window, dataSpace); - if (err != 0) { - ALOGE("error setting native window pixel dataSpace: %s (%d)", - strerror(-err), err); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - // the EGL spec requires that a new EGLSurface default to swap interval - // 1, so explicitly set that on the window here. - ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); - anw->setSwapInterval(anw, 1); - - EGLSurface surface = cnx->egl.eglCreateWindowSurface( - iDpy, config, window, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = - new egl_surface_t(dp.get(), config, window, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } - - // EGLSurface creation failed - native_window_set_buffers_format(window, 0); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - } - return EGL_NO_SURFACE; -} - -EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, - const EGLint *attrib_list) -{ - clearError(); - - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // now select a corresponding sRGB format if needed - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, - &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); - - EGLSurface surface = cnx->egl.eglCreatePixmapSurface( - dp->disp.dpy, config, pixmap, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = - new egl_surface_t(dp.get(), config, NULL, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } - } - return EGL_NO_SURFACE; -} - -EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) -{ - clearError(); - - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // Select correct colorspace based on user's attribute list - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, - &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); - - EGLSurface surface = cnx->egl.eglCreatePbufferSurface( - dp->disp.dpy, config, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = - new egl_surface_t(dp.get(), config, NULL, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } - } - return EGL_NO_SURFACE; +EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, + const EGLint* attrib_list) { + clearError(); + + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list); } -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) -{ +EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, + const EGLAttrib* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { + clearError(); - egl_surface_t * const s = get_surface(surface); - EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); - if (result == EGL_TRUE) { - _s.terminate(); - } - return result; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list); } -EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroySurface(dpy, surface); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) { + clearError(); - egl_surface_t const * const s = get_surface(surface); - if (s->getColorSpaceAttribute(attribute, value)) { - return EGL_TRUE; - } else if (s->getSmpte2086Attribute(attribute, value)) { - return EGL_TRUE; - } else if (s->getCta8613Attribute(attribute, value)) { - return EGL_TRUE; - } - return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQuerySurface(dpy, surface, attribute, value); } void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { ATRACE_CALL(); clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return; - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - } + egl_connection_t* const cnx = &gEGLImpl; + cnx->platform.eglBeginFrame(dpy, surface); } -// ---------------------------------------------------------------------------- -// Contexts -// ---------------------------------------------------------------------------- +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list, + const EGLint* attrib_list) { + clearError(); -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, - EGLContext share_list, const EGLint *attrib_list) -{ - clearError(); - - egl_connection_t* cnx = NULL; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - if (share_list != EGL_NO_CONTEXT) { - if (!ContextRef(dp.get(), share_list).get()) { - return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); - } - egl_context_t* const c = get_context(share_list); - share_list = c->context; - } - EGLContext context = cnx->egl.eglCreateContext( - dp->disp.dpy, config, share_list, attrib_list); - if (context != EGL_NO_CONTEXT) { - // figure out if it's a GLESv1 or GLESv2 - int version = 0; - if (attrib_list) { - while (*attrib_list != EGL_NONE) { - GLint attr = *attrib_list++; - GLint value = *attrib_list++; - if (attr == EGL_CONTEXT_CLIENT_VERSION) { - if (value == 1) { - version = egl_connection_t::GLESv1_INDEX; - } else if (value == 2 || value == 3) { - version = egl_connection_t::GLESv2_INDEX; - } - } - }; - } - egl_context_t* c = new egl_context_t(dpy, context, config, cnx, - version); - return c; - } - } - return EGL_NO_CONTEXT; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list); } -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) -{ +EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) - return EGL_FALSE; - - ContextRef _c(dp.get(), ctx); - if (!_c.get()) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - egl_context_t * const c = get_context(ctx); - EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); - if (result == EGL_TRUE) { - _c.terminate(); - } - return result; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroyContext(dpy, ctx); } -EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) -{ +EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { clearError(); - egl_display_ptr dp = validate_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not - // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is - // a valid but uninitialized display. - if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || - (draw != EGL_NO_SURFACE) ) { - if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } - - // get a reference to the object passed in - ContextRef _c(dp.get(), ctx); - SurfaceRef _d(dp.get(), draw); - SurfaceRef _r(dp.get(), read); - - // validate the context (if not EGL_NO_CONTEXT) - if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { - // EGL_NO_CONTEXT is valid - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - } - - // these are the underlying implementation's object - EGLContext impl_ctx = EGL_NO_CONTEXT; - EGLSurface impl_draw = EGL_NO_SURFACE; - EGLSurface impl_read = EGL_NO_SURFACE; - - // these are our objects structs passed in - egl_context_t * c = NULL; - egl_surface_t const * d = NULL; - egl_surface_t const * r = NULL; - - // these are the current objects structs - egl_context_t * cur_c = get_context(getContext()); - - if (ctx != EGL_NO_CONTEXT) { - c = get_context(ctx); - impl_ctx = c->context; - } else { - // no context given, use the implementation of the current context - if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { - // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); - return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE); - } - if (cur_c == NULL) { - // no current context - // not an error, there is just no current context. - return EGL_TRUE; - } - } - - // retrieve the underlying implementation's draw EGLSurface - if (draw != EGL_NO_SURFACE) { - if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - d = get_surface(draw); - impl_draw = d->surface; - } - - // retrieve the underlying implementation's read EGLSurface - if (read != EGL_NO_SURFACE) { - if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - r = get_surface(read); - impl_read = r->surface; - } - - - EGLBoolean result = dp->makeCurrent(c, cur_c, - draw, read, ctx, - impl_draw, impl_read, impl_ctx); - - if (result == EGL_TRUE) { - if (c) { - setGLHooksThreadSpecific(c->cnx->hooks[c->version]); - egl_tls_t::setContext(ctx); - _c.acquire(); - _r.acquire(); - _d.acquire(); - } else { - setGLHooksThreadSpecific(&gHooksNoContext); - egl_tls_t::setContext(EGL_NO_CONTEXT); - } - } else { - // this will ALOGE the error - egl_connection_t* const cnx = &gEGLImpl; - result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE); - } - return result; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx); } - -EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - ContextRef _c(dp.get(), ctx); - if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - egl_context_t * const c = get_context(ctx); - return c->cnx->egl.eglQueryContext( - dp->disp.dpy, c->context, attribute, value); - + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryContext(dpy, ctx, attribute, value); } -EGLContext eglGetCurrentContext(void) -{ - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would correctly return EGL_NO_CONTEXT. - +EGLContext eglGetCurrentContext(void) { clearError(); - EGLContext ctx = getContext(); - return ctx; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentContext(); } -EGLSurface eglGetCurrentSurface(EGLint readdraw) -{ - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would correctly return EGL_NO_SURFACE. - +EGLSurface eglGetCurrentSurface(EGLint readdraw) { clearError(); - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); - switch (readdraw) { - case EGL_READ: return c->read; - case EGL_DRAW: return c->draw; - default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); - } - } - return EGL_NO_SURFACE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentSurface(readdraw); } -EGLDisplay eglGetCurrentDisplay(void) -{ - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would correctly return EGL_NO_DISPLAY. - +EGLDisplay eglGetCurrentDisplay(void) { clearError(); - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); - return c->dpy; - } - return EGL_NO_DISPLAY; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentDisplay(); } -EGLBoolean eglWaitGL(void) -{ +EGLBoolean eglWaitGL(void) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - return cnx->egl.eglWaitGL(); + return cnx->platform.eglWaitGL(); } -EGLBoolean eglWaitNative(EGLint engine) -{ +EGLBoolean eglWaitNative(EGLint engine) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - return cnx->egl.eglWaitNative(engine); + return cnx->platform.eglWaitNative(engine); } -EGLint eglGetError(void) -{ - EGLint err = EGL_SUCCESS; +EGLint eglGetError(void) { egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - err = cnx->egl.eglGetError(); - } - if (err == EGL_SUCCESS) { - err = egl_tls_t::getError(); - } - return err; -} - -static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( - const char* procname) { - const egl_connection_t* cnx = &gEGLImpl; - void* proc = NULL; - - proc = dlsym(cnx->libEgl, procname); - if (proc) return (__eglMustCastToProperFunctionPointerType)proc; - - proc = dlsym(cnx->libGles2, procname); - if (proc) return (__eglMustCastToProperFunctionPointerType)proc; - - proc = dlsym(cnx->libGles1, procname); - if (proc) return (__eglMustCastToProperFunctionPointerType)proc; - - return NULL; + return cnx->platform.eglGetError(); } -__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) -{ +__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) { // eglGetProcAddress() could be the very first function called // in which case we must make sure we've initialized ourselves, this // happens the first time egl_get_display() is called. @@ -1188,431 +243,98 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) if (egl_init_drivers() == EGL_FALSE) { setError(EGL_BAD_PARAMETER, NULL); - return NULL; - } - - if (FILTER_EXTENSIONS(procname)) { - return NULL; - } - - __eglMustCastToProperFunctionPointerType addr; - addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap)); - if (addr) return addr; - - addr = findBuiltinWrapper(procname); - if (addr) return addr; - - // this protects accesses to sGLExtentionMap and sGLExtentionSlot - pthread_mutex_lock(&sExtensionMapMutex); - - /* - * Since eglGetProcAddress() is not associated to anything, it needs - * to return a function pointer that "works" regardless of what - * the current context is. - * - * For this reason, we return a "forwarder", a small stub that takes - * care of calling the function associated with the context - * currently bound. - * - * We first look for extensions we've already resolved, if we're seeing - * this extension for the first time, we go through all our - * implementations and call eglGetProcAddress() and record the - * result in the appropriate implementation hooks and return the - * address of the forwarder corresponding to that hook set. - * - */ - - const std::string name(procname); - - auto& extentionMap = sGLExtentionMap; - auto pos = extentionMap.find(name); - addr = (pos != extentionMap.end()) ? pos->second : nullptr; - const int slot = sGLExtentionSlot; - - ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, - "no more slots for eglGetProcAddress(\"%s\")", - procname); - - if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { - bool found = false; - - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglGetProcAddress) { - // Extensions are independent of the bound context - addr = - cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = - cnx->egl.eglGetProcAddress(procname); - if (addr) found = true; - } - - if (found) { - addr = gExtensionForwarders[slot]; - extentionMap[name] = addr; - sGLExtentionSlot++; - } - } - - pthread_mutex_unlock(&sExtensionMapMutex); - return addr; -} - -class FrameCompletionThread { -public: - - static void queueSync(EGLSyncKHR sync) { - static FrameCompletionThread thread; - - char name[64]; - - std::lock_guard<std::mutex> lock(thread.mMutex); - snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued); - ATRACE_NAME(name); - - thread.mQueue.push_back(sync); - thread.mCondition.notify_one(); - thread.mFramesQueued++; - ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size())); - } - -private: - - FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) { - std::thread thread(&FrameCompletionThread::loop, this); - thread.detach(); - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" - void loop() { - while (true) { - threadLoop(); - } - } -#pragma clang diagnostic pop - - void threadLoop() { - EGLSyncKHR sync; - uint32_t frameNum; - { - std::unique_lock<std::mutex> lock(mMutex); - while (mQueue.empty()) { - mCondition.wait(lock); - } - sync = mQueue[0]; - frameNum = mFramesCompleted; - } - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - { - char name[64]; - snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum); - ATRACE_NAME(name); - - EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); - if (result == EGL_FALSE) { - ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("FrameCompletion: timeout waiting for fence"); - } - eglDestroySyncKHR(dpy, sync); - } - { - std::lock_guard<std::mutex> lock(mMutex); - mQueue.pop_front(); - mFramesCompleted++; - ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size())); - } + return nullptr; } - uint32_t mFramesQueued; - uint32_t mFramesCompleted; - std::deque<EGLSyncKHR> mQueue; - std::condition_variable mCondition; - std::mutex mMutex; -}; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetProcAddress(procname); +} -EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, - EGLint *rects, EGLint n_rects) -{ +EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects, + EGLint n_rects) { ATRACE_CALL(); clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), draw); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t* const s = get_surface(draw); - - if (CC_UNLIKELY(dp->traceGpuCompletion)) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); - if (sync != EGL_NO_SYNC_KHR) { - FrameCompletionThread::queueSync(sync); - } - } - - if (CC_UNLIKELY(dp->finishOnSwap)) { - uint32_t pixel; - egl_context_t * const c = get_context( egl_tls_t::getContext() ); - if (c) { - // glReadPixels() ensures that the frame is complete - s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, - GL_RGBA,GL_UNSIGNED_BYTE,&pixel); - } - } - - if (!sendSurfaceMetadata(s)) { - native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL); - return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE); - } - - if (n_rects == 0) { - return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); - } - - std::vector<android_native_rect_t> androidRects((size_t)n_rects); - for (int r = 0; r < n_rects; ++r) { - int offset = r * 4; - int x = rects[offset]; - int y = rects[offset + 1]; - int width = rects[offset + 2]; - int height = rects[offset + 3]; - android_native_rect_t androidRect; - androidRect.left = x; - androidRect.top = y + height; - androidRect.right = x + width; - androidRect.bottom = y; - androidRects.push_back(androidRect); - } - native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), androidRects.size()); - - if (s->cnx->egl.eglSwapBuffersWithDamageKHR) { - return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, - rects, n_rects); - } else { - return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); - } -} - -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) -{ - return eglSwapBuffersWithDamageKHR(dpy, surface, NULL, 0); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects); } -EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, - NativePixmapType target) -{ +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { + ATRACE_CALL(); clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t const * const s = get_surface(surface); - return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapBuffers(dpy, surface); } -const char* eglQueryString(EGLDisplay dpy, EGLint name) -{ +EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) { clearError(); - // Generate an error quietly when client extensions (as defined by - // EGL_EXT_client_extensions) are queried. We do not want to rely on - // validate_display to generate the error as validate_display would log - // the error, which can be misleading. - // - // If we want to support EGL_EXT_client_extensions later, we can return - // the client extension string here instead. - if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) - return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)0); - - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return (const char *) NULL; - - switch (name) { - case EGL_VENDOR: - return dp->getVendorString(); - case EGL_VERSION: - return dp->getVersionString(); - case EGL_EXTENSIONS: - return dp->getExtensionString(); - case EGL_CLIENT_APIS: - return dp->getClientApiString(); - default: - break; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCopyBuffers(dpy, surface, target); } -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) -{ +const char* eglQueryString(EGLDisplay dpy, EGLint name) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return (const char *) NULL; - - switch (name) { - case EGL_VENDOR: - return dp->disp.queryString.vendor; - case EGL_VERSION: - return dp->disp.queryString.version; - case EGL_EXTENSIONS: - return dp->disp.queryString.extensions; - case EGL_CLIENT_APIS: - return dp->disp.queryString.clientApi; - default: - break; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryString(dpy, name); } -// ---------------------------------------------------------------------------- -// EGL 1.1 -// ---------------------------------------------------------------------------- - -EGLBoolean eglSurfaceAttrib( - EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) -{ +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t * const s = get_surface(surface); - - if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { - if (!s->getNativeWindow()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - } - int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - if (attribute == EGL_TIMESTAMPS_ANDROID) { - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - if (s->setSmpte2086Attribute(attribute, value)) { - return EGL_TRUE; - } else if (s->setCta8613Attribute(attribute, value)) { - return EGL_TRUE; - } else if (s->cnx->egl.eglSurfaceAttrib) { - return s->cnx->egl.eglSurfaceAttrib( - dp->disp.dpy, s->surface, attribute, value); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryStringImplementationANDROID(dpy, name); } -EGLBoolean eglBindTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ +EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglBindTexImage) { - return s->cnx->egl.eglBindTexImage( - dp->disp.dpy, s->surface, buffer); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value); } -EGLBoolean eglReleaseTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ +EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglReleaseTexImage) { - return s->cnx->egl.eglReleaseTexImage( - dp->disp.dpy, s->surface, buffer); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglBindTexImage(dpy, surface, buffer); } -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) -{ +EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean res = EGL_TRUE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglSwapInterval) { - res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); - } - - return res; + return cnx->platform.eglReleaseTexImage(dpy, surface, buffer); } +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { + clearError(); -// ---------------------------------------------------------------------------- -// EGL 1.2 -// ---------------------------------------------------------------------------- + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapInterval(dpy, interval); +} -EGLBoolean eglWaitClient(void) -{ +EGLBoolean eglWaitClient(void) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - EGLBoolean res; - if (cnx->egl.eglWaitClient) { - res = cnx->egl.eglWaitClient(); - } else { - res = cnx->egl.eglWaitGL(); - } - return res; + return cnx->platform.eglWaitClient(); } -EGLBoolean eglBindAPI(EGLenum api) -{ +EGLBoolean eglBindAPI(EGLenum api) { clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); } - // bind this API on all EGLs - EGLBoolean res = EGL_TRUE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglBindAPI) { - res = cnx->egl.eglBindAPI(api); - } - return res; + return cnx->platform.eglBindAPI(api); } -EGLenum eglQueryAPI(void) -{ +EGLenum eglQueryAPI(void) { clearError(); if (egl_init_drivers() == EGL_FALSE) { @@ -1620,824 +342,324 @@ EGLenum eglQueryAPI(void) } egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryAPI) { - return cnx->egl.eglQueryAPI(); - } - - // or, it can only be OpenGL ES - return EGL_OPENGL_ES_API; + return cnx->platform.eglQueryAPI(); } -EGLBoolean eglReleaseThread(void) -{ +EGLBoolean eglReleaseThread(void) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglReleaseThread) { - cnx->egl.eglReleaseThread(); - } - - // If there is context bound to the thread, release it - egl_display_t::loseCurrent(get_context(getContext())); - - egl_tls_t::clearTLS(); - return EGL_TRUE; + return cnx->platform.eglReleaseThread(); } -EGLSurface eglCreatePbufferFromClientBuffer( - EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, - EGLConfig config, const EGLint *attrib_list) -{ +EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint* attrib_list) { clearError(); - egl_connection_t* cnx = NULL; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (!dp) return EGL_FALSE; - if (cnx->egl.eglCreatePbufferFromClientBuffer) { - return cnx->egl.eglCreatePbufferFromClientBuffer( - dp->disp.dpy, buftype, buffer, config, attrib_list); - } - return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config, + attrib_list); } -// ---------------------------------------------------------------------------- -// EGL_EGLEXT_VERSION 3 -// ---------------------------------------------------------------------------- - -EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, - const EGLint *attrib_list) -{ +EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) { + clearError(); - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglLockSurfaceKHR) { - return s->cnx->egl.eglLockSurfaceKHR( - dp->disp.dpy, s->surface, attrib_list); - } - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglUnlockSurfaceKHR(dpy, surface); } -EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) -{ +EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { + clearError(); - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglUnlockSurfaceKHR) { - return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); - } - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateImage(dpy, ctx, target, buffer, attrib_list); } -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint *attrib_list) -{ - clearError(); - - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_IMAGE_KHR; - - ContextRef _c(dp.get(), ctx); - egl_context_t * const c = _c.get(); - - // Temporary hack: eglImageCreateKHR should accept EGL_GL_COLORSPACE_LINEAR_KHR, - // EGL_GL_COLORSPACE_SRGB_KHR and EGL_GL_COLORSPACE_DEFAULT_EXT if - // EGL_EXT_image_gl_colorspace is supported, but some drivers don't like - // the DEFAULT value and generate an error. - std::vector<EGLint> strippedAttribList; - for (const EGLint *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { - if (attr[0] == EGL_GL_COLORSPACE_KHR && - dp->haveExtension("EGL_EXT_image_gl_colorspace")) { - if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR && - attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) { - continue; - } - } - strippedAttribList.push_back(attr[0]); - strippedAttribList.push_back(attr[1]); - } - strippedAttribList.push_back(EGL_NONE); +EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) { + clearError(); - EGLImageKHR result = EGL_NO_IMAGE_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateImageKHR) { - result = cnx->egl.eglCreateImageKHR( - dp->disp.dpy, - c ? c->context : EGL_NO_CONTEXT, - target, buffer, strippedAttribList.data()); - } - return result; + return cnx->platform.eglDestroyImageKHR(dpy, img); } -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) -{ +EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDestroyImageKHR) { - result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img); - } - return result; + return cnx->platform.eglDestroyImage(dpy, img); } // ---------------------------------------------------------------------------- // EGL_EGLEXT_VERSION 5 // ---------------------------------------------------------------------------- - -EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) -{ +EGLSyncKHR eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_SYNC_KHR; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateSync(dpy, type, attrib_list); +} + +EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { + clearError(); - EGLSyncKHR result = EGL_NO_SYNC_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateSyncKHR) { - result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list); - } - return result; + return cnx->platform.eglCreateSyncKHR(dpy, type, attrib_list); } -EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) -{ +EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroySync(dpy, sync); +} + +EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { + clearError(); - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDestroySyncKHR) { - result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync); - } - return result; + return cnx->platform.eglDestroySyncKHR(dpy, sync); } EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglSignalSyncKHR) { - result = cnx->egl.eglSignalSyncKHR( - dp->disp.dpy, sync, mode); - } - return result; + return cnx->platform.eglSignalSyncKHR(dpy, sync, mode); } -EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, - EGLint flags, EGLTimeKHR timeout) -{ +EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLint result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) { - result = cnx->egl.eglClientWaitSyncKHR( - dp->disp.dpy, sync, flags, timeout); - } - return result; + return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout); } -EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, - EGLint attribute, EGLint *value) -{ +EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) { - result = cnx->egl.eglGetSyncAttribKHR( - dp->disp.dpy, sync, attribute, value); - } - return result; + return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout); } -EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list) -{ +EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_STREAM_KHR; - - EGLStreamKHR result = EGL_NO_STREAM_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateStreamKHR) { - result = cnx->egl.eglCreateStreamKHR( - dp->disp.dpy, attrib_list); - } - return result; + return cnx->platform.eglGetSyncAttrib(dpy, sync, attribute, value); } -EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) -{ +EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDestroyStreamKHR) { - result = cnx->egl.eglDestroyStreamKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value); } -EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLint value) -{ +EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamAttribKHR) { - result = cnx->egl.eglStreamAttribKHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglCreateStreamKHR(dpy, attrib_list); } -EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLint *value) -{ +EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryStreamKHR) { - result = cnx->egl.eglQueryStreamKHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglDestroyStreamKHR(dpy, stream); } -EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLuint64KHR *value) -{ +EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLint value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) { - result = cnx->egl.eglQueryStreamu64KHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value); } -EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLTimeKHR *value) -{ +EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLint* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) { - result = cnx->egl.eglQueryStreamTimeKHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value); } -EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, - EGLStreamKHR stream, const EGLint *attrib_list) -{ +EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLuint64KHR* value) { clearError(); - egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_SURFACE; - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) { - EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR( - dp->disp.dpy, config, stream, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface, - EGL_GL_COLORSPACE_LINEAR_KHR, cnx); - return s; - } - } - return EGL_NO_SURFACE; + return cnx->platform.eglQueryStreamu64KHR(dpy, stream, attribute, value); } -EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, - EGLStreamKHR stream) -{ +EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLTimeKHR* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) { - result = cnx->egl.eglStreamConsumerGLTextureExternalKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value); } -EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, - EGLStreamKHR stream) -{ +EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, + const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) { - result = cnx->egl.eglStreamConsumerAcquireKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list); } -EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, - EGLStreamKHR stream) -{ +EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) { - result = cnx->egl.eglStreamConsumerReleaseKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream); } -EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR( - EGLDisplay dpy, EGLStreamKHR stream) -{ +EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR; - - EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) { - result = cnx->egl.eglGetStreamFileDescriptorKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglStreamConsumerAcquireKHR(dpy, stream); } -EGLStreamKHR eglCreateStreamFromFileDescriptorKHR( - EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor) -{ +EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_STREAM_KHR; - - EGLStreamKHR result = EGL_NO_STREAM_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) { - result = cnx->egl.eglCreateStreamFromFileDescriptorKHR( - dp->disp.dpy, file_descriptor); - } - return result; + return cnx->platform.eglStreamConsumerReleaseKHR(dpy, stream); } -// ---------------------------------------------------------------------------- -// EGL_EGLEXT_VERSION 15 -// ---------------------------------------------------------------------------- - -EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - EGLint result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglWaitSyncKHR) { - result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags); - } - return result; + return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream); } -// ---------------------------------------------------------------------------- -// ANDROID extensions -// ---------------------------------------------------------------------------- - -EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) -{ +EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy, + EGLNativeFileDescriptorKHR file_descriptor) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor); +} - EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; +EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { + clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { - result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); - } - return result; + return cnx->platform.eglWaitSyncKHR(dpy, sync, flags); } -EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, - EGLnsecsANDROID time) -{ +EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) { clearError(); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglWaitSync(dpy, sync, flags); +} - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return EGL_FALSE; - } +EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) { + clearError(); - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync); +} - egl_surface_t const * const s = get_surface(surface); - native_window_set_buffers_timestamp(s->getNativeWindow(), time); +EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) { + clearError(); - return EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time); } -EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) { +EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) { clearError(); - // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus - // this function cannot be implemented when this libEGL is built for - // vendors. -#ifndef __ANDROID_VNDK__ - if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); - return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); -#else - return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); -#endif + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetNativeClientBufferANDROID(buffer); } -// ---------------------------------------------------------------------------- -// NVIDIA extensions -// ---------------------------------------------------------------------------- -EGLuint64NV eglGetSystemTimeFrequencyNV() -{ +EGLuint64NV eglGetSystemTimeFrequencyNV() { clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE); } - EGLuint64NV ret = 0; egl_connection_t* const cnx = &gEGLImpl; - - if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { - return cnx->egl.eglGetSystemTimeFrequencyNV(); - } - - return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); + return cnx->platform.eglGetSystemTimeFrequencyNV(); } -EGLuint64NV eglGetSystemTimeNV() -{ +EGLuint64NV eglGetSystemTimeNV() { clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE); } - EGLuint64NV ret = 0; egl_connection_t* const cnx = &gEGLImpl; - - if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { - return cnx->egl.eglGetSystemTimeNV(); - } - - return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); + return cnx->platform.eglGetSystemTimeNV(); } -// ---------------------------------------------------------------------------- -// Partial update extension -// ---------------------------------------------------------------------------- -EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, - EGLint *rects, EGLint n_rects) -{ +EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects, + EGLint n_rects) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - setError(EGL_BAD_DISPLAY, EGL_FALSE); - return EGL_FALSE; - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - return EGL_FALSE; - } - - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglSetDamageRegionKHR) { - return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, - rects, n_rects); - } - - return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSetDamageRegionKHR(dpy, surface, rects, n_rects); } -EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, - EGLuint64KHR *frameId) { +EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - uint64_t nextFrameId = 0; - int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId); - - if (ret != 0) { - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetNextFrameId: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } - - *frameId = nextFrameId; - return EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetNextFrameIdANDROID(dpy, surface, frameId); } -EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, - EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) -{ +EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, + const EGLint* names, EGLnsecsANDROID* values) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - nsecs_t* compositeDeadline = nullptr; - nsecs_t* compositeInterval = nullptr; - nsecs_t* compositeToPresentLatency = nullptr; - - for (int i = 0; i < numTimestamps; i++) { - switch (names[i]) { - case EGL_COMPOSITE_DEADLINE_ANDROID: - compositeDeadline = &values[i]; - break; - case EGL_COMPOSITE_INTERVAL_ANDROID: - compositeInterval = &values[i]; - break; - case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: - compositeToPresentLatency = &values[i]; - break; - default: - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - } - - int ret = native_window_get_compositor_timing(s->getNativeWindow(), - compositeDeadline, compositeInterval, compositeToPresentLatency); - - switch (ret) { - case 0: - return EGL_TRUE; - case -ENOSYS: - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - default: - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetCompositorTiming: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCompositorTimingANDROID(dpy, surface, numTimestamps, names, values); } -EGLBoolean eglGetCompositorTimingSupportedANDROID( - EGLDisplay dpy, EGLSurface surface, EGLint name) -{ +EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - ANativeWindow* window = s->getNativeWindow(); - if (!window) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - switch (name) { - case EGL_COMPOSITE_DEADLINE_ANDROID: - case EGL_COMPOSITE_INTERVAL_ANDROID: - case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: - return EGL_TRUE; - default: - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCompositorTimingSupportedANDROID(dpy, surface, name); } -EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, - EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, - EGLnsecsANDROID *values) -{ +EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, + EGLint numTimestamps, const EGLint* timestamps, + EGLnsecsANDROID* values) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - nsecs_t* requestedPresentTime = nullptr; - nsecs_t* acquireTime = nullptr; - nsecs_t* latchTime = nullptr; - nsecs_t* firstRefreshStartTime = nullptr; - nsecs_t* gpuCompositionDoneTime = nullptr; - nsecs_t* lastRefreshStartTime = nullptr; - nsecs_t* displayPresentTime = nullptr; - nsecs_t* dequeueReadyTime = nullptr; - nsecs_t* releaseTime = nullptr; - - for (int i = 0; i < numTimestamps; i++) { - switch (timestamps[i]) { - case EGL_REQUESTED_PRESENT_TIME_ANDROID: - requestedPresentTime = &values[i]; - break; - case EGL_RENDERING_COMPLETE_TIME_ANDROID: - acquireTime = &values[i]; - break; - case EGL_COMPOSITION_LATCH_TIME_ANDROID: - latchTime = &values[i]; - break; - case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: - firstRefreshStartTime = &values[i]; - break; - case EGL_LAST_COMPOSITION_START_TIME_ANDROID: - lastRefreshStartTime = &values[i]; - break; - case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: - gpuCompositionDoneTime = &values[i]; - break; - case EGL_DISPLAY_PRESENT_TIME_ANDROID: - displayPresentTime = &values[i]; - break; - case EGL_DEQUEUE_READY_TIME_ANDROID: - dequeueReadyTime = &values[i]; - break; - case EGL_READS_DONE_TIME_ANDROID: - releaseTime = &values[i]; - break; - default: - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - } - - int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId, - requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime, - lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime, - dequeueReadyTime, releaseTime); - - switch (ret) { - case 0: - return EGL_TRUE; - case -ENOENT: - return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); - case -ENOSYS: - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - case -EINVAL: - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - default: - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetFrameTimestamps: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetFrameTimestampsANDROID(dpy, surface, frameId, numTimestamps, + timestamps, values); } -EGLBoolean eglGetFrameTimestampSupportedANDROID( - EGLDisplay dpy, EGLSurface surface, EGLint timestamp) -{ +EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint timestamp) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - ANativeWindow* window = s->getNativeWindow(); - if (!window) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - switch (timestamp) { - case EGL_COMPOSITE_DEADLINE_ANDROID: - case EGL_COMPOSITE_INTERVAL_ANDROID: - case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: - case EGL_REQUESTED_PRESENT_TIME_ANDROID: - case EGL_RENDERING_COMPLETE_TIME_ANDROID: - case EGL_COMPOSITION_LATCH_TIME_ANDROID: - case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: - case EGL_LAST_COMPOSITION_START_TIME_ANDROID: - case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: - case EGL_DEQUEUE_READY_TIME_ANDROID: - case EGL_READS_DONE_TIME_ANDROID: - return EGL_TRUE; - case EGL_DISPLAY_PRESENT_TIME_ANDROID: { - int value = 0; - window->query(window, - NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); - return value == 0 ? EGL_FALSE : EGL_TRUE; - } - default: - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetFrameTimestampSupportedANDROID(dpy, surface, timestamp); } diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp new file mode 100644 index 0000000000..00caff222b --- /dev/null +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(__ANDROID__) + +#include <cutils/properties.h> +#include "Loader.h" +#include "egl_angle_platform.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include <EGL/Platform.h> +#pragma GCC diagnostic pop + +#include <android/dlext.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> +#include <time.h> +#include <log/log.h> + +namespace angle { + +static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr; +static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr; + +static time_t startTime = time(nullptr); + +static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/, + const char* /*categoryName*/) { + // Returning ptr to 'g' (non-zero) to ALWAYS enable tracing initially. + // This ptr is what will be passed into "category_group_enabled" of addTraceEvent + static const unsigned char traceEnabled = 'g'; + return &traceEnabled; +} + +static double monotonicallyIncreasingTime(PlatformMethods* /*platform*/) { + return difftime(time(nullptr), startTime); +} + +static void logError(PlatformMethods* /*platform*/, const char* errorMessage) { + ALOGE("ANGLE Error:%s", errorMessage); +} + +static void logWarning(PlatformMethods* /*platform*/, const char* warningMessage) { + ALOGW("ANGLE Warn:%s", warningMessage); +} + +static void logInfo(PlatformMethods* /*platform*/, const char* infoMessage) { + ALOGD("ANGLE Info:%s", infoMessage); +} + +static TraceEventHandle addTraceEvent( + PlatformMethods* /**platform*/, char phase, const unsigned char* /*category_group_enabled*/, + const char* name, unsigned long long /*id*/, double /*timestamp*/, int /*num_args*/, + const char** /*arg_names*/, const unsigned char* /*arg_types*/, + const unsigned long long* /*arg_values*/, unsigned char /*flags*/) { + switch (phase) { + case 'B': { + ATRACE_BEGIN(name); + break; + } + case 'E': { + ATRACE_END(); + break; + } + case 'I': { + ATRACE_NAME(name); + break; + } + default: + // Could handle other event types here + break; + } + // Return any non-zero handle to avoid assert in ANGLE + TraceEventHandle result = 1.0; + return result; +} + +static void assignAnglePlatformMethods(PlatformMethods* platformMethods) { + platformMethods->addTraceEvent = addTraceEvent; + platformMethods->getTraceCategoryEnabledFlag = getTraceCategoryEnabledFlag; + platformMethods->monotonicallyIncreasingTime = monotonicallyIncreasingTime; + platformMethods->logError = logError; + platformMethods->logWarning = logWarning; + platformMethods->logInfo = logInfo; +} + +// Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace +bool initializeAnglePlatform(EGLDisplay dpy) { + // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform + android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo); + angleGetDisplayPlatform = + reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform")); + + if (!angleGetDisplayPlatform) { + ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!"); + return false; + } + + angleResetDisplayPlatform = + reinterpret_cast<ResetDisplayPlatformFunc>( + eglGetProcAddress("ANGLEResetDisplayPlatform")); + + PlatformMethods* platformMethods = nullptr; + if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, + g_NumPlatformMethods, nullptr, + &platformMethods))) { + ALOGE("ANGLEGetDisplayPlatform call failed!"); + return false; + } + if (platformMethods) { + assignAnglePlatformMethods(platformMethods); + } else { + ALOGE("In initializeAnglePlatform() platformMethods struct ptr is NULL. Not assigning " + "tracing function ptrs!"); + } + return true; +} + +void resetAnglePlatform(EGLDisplay dpy) { + if (angleResetDisplayPlatform) { + angleResetDisplayPlatform(dpy); + } +} + +}; // namespace angle + +#endif // __ANDROID__ diff --git a/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h new file mode 100644 index 0000000000..6c24aa5418 --- /dev/null +++ b/opengl/libs/EGL/egl_angle_platform.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#if defined(__ANDROID__) + +#include "egldefs.h" + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include "egl_trace.h" + +namespace angle { + +bool initializeAnglePlatform(EGLDisplay dpy); +void resetAnglePlatform(EGLDisplay dpy); + +}; // namespace angle + +#endif // __ANDROID__ diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index ec548f3121..bcf496164b 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -95,7 +95,7 @@ void egl_cache_t::initialize(egl_display_t *display) { reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( cnx->egl.eglGetProcAddress( "eglSetBlobCacheFuncsANDROID")); - if (eglSetBlobCacheFuncsANDROID == NULL) { + if (eglSetBlobCacheFuncsANDROID == nullptr) { ALOGE("EGL_ANDROID_blob_cache advertised, " "but unable to get eglSetBlobCacheFuncsANDROID"); return; @@ -119,7 +119,7 @@ void egl_cache_t::terminate() { if (mBlobCache) { mBlobCache->writeToFile(); } - mBlobCache = NULL; + mBlobCache = nullptr; } void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 2aec249aa5..67d69b4d06 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -21,14 +21,19 @@ #include "../egl_impl.h" +#include <EGL/eglext_angle.h> #include <private/EGL/display.h> +#include <cutils/properties.h> +#include "Loader.h" +#include "egl_angle_platform.h" #include "egl_cache.h" #include "egl_object.h" #include "egl_tls.h" -#include "egl_trace.h" -#include "Loader.h" -#include <cutils/properties.h> + +#include <android/dlext.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> @@ -41,7 +46,8 @@ namespace android { // ---------------------------------------------------------------------------- static char const * const sVendorString = "Android"; -static char const * const sVersionString = "1.4 Android META-EGL"; +static char const* const sVersionString14 = "1.4 Android META-EGL"; +static char const* const sVersionString15 = "1.5 Android META-EGL"; static char const * const sClientApiString = "OpenGL_ES"; extern char const * const gBuiltinExtensionString; @@ -65,6 +71,11 @@ bool findExtension(const char* exts, const char* name, size_t nameLen) { return false; } +bool needsAndroidPEglMitigation() { + static const int32_t vndk_version = property_get_int32("ro.vndk.version", -1); + return vndk_version <= 28; +} + int egl_get_init_count(EGLDisplay dpy) { egl_display_t* eglDisplay = egl_display_t::get(dpy); return eglDisplay ? eglDisplay->getRefsCount() : 0; @@ -114,15 +125,91 @@ bool egl_display_t::getObject(egl_object_t* object) const { return false; } -EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) { +EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp, + const EGLAttrib* attrib_list) { if (uintptr_t(disp) >= NUM_DISPLAYS) - return NULL; + return nullptr; + + return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list); +} - return sDisplay[uintptr_t(disp)].getDisplay(disp); +static bool addAnglePlatformAttributes(egl_connection_t* const cnx, + std::vector<EGLAttrib>& attrs) { + intptr_t vendorEGL = (intptr_t)cnx->vendorEGL; + + attrs.reserve(4 * 2); + + attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + attrs.push_back(cnx->angleBackend); + + switch (cnx->angleBackend) { + case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE: + ALOGV("%s: Requesting Vulkan ANGLE back-end", __FUNCTION__); + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.angle.validation", prop, "0"); + attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE); + attrs.push_back(atoi(prop)); + break; + case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: + ALOGV("%s: Requesting Default ANGLE back-end", __FUNCTION__); + break; + case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: + ALOGV("%s: Requesting OpenGL ES ANGLE back-end", __FUNCTION__); + // NOTE: This is only valid if the backend is OpenGL + attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE); + attrs.push_back(vendorEGL); + break; + default: + ALOGV("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend); + break; + } + attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE); + attrs.push_back(EGL_FALSE); + + return true; } -EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { +static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx, + const EGLAttrib* attrib_list, EGLint* error) { + EGLDisplay dpy = EGL_NO_DISPLAY; + *error = EGL_NONE; + + if (cnx->egl.eglGetPlatformDisplay) { + std::vector<EGLAttrib> attrs; + if (attrib_list) { + for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) { + attrs.push_back(attr[0]); + attrs.push_back(attr[1]); + } + } + + if (!addAnglePlatformAttributes(cnx, attrs)) { + ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display); + *error = EGL_BAD_PARAMETER; + return EGL_NO_DISPLAY; + } + attrs.push_back(EGL_NONE); + dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, + reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY), + attrs.data()); + if (dpy == EGL_NO_DISPLAY) { + ALOGE("eglGetPlatformDisplay failed!"); + } else { + if (!angle::initializeAnglePlatform(dpy)) { + ALOGE("initializeAnglePlatform failed!"); + } + } + } else { + ALOGE("eglGetDisplay(%p) failed: Unable to look up eglGetPlatformDisplay from ANGLE", + display); + } + + return dpy; +} + +EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { std::lock_guard<std::mutex> _l(lock); ATRACE_CALL(); @@ -130,12 +217,38 @@ EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { Loader& loader(Loader::getInstance()); egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) { - EGLDisplay dpy = cnx->egl.eglGetDisplay(display); + if (cnx->dso) { + EGLDisplay dpy = EGL_NO_DISPLAY; + + if (cnx->useAngle) { + EGLint error; + dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error); + if (error != EGL_NONE) { + return setError(error, dpy); + } + } + if (dpy == EGL_NO_DISPLAY) { + // NOTE: eglGetPlatformDisplay with a empty attribute list + // behaves the same as eglGetDisplay + if (cnx->egl.eglGetPlatformDisplay) { + dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display, + attrib_list); + } + + // It is possible that eglGetPlatformDisplay does not have a + // working implementation for Android platform; in that case, + // one last fallback to eglGetDisplay + if(dpy == EGL_NO_DISPLAY) { + if (attrib_list) { + ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored"); + } + dpy = cnx->egl.eglGetDisplay(display); + } + } + disp.dpy = dpy; if (dpy == EGL_NO_DISPLAY) { - loader.close(cnx->dso); - cnx->dso = NULL; + loader.close(cnx); } } @@ -148,13 +261,20 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { std::unique_lock<std::mutex> _l(refLock); refs++; if (refs > 1) { - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; + // We don't know what to report until we know what the + // driver supports. Make sure we are initialized before + // returning the version info. while(!eglIsInitialized) { refCond.wait(_l); } + egl_connection_t* const cnx = &gEGLImpl; + + // TODO: If device doesn't provide 1.4 or 1.5 then we'll be + // changing the behavior from the past where we always advertise + // version 1.4. May need to check that revision is valid + // before using cnx->major & cnx->minor + if (major != nullptr) *major = cnx->major; + if (minor != nullptr) *minor = cnx->minor; return EGL_TRUE; } while(eglIsInitialized) { @@ -199,9 +319,37 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { } } + if (cnx->minor == 5) { + // full list in egl_entries.in + if (!cnx->egl.eglCreateImage || + !cnx->egl.eglDestroyImage || + !cnx->egl.eglGetPlatformDisplay || + !cnx->egl.eglCreatePlatformWindowSurface || + !cnx->egl.eglCreatePlatformPixmapSurface || + !cnx->egl.eglCreateSync || + !cnx->egl.eglDestroySync || + !cnx->egl.eglClientWaitSync || + !cnx->egl.eglGetSyncAttrib || + !cnx->egl.eglWaitSync) { + ALOGE("Driver indicates EGL 1.5 support, but does not have " + "a critical API"); + cnx->minor = 4; + } + } + // the query strings are per-display mVendorString = sVendorString; - mVersionString = sVersionString; + mVersionString.clear(); + cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0); + mVersionString = sVersionString14; + if ((cnx->major == 1) && (cnx->minor == 5)) { + mVersionString = sVersionString15; + cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0); + } + if (mVersionString.empty()) { + ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor); + mVersionString = sVersionString14; + } mClientApiString = sClientApiString; mExtensionString = gBuiltinExtensionString; @@ -218,7 +366,8 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (wideColorBoardConfig && hasColorSpaceSupport) { mExtensionString.append( "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear " - "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "); + "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 " + "EGL_EXT_gl_colorspace_display_p3_passthrough "); } bool hasHdrBoardConfig = @@ -240,9 +389,10 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (len) { // NOTE: we could avoid the copy if we had strnstr. const std::string ext(start, len); - // Temporary hack: Adreno 530 driver exposes this extension under the draft - // KHR name, but during Khronos review it was decided to demote it to EXT. - if (ext == "EGL_EXT_image_gl_colorspace" && + // Mitigation for Android P vendor partitions: Adreno 530 driver shipped on + // some Android P vendor partitions this extension under the draft KHR name, + // but during Khronos review it was decided to demote it to EXT. + if (needsAndroidPEglMitigation() && ext == "EGL_EXT_image_gl_colorspace" && findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) { mExtensionString.append("EGL_EXT_image_gl_colorspace "); } @@ -268,10 +418,12 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { traceGpuCompletion = true; } - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; + // TODO: If device doesn't provide 1.4 or 1.5 then we'll be + // changing the behavior from the past where we always advertise + // version 1.4. May need to check that revision is valid + // before using cnx->major & cnx->minor + if (major != nullptr) *major = cnx->major; + if (minor != nullptr) *minor = cnx->minor; } { // scope for refLock @@ -311,6 +463,10 @@ EGLBoolean egl_display_t::terminate() { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && disp.state == egl_display_t::INITIALIZED) { + // If we're using ANGLE reset any custom DisplayPlatform + if (cnx->useAngle) { + angle::resetAnglePlatform(disp.dpy); + } if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, egl_tls_t::egl_strerror(cnx->egl.eglGetError())); @@ -361,8 +517,8 @@ void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) // by construction, these are either 0 or valid (possibly terminated) // it should be impossible for these to be invalid ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); - SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr); { // scope for the lock std::lock_guard<std::mutex> _l(lock); @@ -387,8 +543,8 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, // by construction, these are either 0 or valid (possibly terminated) // it should be impossible for these to be invalid ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); - SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr); { // scope for the lock std::lock_guard<std::mutex> _l(lock); diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 79a9f082a6..e117314d71 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -43,12 +43,14 @@ class egl_context_t; struct egl_connection_t; bool findExtension(const char* exts, const char* name, size_t nameLen = 0); +bool needsAndroidPEglMitigation(); // ---------------------------------------------------------------------------- class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes static egl_display_t sDisplay[NUM_DISPLAYS]; EGLDisplay getDisplay(EGLNativeDisplayType display); + EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list); void loseCurrentImpl(egl_context_t * cur_c); public: @@ -72,7 +74,7 @@ public: bool getObject(egl_object_t* object) const; static egl_display_t* get(EGLDisplay dpy); - static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp); + static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list); EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read, EGLContext ctx, @@ -160,7 +162,7 @@ public: const egl_display_t* get() const { return mDpy; } egl_display_t* get() { return mDpy; } - operator bool() const { return mDpy != NULL; } + operator bool() const { return mDpy != nullptr; } private: egl_display_t* mDpy; diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in index b587a16203..2921d512f1 100644 --- a/opengl/libs/EGL/egl_entries.in +++ b/opengl/libs/EGL/egl_entries.in @@ -44,6 +44,18 @@ EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGL /* EGL 1.4 */ +/* EGL 1.5 */ +EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *) +EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage) +EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, void *, const EGLAttrib *) +EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *) +EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *) +EGL_ENTRY(EGLSyncKHR, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib *) +EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync) +EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR) +EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSync, EGLint, EGLAttrib *) +EGL_ENTRY(EGLBoolean, eglWaitSync, EGLDisplay, EGLSync, EGLint) + /* EGL_EGLEXT_VERSION 3 */ EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint *) @@ -75,6 +87,10 @@ EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR) EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint) +/* EGL_EGLEXT_VERSION 20170627 */ +EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *) +EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *) + /* ANDROID extensions */ EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint) diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp new file mode 100644 index 0000000000..ac01dc8f96 --- /dev/null +++ b/opengl/libs/EGL/egl_layers.cpp @@ -0,0 +1,444 @@ +/* + ** Copyright 2018, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "egl_layers.h" + +#include <EGL/egl.h> +#include <android-base/file.h> +#include <android-base/strings.h> +#include <android/dlext.h> +#include <cutils/properties.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> +#include <log/log.h> +#include <nativebridge/native_bridge.h> +#include <nativeloader/native_loader.h> +#include <sys/prctl.h> + +namespace android { + +// GLES Layers +// +// - Layer discovery - +// 1. Check for debug layer list from GraphicsEnv +// 2. If none enabled, check system properties +// +// - Layer initializing - +// - AndroidGLESLayer_Initialize (provided by layer, called by loader) +// - AndroidGLESLayer_GetProcAddress (provided by layer, called by loader) +// - getNextLayerProcAddress (provided by loader, called by layer) +// +// 1. Walk through defs for egl and each gl version +// 2. Call GetLayerProcAddress passing the name and the target hook entry point +// - This tells the layer the next point in the chain it should call +// 3. Replace the hook with the layer's entry point +// - All entryoints will be present, anything unsupported by the driver will +// have gl_unimplemented +// +// - Extension layering - +// Not all functions are known to Android, so libEGL handles extensions. +// They are looked up by applications using eglGetProcAddress +// Layers can look them up with getNextLayerProcAddress + +const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) + + sizeof(gl_hooks_t) / sizeof(char*); + +typedef struct FunctionTable { + EGLFuncPointer x[kFuncCount]; + EGLFuncPointer& operator[](int i) { return x[i]; } +} FunctionTable; + +// TODO: Move these to class +std::unordered_map<std::string, int> func_indices; +// func_indices.reserve(kFuncCount); + +std::unordered_map<int, std::string> func_names; +// func_names.reserve(kFuncCount); + +std::vector<FunctionTable> layer_functions; + +const void* getNextLayerProcAddress(void* layer_id, const char* name) { + // Use layer_id to find funcs for layer below current + // This is the same key provided in AndroidGLESLayer_Initialize + auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id); + EGLFuncPointer val; + + ALOGV("getNextLayerProcAddress servicing %s", name); + + if (func_indices.find(name) == func_indices.end()) { + // No entry for this function - it is an extension + // call down the GPA chain directly to the impl + ALOGV("getNextLayerProcAddress - name(%s) no func_indices entry found", name); + + // Look up which GPA we should use + int gpaIndex = func_indices["eglGetProcAddress"]; + ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex); + EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex]; + ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext); + + + // Call it for the requested function + typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*); + PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext); + + val = reinterpret_cast<EGLFuncPointer>(next(name)); + ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val); + + // We should store it now, but to do that, we need to move func_idx to the class so we can + // increment it separately + // TODO: Move func_idx to class and store the result of GPA + return reinterpret_cast<void*>(val); + } + + int index = func_indices[name]; + val = (*next_layer_funcs)[index]; + ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val); + return reinterpret_cast<void*>(val); +} + +void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr, + int& func_idx) { + while (*entries) { + const char* name = *entries; + + // Some names overlap, only fill with initial entry + // This does mean that some indices will not be used + if (func_indices.find(name) == func_indices.end()) { + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx); + func_names[func_idx] = name; + func_indices[name] = func_idx; + } else { + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx); + } + + // Populate layer_functions once with initial value + // These values will arrive in priority order, starting with platform entries + if (functions[func_idx] == nullptr) { + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr); + functions[func_idx] = *curr; + } else { + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]); + } + + entries++; + curr++; + func_idx++; + } +} + +LayerLoader& LayerLoader::getInstance() { + // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl + static LayerLoader layer_loader; + + if (!layer_loader.layers_loaded_) layer_loader.LoadLayers(); + + return layer_loader; +} + +const char kSystemLayerLibraryDir[] = "/data/local/debug/gles"; + +std::string LayerLoader::GetDebugLayers() { + // Layers can be specified at the Java level in GraphicsEnvironemnt + // gpu_debug_layers_gles = layer1:layer2:layerN + std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES(); + + if (debug_layers.empty()) { + // Only check system properties if Java settings are empty + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.gles.layers", prop, ""); + debug_layers = prop; + } + + return debug_layers; +} + +EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name, + EGLFuncPointer next) { + // Walk through our list of LayerSetup functions (they will already be in reverse order) to + // build up a call chain from the driver + + EGLFuncPointer layer_entry = next; + + layer_entry = layer_setup(name, layer_entry); + + if (next != layer_entry) { + ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s", + (unsigned long long)next, (unsigned long long)layer_entry, name); + } + + return layer_entry; +} + +EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) { + if (!layers_loaded_ || layer_setup_.empty()) return next; + + ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name, + (unsigned long long)next, current_layer_); + + EGLFuncPointer val = next; + + // Only ApplyLayers for layers that have been setup, not all layers yet + for (unsigned i = 0; i < current_layer_; i++) { + ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name, + (unsigned long long)next); + val = ApplyLayer(layer_setup_[i], name, val); + } + + ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name); + + return val; +} + +void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr, + char const* const* entries) { + while (*entries) { + char const* name = *entries; + + EGLFuncPointer prev = *curr; + + // Pass the existing entry point into the layer, replace the call with return value + *curr = ApplyLayer(layer_setup, name, *curr); + + if (prev != *curr) { + ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s", + (unsigned long long)prev, (unsigned long long)*curr, name); + } else { + ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not " + "intercept", + (unsigned long long)prev, name); + } + + curr++; + entries++; + } +} + +void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr, + char const* const* entries) { + while (*entries) { + char const* name = *entries; + EGLFuncPointer prev = *curr; + + // Only apply layers to driver entries if not handled by the platform + if (FindPlatformImplAddr(name) == nullptr) { + // Pass the existing entry point into the layer, replace the call with return value + *curr = ApplyLayer(layer_setup, name, *prev); + + if (prev != *curr) { + ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s", + (unsigned long long)prev, (unsigned long long)*curr, name); + } + + } else { + ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name); + } + + curr++; + entries++; + } +} + +bool LayerLoader::Initialized() { + return initialized_; +} + +void LayerLoader::InitLayers(egl_connection_t* cnx) { + if (!layers_loaded_) return; + + if (initialized_) return; + + if (layer_setup_.empty()) { + initialized_ = true; + return; + } + + // Include the driver in layer_functions + layer_functions.resize(layer_setup_.size() + 1); + + // Walk through the initial lists and create layer_functions[0] + int func_idx = 0; + char const* const* entries; + EGLFuncPointer* curr; + + entries = platform_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after platform_names: %i", func_idx); + + entries = egl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after egl_names: %i", func_idx); + + entries = gl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after gl_names: %i", func_idx); + + // Walk through each layer's entry points per API, starting just above the driver + for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) { + // Init the layer with a key that points to layer just below it + layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]), + reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>( + getNextLayerProcAddress)); + + // Check functions implemented by the platform + func_idx = 0; + entries = platform_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform); + LayerPlatformEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + + // EGL + entries = egl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl); + LayerDriverEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + + // GLES 2+ + // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x + // If it were added in the future, a different layer initialization model would be needed, + // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase + // initialization. + entries = gl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl); + LayerDriverEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + } + + // We only want to apply layers once + initialized_ = true; +} + +void LayerLoader::LoadLayers() { + std::string debug_layers = GetDebugLayers(); + + // If no layers are specified, we're done + if (debug_layers.empty()) return; + + // Only enable the system search path for non-user builds + std::string system_path; + if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + system_path = kSystemLayerLibraryDir; + } + + ALOGI("Debug layer list: %s", debug_layers.c_str()); + std::vector<std::string> layers = android::base::Split(debug_layers, ":"); + + // Load the layers in reverse order so we start with the driver's entrypoint and work our way up + for (int32_t i = layers.size() - 1; i >= 0; i--) { + // Check each layer path for the layer + std::vector<std::string> paths = + android::base::Split(android::GraphicsEnv::getInstance().getLayerPaths().c_str(), + ":"); + + if (!system_path.empty()) { + // Prepend the system paths so they override other layers + auto it = paths.begin(); + paths.insert(it, system_path); + } + + bool layer_found = false; + for (uint32_t j = 0; j < paths.size() && !layer_found; j++) { + std::string layer; + + ALOGI("Searching %s for GLES layers", paths[j].c_str()); + + // Realpath will return null for non-existent files + android::base::Realpath(paths[j] + "/" + layers[i], &layer); + + if (!layer.empty()) { + layer_found = true; + ALOGI("GLES layer found: %s", layer.c_str()); + + // Load the layer + // + // TODO: This code is common with Vulkan loader, refactor + // + // Libraries in the system layer library dir can't be loaded into + // the application namespace. That causes compatibility problems, since + // any symbol dependencies will be resolved by system libraries. They + // can't safely use libc++_shared, for example. Which is one reason + // (among several) we only allow them in non-user builds. + void* handle = nullptr; + auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace(); + if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) { + bool native_bridge = false; + char* error_message = nullptr; + handle = OpenNativeLibraryInNamespace( + app_namespace, layer.c_str(), &native_bridge, &error_message); + if (!handle) { + ALOGE("Failed to load layer %s with error: %s", layer.c_str(), + error_message); + android::NativeLoaderFreeErrorMessage(error_message); + return; + } + + } else { + handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL); + } + + if (handle) { + ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle, + layers[i].c_str()); + } else { + // If the layer is found but can't be loaded, try setenforce 0 + const char* dlsym_error = dlerror(); + ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error); + return; + } + + // Find the layer's Initialize function + std::string init_func = "AndroidGLESLayer_Initialize"; + ALOGV("Looking for entrypoint %s", init_func.c_str()); + + layer_init_func LayerInit = + reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str())); + if (LayerInit) { + ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str()); + layer_init_.push_back(LayerInit); + } else { + ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str()); + return; + } + + // Find the layer's setup function + std::string setup_func = "AndroidGLESLayer_GetProcAddress"; + ALOGV("Looking for entrypoint %s", setup_func.c_str()); + + layer_setup_func LayerSetup = + reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str())); + if (LayerSetup) { + ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str()); + layer_setup_.push_back(LayerSetup); + } else { + ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str()); + return; + } + } + } + } + // Track this so we only attempt to load these once + layers_loaded_ = true; +} + +} // namespace android diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h new file mode 100644 index 0000000000..e401b448cf --- /dev/null +++ b/opengl/libs/EGL/egl_layers.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_EGL_LAYERS_H +#define ANDROID_EGL_LAYERS_H + +#include <string> +#include <unordered_map> +#include <vector> + +#include <EGL/egldefs.h> + +#include "egl_platform_entries.h" + +typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; + +namespace android { + +class LayerLoader { +public: + static LayerLoader& getInstance(); + ~LayerLoader(){}; + + typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); + typedef EGLFuncPointer (*layer_init_func)( + const void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address); + typedef EGLFuncPointer (*layer_setup_func)(const char* name, EGLFuncPointer next); + + void LoadLayers(); + void InitLayers(egl_connection_t*); + void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*); + void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*); + bool Initialized(); + std::string GetDebugLayers(); + + EGLFuncPointer GetGpaNext(unsigned i); + EGLFuncPointer ApplyLayer(layer_setup_func layer_setup, const char* name, EGLFuncPointer next); + EGLFuncPointer ApplyLayers(const char* name, EGLFuncPointer next); + + std::vector<layer_init_func> layer_init_; + std::vector<layer_setup_func> layer_setup_; + +private: + LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){}; + bool layers_loaded_; + bool initialized_; + unsigned current_layer_; +}; + +}; // namespace android + +#endif // ANDROID_EGL_LAYERS_H diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index f879254f76..ff4fe2dd9c 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -81,14 +81,14 @@ egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWind } egl_surface_t::~egl_surface_t() { - if (win != NULL) { + if (win != nullptr) { disconnect(); win->decStrong(this); } } void egl_surface_t::disconnect() { - if (win != NULL && connected) { + if (win != nullptr && connected) { native_window_set_buffers_format(win, 0); if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) { ALOGW("EGLNativeWindowType %p disconnect failed", win); @@ -281,12 +281,12 @@ void egl_surface_t::terminate() { egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx, int version) : egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context), - config(config), read(0), draw(0), cnx(cnx), version(version) { + config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) { } void egl_context_t::onLooseCurrent() { - read = NULL; - draw = NULL; + read = nullptr; + draw = nullptr; } void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) { diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index 4e1de5cec4..fb2bdf4c51 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -67,7 +67,7 @@ public: public: ~LocalRef(); explicit LocalRef(egl_object_t* rhs); - explicit LocalRef(egl_display_t const* display, T o) : ref(0) { + explicit LocalRef(egl_display_t const* display, T o) : ref(nullptr) { egl_object_t* native = reinterpret_cast<N*>(o); if (o && egl_object_t::get(display, native)) { ref = native; diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp new file mode 100644 index 0000000000..e996be6853 --- /dev/null +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -0,0 +1,2736 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "egl_platform_entries.h" + +#include <ctype.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <string.h> + +#include <hardware/gralloc1.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <EGL/eglext_angle.h> + +#include <android/hardware_buffer.h> +#include <android-base/strings.h> +#include <graphicsenv/GraphicsEnv.h> +#include <private/android/AHardwareBufferHelpers.h> + +#include <cutils/compiler.h> +#include <cutils/properties.h> +#include <log/log.h> + +#include <condition_variable> +#include <deque> +#include <mutex> +#include <unordered_map> +#include <string> +#include <thread> + +#include "../egl_impl.h" + +#include "egl_display.h" +#include "egl_object.h" +#include "egl_layers.h" +#include "egl_tls.h" +#include "egl_trace.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +namespace android { + +using nsecs_t = int64_t; + +struct extention_map_t { + const char* name; + __eglMustCastToProperFunctionPointerType address; +}; + +/* + * This is the list of EGL extensions exposed to applications. + * + * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL + * wrapper and are always available. + * + * The rest (gExtensionString) depend on support in the EGL driver, and are + * only available if the driver supports them. However, some of these must be + * supported because they are used by the Android system itself; these are + * listed as mandatory below and are required by the CDD. The system *assumes* + * the mandatory extensions are present and may not function properly if some + * are missing. + * + * NOTE: Both strings MUST have a single space as the last character. + */ + +extern char const * const gBuiltinExtensionString; +extern char const * const gExtensionString; + +// clang-format off +// Extensions implemented by the EGL wrapper. +char const * const gBuiltinExtensionString = + "EGL_KHR_get_all_proc_addresses " + "EGL_ANDROID_presentation_time " + "EGL_KHR_swap_buffers_with_damage " + "EGL_ANDROID_get_native_client_buffer " + "EGL_ANDROID_front_buffer_auto_refresh " + "EGL_ANDROID_get_frame_timestamps " + "EGL_EXT_surface_SMPTE2086_metadata " + "EGL_EXT_surface_CTA861_3_metadata " + ; + +// Whitelist of extensions exposed to applications if implemented in the vendor driver. +char const * const gExtensionString = + "EGL_KHR_image " // mandatory + "EGL_KHR_image_base " // mandatory + "EGL_EXT_image_gl_colorspace " + "EGL_KHR_image_pixmap " + "EGL_KHR_lock_surface " + "EGL_KHR_gl_colorspace " + "EGL_KHR_gl_texture_2D_image " + "EGL_KHR_gl_texture_3D_image " + "EGL_KHR_gl_texture_cubemap_image " + "EGL_KHR_gl_renderbuffer_image " + "EGL_KHR_reusable_sync " + "EGL_KHR_fence_sync " + "EGL_KHR_create_context " + "EGL_KHR_config_attribs " + "EGL_KHR_surfaceless_context " + "EGL_KHR_stream " + "EGL_KHR_stream_fifo " + "EGL_KHR_stream_producer_eglsurface " + "EGL_KHR_stream_consumer_gltexture " + "EGL_KHR_stream_cross_process_fd " + "EGL_EXT_create_context_robustness " + "EGL_NV_system_time " + "EGL_ANDROID_image_native_buffer " // mandatory + "EGL_KHR_wait_sync " // strongly recommended + "EGL_ANDROID_recordable " // mandatory + "EGL_KHR_partial_update " // strongly recommended + "EGL_EXT_pixel_format_float " + "EGL_EXT_buffer_age " // strongly recommended with partial_update + "EGL_KHR_create_context_no_error " + "EGL_KHR_mutable_render_buffer " + "EGL_EXT_yuv_surface " + "EGL_EXT_protected_content " + "EGL_IMG_context_priority " + "EGL_KHR_no_config_context " + ; + +char const * const gClientExtensionString = + "EGL_EXT_client_extensions " + "EGL_KHR_platform_android " + "EGL_ANGLE_platform_angle " + "EGL_ANDROID_GLES_layers"; +// clang-format on + +// extensions not exposed to applications but used by the ANDROID system +// "EGL_ANDROID_blob_cache " // strongly recommended +// "EGL_IMG_hibernate_process " // optional +// "EGL_ANDROID_native_fence_sync " // strongly recommended +// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 + +/* + * EGL Extensions entry-points exposed to 3rd party applications + * (keep in sync with gExtensionString above) + * + */ +static const extention_map_t sExtensionMap[] = { + // EGL_KHR_lock_surface + { "eglLockSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, + { "eglUnlockSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, + + // EGL_KHR_image, EGL_KHR_image_base + { "eglCreateImageKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, + { "eglDestroyImageKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, + + // EGL_KHR_reusable_sync, EGL_KHR_fence_sync + { "eglCreateSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, + { "eglDestroySyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, + { "eglClientWaitSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, + { "eglSignalSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, + { "eglGetSyncAttribKHR", + (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, + + // EGL_NV_system_time + { "eglGetSystemTimeFrequencyNV", + (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, + { "eglGetSystemTimeNV", + (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, + + // EGL_KHR_wait_sync + { "eglWaitSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, + + // EGL_ANDROID_presentation_time + { "eglPresentationTimeANDROID", + (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, + + // EGL_KHR_swap_buffers_with_damage + { "eglSwapBuffersWithDamageKHR", + (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, + + // EGL_ANDROID_get_native_client_buffer + { "eglGetNativeClientBufferANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, + + // EGL_KHR_partial_update + { "eglSetDamageRegionKHR", + (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, + + { "eglCreateStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, + { "eglDestroyStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, + { "eglStreamAttribKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, + { "eglQueryStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, + { "eglQueryStreamu64KHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, + { "eglQueryStreamTimeKHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, + { "eglCreateStreamProducerSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, + { "eglStreamConsumerGLTextureExternalKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, + { "eglStreamConsumerAcquireKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, + { "eglStreamConsumerReleaseKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, + { "eglGetStreamFileDescriptorKHR", + (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, + { "eglCreateStreamFromFileDescriptorKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, + + // EGL_ANDROID_get_frame_timestamps + { "eglGetNextFrameIdANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, + { "eglGetCompositorTimingANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, + { "eglGetCompositorTimingSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, + { "eglGetFrameTimestampsANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, + { "eglGetFrameTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, + + // EGL_ANDROID_native_fence_sync + { "eglDupNativeFenceFDANDROID", + (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, +}; + +/* + * These extensions entry-points should not be exposed to applications. + * They're used internally by the Android EGL layer. + */ +#define FILTER_EXTENSIONS(procname) \ + (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \ + !strcmp((procname), "eglHibernateProcessIMG") || \ + !strcmp((procname), "eglAwakenProcessIMG")) + +// accesses protected by sExtensionMapMutex +static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; + +static int sGLExtentionSlot = 0; +static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; + +static void(*findProcAddress(const char* name, + const extention_map_t* map, size_t n))() { + for (uint32_t i=0 ; i<n ; i++) { + if (!strcmp(name, map[i].name)) { + return map[i].address; + } + } + return nullptr; +} + +// ---------------------------------------------------------------------------- + +extern void setGLHooksThreadSpecific(gl_hooks_t const *value); +extern EGLBoolean egl_init_drivers(); +extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; +extern gl_hooks_t gHooksTrace; + +// ---------------------------------------------------------------------------- + +static inline EGLContext getContext() { return egl_tls_t::getContext(); } + +// ---------------------------------------------------------------------------- + +static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { + if (platform != EGL_PLATFORM_ANDROID_KHR) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } + + uintptr_t index = reinterpret_cast<uintptr_t>(display); + if (index >= NUM_DISPLAYS) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } + + EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display, attrib_list); + return dpy; +} + +EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) { + return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr); +} + +EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display, + const EGLAttrib* attrib_list) { + return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display), + attrib_list); +} + +// ---------------------------------------------------------------------------- +// Initialization +// ---------------------------------------------------------------------------- + +EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + egl_display_ptr dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + EGLBoolean res = dp->initialize(major, minor); + + return res; +} + +EGLBoolean eglTerminateImpl(EGLDisplay dpy) +{ + // NOTE: don't unload the drivers b/c some APIs can be called + // after eglTerminate() has been called. eglTerminate() only + // terminates an EGLDisplay, not a EGL itself. + + egl_display_ptr dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + EGLBoolean res = dp->terminate(); + + return res; +} + +// ---------------------------------------------------------------------------- +// configuration +// ---------------------------------------------------------------------------- + +EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, + EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + if (num_config==nullptr) { + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + res = cnx->egl.eglGetConfigs( + dp->disp.dpy, configs, config_size, num_config); + } + + return res; +} + +EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + if (num_config==nullptr) { + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + if (attrib_list) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.force_msaa", value, "false"); + + if (!strcmp(value, "true")) { + size_t attribCount = 0; + EGLint attrib = attrib_list[0]; + + // Only enable MSAA if the context is OpenGL ES 2.0 and + // if no caveat is requested + const EGLint *attribRendererable = nullptr; + const EGLint *attribCaveat = nullptr; + + // Count the number of attributes and look for + // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT + while (attrib != EGL_NONE) { + attrib = attrib_list[attribCount]; + switch (attrib) { + case EGL_RENDERABLE_TYPE: + attribRendererable = &attrib_list[attribCount]; + break; + case EGL_CONFIG_CAVEAT: + attribCaveat = &attrib_list[attribCount]; + break; + default: + break; + } + attribCount++; + } + + if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && + (!attribCaveat || attribCaveat[1] != EGL_NONE)) { + + // Insert 2 extra attributes to force-enable MSAA 4x + EGLint aaAttribs[attribCount + 4]; + aaAttribs[0] = EGL_SAMPLE_BUFFERS; + aaAttribs[1] = 1; + aaAttribs[2] = EGL_SAMPLES; + aaAttribs[3] = 4; + + memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); + + EGLint numConfigAA; + EGLBoolean resAA = cnx->egl.eglChooseConfig( + dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); + + if (resAA == EGL_TRUE && numConfigAA > 0) { + ALOGD("Enabling MSAA 4x"); + *num_config = numConfigAA; + return resAA; + } + } + } + } + + res = cnx->egl.eglChooseConfig( + dp->disp.dpy, attrib_list, configs, config_size, num_config); + } + return res; +} + +EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (!dp) return EGL_FALSE; + + return cnx->egl.eglGetConfigAttrib( + dp->disp.dpy, config, attribute, value); +} + +// ---------------------------------------------------------------------------- +// surfaces +// ---------------------------------------------------------------------------- + +// Translates EGL color spaces to Android data spaces. +static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { + if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { + return HAL_DATASPACE_UNKNOWN; + } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { + return HAL_DATASPACE_V0_SRGB; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { + return HAL_DATASPACE_DISPLAY_P3; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) { + return HAL_DATASPACE_DISPLAY_P3_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT) { + return HAL_DATASPACE_DISPLAY_P3; + } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) { + return HAL_DATASPACE_V0_SCRGB; + } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) { + return HAL_DATASPACE_V0_SCRGB_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) { + return HAL_DATASPACE_BT2020_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) { + return HAL_DATASPACE_BT2020_PQ; + } + return HAL_DATASPACE_UNKNOWN; +} + +// Get the colorspace value that should be reported from queries. When the colorspace +// is unknown (no attribute passed), default to reporting LINEAR. +static EGLint getReportedColorSpace(EGLint colorspace) { + return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace; +} + +// Returns a list of color spaces understood by the vendor EGL driver. +static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { + std::vector<EGLint> colorSpaces; + + // sRGB and linear are always supported when color space support is present. + colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR); + colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR); + + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_pq")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT); + } + return colorSpaces; +} + +// Cleans up color space related parameters that the driver does not understand. +// If there is no color space attribute in attrib_list, colorSpace is left +// unmodified. +template <typename AttrType> +static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window, + const AttrType* attrib_list, EGLint* colorSpace, + std::vector<AttrType>* strippedAttribList) { + for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { + bool copyAttribute = true; + if (attr[0] == EGL_GL_COLORSPACE_KHR) { + switch (attr[1]) { + case EGL_GL_COLORSPACE_LINEAR_KHR: + case EGL_GL_COLORSPACE_SRGB_KHR: + case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: + case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: + case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: + case EGL_GL_COLORSPACE_SCRGB_EXT: + case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: + case EGL_GL_COLORSPACE_BT2020_PQ_EXT: + case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT: + // Fail immediately if the driver doesn't have color space support at all. + if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + break; + default: + // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_* + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + } + *colorSpace = static_cast<EGLint>(attr[1]); + + // Strip the attribute if the driver doesn't understand it. + copyAttribute = false; + std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp); + for (auto driverColorSpace : driverColorSpaces) { + if (static_cast<EGLint>(attr[1]) == driverColorSpace) { + copyAttribute = true; + break; + } + } + + // If the driver doesn't understand it, we should map sRGB-encoded P3 to + // sRGB rather than just dropping the colorspace on the floor. + // For this format, the driver is expected to apply the sRGB + // transfer function during framebuffer operations. + if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { + strippedAttribList->push_back(attr[0]); + strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR); + } + } + if (copyAttribute) { + strippedAttribList->push_back(attr[0]); + strippedAttribList->push_back(attr[1]); + } + } + // Terminate the attribute list. + strippedAttribList->push_back(EGL_NONE); + + // If the passed color space has wide color gamut, check whether the target native window + // supports wide color. + const bool colorSpaceIsNarrow = *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR || + *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || *colorSpace == EGL_UNKNOWN; + if (window && !colorSpaceIsNarrow) { + bool windowSupportsWideColor = true; + // Ordinarily we'd put a call to native_window_get_wide_color_support + // at the beginning of the function so that we'll have the + // result when needed elsewhere in the function. + // However, because eglCreateWindowSurface is called by SurfaceFlinger and + // SurfaceFlinger is required to answer the call below we would + // end up in a deadlock situation. By moving the call to only happen + // if the application has specifically asked for wide-color we avoid + // the deadlock with SurfaceFlinger since it will not ask for a + // wide-color surface. + int err = native_window_get_wide_color_support(window, &windowSupportsWideColor); + + if (err) { + ALOGE("processAttributes: invalid window (win=%p) " + "failed (%#x) (already connected to another API?)", + window, err); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE); + } + if (!windowSupportsWideColor) { + // Application has asked for a wide-color colorspace but + // wide-color support isn't available on the display the window is on. + return setError(EGL_BAD_MATCH, EGL_FALSE); + } + } + return true; +} + +// Note: This only works for existing GLenum's that are all 32bits. +// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this. +void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) { + for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) { + newList.push_back(static_cast<EGLint>(attr[0])); + newList.push_back(static_cast<EGLint>(attr[1])); + } + newList.push_back(EGL_NONE); +} + +// Gets the native pixel format corrsponding to the passed EGLConfig. +void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, + android_pixel_format* format) { + // Set the native window's buffers format to match what this config requests. + // Whether to use sRGB gamma is not part of the EGLconfig, but is part + // of our native format. So if sRGB gamma is requested, we have to + // modify the EGLconfig's format before setting the native window's + // format. + + EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType); + + EGLint a = 0; + EGLint r, g, b; + r = g = b = 0; + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a); + EGLint colorDepth = r + g + b; + + // Today, the driver only understands sRGB and linear on 888X + // formats. Strip other colorspaces from the attribute list and + // only use them to set the dataspace via + // native_window_set_buffers_dataspace + // if pixel format is RGBX 8888 + // TBD: Can test for future extensions that indicate that driver + // handles requested color space and we can let it through. + // allow SRGB and LINEAR. All others need to be stripped. + // else if 565, 4444 + // TBD: Can we assume these are supported if 8888 is? + // else if FP16 or 1010102 + // strip colorspace from attribs. + // endif + if (a == 0) { + if (colorDepth <= 16) { + *format = HAL_PIXEL_FORMAT_RGB_565; + } else { + if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { + if (colorDepth > 24) { + *format = HAL_PIXEL_FORMAT_RGBA_1010102; + } else { + *format = HAL_PIXEL_FORMAT_RGBX_8888; + } + } else { + *format = HAL_PIXEL_FORMAT_RGBA_FP16; + } + } + } else { + if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { + if (colorDepth > 24) { + *format = HAL_PIXEL_FORMAT_RGBA_1010102; + } else { + *format = HAL_PIXEL_FORMAT_RGBA_8888; + } + } else { + *format = HAL_PIXEL_FORMAT_RGBA_FP16; + } + } +} + +EGLBoolean sendSurfaceMetadata(egl_surface_t* s) { + android_smpte2086_metadata smpteMetadata; + if (s->getSmpte2086Metadata(smpteMetadata)) { + int err = + native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata); + s->resetSmpte2086Metadata(); + if (err != 0) { + ALOGE("error setting native window smpte2086 metadata: %s (%d)", strerror(-err), err); + return EGL_FALSE; + } + } + android_cta861_3_metadata cta8613Metadata; + if (s->getCta8613Metadata(cta8613Metadata)) { + int err = + native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata); + s->resetCta8613Metadata(); + if (err != 0) { + ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", strerror(-err), err); + return EGL_FALSE; + } + } + return EGL_TRUE; +} + +template <typename AttrType, typename CreateFuncType> +EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config, + ANativeWindow* window, const AttrType* attrib_list, + CreateFuncType createWindowSurfaceFunc) { + const AttrType* origAttribList = attrib_list; + + if (!window) { + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + int value = 0; + window->query(window, NATIVE_WINDOW_IS_VALID, &value); + if (!value) { + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + // NOTE: When using Vulkan backend, the Vulkan runtime makes all the + // native_window_* calls, so don't do them here. + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); + if (result < 0) { + ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " + "failed (%#x) (already connected to another API?)", + window, result); + return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + } + } + + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // now select correct colorspace and dataspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<AttrType> strippedAttribList; + if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + return EGL_NO_SURFACE; + } + attrib_list = strippedAttribList.data(); + + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + int err = native_window_set_buffers_format(window, format); + if (err != 0) { + ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace); + // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. + // HAL_DATASPACE_UNKNOWN is the default value, but it may have changed + // at this point. + err = native_window_set_buffers_data_space(window, dataSpace); + if (err != 0) { + ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err), err); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + } + + // the EGL spec requires that a new EGLSurface default to swap interval + // 1, so explicitly set that on the window here. + window->setSwapInterval(window, 1); + + EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface, + getReportedColorSpace(colorSpace), cnx); + return s; + } + + // EGLSurface creation failed + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_set_buffers_format(window, 0); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + return EGL_NO_SURFACE; +} + +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, + NativeWindowType window, + const EGLint* attrib_list); +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)( + EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list); + +EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window, + const EGLint* attrib_list) { + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return eglCreateWindowSurfaceTmpl< + EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list, + cnx->egl.eglCreateWindowSurface); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreatePlatformWindowSurface) { + return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>( + dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list, + cnx->egl.eglCreatePlatformWindowSurface); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have " + "eglCreatePlatformWindowSurface"); + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) { + return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>( + dp, cnx, config, static_cast<ANativeWindow*>(native_window), + convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT); + } else { + return eglCreateWindowSurfaceTmpl< + EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, + static_cast<ANativeWindow*>( + native_window), + convertedAttribs.data(), + cnx->egl.eglCreateWindowSurface); + } + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, + void* /*native_pixmap*/, + const EGLAttrib* /*attrib_list*/) { + // Per EGL_KHR_platform_android: + // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that + // belongs to the Android platform. Any such call fails and generates + // an EGL_BAD_PARAMETER error. + + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, + NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) { + egl_connection_t* cnx = nullptr; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config, + const EGLint* attrib_list) { + egl_connection_t* cnx = nullptr; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // Select correct colorspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<EGLint> strippedAttribList; + if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + return EGL_NO_SURFACE; + } + attrib_list = strippedAttribList.data(); + + EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + getReportedColorSpace(colorSpace), cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t* const s = get_surface(surface); + EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); + if (result == EGL_TRUE) { + _s.terminate(); + } + return result; +} + +EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint* value) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const* const s = get_surface(surface); + if (s->getColorSpaceAttribute(attribute, value)) { + return EGL_TRUE; + } else if (s->getSmpte2086Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->getCta8613Attribute(attribute, value)) { + return EGL_TRUE; + } + return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value); +} + +void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + } +} + +// ---------------------------------------------------------------------------- +// Contexts +// ---------------------------------------------------------------------------- + +EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, + EGLContext share_list, const EGLint *attrib_list) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + if (share_list != EGL_NO_CONTEXT) { + if (!ContextRef(dp.get(), share_list).get()) { + return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); + } + egl_context_t* const c = get_context(share_list); + share_list = c->context; + } + // b/111083885 - If we are presenting EGL 1.4 interface to apps + // error out on robust access attributes that are invalid + // in EGL 1.4 as the driver may be fine with them but dEQP expects + // tests to fail according to spec. + if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) { + const EGLint* attrib_ptr = attrib_list; + while (*attrib_ptr != EGL_NONE) { + GLint attr = *attrib_ptr++; + GLint value = *attrib_ptr++; + if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) { + // We are GL ES context with EGL 1.4, this is an invalid + // attribute + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT); + } + }; + } + EGLContext context = cnx->egl.eglCreateContext( + dp->disp.dpy, config, share_list, attrib_list); + if (context != EGL_NO_CONTEXT) { + // figure out if it's a GLESv1 or GLESv2 + int version = 0; + if (attrib_list) { + while (*attrib_list != EGL_NONE) { + GLint attr = *attrib_list++; + GLint value = *attrib_list++; + if (attr == EGL_CONTEXT_CLIENT_VERSION) { + if (value == 1) { + version = egl_connection_t::GLESv1_INDEX; + } else if (value == 2 || value == 3) { + version = egl_connection_t::GLESv2_INDEX; + } + } + }; + } + egl_context_t* c = new egl_context_t(dpy, context, config, cnx, + version); + return c; + } + } + return EGL_NO_CONTEXT; +} + +EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) + return EGL_FALSE; + + ContextRef _c(dp.get(), ctx); + if (!_c.get()) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + egl_context_t * const c = get_context(ctx); + EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); + if (result == EGL_TRUE) { + _c.terminate(); + } + return result; +} + +EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) +{ + egl_display_ptr dp = validate_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not + // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is + // a valid but uninitialized display. + if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || + (draw != EGL_NO_SURFACE) ) { + if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } + + // get a reference to the object passed in + ContextRef _c(dp.get(), ctx); + SurfaceRef _d(dp.get(), draw); + SurfaceRef _r(dp.get(), read); + + // validate the context (if not EGL_NO_CONTEXT) + if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { + // EGL_NO_CONTEXT is valid + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + } + + // these are the underlying implementation's object + EGLContext impl_ctx = EGL_NO_CONTEXT; + EGLSurface impl_draw = EGL_NO_SURFACE; + EGLSurface impl_read = EGL_NO_SURFACE; + + // these are our objects structs passed in + egl_context_t * c = nullptr; + egl_surface_t const * d = nullptr; + egl_surface_t const * r = nullptr; + + // these are the current objects structs + egl_context_t * cur_c = get_context(getContext()); + + if (ctx != EGL_NO_CONTEXT) { + c = get_context(ctx); + impl_ctx = c->context; + } else { + // no context given, use the implementation of the current context + if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { + // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); + return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE); + } + if (cur_c == nullptr) { + // no current context + // not an error, there is just no current context. + return EGL_TRUE; + } + } + + // retrieve the underlying implementation's draw EGLSurface + if (draw != EGL_NO_SURFACE) { + if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + d = get_surface(draw); + impl_draw = d->surface; + } + + // retrieve the underlying implementation's read EGLSurface + if (read != EGL_NO_SURFACE) { + if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + r = get_surface(read); + impl_read = r->surface; + } + + + EGLBoolean result = dp->makeCurrent(c, cur_c, + draw, read, ctx, + impl_draw, impl_read, impl_ctx); + + if (result == EGL_TRUE) { + if (c) { + setGLHooksThreadSpecific(c->cnx->hooks[c->version]); + egl_tls_t::setContext(ctx); + _c.acquire(); + _r.acquire(); + _d.acquire(); + } else { + setGLHooksThreadSpecific(&gHooksNoContext); + egl_tls_t::setContext(EGL_NO_CONTEXT); + } + } else { + // this will ALOGE the error + egl_connection_t* const cnx = &gEGLImpl; + result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE); + } + return result; +} + +EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + ContextRef _c(dp.get(), ctx); + if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + egl_context_t * const c = get_context(ctx); + return c->cnx->egl.eglQueryContext( + dp->disp.dpy, c->context, attribute, value); + +} + +EGLContext eglGetCurrentContextImpl(void) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_CONTEXT. + EGLContext ctx = getContext(); + return ctx; +} + +EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_SURFACE. + + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + switch (readdraw) { + case EGL_READ: return c->read; + case EGL_DRAW: return c->draw; + default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + } + return EGL_NO_SURFACE; +} + +EGLDisplay eglGetCurrentDisplayImpl(void) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_DISPLAY. + + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + return c->dpy; + } + return EGL_NO_DISPLAY; +} + +EGLBoolean eglWaitGLImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + return cnx->egl.eglWaitGL(); +} + +EGLBoolean eglWaitNativeImpl(EGLint engine) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + return cnx->egl.eglWaitNative(engine); +} + +EGLint eglGetErrorImpl(void) +{ + EGLint err = EGL_SUCCESS; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + err = cnx->egl.eglGetError(); + } + if (err == EGL_SUCCESS) { + err = egl_tls_t::getError(); + } + return err; +} + +static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( + const char* procname) { + const egl_connection_t* cnx = &gEGLImpl; + void* proc = nullptr; + + proc = dlsym(cnx->libEgl, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + proc = dlsym(cnx->libGles2, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + proc = dlsym(cnx->libGles1, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + return nullptr; +} + +__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname) +{ + if (FILTER_EXTENSIONS(procname)) { + return nullptr; + } + + __eglMustCastToProperFunctionPointerType addr; + addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap)); + if (addr) return addr; + + addr = findBuiltinWrapper(procname); + if (addr) return addr; + + // this protects accesses to sGLExtentionMap and sGLExtentionSlot + pthread_mutex_lock(&sExtensionMapMutex); + + /* + * Since eglGetProcAddress() is not associated to anything, it needs + * to return a function pointer that "works" regardless of what + * the current context is. + * + * For this reason, we return a "forwarder", a small stub that takes + * care of calling the function associated with the context + * currently bound. + * + * We first look for extensions we've already resolved, if we're seeing + * this extension for the first time, we go through all our + * implementations and call eglGetProcAddress() and record the + * result in the appropriate implementation hooks and return the + * address of the forwarder corresponding to that hook set. + * + */ + + const std::string name(procname); + + auto& extentionMap = sGLExtentionMap; + auto pos = extentionMap.find(name); + addr = (pos != extentionMap.end()) ? pos->second : nullptr; + const int slot = sGLExtentionSlot; + + ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, + "no more slots for eglGetProcAddress(\"%s\")", + procname); + + egl_connection_t* const cnx = &gEGLImpl; + LayerLoader& layer_loader(LayerLoader::getInstance()); + + if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { + + if (cnx->dso && cnx->egl.eglGetProcAddress) { + + // Extensions are independent of the bound context + addr = cnx->egl.eglGetProcAddress(procname); + if (addr) { + + // purposefully track the bottom of the stack in extensionMap + extentionMap[name] = addr; + + // Apply layers + addr = layer_loader.ApplyLayers(procname, addr); + + // Track the top most entry point + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; + sGLExtentionSlot++; + } + } + + } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { + + // We've seen this func before, but we tracked the bottom, so re-apply layers + // More layers might have been enabled + addr = layer_loader.ApplyLayers(procname, addr); + + // Track the top most entry point + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; + } + + pthread_mutex_unlock(&sExtensionMapMutex); + return addr; +} + +class FrameCompletionThread { +public: + + static void queueSync(EGLSyncKHR sync) { + static FrameCompletionThread thread; + + char name[64]; + + std::lock_guard<std::mutex> lock(thread.mMutex); + snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued); + ATRACE_NAME(name); + + thread.mQueue.push_back(sync); + thread.mCondition.notify_one(); + thread.mFramesQueued++; + ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size())); + } + +private: + + FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) { + std::thread thread(&FrameCompletionThread::loop, this); + thread.detach(); + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + void loop() { + while (true) { + threadLoop(); + } + } +#pragma clang diagnostic pop + + void threadLoop() { + EGLSyncKHR sync; + uint32_t frameNum; + { + std::unique_lock<std::mutex> lock(mMutex); + while (mQueue.empty()) { + mCondition.wait(lock); + } + sync = mQueue[0]; + frameNum = mFramesCompleted; + } + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + { + char name[64]; + snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum); + ATRACE_NAME(name); + + EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); + if (result == EGL_FALSE) { + ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("FrameCompletion: timeout waiting for fence"); + } + eglDestroySyncKHR(dpy, sync); + } + { + std::lock_guard<std::mutex> lock(mMutex); + mQueue.pop_front(); + mFramesCompleted++; + ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size())); + } + } + + uint32_t mFramesQueued; + uint32_t mFramesCompleted; + std::deque<EGLSyncKHR> mQueue; + std::condition_variable mCondition; + std::mutex mMutex; +}; + +EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, + EGLint *rects, EGLint n_rects) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), draw); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t* const s = get_surface(draw); + + if (CC_UNLIKELY(dp->traceGpuCompletion)) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (sync != EGL_NO_SYNC_KHR) { + FrameCompletionThread::queueSync(sync); + } + } + + if (CC_UNLIKELY(dp->finishOnSwap)) { + uint32_t pixel; + egl_context_t * const c = get_context( egl_tls_t::getContext() ); + if (c) { + // glReadPixels() ensures that the frame is complete + s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, + GL_RGBA,GL_UNSIGNED_BYTE,&pixel); + } + } + + if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + if (!sendSurfaceMetadata(s)) { + native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE); + } + } + + if (n_rects == 0) { + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + } + + std::vector<android_native_rect_t> androidRects((size_t)n_rects); + for (int r = 0; r < n_rects; ++r) { + int offset = r * 4; + int x = rects[offset]; + int y = rects[offset + 1]; + int width = rects[offset + 2]; + int height = rects[offset + 3]; + android_native_rect_t androidRect; + androidRect.left = x; + androidRect.top = y + height; + androidRect.right = x + width; + androidRect.bottom = y; + androidRects.push_back(androidRect); + } + if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), + androidRects.size()); + } + + if (s->cnx->egl.eglSwapBuffersWithDamageKHR) { + return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, + rects, n_rects); + } else { + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + } +} + +EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) +{ + return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0); +} + +EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface, + NativePixmapType target) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); +} + +const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) +{ + if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) { + // Return list of client extensions + return gClientExtensionString; + } + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return (const char *) nullptr; + + switch (name) { + case EGL_VENDOR: + return dp->getVendorString(); + case EGL_VERSION: + return dp->getVersionString(); + case EGL_EXTENSIONS: + return dp->getExtensionString(); + case EGL_CLIENT_APIS: + return dp->getClientApiString(); + default: + break; + } + return setError(EGL_BAD_PARAMETER, (const char *)nullptr); +} + +EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return (const char *) nullptr; + + switch (name) { + case EGL_VENDOR: + return dp->disp.queryString.vendor; + case EGL_VERSION: + return dp->disp.queryString.version; + case EGL_EXTENSIONS: + return dp->disp.queryString.extensions; + case EGL_CLIENT_APIS: + return dp->disp.queryString.clientApi; + default: + break; + } + return setError(EGL_BAD_PARAMETER, (const char *)nullptr); +} + +// ---------------------------------------------------------------------------- +// EGL 1.1 +// ---------------------------------------------------------------------------- + +EGLBoolean eglSurfaceAttribImpl( + EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t * const s = get_surface(surface); + + if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { + if (!s->getNativeWindow()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + } + int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); + return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + if (attribute == EGL_TIMESTAMPS_ANDROID) { + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); + return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + if (s->setSmpte2086Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->setCta8613Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->cnx->egl.eglSurfaceAttrib) { + return s->cnx->egl.eglSurfaceAttrib( + dp->disp.dpy, s->surface, attribute, value); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglBindTexImageImpl( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglBindTexImage) { + return s->cnx->egl.eglBindTexImage( + dp->disp.dpy, s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglReleaseTexImageImpl( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglReleaseTexImage) { + return s->cnx->egl.eglReleaseTexImage( + dp->disp.dpy, s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglSwapInterval) { + res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); + } + + return res; +} + + +// ---------------------------------------------------------------------------- +// EGL 1.2 +// ---------------------------------------------------------------------------- + +EGLBoolean eglWaitClientImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + EGLBoolean res; + if (cnx->egl.eglWaitClient) { + res = cnx->egl.eglWaitClient(); + } else { + res = cnx->egl.eglWaitGL(); + } + return res; +} + +EGLBoolean eglBindAPIImpl(EGLenum api) +{ + // bind this API on all EGLs + EGLBoolean res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglBindAPI) { + res = cnx->egl.eglBindAPI(api); + } + return res; +} + +EGLenum eglQueryAPIImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryAPI) { + return cnx->egl.eglQueryAPI(); + } + + // or, it can only be OpenGL ES + return EGL_OPENGL_ES_API; +} + +EGLBoolean eglReleaseThreadImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglReleaseThread) { + cnx->egl.eglReleaseThread(); + } + + // If there is context bound to the thread, release it + egl_display_t::loseCurrent(get_context(getContext())); + + egl_tls_t::clearTLS(); + return EGL_TRUE; +} + +EGLSurface eglCreatePbufferFromClientBufferImpl( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (!dp) return EGL_FALSE; + if (cnx->egl.eglCreatePbufferFromClientBuffer) { + return cnx->egl.eglCreatePbufferFromClientBuffer( + dp->disp.dpy, buftype, buffer, config, attrib_list); + } + return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 3 +// ---------------------------------------------------------------------------- + +EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, + const EGLint *attrib_list) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglLockSurfaceKHR) { + return s->cnx->egl.eglLockSurfaceKHR( + dp->disp.dpy, s->surface, attrib_list); + } + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglUnlockSurfaceKHR) { + return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); + } + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); +} + +// Note: EGLImageKHR and EGLImage are the same thing so no need +// to templatize that. +template <typename AttrType, typename FuncType> +EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const AttrType* attrib_list, + FuncType eglCreateImageFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_IMAGE_KHR; + + std::vector<AttrType> strippedAttribs; + if (needsAndroidPEglMitigation()) { + // Mitigation for Android P vendor partitions: eglImageCreateKHR should accept + // EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and + // EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported, + // but some drivers don't like the DEFAULT value and generate an error. + for (const AttrType *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { + if (attr[0] == EGL_GL_COLORSPACE_KHR && + dp->haveExtension("EGL_EXT_image_gl_colorspace")) { + if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR && + attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) { + continue; + } + } + strippedAttribs.push_back(attr[0]); + strippedAttribs.push_back(attr[1]); + } + strippedAttribs.push_back(EGL_NONE); + } + + ContextRef _c(dp.get(), ctx); + egl_context_t* const c = _c.get(); + + EGLImageKHR result = EGL_NO_IMAGE_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglCreateImageFunc) { + result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer, + needsAndroidPEglMitigation() ? strippedAttribs.data() : attrib_list); + } + return result; +} + +typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, + const EGLAttrib* attrib_list); + +EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint* attrib_list) { + return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer, + attrib_list, + gEGLImpl.egl.eglCreateImageKHR); +} + +EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreateImage) { + return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer, + attrib_list, + cnx->egl.eglCreateImage); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage"); + return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer, + convertedAttribs.data(), + gEGLImpl.egl.eglCreateImageKHR); +} + +EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img, + PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && destroyImageFunc) { + result = destroyImageFunc(dp->disp.dpy, img); + } + return result; +} + +EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) { + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR); +} + +EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglDestroyImage) { + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage"); + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR); +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 5 +// ---------------------------------------------------------------------------- + +// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize +template <typename AttrType, typename FuncType> +EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list, + FuncType eglCreateSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_SYNC_KHR; + + egl_connection_t* const cnx = &gEGLImpl; + EGLSyncKHR result = EGL_NO_SYNC_KHR; + if (cnx->dso && eglCreateSyncFunc) { + result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list); + } + return result; +} + +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type, + const EGLAttrib* attrib_list); + +EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { + return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list, + gEGLImpl.egl.eglCreateSyncKHR); +} + +EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreateSync) { + return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list, + cnx->egl.eglCreateSync); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync"); + return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(), + cnx->egl.eglCreateSyncKHR); +} + +EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglDestroySyncFunc) { + result = eglDestroySyncFunc(dp->disp.dpy, sync); + } + return result; +} + +EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) { + return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR); +} + +EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglDestroySync) { + return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync"); + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR); +} + +EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && gEGLImpl.egl.eglSignalSyncKHR) { + result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode); + } + return result; +} + +EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout, + PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLint result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglClientWaitSyncFunc) { + result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout); + } + return result; +} + +EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { + egl_connection_t* const cnx = &gEGLImpl; + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR); +} + +EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglClientWaitSync) { + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync"); + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR); +} + +template <typename AttrType, typename FuncType> +EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value, + FuncType eglGetSyncAttribFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglGetSyncAttribFunc) { + result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value); + } + return result; +} + +typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute, + EGLAttrib* value); + +EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglGetSyncAttrib) { + return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value, + cnx->egl.eglGetSyncAttrib); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib"); + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib + EGLint attribValue; + EGLBoolean ret = + eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, + &attribValue, + gEGLImpl.egl + .eglGetSyncAttribKHR); + if (ret) { + *value = static_cast<EGLAttrib>(attribValue); + } + return ret; +} + +EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, + EGLint* value) { + return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value, + gEGLImpl.egl + .eglGetSyncAttribKHR); +} + +EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_STREAM_KHR; + + EGLStreamKHR result = EGL_NO_STREAM_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamKHR) { + result = cnx->egl.eglCreateStreamKHR( + dp->disp.dpy, attrib_list); + } + return result; +} + +EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglDestroyStreamKHR) { + result = cnx->egl.eglDestroyStreamKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLint value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamAttribKHR) { + result = cnx->egl.eglStreamAttribKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLint *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamKHR) { + result = cnx->egl.eglQueryStreamKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLuint64KHR *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) { + result = cnx->egl.eglQueryStreamu64KHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLTimeKHR *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) { + result = cnx->egl.eglQueryStreamTimeKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config, + EGLStreamKHR stream, const EGLint *attrib_list) +{ + egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_SURFACE; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) { + EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR( + dp->disp.dpy, config, stream, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + EGL_GL_COLORSPACE_LINEAR_KHR, cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) { + result = cnx->egl.eglStreamConsumerGLTextureExternalKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) { + result = cnx->egl.eglStreamConsumerAcquireKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) { + result = cnx->egl.eglStreamConsumerReleaseKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl( + EGLDisplay dpy, EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR; + + EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) { + result = cnx->egl.eglGetStreamFileDescriptorKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl( + EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_STREAM_KHR; + + EGLStreamKHR result = EGL_NO_STREAM_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) { + result = cnx->egl.eglCreateStreamFromFileDescriptorKHR( + dp->disp.dpy, file_descriptor); + } + return result; +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 15 +// ---------------------------------------------------------------------------- + +// Need to template function type because return type is different +template <typename ReturnType, typename FuncType> +ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, + FuncType eglWaitSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + ReturnType result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglWaitSyncFunc) { + result = eglWaitSyncFunc(dp->disp.dpy, sync, flags); + } + return result; +} + +typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags); + +EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { + egl_connection_t* const cnx = &gEGLImpl; + return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags, + cnx->egl.eglWaitSyncKHR); +} + +EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglWaitSync) { + return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags, + cnx->egl.eglWaitSync); + } + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + return static_cast<EGLBoolean>( + eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags, + cnx->egl.eglWaitSyncKHR)); +} + +// ---------------------------------------------------------------------------- +// ANDROID extensions +// ---------------------------------------------------------------------------- + +EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; + + EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { + result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); + } + return result; +} + +EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLnsecsANDROID time) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + native_window_set_buffers_timestamp(s->getNativeWindow(), time); + + return EGL_TRUE; +} + +EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) { + // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus + // this function cannot be implemented when this libEGL is built for + // vendors. +#ifndef __ANDROID_VNDK__ + if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); + return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); +#else + return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); +#endif +} + +// ---------------------------------------------------------------------------- +// NVIDIA extensions +// ---------------------------------------------------------------------------- +EGLuint64NV eglGetSystemTimeFrequencyNVImpl() +{ + EGLuint64NV ret = 0; + egl_connection_t* const cnx = &gEGLImpl; + + if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { + return cnx->egl.eglGetSystemTimeFrequencyNV(); + } + + return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); +} + +EGLuint64NV eglGetSystemTimeNVImpl() +{ + EGLuint64NV ret = 0; + egl_connection_t* const cnx = &gEGLImpl; + + if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { + return cnx->egl.eglGetSystemTimeNV(); + } + + return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); +} + +// ---------------------------------------------------------------------------- +// Partial update extension +// ---------------------------------------------------------------------------- +EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, + EGLint *rects, EGLint n_rects) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglSetDamageRegionKHR) { + return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, + rects, n_rects); + } + + return EGL_FALSE; +} + +EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLuint64KHR *frameId) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + uint64_t nextFrameId = 0; + int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId); + + if (ret != 0) { + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetNextFrameId: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } + + *frameId = nextFrameId; + return EGL_TRUE; +} + +EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + nsecs_t* compositeDeadline = nullptr; + nsecs_t* compositeInterval = nullptr; + nsecs_t* compositeToPresentLatency = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (names[i]) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + compositeDeadline = &values[i]; + break; + case EGL_COMPOSITE_INTERVAL_ANDROID: + compositeInterval = &values[i]; + break; + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + compositeToPresentLatency = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + } + + int ret = native_window_get_compositor_timing(s->getNativeWindow(), + compositeDeadline, compositeInterval, compositeToPresentLatency); + + switch (ret) { + case 0: + return EGL_TRUE; + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetCompositorTiming: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } +} + +EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl( + EGLDisplay dpy, EGLSurface surface, EGLint name) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->getNativeWindow(); + if (!window) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + switch (name) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + return EGL_TRUE; + default: + return EGL_FALSE; + } +} + +EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + nsecs_t* requestedPresentTime = nullptr; + nsecs_t* acquireTime = nullptr; + nsecs_t* latchTime = nullptr; + nsecs_t* firstRefreshStartTime = nullptr; + nsecs_t* gpuCompositionDoneTime = nullptr; + nsecs_t* lastRefreshStartTime = nullptr; + nsecs_t* displayPresentTime = nullptr; + nsecs_t* dequeueReadyTime = nullptr; + nsecs_t* releaseTime = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (timestamps[i]) { + case EGL_REQUESTED_PRESENT_TIME_ANDROID: + requestedPresentTime = &values[i]; + break; + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + acquireTime = &values[i]; + break; + case EGL_COMPOSITION_LATCH_TIME_ANDROID: + latchTime = &values[i]; + break; + case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: + firstRefreshStartTime = &values[i]; + break; + case EGL_LAST_COMPOSITION_START_TIME_ANDROID: + lastRefreshStartTime = &values[i]; + break; + case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: + gpuCompositionDoneTime = &values[i]; + break; + case EGL_DISPLAY_PRESENT_TIME_ANDROID: + displayPresentTime = &values[i]; + break; + case EGL_DEQUEUE_READY_TIME_ANDROID: + dequeueReadyTime = &values[i]; + break; + case EGL_READS_DONE_TIME_ANDROID: + releaseTime = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + } + + int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId, + requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime, + lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime, + dequeueReadyTime, releaseTime); + + switch (ret) { + case 0: + return EGL_TRUE; + case -ENOENT: + return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + case -EINVAL: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetFrameTimestamps: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } +} + +EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl( + EGLDisplay dpy, EGLSurface surface, EGLint timestamp) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->getNativeWindow(); + if (!window) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + switch (timestamp) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + case EGL_REQUESTED_PRESENT_TIME_ANDROID: + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + case EGL_COMPOSITION_LATCH_TIME_ANDROID: + case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: + case EGL_LAST_COMPOSITION_START_TIME_ANDROID: + case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: + case EGL_DEQUEUE_READY_TIME_ANDROID: + case EGL_READS_DONE_TIME_ANDROID: + return EGL_TRUE; + case EGL_DISPLAY_PRESENT_TIME_ANDROID: { + int value = 0; + window->query(window, + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); + return value == 0 ? EGL_FALSE : EGL_TRUE; + } + default: + return EGL_FALSE; + } +} + +const GLubyte * glGetStringImpl(GLenum name) { + const GLubyte * ret = egl_get_string_for_current_context(name); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if(_c) ret = _c->glGetString(name); + } + return ret; +} + +const GLubyte * glGetStringiImpl(GLenum name, GLuint index) { + const GLubyte * ret = egl_get_string_for_current_context(name, index); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if(_c) ret = _c->glGetStringi(name, index); + } + return ret; +} + +void glGetBooleanvImpl(GLenum pname, GLboolean * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = num_exts > 0 ? GL_TRUE : GL_FALSE; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetBooleanv(pname, data); +} + +void glGetFloatvImpl(GLenum pname, GLfloat * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLfloat)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetFloatv(pname, data); +} + +void glGetIntegervImpl(GLenum pname, GLint * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLint)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetIntegerv(pname, data); +} + +void glGetInteger64vImpl(GLenum pname, GLint64 * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLint64)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetInteger64v(pname, data); +} + +struct implementation_map_t { + const char* name; + EGLFuncPointer address; +}; + +static const implementation_map_t sPlatformImplMap[] = { + // clang-format off + { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl }, + { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl }, + { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl }, + { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl }, + { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl }, + { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl }, + { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl }, + { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl }, + { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl }, + { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl }, + { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl }, + { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl }, + { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl }, + { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl }, + { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl }, + { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl }, + { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl }, + { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl }, + { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl }, + { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl }, + { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl }, + { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl }, + { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl }, + { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl }, + { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl }, + { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl }, + { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl }, + { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl }, + { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl }, + { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl }, + { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl }, + { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl }, + { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl }, + { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl }, + { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl }, + { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl }, + { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl }, + { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl }, + { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl }, + { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl }, + { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl }, + { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl }, + { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl }, + { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl }, + { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl }, + { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl }, + { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl }, + { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl }, + { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl }, + { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl }, + { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl }, + { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl }, + { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl }, + { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl }, + { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl }, + { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl }, + { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl }, + { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl }, + { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl }, + { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl }, + { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl }, + { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl }, + { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl }, + { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl }, + { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl }, + { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl }, + { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl }, + { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl }, + { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl }, + { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl }, + { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl }, + { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl }, + { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl }, + { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl }, + { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl }, + { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl }, + { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl }, + { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl }, + { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl }, + { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl }, + { "glGetString", (EGLFuncPointer)&glGetStringImpl }, + { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl }, + { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl }, + { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl }, + { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl }, + { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl }, + // clang-format on +}; + +EGLFuncPointer FindPlatformImplAddr(const char* name) +{ + static const bool DEBUG = false; + + if (name == nullptr) { + ALOGV("FindPlatformImplAddr called with null name"); + return nullptr; + } + + for (int i = 0; i < NELEM(sPlatformImplMap); i++) { + if (sPlatformImplMap[i].name == nullptr) { + ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name); + return nullptr; + } + if (!strcmp(name, sPlatformImplMap[i].name)) { + ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name); + return sPlatformImplMap[i].address; + } + } + + ALOGV("FindPlatformImplAddr did not find an entry for %s", name); + return nullptr; +} +} // namespace android diff --git a/opengl/libs/EGL/egl_platform_entries.h b/opengl/libs/EGL/egl_platform_entries.h new file mode 100644 index 0000000000..85b1db3fae --- /dev/null +++ b/opengl/libs/EGL/egl_platform_entries.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_EGLAPI_H +#define ANDROID_EGLAPI_H + +#include <EGL/egl.h> + +typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; + +namespace android { + +EGLint eglGetErrorImpl(); +EGLFuncPointer FindPlatformImplAddr(const char* name); + +}; // namespace android + +#endif // ANDROID_EGLAPI_H + diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp index 8508c5fe9d..aaecb62194 100644 --- a/opengl/libs/EGL/egl_tls.cpp +++ b/opengl/libs/EGL/egl_tls.cpp @@ -21,6 +21,7 @@ #include <cutils/properties.h> #include <log/log.h> #include "CallStack.h" +#include "egl_platform_entries.h" namespace android { @@ -28,7 +29,7 @@ pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED; pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT; egl_tls_t::egl_tls_t() - : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(true) { + : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) { } const char *egl_tls_t::egl_strerror(EGLint err) { @@ -55,13 +56,38 @@ const char *egl_tls_t::egl_strerror(EGLint err) { void egl_tls_t::validateTLSKey() { struct TlsKeyInitializer { - static void create() { - pthread_key_create(&sKey, (void (*)(void*))&eglReleaseThread); - } + static void create() { pthread_key_create(&sKey, destructTLSData); } }; pthread_once(&sOnceKey, TlsKeyInitializer::create); } +void egl_tls_t::destructTLSData(void* data) { + egl_tls_t* tls = static_cast<egl_tls_t*>(data); + if (!tls) return; + + // Several things in the call tree of eglReleaseThread expect to be able to get the current + // thread state directly from TLS. That's a problem because Bionic has already cleared our + // TLS pointer before calling this function (pthread_getspecific(sKey) will return nullptr). + // Instead the data is passed as our parameter. + // + // Ideally we'd refactor this so we have thin wrappers that retrieve thread state from TLS and + // then pass it as a parameter (or 'this' pointer) to functions that do the real work without + // touching TLS. Then from here we could just call those implementation functions with the the + // TLS data we just received as a parameter. + // + // But that's a fairly invasive refactoring, so to do this robustly in the short term we just + // put the data *back* in TLS and call the top-level eglReleaseThread. It and it's call tree + // will retrieve the value from TLS, and then finally clear the TLS data. Bionic explicitly + // tolerates re-setting the value that it's currently trying to destruct (see + // pthread_key_clean_all()). Even if we forgot to clear the restored TLS data, bionic would + // call the destructor again, but eventually gives up and just leaks the data rather than + // enter an infinite loop. + pthread_setspecific(sKey, tls); + eglReleaseThread(); + ALOGE_IF(pthread_getspecific(sKey) != nullptr, + "EGL TLS data still exists after eglReleaseThread"); +} + void egl_tls_t::setErrorEtcImpl( const char* caller, int line, EGLint error, bool quiet) { validateTLSKey(); @@ -92,7 +118,7 @@ bool egl_tls_t::logNoContextCall() { egl_tls_t* egl_tls_t::getTLS() { egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); - if (tls == 0) { + if (tls == nullptr) { tls = new egl_tls_t; pthread_setspecific(sKey, tls); } @@ -103,7 +129,7 @@ void egl_tls_t::clearTLS() { if (sKey != TLS_KEY_NOT_INITIALIZED) { egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); if (tls) { - pthread_setspecific(sKey, 0); + pthread_setspecific(sKey, nullptr); delete tls; } } @@ -112,7 +138,7 @@ void egl_tls_t::clearTLS() { void egl_tls_t::clearError() { // This must clear the error from all the underlying EGL implementations as // well as the EGL wrapper layer. - eglGetError(); + android::eglGetErrorImpl(); } EGLint egl_tls_t::getError() { diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h index 9feae681bd..86a375c002 100644 --- a/opengl/libs/EGL/egl_tls.h +++ b/opengl/libs/EGL/egl_tls.h @@ -38,6 +38,7 @@ class egl_tls_t { egl_tls_t(); static void validateTLSKey(); + static void destructTLSData(void* data); static void setErrorEtcImpl( const char* caller, int line, EGLint error, bool quiet); diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index acc205aaa1..7bb9b59ea4 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -18,9 +18,13 @@ #define ANDROID_EGLDEFS_H #include "../hooks.h" +#include "egl_platform_entries.h" + +#include <log/log.h> #define VERSION_MAJOR 1 #define VERSION_MINOR 4 +#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) // ---------------------------------------------------------------------------- namespace android { @@ -31,23 +35,59 @@ const unsigned int NUM_DISPLAYS = 1; // ---------------------------------------------------------------------------- +extern char const * const platform_names[]; + +// clang-format off struct egl_connection_t { enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 }; - inline egl_connection_t() : dso(0) { } + inline egl_connection_t() : dso(nullptr), + libEgl(nullptr), + libGles1(nullptr), + libGles2(nullptr), + systemDriverUnloaded(false) { + + char const* const* entries = platform_names; + EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform); + while (*entries) { + const char* name = *entries; + EGLFuncPointer f = FindPlatformImplAddr(name); + + if (f == nullptr) { + // If no entry found, update the lookup table: sPlatformImplMap + ALOGE("No entry found in platform lookup table for %s", name); + } + + *curr++ = f; + entries++; + } + } + void * dso; gl_hooks_t * hooks[2]; EGLint major; EGLint minor; + EGLint driverVersion; egl_t egl; + // Functions implemented or redirected by platform libraries + platform_impl_t platform; + void* libEgl; void* libGles1; void* libGles2; + + bool systemDriverUnloaded; + bool shouldUseAngle; // Should we attempt to load ANGLE + bool angleDecided; // Have we tried to load ANGLE + bool useAngle; // Was ANGLE successfully loaded + EGLint angleBackend; + void* vendorEGL; }; +// clang-format on // ---------------------------------------------------------------------------- diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index f7fde9625f..65f50f54fb 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -301,71 +301,31 @@ extern "C" { } const GLubyte * glGetString(GLenum name) { - const GLubyte * ret = egl_get_string_for_current_context(name); - if (ret == NULL) { - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if(_c) ret = _c->glGetString(name); - } - return ret; + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetString(name); } const GLubyte * glGetStringi(GLenum name, GLuint index) { - const GLubyte * ret = egl_get_string_for_current_context(name, index); - if (ret == NULL) { - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if(_c) ret = _c->glGetStringi(name, index); - } - return ret; + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetStringi(name, index); } void glGetBooleanv(GLenum pname, GLboolean * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = num_exts > 0 ? GL_TRUE : GL_FALSE; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetBooleanv(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetBooleanv(pname, data); } void glGetFloatv(GLenum pname, GLfloat * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = (GLfloat)num_exts; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetFloatv(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetFloatv(pname, data); } void glGetIntegerv(GLenum pname, GLint * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = (GLint)num_exts; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetIntegerv(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetIntegerv(pname, data); } void glGetInteger64v(GLenum pname, GLint64 * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = (GLint64)num_exts; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetInteger64v(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetInteger64v(pname, data); } diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index a8855efa57..0af050175b 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -21,15 +21,18 @@ #include <EGL/eglext.h> #include <EGL/eglplatform.h> +#include "EGL/egldefs.h" #include "hooks.h" // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- + EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name); EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index); EGLAPI GLint egl_get_num_extensions_for_current_context(); +EGLAPI egl_connection_t* egl_get_connection(); // ---------------------------------------------------------------------------- }; // namespace android diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index 81dbe0e34b..63a0e140cc 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -59,6 +59,10 @@ namespace android { #define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__); #define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__); +struct platform_impl_t { + #include "platform_entries.in" +}; + struct egl_t { #include "EGL/egl_entries.in" }; diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt index fa26e33f39..b2d795745f 100644 --- a/opengl/libs/libEGL.map.txt +++ b/opengl/libs/libEGL.map.txt @@ -3,23 +3,30 @@ LIBEGL { eglBindAPI; eglBindTexImage; eglChooseConfig; + eglClientWaitSync; # introduced=29 eglClientWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglCopyBuffers; eglCreateContext; + eglCreateImage; # introduced=29 eglCreateImageKHR; eglCreateNativeClientBufferANDROID; # introduced=24 eglCreatePbufferFromClientBuffer; eglCreatePbufferSurface; eglCreatePixmapSurface; + eglCreatePlatformPixmapSurface; # introduced=29 + eglCreatePlatformWindowSurface; # introduced=29 eglCreateStreamFromFileDescriptorKHR; # introduced=23 eglCreateStreamKHR; # introduced=23 eglCreateStreamProducerSurfaceKHR; # introduced=23 + eglCreateSync; # introduced=29 eglCreateSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglCreateWindowSurface; eglDestroyContext; + eglDestroyImage; # introduced=29 eglDestroyImageKHR; eglDestroyStreamKHR; # introduced=23 eglDestroySurface; + eglDestroySync; # introduced=29 eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglDupNativeFenceFDANDROID; # vndk eglGetConfigAttrib; @@ -30,8 +37,10 @@ LIBEGL { eglGetDisplay; eglGetError; eglGetNativeClientBufferANDROID; # introduced=26 + eglGetPlatformDisplay; # introduced=29 eglGetProcAddress; eglGetStreamFileDescriptorKHR; # introduced=23 + eglGetSyncAttrib; # introduced=29 eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglGetSystemTimeFrequencyNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21 eglGetSystemTimeNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21 @@ -64,6 +73,7 @@ LIBEGL { eglWaitClient; eglWaitGL; eglWaitNative; + eglWaitSync; # introduced=29 eglWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 local: *; diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in new file mode 100644 index 0000000000..46734112d3 --- /dev/null +++ b/opengl/libs/platform_entries.in @@ -0,0 +1,86 @@ +EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType) +EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, EGLNativeDisplayType, const EGLAttrib*) +EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*) +EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay) +EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*) +EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*) +EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint*) +EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint*) +EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*) +EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*) +EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*) +EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint*) +EGL_ENTRY(void, eglBeginFrame, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext) +EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext) +EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint*) +EGL_ENTRY(EGLContext, eglGetCurrentContext, void) +EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint) +EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void) +EGL_ENTRY(EGLBoolean, eglWaitGL, void) +EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint) +EGL_ENTRY(EGLint, eglGetError, void) +EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*) +EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType) +EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint) +EGL_ENTRY(const char*, eglQueryStringImplementationANDROID, EGLDisplay, EGLint) +EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint) +EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint) +EGL_ENTRY(EGLBoolean, eglWaitClient, void) +EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum) +EGL_ENTRY(EGLenum, eglQueryAPI, void) +EGL_ENTRY(EGLBoolean, eglReleaseThread, void) +EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint*) +EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint*) +EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib*) +EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage) +EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR) +EGL_ENTRY(EGLSync, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib*) +EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync) +EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR) +EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSyncKHR, EGLint, EGLAttrib*) +EGL_ENTRY(EGLSyncKHR, eglCreateSyncKHR, EGLDisplay, EGLenum, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroySyncKHR, EGLDisplay, EGLSyncKHR) +EGL_ENTRY(EGLBoolean, eglSignalSyncKHR, EGLDisplay, EGLSyncKHR, EGLenum) +EGL_ENTRY(EGLint, eglClientWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR) +EGL_ENTRY(EGLBoolean, eglGetSyncAttribKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLint*) +EGL_ENTRY(EGLStreamKHR, eglCreateStreamKHR, EGLDisplay, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroyStreamKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLBoolean, eglStreamAttribKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint) +EGL_ENTRY(EGLBoolean, eglQueryStreamKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint*) +EGL_ENTRY(EGLBoolean, eglQueryStreamu64KHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLuint64KHR*) +EGL_ENTRY(EGLBoolean, eglQueryStreamTimeKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLTimeKHR*) +EGL_ENTRY(EGLSurface, eglCreateStreamProducerSurfaceKHR, EGLDisplay, EGLConfig, EGLStreamKHR, const EGLint*) +EGL_ENTRY(EGLBoolean, eglStreamConsumerGLTextureExternalKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLBoolean, eglStreamConsumerAcquireKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLBoolean, eglStreamConsumerReleaseKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR) +EGL_ENTRY(EGLint, eglWaitSync, EGLDisplay, EGLSync, EGLint) +EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint) +EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR) +EGL_ENTRY(EGLBoolean, eglPresentationTimeANDROID, EGLDisplay, EGLSurface, EGLnsecsANDROID) +EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer*) +EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void) +EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void) +EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint*, EGLint) +EGL_ENTRY(EGLBoolean, eglGetNextFrameIdANDROID, EGLDisplay, EGLSurface, EGLuint64KHR*) +EGL_ENTRY(EGLBoolean, eglGetCompositorTimingANDROID, EGLDisplay, EGLSurface, EGLint, const EGLint*, EGLnsecsANDROID*) +EGL_ENTRY(EGLBoolean, eglGetCompositorTimingSupportedANDROID, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglGetFrameTimestampsANDROID, EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint*, EGLnsecsANDROID*) +EGL_ENTRY(EGLBoolean, eglGetFrameTimestampSupportedANDROID, EGLDisplay, EGLSurface, EGLint) +GL_ENTRY(const GLubyte*, glGetString, GLenum) +GL_ENTRY(const GLubyte*, glGetStringi, GLenum, GLuint) +GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean*) +GL_ENTRY(void, glGetFloatv, GLenum, GLfloat*) +GL_ENTRY(void, glGetIntegerv, GLenum, GLint*) +GL_ENTRY(void, glGetInteger64v, GLenum, GLint64*) diff --git a/opengl/libs/tools/genfiles b/opengl/libs/tools/genfiles deleted file mode 100755 index feef3186a8..0000000000 --- a/opengl/libs/tools/genfiles +++ /dev/null @@ -1,50 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2008 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Force a specific locale for sorting to avoid irrelevant differences -# in the generated files that could hide real differences. -export LC_ALL=POSIX - -./glapigen ../../include/GLES/gl.h > ../GLES_CM/gl_api.in -./glapigen ../../include/GLES/glext.h > ../GLES_CM/glext_api.in -./glapigen ../../include/GLES3/gl3.h > ../GLES2/gl2_api.in -./glapigen ../../include/GLES2/gl2ext.h > ../GLES2/gl2ext_api.in - -./glentrygen ../../include/GLES/gl.h > /tmp/gl_entries.in -./glentrygen ../../include/GLES/glext.h > /tmp/glext_entries.in -./glentrygen ../../include/GLES3/gl3.h > /tmp/gl2_entries.in -./glentrygen ../../include/GLES2/gl2ext.h > /tmp/gl2ext_entries.in - -# The awk command removes lines with the same function name as an earlier -# line, even if the rest of the line differs. Although signatures of -# functions with the same name should be the same, the different versions -# have some irrelevant whitespace and parameter name differences. -cat /tmp/gl_entries.in \ - /tmp/glext_entries.in \ - /tmp/gl2_entries.in \ - /tmp/gl2ext_entries.in \ - | sort -t, -k2 \ - | awk -F, '!_[$2]++' \ - > ../entries.in - -cat ../../include/GLES/gl.h \ - ../../include/GLES/glext.h \ - ../../include/GLES2/gl2ext.h \ - ../../include/GLES3/gl3.h \ - | ./glenumsgen \ - | sort \ - > ../enums.in - diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen deleted file mode 100755 index 4d8334f90f..0000000000 --- a/opengl/libs/tools/glapigen +++ /dev/null @@ -1,76 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2008 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -sub rtrim($) -{ - my $string = shift; - $string =~ s/\s+$//; - return $string; -} - -while (my $line = <>) { - next if $line =~ /^\//; - next if $line =~ /^#/; - next if $line =~ /^\s*$/; - if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) { - next; - } - my $type = rtrim($2); - my $name = $3; - my $args = $4; - - #printf("%s", $line); - - my $prefix = ""; - if ($name eq "glGetString") { - $prefix = "__"; - } - - printf("%s API_ENTRY(%s%s)(%s)", $type, $prefix, $name, $args); - - printf(" {\n"); - if ($type eq "void") { - printf(" CALL_GL_API(%s", $name); - } else { - printf(" CALL_GL_API_RETURN(%s", $name); - } - my @args = split ',', $args; - my $len = scalar(@args); - for (my $num = 0; $num < $len; $num++) { - if ($args[$num] ne "void") { - print ", "; - # - # extract the name from the parameter - # type name - # const type *name - # type *name - # type name[4] - # - if ($args[$num] =~ /(\S+\s)+\**\s*([\w]+)/) { - printf("%s", $2); - } - } - } - printf(");\n"); - printf("}\n"); -} - - - - - diff --git a/opengl/libs/tools/glentrygen b/opengl/libs/tools/glentrygen deleted file mode 100755 index 170f04131a..0000000000 --- a/opengl/libs/tools/glentrygen +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2008 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -sub rtrim($) -{ - my $string = shift; - $string =~ s/\s+$//; - return $string; -} - -while (my $line = <>) { - next if $line =~ /^\//; - next if $line =~ /^#/; - next if $line =~ /^\s*$/; - if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) { - next; - } - my $type = rtrim($2); - my $name = $3; - my $args = $4; - - printf("GL_ENTRY(%s, %s, %s)\n", $type, $name, $args); -} diff --git a/opengl/libs/tools/glenumsgen b/opengl/libs/tools/glenumsgen deleted file mode 100755 index 2ae5fbfa25..0000000000 --- a/opengl/libs/tools/glenumsgen +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2010 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -my %enumHash = (); - -while (my $line = <STDIN>) { - next if $line =~ /^\//; - # Skip bitfield definitions. - next if $line =~ /_BIT(\d+_|\s+)/; - if ($line !~ /^#define\s+(\S+)\s+(0x\S+)/) { - next; - } - my $enumName = $1; - my $enumValue = $2; - next if exists($enumHash { $enumValue }); - $enumHash { $enumValue } = $enumName; - printf("GL_ENUM(%s,%s)\n", $enumValue, $enumName); -} - - - - - |