/*
* Copyright (C) 2011 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 "eglDisplay.h"
#include "HostConnection.h"
#include <dlfcn.h>
static const int systemEGLVersionMajor = 1;
static const int systemEGLVersionMinor = 4;
static const char systemEGLVendor[] = "Google Android emulator";
// list of extensions supported by this EGL implementation
// NOTE that each extension name should be suffixed with space
static const char systemStaticEGLExtensions[] =
"EGL_ANDROID_image_native_buffer "
"EGL_KHR_fence_sync ";
// list of extensions supported by this EGL implementation only if supported
// on the host implementation.
// NOTE that each extension name should be suffixed with space
static const char systemDynamicEGLExtensions[] =
"EGL_KHR_image_base "
"EGL_KHR_gl_texture_2d_image ";
static void *s_gles_lib = NULL;
static void *s_gles2_lib = NULL;
// The following function will be called when we (libEGL)
// gets unloaded
// At this point we want to unload the gles libraries we
// might have loaded during initialization
static void __attribute__ ((destructor)) do_on_unload(void)
{
if (s_gles_lib) {
dlclose(s_gles_lib);
}
if (s_gles2_lib) {
dlclose(s_gles2_lib);
}
}
eglDisplay::eglDisplay() :
m_initialized(false),
m_major(0),
m_minor(0),
m_hostRendererVersion(0),
m_numConfigs(0),
m_numConfigAttribs(0),
m_attribs(DefaultKeyedVector<EGLint, EGLint>(ATTRIBUTE_NONE)),
m_configs(NULL),
m_gles_iface(NULL),
m_gles2_iface(NULL),
m_versionString(NULL),
m_vendorString(NULL),
m_extensionString(NULL)
{
pthread_mutex_init(&m_lock, NULL);
}
eglDisplay::~eglDisplay()
{
pthread_mutex_destroy(&m_lock);
}
bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
{
pthread_mutex_lock(&m_lock);
if (!m_initialized) {
//
// load GLES client API
//
m_gles_iface = loadGLESClientAPI("/system/lib/egl/libGLESv1_CM_emulation.so",
eglIface,
&s_gles_lib);
if (!m_gles_iface) {
pthread_mutex_unlock(&m_lock);
ALOGE("Failed to load gles1 iface");
return false;
}
#ifdef WITH_GLES2
m_gles2_iface = loadGLESClientAPI("/system/lib/egl/libGLESv2_emulation.so",
eglIface,
&s_gles2_lib);
// Note that if loading gles2 failed, we can still run with no
// GLES2 support, having GLES2 is not mandatory.
#endif
//
// establish connection with the host
//
HostConnection *hcon = HostConnection::get();
if (!hcon) {
pthread_mutex_unlock(&m_lock);
ALOGE("Failed to establish connection with the host\n");
return false;
}
//
// get renderControl encoder instance
//
renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
if (!rcEnc) {
pthread_mutex_unlock(&m_lock);
ALOGE("Failed to get renderControl encoder instance");
return false;
}
//
// Query host reneder and EGL version
//
m_hostRendererVersion = rcEnc->rcGetRendererVersion(rcEnc);
EGLint status = rcEnc->rcGetEGLVersion(rcEnc, &m_major, &m_minor);
if (status != EGL_TRUE) {
// host EGL initialization failed !!
pthread_mutex_unlock(&m_lock);
return false;
}
//
// Take minimum version beween what we support and what the host support
//
if (m_major > systemEGLVersionMajor) {
m_major = systemEGLVersionMajor;
m_minor = systemEGLVersionMinor;
}
else if (m_major == systemEGLVersionMajor &&
m_minor > systemEGLVersionMinor) {
m_minor = systemEGLVersionMinor;
}
//
// Query the host for the set of configs
//
m_numConfigs = rcEnc->rcGetNumConfigs(rcEnc, (uint32_t*)&m_numConfigAttribs);
if (m_numConfigs <= 0 || m_numConfigAttribs <= 0) {
// just sanity check - should never happen
pthread_mutex_unlock(&m_lock);
return false;
}
uint32_t nInts = m_numConfigAttribs * (m_numConfigs + 1);
EGLint tmp_buf[nInts];
m_configs = new EGLint[nInts-m_numConfigAttribs];
if (!m_configs) {
pthread_mutex_unlock(&m_lock);
return false;
}
//EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), m_configs);
EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), (GLuint*)tmp_buf);
if (n != m_numConfigs) {
pthread_mutex_unlock(&m_lock);
return false;
}
//Fill the attributes vector.
//The first m_numConfigAttribs values of tmp_buf are the actual attributes enums.
for (int i=0; i<m_numConfigAttribs; i++) {
m_attribs.add(tmp_buf[i], i);
}
//Copy the actual configs data to m_configs
memcpy(m_configs, tmp_buf + m_numConfigAttribs, m_numConfigs*m_numConfigAttribs*sizeof(EGLint));
m_initialized = true;
}
pthread_mutex_unlock(&m_lock);
processConfigs();
return true;
}
void eglDisplay::processConfigs()
{
for (int i=0; i<m_numConfigs; i++) {
EGLConfig config = (EGLConfig)i;
//Setup the EGL_NATIVE_VISUAL_ID attribute
PixelFormat format;
if (getConfigNativePixelFormat(config, &format)) {
setConfigAttrib(config, EGL_NATIVE_VISUAL_ID, format);
}
}
}
void eglDisplay::terminate()
{
pthread_mutex_lock(&m_lock);
if (m_initialized) {
m_initialized = false;
delete [] m_configs;
m_configs = NULL;
if (m_versionString) {
free(m_versionString);
m_versionString = NULL;
}
if (m_vendorString) {
free(m_vendorString);
m_vendorString = NULL;
}
if (m_extensionString) {
free(m_extensionString);
m_extensionString = NULL;
}
}
pthread_mutex_unlock(&m_lock);
}
EGLClient_glesInterface *eglDisplay::loadGLESClientAPI(const char *libName,
EGLClient_eglInterface *eglIface,
void **libHandle)
{
void *lib = dlopen(libName, RTLD_NOW);
if (!lib) {
ALOGE("Failed to dlopen %s", libName);
return NULL;
}
init_emul_gles_t init_gles_func = (init_emul_gles_t)dlsym(lib,"init_emul_gles");
if (!init_gles_func) {
ALOGE("Failed to find init_emul_gles");
dlclose((void*)lib);
return NULL;
}
*libHandle = lib;
return (*init_gles_func)(eglIface);
}
static char *queryHostEGLString(EGLint name)
{
HostConnection *hcon = HostConnection::get();
if (hcon) {
renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
if (rcEnc) {
int n = rcEnc->rcQueryEGLString(rcEnc, name, NULL, 0);
if (n < 0) {
// allocate space for the string with additional
// space charachter to be suffixed at the end.
char *str = (char *)malloc(-n+2);
n = rcEnc->rcQueryEGLString(rcEnc, name, str, -n);
if (n > 0) {
// add extra space at end of string which will be
// needed later when filtering the extension list.
strcat(str, " ");
return str;
}
free(str);
}
}
}
return NULL;
}
static bool findExtInList(const char* token, int tokenlen, const char* list)
{
const char* p = list;
while (*p != '\0') {
const char* q = strchr(p, ' ');
if (q == NULL) {
/* should not happen, list must be space-terminated */
break;
}
if (tokenlen == (q - p) && !memcmp(token, p, tokenlen)) {
return true; /* found it */
}
p = q+1;
}
return false; /* not found */
}
static char *buildExtensionString()
{
//Query host extension string
char *hostExt = queryHostEGLString(EGL_EXTENSIONS);
if (!hostExt || (hostExt[1] == '\0')) {
// no extensions on host - only static extension list supported
return strdup(systemStaticEGLExtensions);
}
//
// Filter host extension list to include only extensions
// we can support (in the systemDynamicEGLExtensions list)
//
char *ext = (char *)hostExt;
char *c = ext;
char *insert = ext;
while(*c != '\0') {
if (*c == ' ') {
int len = c - ext;
if (findExtInList(ext, len, systemDynamicEGLExtensions)) {
if (ext != insert) {
memcpy(insert, ext, len+1); // including space
}
insert += (len + 1);
}
ext = c + 1;
}
c++;
}
*insert = '\0';
int n = strlen(hostExt);
if (n > 0) {
char *str;
asprintf(&str,"%s%s", systemStaticEGLExtensions, hostExt);
free((char*)hostExt);
return str;
}
else {
free((char*)hostExt);
return strdup(systemStaticEGLExtensions);
}
}
const char *eglDisplay::queryString(EGLint name)
{
if (name == EGL_CLIENT_APIS) {
return "OpenGL_ES";
}
else if (name == EGL_VERSION) {
pthread_mutex_lock(&m_lock);
if (m_versionString) {
pthread_mutex_unlock(&m_lock);
return m_versionString;
}
// build version string
asprintf(&m_versionString, "%d.%d", m_major, m_minor);
pthread_mutex_unlock(&m_lock);
return m_versionString;
}
else if (name == EGL_VENDOR) {
pthread_mutex_lock(&m_lock);
if (m_vendorString) {
pthread_mutex_unlock(&m_lock);
return m_vendorString;
}
// build vendor string
const char *hostVendor = queryHostEGLString(EGL_VENDOR);
if (hostVendor) {
asprintf(&m_vendorString, "%s Host: %s",
systemEGLVendor, hostVendor);
free((char*)hostVendor);
}
else {
m_vendorString = (char *)systemEGLVendor;
}
pthread_mutex_unlock(&m_lock);
return m_vendorString;
}
else if (name == EGL_EXTENSIONS) {
pthread_mutex_lock(&m_lock);
if (m_extensionString) {
pthread_mutex_unlock(&m_lock);
return m_extensionString;
}
// build extension string
m_extensionString = buildExtensionString();
pthread_mutex_unlock(&m_lock);
return m_extensionString;
}
else {
ALOGE("[%s] Unknown name %d\n", __FUNCTION__, name);
return NULL;
}
}
/* To get the value of attribute <a> of config <c> use the following formula:
* value = *(m_configs + (int)c*m_numConfigAttribs + a);
*/
EGLBoolean eglDisplay::getAttribValue(EGLConfig config, EGLint attribIdx, EGLint * value)
{
if (attribIdx == ATTRIBUTE_NONE)
{
ALOGE("[%s] Bad attribute idx\n", __FUNCTION__);
return EGL_FALSE;
}
*value = *(m_configs + (int)config*m_numConfigAttribs + attribIdx);
return EGL_TRUE;
}
EGLBoolean eglDisplay::getConfigAttrib(EGLConfig config, EGLint attrib, EGLint * value)
{
//Though it seems that valueFor() is thread-safe, we don't take chanses
pthread_mutex_lock(&m_lock);
EGLBoolean ret = getAttribValue(config, m_attribs.valueFor(attrib), value);
pthread_mutex_unlock(&m_lock);
return ret;
}
void eglDisplay::dumpConfig(EGLConfig config)
{
EGLint value = 0;
DBG("^^^^^^^^^^ dumpConfig %d ^^^^^^^^^^^^^^^^^^", (int)config);
for (int i=0; i<m_numConfigAttribs; i++) {
getAttribValue(config, i, &value);
DBG("{%d}[%d] %d\n", (int)config, i, value);
}
}
/* To set the value of attribute <a> of config <c> use the following formula:
* *(m_configs + (int)c*m_numConfigAttribs + a) = value;
*/
EGLBoolean eglDisplay::setAttribValue(EGLConfig config, EGLint attribIdx, EGLint value)
{
if (attribIdx == ATTRIBUTE_NONE)
{
ALOGE("[%s] Bad attribute idx\n", __FUNCTION__);
return EGL_FALSE;
}
*(m_configs + (int)config*m_numConfigAttribs + attribIdx) = value;
return EGL_TRUE;
}
EGLBoolean eglDisplay::setConfigAttrib(EGLConfig config, EGLint attrib, EGLint value)
{
//Though it seems that valueFor() is thread-safe, we don't take chanses
pthread_mutex_lock(&m_lock);
EGLBoolean ret = setAttribValue(config, m_attribs.valueFor(attrib), value);
pthread_mutex_unlock(&m_lock);
return ret;
}
EGLBoolean eglDisplay::getConfigNativePixelFormat(EGLConfig config, PixelFormat * format)
{
EGLint redSize, blueSize, greenSize, alphaSize;
if ( !(getAttribValue(config, m_attribs.valueFor(EGL_RED_SIZE), &redSize) &&
getAttribValue(config, m_attribs.valueFor(EGL_BLUE_SIZE), &blueSize) &&
getAttribValue(config, m_attribs.valueFor(EGL_GREEN_SIZE), &greenSize) &&
getAttribValue(config, m_attribs.valueFor(EGL_ALPHA_SIZE), &alphaSize)) )
{
ALOGE("Couldn't find value for one of the pixel format attributes");
return EGL_FALSE;
}
//calculate the GL internal format
if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==8)) *format = PIXEL_FORMAT_RGBA_8888; //XXX: BGR?
else if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==0)) *format = PIXEL_FORMAT_RGBX_8888; //XXX or PIXEL_FORMAT_RGB_888
else if ((redSize==5)&&(greenSize==6)&&(blueSize==5)&&(alphaSize==0)) *format = PIXEL_FORMAT_RGB_565;
else if ((redSize==5)&&(greenSize==5)&&(blueSize==5)&&(alphaSize==1)) *format = PIXEL_FORMAT_RGBA_5551;
else if ((redSize==4)&&(greenSize==4)&&(blueSize==4)&&(alphaSize==4)) *format = PIXEL_FORMAT_RGBA_4444;
else {
return EGL_FALSE;
}
return EGL_TRUE;
}
EGLBoolean eglDisplay::getConfigGLPixelFormat(EGLConfig config, GLenum * format)
{
EGLint redSize, blueSize, greenSize, alphaSize;
if ( !(getAttribValue(config, m_attribs.valueFor(EGL_RED_SIZE), &redSize) &&
getAttribValue(config, m_attribs.valueFor(EGL_BLUE_SIZE), &blueSize) &&
getAttribValue(config, m_attribs.valueFor(EGL_GREEN_SIZE), &greenSize) &&
getAttribValue(config, m_attribs.valueFor(EGL_ALPHA_SIZE), &alphaSize)) )
{
ALOGE("Couldn't find value for one of the pixel format attributes");
return EGL_FALSE;
}
//calculate the GL internal format
if ((redSize==8)&&(blueSize==8)&&(blueSize==8)&&(alphaSize==8)) *format = GL_RGBA;
else if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==0)) *format = GL_RGB;
else if ((redSize==5)&&(greenSize==6)&&(blueSize==5)&&(alphaSize==0)) *format = GL_RGB565_OES;
else if ((redSize==5)&&(greenSize==5)&&(blueSize==5)&&(alphaSize==1)) *format = GL_RGB5_A1_OES;
else if ((redSize==4)&&(greenSize==4)&&(blueSize==4)&&(alphaSize==4)) *format = GL_RGBA4_OES;
else return EGL_FALSE;
return EGL_TRUE;
}