replicant-frameworks_native/opengl/libs/EGL/egl.cpp
Jack Palevich 81cd084485 Implement eglInitialize / eglTerminate reference counting
Previously we imlpemented the standard semantics for
eglInitialize / eglTerminate, which are that
eglInitialize may be called any number of times,
but the first call to eglTerminate will terminate
the display.

Now we follow reference-countins semantics, which
means that eglTerminate will only terminate the
display when the reference count returns to zero.

This change allows EGL to be used by multiple
independently written modules in the same process.

(Otherwise there is no way for the independent
modules to coordinate their use of the display.)
2010-03-15 21:21:51 -07:00

1777 lines
57 KiB
C++

/*
** 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.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/ioctl.h>
#if HAVE_ANDROID_OS
#include <linux/android_pmem.h>
#endif
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <cutils/memory.h>
#include <utils/SortedVector.h>
#include "hooks.h"
#include "egl_impl.h"
#include "Loader.h"
#define MAKE_CONFIG(_impl, _index) ((EGLConfig)(((_impl)<<24) | (_index)))
#define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
static char const * const gVendorString = "Android";
static char const * const gVersionString = "1.4 Android META-EGL";
static char const * const gClientApiString = "OpenGL ES";
static char const * const gExtensionString =
"EGL_KHR_image "
"EGL_KHR_image_base "
"EGL_KHR_image_pixmap "
"EGL_ANDROID_image_native_buffer "
"EGL_ANDROID_swap_rectangle "
"EGL_ANDROID_get_render_buffer "
;
// ----------------------------------------------------------------------------
class egl_object_t {
static SortedVector<egl_object_t*> sObjects;
static Mutex sLock;
volatile int32_t terminated;
mutable volatile int32_t count;
public:
egl_object_t() : terminated(0), count(1) {
Mutex::Autolock _l(sLock);
sObjects.add(this);
}
inline bool isAlive() const { return !terminated; }
private:
bool get() {
Mutex::Autolock _l(sLock);
if (egl_object_t::sObjects.indexOf(this) >= 0) {
android_atomic_inc(&count);
return true;
}
return false;
}
bool put() {
Mutex::Autolock _l(sLock);
if (android_atomic_dec(&count) == 1) {
sObjects.remove(this);
return true;
}
return false;
}
public:
template <typename N, typename T>
struct LocalRef {
N* ref;
LocalRef(T o) : ref(0) {
N* native = reinterpret_cast<N*>(o);
if (o && native->get()) {
ref = native;
}
}
~LocalRef() {
if (ref && ref->put()) {
delete ref;
}
}
inline N* get() {
return ref;
}
void acquire() const {
if (ref) {
android_atomic_inc(&ref->count);
}
}
void release() const {
if (ref) {
int32_t c = android_atomic_dec(&ref->count);
// ref->count cannot be 1 prior atomic_dec because we have
// a reference, and if we have one, it means there was
// already one before us.
LOGE_IF(c==1, "refcount is now 0 in release()");
}
}
void terminate() {
if (ref) {
ref->terminated = 1;
release();
}
}
};
};
SortedVector<egl_object_t*> egl_object_t::sObjects;
Mutex egl_object_t::sLock;
struct egl_display_t {
enum { NOT_INITIALIZED, INITIALIZED, TERMINATED };
struct strings_t {
char const * vendor;
char const * version;
char const * clientApi;
char const * extensions;
};
struct DisplayImpl {
DisplayImpl() : dpy(EGL_NO_DISPLAY), config(0),
state(NOT_INITIALIZED), numConfigs(0) { }
EGLDisplay dpy;
EGLConfig* config;
EGLint state;
EGLint numConfigs;
strings_t queryString;
};
uint32_t magic;
DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS];
EGLint numTotalConfigs;
uint32_t refs;
Mutex lock;
egl_display_t() : magic('_dpy'), numTotalConfigs(0) { }
~egl_display_t() { magic = 0; }
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
};
struct egl_surface_t : public egl_object_t
{
typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
egl_surface_t(EGLDisplay dpy, EGLSurface surface,
int impl, egl_connection_t const* cnx)
: dpy(dpy), surface(surface), impl(impl), cnx(cnx) {
}
~egl_surface_t() {
}
EGLDisplay dpy;
EGLSurface surface;
int impl;
egl_connection_t const* cnx;
};
struct egl_context_t : public egl_object_t
{
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
egl_context_t(EGLDisplay dpy, EGLContext context,
int impl, egl_connection_t const* cnx, int version)
: dpy(dpy), context(context), read(0), draw(0), impl(impl), cnx(cnx),
version(version)
{
}
EGLDisplay dpy;
EGLContext context;
EGLSurface read;
EGLSurface draw;
int impl;
egl_connection_t const* cnx;
int version;
};
struct egl_image_t : public egl_object_t
{
typedef egl_object_t::LocalRef<egl_image_t, EGLImageKHR> Ref;
egl_image_t(EGLDisplay dpy, EGLContext context)
: dpy(dpy), context(context)
{
memset(images, 0, sizeof(images));
}
EGLDisplay dpy;
EGLConfig context;
EGLImageKHR images[IMPL_NUM_IMPLEMENTATIONS];
};
typedef egl_surface_t::Ref SurfaceRef;
typedef egl_context_t::Ref ContextRef;
typedef egl_image_t::Ref ImageRef;
struct tls_t
{
tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { }
EGLint error;
EGLContext ctx;
EGLBoolean logCallWithNoContext;
};
// ----------------------------------------------------------------------------
egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
static egl_display_t gDisplay[NUM_DISPLAYS];
static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t gEGLThreadLocalStorageKey = -1;
// ----------------------------------------------------------------------------
EGLAPI gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
EGLAPI gl_hooks_t gHooksNoContext;
EGLAPI pthread_key_t gGLWrapperKey = -1;
// ----------------------------------------------------------------------------
static __attribute__((noinline))
const char *egl_strerror(EGLint err)
{
switch (err){
case EGL_SUCCESS: return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
default: return "UNKNOWN";
}
}
static __attribute__((noinline))
void clearTLS() {
if (gEGLThreadLocalStorageKey != -1) {
tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
if (tls) {
delete tls;
pthread_setspecific(gEGLThreadLocalStorageKey, 0);
}
}
}
static tls_t* getTLS()
{
tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
if (tls == 0) {
tls = new tls_t;
pthread_setspecific(gEGLThreadLocalStorageKey, tls);
}
return tls;
}
template<typename T>
static __attribute__((noinline))
T setErrorEtc(const char* caller, int line, EGLint error, T returnValue) {
if (gEGLThreadLocalStorageKey == -1) {
pthread_mutex_lock(&gThreadLocalStorageKeyMutex);
if (gEGLThreadLocalStorageKey == -1)
pthread_key_create(&gEGLThreadLocalStorageKey, NULL);
pthread_mutex_unlock(&gThreadLocalStorageKeyMutex);
}
tls_t* tls = getTLS();
if (tls->error != error) {
LOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
tls->error = error;
}
return returnValue;
}
static __attribute__((noinline))
GLint getError() {
if (gEGLThreadLocalStorageKey == -1)
return EGL_SUCCESS;
tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
if (!tls) return EGL_SUCCESS;
GLint error = tls->error;
tls->error = EGL_SUCCESS;
return error;
}
static __attribute__((noinline))
void setContext(EGLContext ctx) {
if (gEGLThreadLocalStorageKey == -1) {
pthread_mutex_lock(&gThreadLocalStorageKeyMutex);
if (gEGLThreadLocalStorageKey == -1)
pthread_key_create(&gEGLThreadLocalStorageKey, NULL);
pthread_mutex_unlock(&gThreadLocalStorageKeyMutex);
}
tls_t* tls = getTLS();
tls->ctx = ctx;
}
static __attribute__((noinline))
EGLContext getContext() {
if (gEGLThreadLocalStorageKey == -1)
return EGL_NO_CONTEXT;
tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey);
if (!tls) return EGL_NO_CONTEXT;
return tls->ctx;
}
/*****************************************************************************/
template<typename T>
static __attribute__((noinline))
int binarySearch(
T const sortedArray[], int first, int last, T key)
{
while (first <= last) {
int mid = (first + last) / 2;
if (key > sortedArray[mid]) {
first = mid + 1;
} else if (key < sortedArray[mid]) {
last = mid - 1;
} else {
return mid;
}
}
return -1;
}
static EGLint configToUniqueId(egl_display_t const* dp, int i, int index)
{
// NOTE: this mapping works only if we have no more than two EGLimpl
return (i>0 ? dp->disp[0].numConfigs : 0) + index;
}
static void uniqueIdToConfig(egl_display_t const* dp, EGLint configId,
int& i, int& index)
{
// NOTE: this mapping works only if we have no more than two EGLimpl
size_t numConfigs = dp->disp[0].numConfigs;
i = configId / numConfigs;
index = configId % numConfigs;
}
static int cmp_configs(const void* a, const void *b)
{
EGLConfig c0 = *(EGLConfig const *)a;
EGLConfig c1 = *(EGLConfig const *)b;
return c0<c1 ? -1 : (c0>c1 ? 1 : 0);
}
struct extention_map_t {
const char* name;
__eglMustCastToProperFunctionPointerType address;
};
static const extention_map_t gExtentionMap[] = {
{ "eglLockSurfaceKHR",
(__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
{ "eglUnlockSurfaceKHR",
(__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
{ "eglCreateImageKHR",
(__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
{ "eglDestroyImageKHR",
(__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
{ "eglSetSwapRectangleANDROID",
(__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID },
{ "eglGetRenderBufferANDROID",
(__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS];
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;
}
// ----------------------------------------------------------------------------
static void gl_no_context() {
tls_t* tls = getTLS();
if (tls->logCallWithNoContext == EGL_TRUE) {
tls->logCallWithNoContext = EGL_FALSE;
LOGE("call to OpenGL ES API with no current context "
"(logged once per thread)");
}
}
static void early_egl_init(void)
{
#if !USE_FAST_TLS_KEY
pthread_key_create(&gGLWrapperKey, NULL);
#endif
uint32_t addr = (uint32_t)((void*)gl_no_context);
android_memset32(
(uint32_t*)(void*)&gHooksNoContext,
addr,
sizeof(gHooksNoContext));
setGlThreadSpecific(&gHooksNoContext);
}
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
static inline
egl_display_t* get_display(EGLDisplay dpy)
{
uintptr_t index = uintptr_t(dpy)-1U;
return (index >= NUM_DISPLAYS) ? NULL : &gDisplay[index];
}
template<typename NATIVE, typename EGL>
static inline NATIVE* egl_to_native_cast(EGL arg) {
return reinterpret_cast<NATIVE*>(arg);
}
static inline
egl_surface_t* get_surface(EGLSurface surface) {
return egl_to_native_cast<egl_surface_t>(surface);
}
static inline
egl_context_t* get_context(EGLContext context) {
return egl_to_native_cast<egl_context_t>(context);
}
static inline
egl_image_t* get_image(EGLImageKHR image) {
return egl_to_native_cast<egl_image_t>(image);
}
static egl_connection_t* validate_display_config(
EGLDisplay dpy, EGLConfig config,
egl_display_t const*& dp, int& impl, int& index)
{
dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (egl_connection_t*)NULL);
impl = uintptr_t(config)>>24;
if (uint32_t(impl) >= IMPL_NUM_IMPLEMENTATIONS) {
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
index = uintptr_t(config) & 0xFFFFFF;
if (index >= dp->disp[impl].numConfigs) {
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
egl_connection_t* const cnx = &gEGLImpl[impl];
if (cnx->dso == 0) {
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
return cnx;
}
static EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx)
{
if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (!get_display(dpy)->isAlive())
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (!get_context(ctx)->isAlive())
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
return EGL_TRUE;
}
static EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface)
{
if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (!get_display(dpy)->isAlive())
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (!get_surface(surface)->isAlive())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_TRUE;
}
EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image)
{
ImageRef _i(image);
if (!_i.get()) return EGL_NO_IMAGE_KHR;
EGLContext context = getContext();
if (context == EGL_NO_CONTEXT || image == EGL_NO_IMAGE_KHR)
return EGL_NO_IMAGE_KHR;
egl_context_t const * const c = get_context(context);
if (!c->isAlive())
return EGL_NO_IMAGE_KHR;
egl_image_t const * const i = get_image(image);
return i->images[c->impl];
}
// ----------------------------------------------------------------------------
// this mutex protects:
// d->disp[]
// egl_init_drivers_locked()
//
static pthread_mutex_t gInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
EGLBoolean egl_init_drivers_locked()
{
if (sEarlyInitState) {
// initialized by static ctor. should be set here.
return EGL_FALSE;
}
// get our driver loader
Loader& loader(Loader::getInstance());
// dynamically load all our EGL implementations for all displays
// and retrieve the corresponding EGLDisplay
// if that fails, don't use this driver.
// TODO: currently we only deal with EGL_DEFAULT_DISPLAY
egl_connection_t* cnx;
egl_display_t* d = &gDisplay[0];
cnx = &gEGLImpl[IMPL_SOFTWARE];
if (cnx->dso == 0) {
cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_SOFTWARE];
cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_SOFTWARE];
cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 0, cnx);
if (cnx->dso) {
EGLDisplay dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOGE_IF(dpy==EGL_NO_DISPLAY, "No EGLDisplay for software EGL!");
d->disp[IMPL_SOFTWARE].dpy = dpy;
if (dpy == EGL_NO_DISPLAY) {
loader.close(cnx->dso);
cnx->dso = NULL;
}
}
}
cnx = &gEGLImpl[IMPL_HARDWARE];
if (cnx->dso == 0) {
char value[PROPERTY_VALUE_MAX];
property_get("debug.egl.hw", value, "1");
if (atoi(value) != 0) {
cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_HARDWARE];
cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_HARDWARE];
cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 1, cnx);
if (cnx->dso) {
EGLDisplay dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOGE_IF(dpy==EGL_NO_DISPLAY, "No EGLDisplay for hardware EGL!");
d->disp[IMPL_HARDWARE].dpy = dpy;
if (dpy == EGL_NO_DISPLAY) {
loader.close(cnx->dso);
cnx->dso = NULL;
}
}
} else {
LOGD("3D hardware acceleration is disabled");
}
}
if (!gEGLImpl[IMPL_SOFTWARE].dso && !gEGLImpl[IMPL_HARDWARE].dso) {
return EGL_FALSE;
}
return EGL_TRUE;
}
EGLBoolean egl_init_drivers()
{
EGLBoolean res;
pthread_mutex_lock(&gInitDriverMutex);
res = egl_init_drivers_locked();
pthread_mutex_unlock(&gInitDriverMutex);
return res;
}
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
using namespace android;
EGLDisplay eglGetDisplay(NativeDisplayType display)
{
uint32_t index = uint32_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 = EGLDisplay(uintptr_t(display) + 1LU);
return dpy;
}
// ----------------------------------------------------------------------------
// Initialization
// ----------------------------------------------------------------------------
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
{
egl_display_t * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
Mutex::Autolock _l(dp->lock);
if (dp->refs > 0) {
if (major != NULL) *major = VERSION_MAJOR;
if (minor != NULL) *minor = VERSION_MINOR;
dp->refs++;
return EGL_TRUE;
}
setGlThreadSpecific(&gHooksNoContext);
// initialize each EGL and
// build our own extension string first, based on the extension we know
// and the extension supported by our client implementation
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
cnx->major = -1;
cnx->minor = -1;
if (!cnx->dso)
continue;
#if defined(ADRENO130)
#warning "Adreno-130 eglInitialize() workaround"
/*
* The ADRENO 130 driver returns a different EGLDisplay each time
* eglGetDisplay() is called, but also makes the EGLDisplay invalid
* after eglTerminate() has been called, so that eglInitialize()
* cannot be called again. Therefore, we need to make sure to call
* eglGetDisplay() before calling eglInitialize();
*/
if (i == IMPL_HARDWARE) {
dp->disp[i].dpy =
cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
#endif
EGLDisplay idpy = dp->disp[i].dpy;
if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
//LOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p",
// i, idpy, cnx->major, cnx->minor, cnx);
// display is now initialized
dp->disp[i].state = egl_display_t::INITIALIZED;
// get the query-strings for this display for each implementation
dp->disp[i].queryString.vendor =
cnx->egl.eglQueryString(idpy, EGL_VENDOR);
dp->disp[i].queryString.version =
cnx->egl.eglQueryString(idpy, EGL_VERSION);
dp->disp[i].queryString.extensions =
cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS);
dp->disp[i].queryString.clientApi =
cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
} else {
LOGW("%d: eglInitialize(%p) failed (%s)", i, idpy,
egl_strerror(cnx->egl.eglGetError()));
}
}
EGLBoolean res = EGL_FALSE;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso && cnx->major>=0 && cnx->minor>=0) {
EGLint n;
if (cnx->egl.eglGetConfigs(dp->disp[i].dpy, 0, 0, &n)) {
dp->disp[i].config = (EGLConfig*)malloc(sizeof(EGLConfig)*n);
if (dp->disp[i].config) {
if (cnx->egl.eglGetConfigs(
dp->disp[i].dpy, dp->disp[i].config, n,
&dp->disp[i].numConfigs))
{
// sort the configurations so we can do binary searches
qsort( dp->disp[i].config,
dp->disp[i].numConfigs,
sizeof(EGLConfig), cmp_configs);
dp->numTotalConfigs += n;
res = EGL_TRUE;
}
}
}
}
}
if (res == EGL_TRUE) {
dp->refs++;
if (major != NULL) *major = VERSION_MAJOR;
if (minor != NULL) *minor = VERSION_MINOR;
return EGL_TRUE;
}
return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
}
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.
egl_display_t* const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
Mutex::Autolock _l(dp->lock);
if (dp->refs == 0) {
return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
}
// this is specific to Android, display termination is ref-counted.
if (dp->refs > 1) {
dp->refs--;
return EGL_TRUE;
}
EGLBoolean res = EGL_FALSE;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso && dp->disp[i].state == egl_display_t::INITIALIZED) {
if (cnx->egl.eglTerminate(dp->disp[i].dpy) == EGL_FALSE) {
LOGW("%d: eglTerminate(%p) failed (%s)", i, dp->disp[i].dpy,
egl_strerror(cnx->egl.eglGetError()));
}
// REVISIT: it's unclear what to do if eglTerminate() fails
free(dp->disp[i].config);
dp->disp[i].numConfigs = 0;
dp->disp[i].config = 0;
dp->disp[i].state = egl_display_t::TERMINATED;
res = EGL_TRUE;
}
}
// TODO: all egl_object_t should be marked for termination
dp->refs--;
dp->numTotalConfigs = 0;
clearTLS();
return res;
}
// ----------------------------------------------------------------------------
// configuration
// ----------------------------------------------------------------------------
EGLBoolean eglGetConfigs( EGLDisplay dpy,
EGLConfig *configs,
EGLint config_size, EGLint *num_config)
{
egl_display_t const * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
GLint numConfigs = dp->numTotalConfigs;
if (!configs) {
*num_config = numConfigs;
return EGL_TRUE;
}
GLint n = 0;
for (int j=0 ; j<IMPL_NUM_IMPLEMENTATIONS ; j++) {
for (int i=0 ; i<dp->disp[j].numConfigs && config_size ; i++) {
*configs++ = MAKE_CONFIG(j, i);
config_size--;
n++;
}
}
*num_config = n;
return EGL_TRUE;
}
EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
EGLConfig *configs, EGLint config_size,
EGLint *num_config)
{
egl_display_t const * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (num_config==0) {
return setError(EGL_BAD_PARAMETER, EGL_FALSE);
}
EGLint n;
EGLBoolean res = EGL_FALSE;
*num_config = 0;
// It is unfortunate, but we need to remap the EGL_CONFIG_IDs,
// to do this, we have to go through the attrib_list array once
// to figure out both its size and if it contains an EGL_CONFIG_ID
// key. If so, the full array is copied and patched.
// NOTE: we assume that there can be only one occurrence
// of EGL_CONFIG_ID.
EGLint patch_index = -1;
GLint attr;
size_t size = 0;
while ((attr=attrib_list[size]) != EGL_NONE) {
if (attr == EGL_CONFIG_ID)
patch_index = size;
size += 2;
}
if (patch_index >= 0) {
size += 2; // we need copy the sentinel as well
EGLint* new_list = (EGLint*)malloc(size*sizeof(EGLint));
if (new_list == 0)
return setError(EGL_BAD_ALLOC, EGL_FALSE);
memcpy(new_list, attrib_list, size*sizeof(EGLint));
// patch the requested EGL_CONFIG_ID
int i, index;
EGLint& configId(new_list[patch_index+1]);
uniqueIdToConfig(dp, configId, i, index);
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
cnx->egl.eglGetConfigAttrib(
dp->disp[i].dpy, dp->disp[i].config[index],
EGL_CONFIG_ID, &configId);
// and switch to the new list
attrib_list = const_cast<const EGLint *>(new_list);
// At this point, the only configuration that can match is
// dp->configs[i][index], however, we don't know if it would be
// rejected because of the other attributes, so we do have to call
// cnx->egl.eglChooseConfig() -- but we don't have to loop
// through all the EGLimpl[].
// We also know we can only get a single config back, and we know
// which one.
res = cnx->egl.eglChooseConfig(
dp->disp[i].dpy, attrib_list, configs, config_size, &n);
if (res && n>0) {
// n has to be 0 or 1, by construction, and we already know
// which config it will return (since there can be only one).
if (configs) {
configs[0] = MAKE_CONFIG(i, index);
}
*num_config = 1;
}
}
free(const_cast<EGLint *>(attrib_list));
return res;
}
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->egl.eglChooseConfig(
dp->disp[i].dpy, attrib_list, configs, config_size, &n)) {
if (configs) {
// now we need to convert these client EGLConfig to our
// internal EGLConfig format. This is done in O(n log n).
for (int j=0 ; j<n ; j++) {
int index = binarySearch<EGLConfig>(
dp->disp[i].config, 0,
dp->disp[i].numConfigs-1, configs[j]);
if (index >= 0) {
if (configs) {
configs[j] = MAKE_CONFIG(i, index);
}
} else {
return setError(EGL_BAD_CONFIG, EGL_FALSE);
}
}
configs += n;
config_size -= n;
}
*num_config += n;
res = EGL_TRUE;
}
}
}
return res;
}
EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
EGLint attribute, EGLint *value)
{
egl_display_t const* dp = 0;
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (!cnx) return EGL_FALSE;
if (attribute == EGL_CONFIG_ID) {
// EGL_CONFIG_IDs must be unique, just use the order of the selected
// EGLConfig.
*value = configToUniqueId(dp, i, index);
return EGL_TRUE;
}
return cnx->egl.eglGetConfigAttrib(
dp->disp[i].dpy, dp->disp[i].config[index], attribute, value);
}
// ----------------------------------------------------------------------------
// surfaces
// ----------------------------------------------------------------------------
EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
NativeWindowType window,
const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLSurface surface = cnx->egl.eglCreateWindowSurface(
dp->disp[i].dpy, dp->disp[i].config[index], window, attrib_list);
if (surface != EGL_NO_SURFACE) {
egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
return s;
}
}
return EGL_NO_SURFACE;
}
EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config,
NativePixmapType pixmap,
const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
dp->disp[i].dpy, dp->disp[i].config[index], pixmap, attrib_list);
if (surface != EGL_NO_SURFACE) {
egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
return s;
}
}
return EGL_NO_SURFACE;
}
EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
dp->disp[i].dpy, dp->disp[i].config[index], attrib_list);
if (surface != EGL_NO_SURFACE) {
egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
return s;
}
}
return EGL_NO_SURFACE;
}
EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t * const s = get_surface(surface);
EGLBoolean result = s->cnx->egl.eglDestroySurface(
dp->disp[s->impl].dpy, s->surface);
if (result == EGL_TRUE) {
_s.terminate();
}
return result;
}
EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
EGLint attribute, EGLint *value)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
return s->cnx->egl.eglQuerySurface(
dp->disp[s->impl].dpy, s->surface, attribute, value);
}
// ----------------------------------------------------------------------------
// contextes
// ----------------------------------------------------------------------------
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
EGLContext share_list, const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLContext context = cnx->egl.eglCreateContext(
dp->disp[i].dpy, dp->disp[i].config[index],
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 = GLESv1_INDEX;
} else if (value == 2) {
version = GLESv2_INDEX;
}
}
};
}
egl_context_t* c = new egl_context_t(dpy, context, i, cnx, version);
return c;
}
}
return EGL_NO_CONTEXT;
}
EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
{
ContextRef _c(ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_context_t * const c = get_context(ctx);
EGLBoolean result = c->cnx->egl.eglDestroyContext(
dp->disp[c->impl].dpy, c->context);
if (result == EGL_TRUE) {
_c.terminate();
}
return result;
}
EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
EGLSurface read, EGLContext ctx)
{
// get a reference to the object passed in
ContextRef _c(ctx);
SurfaceRef _d(draw);
SurfaceRef _r(read);
// validate the display and the context (if not EGL_NO_CONTEXT)
egl_display_t const * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if ((ctx != EGL_NO_CONTEXT) && (!validate_display_context(dpy, ctx))) {
// EGL_NO_CONTEXT is valid
return 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());
egl_surface_t * cur_r = NULL;
egl_surface_t * cur_d = NULL;
if (ctx != EGL_NO_CONTEXT) {
c = get_context(ctx);
cur_r = get_surface(c->read);
cur_d = get_surface(c->draw);
impl_ctx = c->context;
} else {
// no context given, use the implementation of the current context
if (cur_c == NULL) {
// no current context
if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
// calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
return setError(EGL_BAD_MATCH, EGL_FALSE);
}
// not an error, there is just no current context.
return EGL_TRUE;
}
}
// retrieve the underlying implementation's draw EGLSurface
if (draw != EGL_NO_SURFACE) {
d = get_surface(draw);
// make sure the EGLContext and EGLSurface passed in are for
// the same driver
if (c && d->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
impl_draw = d->surface;
}
// retrieve the underlying implementation's read EGLSurface
if (read != EGL_NO_SURFACE) {
r = get_surface(read);
// make sure the EGLContext and EGLSurface passed in are for
// the same driver
if (c && r->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
impl_read = r->surface;
}
EGLBoolean result;
if (c) {
result = c->cnx->egl.eglMakeCurrent(
dp->disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
} else {
result = cur_c->cnx->egl.eglMakeCurrent(
dp->disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
}
if (result == EGL_TRUE) {
// 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_r);
SurfaceRef _cur_d(cur_d);
// cur_c has to be valid here (but could be terminated)
if (ctx != EGL_NO_CONTEXT) {
setGlThreadSpecific(c->cnx->hooks[c->version]);
setContext(ctx);
_c.acquire();
} else {
setGlThreadSpecific(&gHooksNoContext);
setContext(EGL_NO_CONTEXT);
}
_cur_c.release();
_r.acquire();
_cur_r.release();
if (c) c->read = read;
_d.acquire();
_cur_d.release();
if (c) c->draw = draw;
}
return result;
}
EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
EGLint attribute, EGLint *value)
{
ContextRef _c(ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_context_t * const c = get_context(ctx);
return c->cnx->egl.eglQueryContext(
dp->disp[c->impl].dpy, c->context, 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 ctx = getContext();
return ctx;
}
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.
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 eglGetCurrentDisplay(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 eglWaitGL(void)
{
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would return GL_TRUE, which isn't wrong.
EGLBoolean res = EGL_TRUE;
EGLContext ctx = getContext();
if (ctx) {
egl_context_t const * const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
if (uint32_t(c->impl)>=2)
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
egl_connection_t* const cnx = &gEGLImpl[c->impl];
if (!cnx->dso)
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
res = cnx->egl.eglWaitGL();
}
return res;
}
EGLBoolean eglWaitNative(EGLint engine)
{
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would return GL_TRUE, which isn't wrong.
EGLBoolean res = EGL_TRUE;
EGLContext ctx = getContext();
if (ctx) {
egl_context_t const * const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
if (uint32_t(c->impl)>=2)
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
egl_connection_t* const cnx = &gEGLImpl[c->impl];
if (!cnx->dso)
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
res = cnx->egl.eglWaitNative(engine);
}
return res;
}
EGLint eglGetError(void)
{
EGLint result = EGL_SUCCESS;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
EGLint err = EGL_SUCCESS;
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso)
err = cnx->egl.eglGetError();
if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
result = err;
}
if (result == EGL_SUCCESS)
result = getError();
return result;
}
__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.
if (egl_init_drivers() == EGL_FALSE) {
setError(EGL_BAD_PARAMETER, NULL);
return NULL;
}
__eglMustCastToProperFunctionPointerType addr;
addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap));
if (addr) return addr;
return NULL; // TODO: finish implementation below
addr = findProcAddress(procname, gGLExtentionMap, NELEM(gGLExtentionMap));
if (addr) return addr;
addr = 0;
int slot = -1;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->egl.eglGetProcAddress) {
addr = cnx->egl.eglGetProcAddress(procname);
if (addr) {
if (slot == -1) {
slot = 0; // XXX: find free slot
if (slot == -1) {
addr = 0;
break;
}
}
//cnx->hooks->ext.extensions[slot] = addr;
}
}
}
}
if (slot >= 0) {
addr = 0; // XXX: address of stub 'slot'
gGLExtentionMap[slot].name = strdup(procname);
gGLExtentionMap[slot].address = addr;
}
return addr;
/*
* TODO: For OpenGL ES extensions, we must generate a stub
* that looks like
* mov r12, #0xFFFF0FFF
* ldr r12, [r12, #-15]
* ldr r12, [r12, #TLS_SLOT_OPENGL_API*4]
* mov r12, [r12, #api_offset]
* ldrne pc, r12
* mov pc, #unsupported_extension
*
* and write the address of the extension in *all*
* gl_hooks_t::gl_ext_t at offset "api_offset" from gl_hooks_t
*
*/
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
SurfaceRef _s(draw);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, draw))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(draw);
return s->cnx->egl.eglSwapBuffers(dp->disp[s->impl].dpy, s->surface);
}
EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface,
NativePixmapType target)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
return s->cnx->egl.eglCopyBuffers(
dp->disp[s->impl].dpy, s->surface, target);
}
const char* eglQueryString(EGLDisplay dpy, EGLint name)
{
egl_display_t const * const dp = get_display(dpy);
switch (name) {
case EGL_VENDOR:
return gVendorString;
case EGL_VERSION:
return gVersionString;
case EGL_EXTENSIONS:
return gExtensionString;
case EGL_CLIENT_APIS:
return gClientApiString;
}
return setError(EGL_BAD_PARAMETER, (const char *)0);
}
// ----------------------------------------------------------------------------
// EGL 1.1
// ----------------------------------------------------------------------------
EGLBoolean eglSurfaceAttrib(
EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->egl.eglSurfaceAttrib) {
return s->cnx->egl.eglSurfaceAttrib(
dp->disp[s->impl].dpy, s->surface, attribute, value);
}
return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
EGLBoolean eglBindTexImage(
EGLDisplay dpy, EGLSurface surface, EGLint buffer)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->egl.eglBindTexImage) {
return s->cnx->egl.eglBindTexImage(
dp->disp[s->impl].dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
EGLBoolean eglReleaseTexImage(
EGLDisplay dpy, EGLSurface surface, EGLint buffer)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->egl.eglReleaseTexImage) {
return s->cnx->egl.eglReleaseTexImage(
dp->disp[s->impl].dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
{
egl_display_t * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
EGLBoolean res = EGL_TRUE;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->egl.eglSwapInterval) {
if (cnx->egl.eglSwapInterval(
dp->disp[i].dpy, interval) == EGL_FALSE) {
res = EGL_FALSE;
}
}
}
}
return res;
}
// ----------------------------------------------------------------------------
// EGL 1.2
// ----------------------------------------------------------------------------
EGLBoolean eglWaitClient(void)
{
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would return GL_TRUE, which isn't wrong.
EGLBoolean res = EGL_TRUE;
EGLContext ctx = getContext();
if (ctx) {
egl_context_t const * const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
if (uint32_t(c->impl)>=2)
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
egl_connection_t* const cnx = &gEGLImpl[c->impl];
if (!cnx->dso)
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
if (cnx->egl.eglWaitClient) {
res = cnx->egl.eglWaitClient();
} else {
res = cnx->egl.eglWaitGL();
}
}
return res;
}
EGLBoolean eglBindAPI(EGLenum api)
{
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, EGL_FALSE);
}
// bind this API on all EGLs
EGLBoolean res = EGL_TRUE;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->egl.eglBindAPI) {
if (cnx->egl.eglBindAPI(api) == EGL_FALSE) {
res = EGL_FALSE;
}
}
}
}
return res;
}
EGLenum eglQueryAPI(void)
{
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, EGL_FALSE);
}
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->egl.eglQueryAPI) {
// the first one we find is okay, because they all
// should be the same
return cnx->egl.eglQueryAPI();
}
}
}
// or, it can only be OpenGL ES
return EGL_OPENGL_ES_API;
}
EGLBoolean eglReleaseThread(void)
{
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->egl.eglReleaseThread) {
cnx->egl.eglReleaseThread();
}
}
}
clearTLS();
return EGL_TRUE;
}
EGLSurface eglCreatePbufferFromClientBuffer(
EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
EGLConfig config, const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (!cnx) return EGL_FALSE;
if (cnx->egl.eglCreatePbufferFromClientBuffer) {
return cnx->egl.eglCreatePbufferFromClientBuffer(
dp->disp[i].dpy, buftype, buffer,
dp->disp[i].config[index], attrib_list);
}
return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
}
// ----------------------------------------------------------------------------
// EGL_EGLEXT_VERSION 3
// ----------------------------------------------------------------------------
EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
const EGLint *attrib_list)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->egl.eglLockSurfaceKHR) {
return s->cnx->egl.eglLockSurfaceKHR(
dp->disp[s->impl].dpy, s->surface, attrib_list);
}
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
{
SurfaceRef _s(surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->egl.eglUnlockSurfaceKHR) {
return s->cnx->egl.eglUnlockSurfaceKHR(
dp->disp[s->impl].dpy, s->surface);
}
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
EGLClientBuffer buffer, const EGLint *attrib_list)
{
if (ctx != EGL_NO_CONTEXT) {
ContextRef _c(ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
if (!validate_display_context(dpy, ctx))
return EGL_NO_IMAGE_KHR;
egl_display_t const * const dp = get_display(dpy);
egl_context_t * const c = get_context(ctx);
// since we have an EGLContext, we know which implementation to use
EGLImageKHR image = c->cnx->egl.eglCreateImageKHR(
dp->disp[c->impl].dpy, c->context, target, buffer, attrib_list);
if (image == EGL_NO_IMAGE_KHR)
return image;
egl_image_t* result = new egl_image_t(dpy, ctx);
result->images[c->impl] = image;
return (EGLImageKHR)result;
} else {
// EGL_NO_CONTEXT is a valid parameter
egl_display_t const * const dp = get_display(dpy);
if (dp == 0) {
return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR);
}
/* Since we don't have a way to know which implementation to call,
* we're calling all of them. If at least one of the implementation
* succeeded, this is a success.
*/
EGLint currentError = eglGetError();
EGLImageKHR implImages[IMPL_NUM_IMPLEMENTATIONS];
bool success = false;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
implImages[i] = EGL_NO_IMAGE_KHR;
if (cnx->dso) {
if (cnx->egl.eglCreateImageKHR) {
implImages[i] = cnx->egl.eglCreateImageKHR(
dp->disp[i].dpy, ctx, target, buffer, attrib_list);
if (implImages[i] != EGL_NO_IMAGE_KHR) {
success = true;
}
}
}
}
if (!success) {
// failure, if there was an error when we entered this function,
// the error flag must not be updated.
// Otherwise, the error is whatever happened in the implementation
// that faulted.
if (currentError != EGL_SUCCESS) {
setError(currentError, EGL_NO_IMAGE_KHR);
}
return EGL_NO_IMAGE_KHR;
} else {
// In case of success, we need to clear all error flags
// (especially those caused by the implementation that didn't
// succeed). TODO: we could avoid this if we knew this was
// a "full" success (all implementation succeeded).
eglGetError();
}
egl_image_t* result = new egl_image_t(dpy, ctx);
memcpy(result->images, implImages, sizeof(implImages));
return (EGLImageKHR)result;
}
}
EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
{
egl_display_t const * const dp = get_display(dpy);
if (dp == 0) {
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
ImageRef _i(img);
if (!_i.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
egl_image_t* image = get_image(img);
bool success = false;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (image->images[i] != EGL_NO_IMAGE_KHR) {
if (cnx->dso) {
if (cnx->egl.eglCreateImageKHR) {
if (cnx->egl.eglDestroyImageKHR(
dp->disp[i].dpy, image->images[i])) {
success = true;
}
}
}
}
}
if (!success)
return EGL_FALSE;
_i.terminate();
return EGL_TRUE;
}
// ----------------------------------------------------------------------------
// ANDROID extensions
// ----------------------------------------------------------------------------
EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
EGLint left, EGLint top, EGLint width, EGLint height)
{
SurfaceRef _s(draw);
if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (!validate_display_surface(dpy, draw))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(draw);
if (s->cnx->egl.eglSetSwapRectangleANDROID) {
return s->cnx->egl.eglSetSwapRectangleANDROID(
dp->disp[s->impl].dpy, s->surface, left, top, width, height);
}
return setError(EGL_BAD_DISPLAY, NULL);
}
EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
{
SurfaceRef _s(draw);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLClientBuffer*)0);
if (!validate_display_surface(dpy, draw))
return 0;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(draw);
if (s->cnx->egl.eglGetRenderBufferANDROID) {
return s->cnx->egl.eglGetRenderBufferANDROID(
dp->disp[s->impl].dpy, s->surface);
}
return setError(EGL_BAD_DISPLAY, (EGLClientBuffer*)0);
}