/* * 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. */ #ifdef _WIN32 #undef EGLAPI #define EGLAPI #define EGLAPIENTRY #endif #include #include "GLcommon/GLEScontext.h" #include "GLcommon/GLutils.h" #include "GLcommon/TextureData.h" #include "GLcommon/TextureUtils.h" #include "GLcommon/TranslatorIfaces.h" #include "ThreadInfo.h" #include "aemu/base/synchronization/Lock.h" #include "aemu/base/files/Stream.h" #include "aemu/base/system/System.h" #include "aemu/base/SharedLibrary.h" #include "host-common/GfxstreamFatalError.h" #include "host-common/emugl_vm_operations.h" #include "host-common/logging.h" #include "EglWindowSurface.h" #include "EglPbufferSurface.h" #include "EglGlobalInfo.h" #include "EglThreadInfo.h" #include "EglValidate.h" #include "EglDisplay.h" #include "EglContext.h" #include "EglConfig.h" #include "EglOsApi.h" #include "ClientAPIExts.h" #include #include #include #include #include #define MAJOR 1 #define MINOR 4 using emugl::ABORT_REASON_OTHER; using emugl::FatalError; //declarations namespace translator { namespace egl { ImagePtr getEGLImage(unsigned int imageId); GLEScontext* getGLESContext(); GlLibrary* getGlLibrary(); void* getProcAddressFromEGL(const char*); static bool createAndBindAuxiliaryContext( EGLContext* context_out, EGLSurface* surface_out); static bool unbindAndDestroyAuxiliaryContext( EGLContext context, EGLSurface surface); static bool bindAuxiliaryContext( EGLContext context, EGLSurface surface); static bool unbindAuxiliaryContext(); } // namespace translator } // namespace egl #define tls_thread EglThreadInfo::get() EglGlobalInfo* g_eglInfo = NULL; android::base::Lock s_eglLock; android::base::Lock s_surfaceDestroyLock; void initGlobalInfo() { android::base::AutoLock mutex(s_eglLock); if (!g_eglInfo) { g_eglInfo = EglGlobalInfo::getInstance(); } } static const EGLiface s_eglIface = { .getGLESContext = translator::egl::getGLESContext, .getEGLImage = translator::egl::getEGLImage, .eglGetGlLibrary = translator::egl::getGlLibrary, .createAndBindAuxiliaryContext = translator::egl::createAndBindAuxiliaryContext, .unbindAndDestroyAuxiliaryContext = translator::egl::unbindAndDestroyAuxiliaryContext, .bindAuxiliaryContext = translator::egl::bindAuxiliaryContext, .unbindAuxiliaryContext = translator::egl::unbindAuxiliaryContext, .getProcAddress = translator::egl::getProcAddressFromEGL, }; static void initGLESx(GLESVersion version) { const GLESiface* iface = g_eglInfo->getIface(version); if (!iface) { ERR("EGL failed to initialize GLESv%d; incompatible interface", version); return; } iface->initGLESx(EglGlobalInfo::isEgl2Egl()); } /***************************************** supported extensions ***********************************************************************/ namespace translator { namespace egl { EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR(EGLDisplay display, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR(EGLDisplay display, EGLImageKHR image); EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR(EGLDisplay display, EGLenum type, const EGLint* attribs); EGLAPI EGLint EGLAPIENTRY eglClientWaitSyncKHR(EGLDisplay display, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR(EGLDisplay display, EGLSyncKHR sync); EGLAPI EGLint EGLAPIENTRY eglGetMaxGLESVersion(EGLDisplay display); EGLAPI EGLint EGLAPIENTRY eglWaitSyncKHR(EGLDisplay display, EGLSyncKHR sync, EGLint flags); EGLAPI void EGLAPIENTRY eglBlitFromCurrentReadBufferANDROID(EGLDisplay display, EGLImageKHR image); EGLAPI void* EGLAPIENTRY eglSetImageFenceANDROID(EGLDisplay display, EGLImageKHR image); EGLAPI void EGLAPIENTRY eglWaitImageFenceANDROID(EGLDisplay display, void* fence); EGLAPI void EGLAPIENTRY eglAddLibrarySearchPathANDROID(const char* path); EGLAPI EGLBoolean EGLAPIENTRY eglQueryVulkanInteropSupportANDROID(void); EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttribKHR(EGLDisplay display, EGLSyncKHR sync, EGLint attribute, EGLint *value); EGLAPI EGLBoolean EGLAPIENTRY eglSetNativeTextureDecompressionEnabledANDROID(EGLDisplay display, EGLBoolean enabled); EGLAPI EGLBoolean EGLAPIENTRY eglSaveConfig(EGLDisplay display, EGLConfig config, EGLStreamKHR stream); EGLAPI EGLConfig EGLAPIENTRY eglLoadConfig(EGLDisplay display, EGLStreamKHR stream); EGLAPI EGLBoolean EGLAPIENTRY eglPreSaveContext(EGLDisplay display, EGLContext contex, EGLStreamKHR stream); EGLAPI EGLBoolean EGLAPIENTRY eglSaveContext(EGLDisplay display, EGLContext contex, EGLStreamKHR stream); EGLAPI EGLBoolean EGLAPIENTRY eglPostSaveContext(EGLDisplay display, EGLContext context, EGLStreamKHR stream); EGLAPI EGLContext EGLAPIENTRY eglLoadContext(EGLDisplay display, const EGLint *attrib_list, EGLStreamKHR stream); EGLAPI EGLBoolean EGLAPIENTRY eglSaveAllImages(EGLDisplay display, EGLStreamKHR stream, const void* textureSaver); EGLAPI EGLBoolean EGLAPIENTRY eglLoadAllImages(EGLDisplay display, EGLStreamKHR stream, const void* textureLoader); EGLAPI EGLBoolean EGLAPIENTRY eglPostLoadAllImages(EGLDisplay display, EGLStreamKHR stream); EGLAPI void EGLAPIENTRY eglUseOsEglApi(EGLBoolean enable, EGLBoolean nullEgl); EGLAPI void EGLAPIENTRY eglSetMaxGLESVersion(EGLint version); EGLAPI void EGLAPIENTRY eglFillUsages(void* usages); EGLAPI EGLDisplay EGLAPIENTRY eglGetNativeDisplayANDROID(EGLDisplay); EGLAPI EGLContext EGLAPIENTRY eglGetNativeContextANDROID(EGLDisplay, EGLContext); EGLAPI EGLImage EGLAPIENTRY eglGetNativeImageANDROID(EGLDisplay, EGLImage); EGLAPI EGLBoolean EGLAPIENTRY eglSetImageInfoANDROID(EGLDisplay, EGLImage, EGLint, EGLint, EGLint); EGLAPI EGLImage EGLAPIENTRY eglImportImageANDROID(EGLDisplay, EGLImage); EGLint eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, const EGLAttrib * attrib_list); } // namespace translator } // namespace egl namespace translator { namespace egl { static const ExtensionDescriptor s_eglExtensions[] = { {"eglCreateImageKHR" , (__eglMustCastToProperFunctionPointerType)eglCreateImageKHR }, {"eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)eglDestroyImageKHR }, {"eglCreateSyncKHR" , (__eglMustCastToProperFunctionPointerType)eglCreateSyncKHR }, {"eglClientWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)eglClientWaitSyncKHR }, {"eglDestroySyncKHR", (__eglMustCastToProperFunctionPointerType)eglDestroySyncKHR }, {"eglGetMaxGLESVersion", (__eglMustCastToProperFunctionPointerType)eglGetMaxGLESVersion }, {"eglWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)eglWaitSyncKHR }, {"eglBlitFromCurrentReadBufferANDROID", (__eglMustCastToProperFunctionPointerType)eglBlitFromCurrentReadBufferANDROID }, {"eglSetImageFenceANDROID", (__eglMustCastToProperFunctionPointerType)eglSetImageFenceANDROID }, {"eglWaitImageFenceANDROID", (__eglMustCastToProperFunctionPointerType)eglWaitImageFenceANDROID }, {"eglAddLibrarySearchPathANDROID", (__eglMustCastToProperFunctionPointerType)eglAddLibrarySearchPathANDROID }, {"eglQueryVulkanInteropSupportANDROID", (__eglMustCastToProperFunctionPointerType)eglQueryVulkanInteropSupportANDROID }, {"eglGetSyncAttribKHR", (__eglMustCastToProperFunctionPointerType)eglGetSyncAttribKHR }, {"eglSetNativeTextureDecompressionEnabledANDROID", (__eglMustCastToProperFunctionPointerType)eglSetNativeTextureDecompressionEnabledANDROID }, }; static const int s_eglExtensionsSize = sizeof(s_eglExtensions) / sizeof(ExtensionDescriptor); } // namespace translator } // namespace egl /****************************************************************************************************************************************/ //macros for accessing global egl info & tls objects extern "C" { } namespace translator { namespace egl { #define CURRENT_THREAD() do {} while (0); #define RETURN_ERROR(ret,err) \ CURRENT_THREAD() \ if(tls_thread->getError() == EGL_SUCCESS) { \ tls_thread->setError(err); \ } \ return ret; #define VALIDATE_DISPLAY_RETURN(EGLDisplay, ret) \ MEM_TRACE_IF(strncmp(__FUNCTION__, "egl", 3) == 0, "EMUGL") \ EglDisplay* dpy = g_eglInfo->getDisplay(EGLDisplay); \ if (!dpy) { \ RETURN_ERROR(ret, EGL_BAD_DISPLAY); \ } \ if (!dpy->isInitialize()) { \ RETURN_ERROR(ret, EGL_NOT_INITIALIZED); \ } #define VALIDATE_CONFIG_RETURN(EGLConfig,ret) \ EglConfig* cfg = dpy->getConfig(EGLConfig); \ if(!cfg) { \ RETURN_ERROR(ret,EGL_BAD_CONFIG); \ } #define VALIDATE_SURFACE_RETURN(EGLSurface,ret,varName) \ SurfacePtr varName = dpy->getSurface(EGLSurface); \ if(!varName.get()) { \ RETURN_ERROR(ret,EGL_BAD_SURFACE); \ } #define VALIDATE_CONTEXT_RETURN(EGLContext,ret) \ ContextPtr ctx = dpy->getContext(EGLContext); \ if(!ctx.get()) { \ RETURN_ERROR(ret,EGL_BAD_CONTEXT); \ } #define VALIDATE_DISPLAY(EGLDisplay) \ VALIDATE_DISPLAY_RETURN(EGLDisplay,EGL_FALSE) #define VALIDATE_CONFIG(EGLConfig) \ VALIDATE_CONFIG_RETURN(EGLConfig,EGL_FALSE) #define VALIDATE_SURFACE(EGLSurface,varName) \ VALIDATE_SURFACE_RETURN(EGLSurface,EGL_FALSE,varName) #define VALIDATE_CONTEXT(EGLContext) \ VALIDATE_CONTEXT_RETURN(EGLContext,EGL_FALSE) GLEScontext* getGLESContext() { ThreadInfo* thread = getThreadInfo(); return thread->glesContext; } GlLibrary* getGlLibrary() { return EglGlobalInfo::getInstance()->getOsEngine()->getGlLibrary(); } void* getProcAddressFromEGL(const char* func) { return EglGlobalInfo::getInstance()->getOsEngine()->eglGetProcAddress(func); } EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id) { MEM_TRACE("EMUGL"); EglDisplay* dpy = NULL; EglOS::Display* internalDisplay = NULL; initGlobalInfo(); if ((dpy = g_eglInfo->getDisplayFromDisplayType(display_id))) { return dpy; } if (display_id != EGL_DEFAULT_DISPLAY) { return EGL_NO_DISPLAY; } internalDisplay = g_eglInfo->getDefaultNativeDisplay(); dpy = g_eglInfo->addDisplay(display_id,internalDisplay); if(!dpy) { return EGL_NO_DISPLAY; } return dpy; } } // namespace translator } // namespace egl #define TRANSLATOR_GETIFACE_NAME "__translator_getIfaces" extern "C" { GLESiface* static_translator_glescm_getIfaces(const EGLiface*); GLESiface* static_translator_glesv2_getIfaces(const EGLiface*); }; // extern "C" #define STATIC_TRANSLATOR_GETIFACE_NAME_GLES_CM static_translator_glescm_getIfaces #define STATIC_TRANSLATOR_GETIFACE_NAME_GLES_V2 static_translator_glesv2_getIfaces #define LIB_GLES_CM_NAME EMUGL_LIBNAME("GLES_CM_translator") #define LIB_GLES_V2_NAME EMUGL_LIBNAME("GLES_V2_translator") static __translator_getGLESIfaceFunc loadIfaces(const char* libName, char* error, size_t errorSize) { if (!strcmp(libName, LIB_GLES_CM_NAME)) { return STATIC_TRANSLATOR_GETIFACE_NAME_GLES_CM; } if (!strcmp(libName, LIB_GLES_V2_NAME)) { return STATIC_TRANSLATOR_GETIFACE_NAME_GLES_V2; } return 0; } namespace translator { namespace egl { EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay display, EGLint *major, EGLint *minor) { MEM_TRACE("EMUGL"); initGlobalInfo(); EglDisplay* dpy = g_eglInfo->getDisplay(display); if(!dpy) { RETURN_ERROR(EGL_FALSE,EGL_BAD_DISPLAY); } if(major) *major = MAJOR; if(minor) *minor = MINOR; __translator_getGLESIfaceFunc func = NULL; int renderableType = EGL_OPENGL_ES_BIT; g_eglInfo->setEglIface(&s_eglIface); char error[256]; // When running on top of another GLES library, our GLES1 // translator uses the GLES library's underlying GLES3. if(!g_eglInfo->getIface(GLES_1_1)) { func = loadIfaces(LIB_GLES_CM_NAME, error, sizeof(error)); if (func) { g_eglInfo->setIface(func(&s_eglIface),GLES_1_1); } else { fprintf(stderr, "%s: Could not find ifaces for GLES CM 1.1 [%s]\n", __FUNCTION__, error); return EGL_FALSE; } initGLESx(GLES_1_1); } if(!g_eglInfo->getIface(GLES_2_0)) { func = loadIfaces(LIB_GLES_V2_NAME, error, sizeof(error)); if (func) { renderableType |= EGL_OPENGL_ES2_BIT; g_eglInfo->setIface(func(&s_eglIface),GLES_2_0); } else { fprintf(stderr, "%s: Could not find ifaces for GLES 2.0 [%s]\n", __FUNCTION__, error); } initGLESx(GLES_2_0); } if(!g_eglInfo->getIface(GLES_3_0)) { func = loadIfaces(LIB_GLES_V2_NAME, error, sizeof(error)); if (func) { renderableType |= EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT; g_eglInfo->setIface(func(&s_eglIface),GLES_3_0); } else { fprintf(stderr, "%s: Could not find ifaces for GLES 3.x [%s]\n", __FUNCTION__, error); } initGLESx(GLES_3_0); } if(!g_eglInfo->getIface(GLES_3_1)) { func = loadIfaces(LIB_GLES_V2_NAME, error, sizeof(error)); if (func) { renderableType |= EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT; g_eglInfo->setIface(func(&s_eglIface),GLES_3_1); } else { fprintf(stderr, "%s: Could not find ifaces for GLES 3.x [%s]\n", __FUNCTION__, error); } initGLESx(GLES_3_1); } dpy->initialize(renderableType); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay display) { VALIDATE_DISPLAY(display); dpy->terminate(); return EGL_TRUE; } EGLAPI const char * EGLAPIENTRY eglQueryString(EGLDisplay display, EGLint name) { VALIDATE_DISPLAY(display); static const char* version = "1.4"; static const char* extensions = "EGL_KHR_image EGL_KHR_image_base " "EGL_KHR_gl_texture_2D_image " "EGL_ANDROID_recordable "; if(!EglValidate::stringName(name)) { RETURN_ERROR(NULL,EGL_BAD_PARAMETER); } switch(name) { case EGL_VENDOR: return dpy->getVendorString(); case EGL_VERSION: return version; case EGL_EXTENSIONS: return extensions; } return NULL; } EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay display, EGLConfig *configs, EGLint config_size, EGLint *num_config) { VALIDATE_DISPLAY(display); if(!num_config) { RETURN_ERROR(EGL_FALSE,EGL_BAD_PARAMETER); } if(configs == NULL) { *num_config = dpy->nConfigs(); } else { *num_config = dpy->getConfigs(configs,config_size); } return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay display, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) { CHOOSE_CONFIG_DLOG("eglChooseConfig: begin. validating arguments..."); VALIDATE_DISPLAY(display); if(!num_config) { CHOOSE_CONFIG_DLOG("num_config is NULL. issue EGL_BAD_PARAMETER"); RETURN_ERROR(EGL_FALSE,EGL_BAD_PARAMETER); } //selection defaults // NOTE: Some variables below are commented out to reduce compiler warnings. // TODO(digit): Look if these variables are really needed or not, and if so // fix the code to do it properly. EGLint surface_type = EGL_WINDOW_BIT; EGLint renderable_type = EGL_OPENGL_ES_BIT; //EGLBoolean bind_to_tex_rgb = EGL_DONT_CARE; //EGLBoolean bind_to_tex_rgba = EGL_DONT_CARE; EGLenum caveat = EGL_DONT_CARE; EGLint config_id = EGL_DONT_CARE; EGLBoolean native_renderable = EGL_DONT_CARE; EGLint native_visual_type = EGL_DONT_CARE; //EGLint max_swap_interval = EGL_DONT_CARE; //EGLint min_swap_interval = EGL_DONT_CARE; EGLint trans_red_val = EGL_DONT_CARE; EGLint trans_green_val = EGL_DONT_CARE; EGLint trans_blue_val = EGL_DONT_CARE; EGLenum transparent_type = EGL_NONE; // EGLint buffer_size = 0; EGLint red_size = 0; EGLint green_size = 0; EGLint blue_size = 0; EGLint alpha_size = 0; EGLint depth_size = 0; EGLint frame_buffer_level = 0; EGLint sample_buffers_num = 0; EGLint samples_per_pixel = 0; EGLint stencil_size = 0; EGLint conformant = 0; EGLBoolean recordable_android = EGL_FALSE; EGLBoolean framebuffer_target_android = EGL_DONT_CARE; EGLint luminance_size = 0; EGLint wanted_buffer_size = EGL_DONT_CARE; std::vector wanted_attribs; if(!EglValidate::noAttribs(attrib_list)) { //there are attribs int i = 0 ; bool hasConfigId = false; while(attrib_list[i] != EGL_NONE && !hasConfigId) { #define CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(attrname) \ CHOOSE_CONFIG_DLOG("EGL_BAD_ATTRIBUTE: " #attrname "defined as 0x%x", attrib_list[i+1]); if (attrib_list[i] != EGL_LEVEL && attrib_list[i] != EGL_MATCH_NATIVE_PIXMAP && attrib_list[i + 1] == EGL_DONT_CARE) { i+=2; continue; } switch(attrib_list[i]) { case EGL_MAX_PBUFFER_WIDTH: case EGL_MAX_PBUFFER_HEIGHT: case EGL_MAX_PBUFFER_PIXELS: case EGL_NATIVE_VISUAL_ID: break; //we dont care from those selection crateria case EGL_LEVEL: if(attrib_list[i+1] == EGL_DONT_CARE) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_LEVEL); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } frame_buffer_level = attrib_list[i+1]; wanted_attribs.push_back(EGL_LEVEL); break; case EGL_BUFFER_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_BUFFER_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_BUFFER_SIZE); wanted_buffer_size = attrib_list[i + 1]; break; case EGL_RED_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_RED_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } red_size = attrib_list[i+1]; wanted_attribs.push_back(EGL_RED_SIZE); break; case EGL_GREEN_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_GREEN_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } green_size = attrib_list[i+1]; wanted_attribs.push_back(EGL_GREEN_SIZE); break; case EGL_BLUE_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_BLUE_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } blue_size = attrib_list[i+1]; wanted_attribs.push_back(EGL_BLUE_SIZE); break; case EGL_LUMINANCE_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_LUMINANCE_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_LUMINANCE_SIZE); luminance_size = attrib_list[i + 1]; break; case EGL_ALPHA_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_ALPHA_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } alpha_size = attrib_list[i+1]; wanted_attribs.push_back(EGL_ALPHA_SIZE); break; case EGL_ALPHA_MASK_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_ALPHA_MASK_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_ALPHA_MASK_SIZE); break; case EGL_BIND_TO_TEXTURE_RGB: if (attrib_list[i+1] != EGL_TRUE && attrib_list[i+1] != EGL_FALSE) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_BIND_TO_TEXTURE_RGB); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_BIND_TO_TEXTURE_RGB); //bind_to_tex_rgb = attrib_list[i+1]; break; case EGL_BIND_TO_TEXTURE_RGBA: if (attrib_list[i+1] != EGL_TRUE && attrib_list[i+1] != EGL_FALSE) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_BIND_TO_TEXTURE_RGBA); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_BIND_TO_TEXTURE_RGBA); //bind_to_tex_rgba = attrib_list[i+1]; break; case EGL_CONFIG_CAVEAT: if(attrib_list[i+1] != EGL_NONE && attrib_list[i+1] != EGL_SLOW_CONFIG && attrib_list[i+1] != EGL_NON_CONFORMANT_CONFIG) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_CONFIG_CAVEAT); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } caveat = attrib_list[i+1]; wanted_attribs.push_back(EGL_CONFIG_CAVEAT); break; case EGL_CONFORMANT: conformant = attrib_list[i+1]; wanted_attribs.push_back(EGL_CONFORMANT); break; case EGL_CONFIG_ID: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_CONFIG_ID); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } config_id = attrib_list[i+1]; hasConfigId = true; wanted_attribs.push_back(EGL_CONFIG_ID); break; case EGL_DEPTH_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_DEPTH_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } depth_size = attrib_list[i+1]; wanted_attribs.push_back(EGL_DEPTH_SIZE); break; case EGL_MAX_SWAP_INTERVAL: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_MAX_SWAP_INTERVAL); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } //max_swap_interval = attrib_list[i+1]; wanted_attribs.push_back(EGL_MAX_SWAP_INTERVAL); break; case EGL_MIN_SWAP_INTERVAL: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_MIN_SWAP_INTERVAL); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } //min_swap_interval = attrib_list[i+1]; wanted_attribs.push_back(EGL_MIN_SWAP_INTERVAL); break; case EGL_NATIVE_RENDERABLE: if (attrib_list[i+1] != EGL_TRUE && attrib_list[i+1] != EGL_FALSE) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_NATIVE_RENDERABLE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } native_renderable = attrib_list[i+1]; wanted_attribs.push_back(EGL_NATIVE_RENDERABLE); break; case EGL_RENDERABLE_TYPE: renderable_type = attrib_list[i+1]; wanted_attribs.push_back(EGL_RENDERABLE_TYPE); break; case EGL_NATIVE_VISUAL_TYPE: native_visual_type = attrib_list[i+1]; break; if(attrib_list[i+1] < 0 || attrib_list[i+1] > 1 ) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_NATIVE_VISUAL_TYPE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_NATIVE_VISUAL_TYPE); case EGL_SAMPLE_BUFFERS: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_SAMPLE_BUFFERS); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } sample_buffers_num = attrib_list[i+1]; wanted_attribs.push_back(EGL_SAMPLE_BUFFERS); break; case EGL_SAMPLES: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_SAMPLES); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } samples_per_pixel = attrib_list[i+1]; wanted_attribs.push_back(EGL_SAMPLES); break; case EGL_STENCIL_SIZE: if(attrib_list[i+1] < 0) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_STENCIL_SIZE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } stencil_size = attrib_list[i+1]; wanted_attribs.push_back(EGL_STENCIL_SIZE); break; case EGL_SURFACE_TYPE: surface_type = attrib_list[i+1]; wanted_attribs.push_back(EGL_SURFACE_TYPE); break; case EGL_TRANSPARENT_TYPE: if(attrib_list[i+1] != EGL_NONE && attrib_list[i+1] != EGL_TRANSPARENT_RGB ) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_TRANSPARENT_TYPE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } transparent_type = attrib_list[i+1]; wanted_attribs.push_back(EGL_TRANSPARENT_TYPE); break; case EGL_TRANSPARENT_RED_VALUE: trans_red_val = attrib_list[i+1]; wanted_attribs.push_back(EGL_TRANSPARENT_RED_VALUE); break; case EGL_TRANSPARENT_GREEN_VALUE: trans_green_val = attrib_list[i+1]; wanted_attribs.push_back(EGL_TRANSPARENT_GREEN_VALUE); break; case EGL_TRANSPARENT_BLUE_VALUE: trans_blue_val = attrib_list[i+1]; wanted_attribs.push_back(EGL_TRANSPARENT_BLUE_VALUE); break; case EGL_COLOR_BUFFER_TYPE: if(attrib_list[i+1] != EGL_RGB_BUFFER && attrib_list[i+1] != EGL_LUMINANCE_BUFFER) { CHOOSE_CONFIG_DLOG_BAD_ATTRIBUTE(EGL_COLOR_BUFFER_TYPE); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } wanted_attribs.push_back(EGL_COLOR_BUFFER_TYPE); break; case EGL_RECORDABLE_ANDROID: recordable_android = attrib_list[i+1]; wanted_attribs.push_back(EGL_RECORDABLE_ANDROID); break; case EGL_FRAMEBUFFER_TARGET_ANDROID: framebuffer_target_android = attrib_list[i+1]; wanted_attribs.push_back(EGL_FRAMEBUFFER_TARGET_ANDROID); break; default: CHOOSE_CONFIG_DLOG("EGL_BAD_ATTRIBUTE: Unknown attribute key 0x%x", attrib_list[i]); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } i+=2; } if(hasConfigId) { EglConfig* pConfig = dpy->getConfig(config_id); if(pConfig) { if(configs) { configs[0] = static_cast(pConfig); } *num_config = 1; CHOOSE_CONFIG_DLOG("Using config id 0x%x. Return EGL_TRUE", config_id); return EGL_TRUE; } else { CHOOSE_CONFIG_DLOG("EGL_BAD_ATTRIBUTE: Using missing config id 0x%x", config_id); RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } } } EglConfig dummy(red_size,green_size,blue_size,alpha_size,caveat,conformant,depth_size, frame_buffer_level,0,0,0,native_renderable,renderable_type,0,native_visual_type, sample_buffers_num, samples_per_pixel,stencil_size,luminance_size,wanted_buffer_size, surface_type,transparent_type,trans_red_val,trans_green_val,trans_blue_val,recordable_android, framebuffer_target_android, NULL); for (size_t i = 0; i < wanted_attribs.size(); i++) { dummy.addWantedAttrib(wanted_attribs[i]); } *num_config = dpy->chooseConfigs(dummy,configs,config_size); CHOOSE_CONFIG_DLOG("eglChooseConfig: Success(EGL_TRUE). Num configs returned:%d", *num_config); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value) { VALIDATE_DISPLAY(display); VALIDATE_CONFIG(config); if(!EglValidate::confAttrib(attribute)){ RETURN_ERROR(EGL_FALSE, EGL_BAD_ATTRIBUTE); } return cfg->getConfAttrib(attribute,value)? EGL_TRUE:EGL_FALSE; } EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list) { VALIDATE_DISPLAY_RETURN(display,EGL_NO_SURFACE); VALIDATE_CONFIG_RETURN(config,EGL_NO_SURFACE); if(!(cfg->surfaceType() & EGL_WINDOW_BIT)) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_MATCH); } if(!dpy->nativeType()->isValidNativeWin(win)) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_NATIVE_WINDOW); } if(!EglValidate::noAttribs(attrib_list)) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ATTRIBUTE); } if(EglWindowSurface::alreadyAssociatedWithConfig(win)) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ALLOC); } android::base::AutoLock mutex(s_eglLock); unsigned int width,height; if(!dpy->nativeType()->checkWindowPixelFormatMatch( win, cfg->nativeFormat(), &width, &height)) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ALLOC); } SurfacePtr wSurface(new EglWindowSurface(dpy, win,cfg,width,height)); if(!wSurface.get()) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ALLOC); } return dpy->addSurface(wSurface); } EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferSurface( EGLDisplay display, EGLConfig config, const EGLint *attrib_list) { VALIDATE_DISPLAY_RETURN(display,EGL_NO_SURFACE); VALIDATE_CONFIG_RETURN(config,EGL_NO_SURFACE); if(!(cfg->surfaceType() & EGL_PBUFFER_BIT)) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_MATCH); } SurfacePtr pbSurface(new EglPbufferSurface(dpy,cfg)); if(!pbSurface.get()) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ALLOC); } if(!EglValidate::noAttribs(attrib_list)) { // There are attribs. int i = 0 ; while(attrib_list[i] != EGL_NONE) { if(!pbSurface->setAttrib(attrib_list[i],attrib_list[i+1])) { RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ATTRIBUTE); } i+=2; } } EGLint width, height, largest, texTarget, texFormat; EglPbufferSurface* tmpPbSurfacePtr = static_cast(pbSurface.get()); tmpPbSurfacePtr->getDim(&width, &height, &largest); tmpPbSurfacePtr->getTexInfo(&texTarget, &texFormat); if(!EglValidate::pbufferAttribs(width, height, texFormat == EGL_NO_TEXTURE, texTarget == EGL_NO_TEXTURE)) { //TODO: RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_VALUE); dont have bad_value RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ATTRIBUTE); } EglOS::PbufferInfo pbinfo; pbinfo.width = width; pbinfo.height = height; pbinfo.largest = largest; pbinfo.target = texTarget; pbinfo.format = texFormat; tmpPbSurfacePtr->getAttrib(EGL_MIPMAP_TEXTURE, &pbinfo.hasMipmap); android::base::AutoLock mutex(s_eglLock); EglOS::Surface* pb = dpy->nativeType()->createPbufferSurface( cfg->nativeFormat(), &pbinfo); if(!pb) { //TODO: RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_VALUE); dont have bad value RETURN_ERROR(EGL_NO_SURFACE,EGL_BAD_ATTRIBUTE); } tmpPbSurfacePtr->setNativePbuffer(pb); return dpy->addSurface(pbSurface); } EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface) { VALIDATE_DISPLAY(display); android::base::AutoLock mutex(s_eglLock); SurfacePtr srfc = dpy->getSurface(surface); if(!srfc.get()) { RETURN_ERROR(EGL_FALSE,EGL_BAD_SURFACE); } g_eglInfo->markSurfaceForDestroy(dpy, surface); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay display, EGLSurface surface, EGLint attribute, EGLint *value) { VALIDATE_DISPLAY(display); VALIDATE_SURFACE(surface,srfc); if(!srfc->getAttrib(attribute,value)) { RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(EGLDisplay display, EGLSurface surface, EGLint attribute, EGLint value) { VALIDATE_DISPLAY(display); VALIDATE_SURFACE(surface,srfc); if(!srfc->setAttrib(attribute,value)) { RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } return EGL_TRUE; } // eglCreateOrLoadContext is the implementation of eglCreateContext and // eglLoadContext. // |stream| is the snapshot file to load from when calling from eglLoadContext // when |stream| is available, config and share group ID will be loaded from stream static EGLContext eglCreateOrLoadContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list, android::base::Stream *stream) { assert(share_context == EGL_NO_CONTEXT || stream == nullptr); VALIDATE_DISPLAY_RETURN(display,EGL_NO_CONTEXT); uint64_t shareGroupId = 0; EglConfig* cfg = nullptr; if (!stream) { cfg = dpy->getConfig(config); if (!cfg) return EGL_NO_CONTEXT; } EGLint major_version = 0; EGLint minor_version = 0; EGLint context_flags = 0; EGLint profile_mask = 0; EGLint reset_notification_strategy = 0; if(!EglValidate::noAttribs(attrib_list)) { int i = 0; while(attrib_list[i] != EGL_NONE) { EGLint attrib_val = attrib_list[i + 1]; switch(attrib_list[i]) { case EGL_CONTEXT_MAJOR_VERSION_KHR: major_version = attrib_val; break; case EGL_CONTEXT_MINOR_VERSION_KHR: minor_version = attrib_val; break; case EGL_CONTEXT_FLAGS_KHR: if ((attrib_val | EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR) || (attrib_val | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) || (attrib_val | EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR)) { context_flags = attrib_val; } else { fprintf(stderr, "%s: wrong context flags, return\n", __func__); RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE); } break; case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR: if ((attrib_val | EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR) || (attrib_val | EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR)) { profile_mask = attrib_val; } else { fprintf(stderr, "%s: wrong profile mask, return\n", __func__); RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE); } break; case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR: switch (attrib_val) { case EGL_NO_RESET_NOTIFICATION_KHR: case EGL_LOSE_CONTEXT_ON_RESET_KHR: break; default: fprintf(stderr, "%s: wrong reset notif strat, return\n", __func__); RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE); } reset_notification_strategy = attrib_val; break; default: fprintf(stderr, "%s: unknown attrib 0x%x\n", __func__, attrib_list[i]); RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE); } i+=2; } } // TODO: Investigate these ignored flags and see which are needed (void)context_flags; (void)reset_notification_strategy; GLESVersion glesVersion; switch (major_version) { case 1: glesVersion = GLES_1_1; break; case 2: glesVersion = GLES_2_0; break; case 3: switch (minor_version) { case 0: glesVersion = GLES_3_0; break; case 1: glesVersion = GLES_3_1; break; default: RETURN_ERROR(EGL_NO_CONTEXT, EGL_BAD_ATTRIBUTE); break; } break; default: RETURN_ERROR(EGL_NO_CONTEXT, EGL_BAD_ATTRIBUTE); break; } const GLESiface* iface = g_eglInfo->getIface(glesVersion); GLEScontext* glesCtx = NULL; if(iface) { glesCtx = iface->createGLESContext(major_version, minor_version, dpy->getGlobalNameSpace(), stream); } else { // there is no interface for this gles version RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE); } if(share_context != EGL_NO_CONTEXT) { ContextPtr sharedCtxPtr = dpy->getContext(share_context); if(!sharedCtxPtr.get()) { RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_CONTEXT); } shareGroupId = sharedCtxPtr->getShareGroup()->getId(); assert(shareGroupId); } android::base::AutoLock mutex(s_eglLock); ContextPtr ctx(new EglContext(dpy, shareGroupId, cfg, glesCtx, glesVersion, profile_mask, dpy->getManager(glesVersion), stream)); if(ctx->nativeType()) { return dpy->addContext(ctx); } else { iface->deleteGLESContext(glesCtx); } return EGL_NO_CONTEXT; } EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list) { return eglCreateOrLoadContext(display, config, share_context, attrib_list, nullptr); } EGLAPI EGLContext EGLAPIENTRY eglLoadContext(EGLDisplay display, const EGLint *attrib_list, android::base::Stream *stream) { return eglCreateOrLoadContext(display, (EGLConfig)0, (EGLContext)0, attrib_list, stream); } EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay display, EGLContext context) { VALIDATE_DISPLAY(display); VALIDATE_CONTEXT(context); android::base::AutoLock mutex(s_eglLock); dpy->removeContext(context); return EGL_TRUE; } static void sGetPbufferSurfaceGLProperties( EglPbufferSurface* surface, EGLint* width, EGLint* height, GLint* multisamples, GLint* colorFormat, GLint* depthStencilFormat) { assert(width); assert(height); assert(multisamples); assert(colorFormat); assert(depthStencilFormat); EGLint r, g, b, a, d, s; surface->getAttrib(EGL_WIDTH, width); surface->getAttrib(EGL_HEIGHT, height); surface->getAttrib(EGL_RED_SIZE, &r); surface->getAttrib(EGL_GREEN_SIZE, &g); surface->getAttrib(EGL_BLUE_SIZE, &b); surface->getAttrib(EGL_ALPHA_SIZE, &a); surface->getAttrib(EGL_DEPTH_SIZE, &d); surface->getAttrib(EGL_STENCIL_SIZE, &s); surface->getAttrib(EGL_SAMPLES, multisamples); // Currently supported: RGBA8888/RGB888/RGB565/RGBA4/RGB5A1 if (r == 8 && g == 8 && b == 8 && a == 8) { *colorFormat = GL_RGBA8; } else if (r == 8 && g == 8 && b == 8 && a == 0) { *colorFormat = GL_RGB8; } else if (r == 5 && g == 6 && b == 5 && a == 0) { *colorFormat = GL_RGB565; } else if (r == 4 && g == 4 && b == 4 && a == 4) { *colorFormat = GL_RGBA4; } else if (r == 5 && g == 5 && b == 5 && a == 1) { *colorFormat = GL_RGB5_A1; } else { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "invalid color format R" << r << "G" << g << "B" << b << "A" << a; } // Blanket provide 24/8 depth/stencil format for now. *depthStencilFormat = GL_DEPTH24_STENCIL8; // TODO: Support more if necessary, or even restrict // EGL configs from host display to only these ones. } EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context) { VALIDATE_DISPLAY(display); bool releaseContext = EglValidate::releaseContext(context, read, draw); if(!releaseContext && EglValidate::badContextMatch(context, read, draw)) { RETURN_ERROR(EGL_FALSE, EGL_BAD_MATCH); } ThreadInfo* thread = getThreadInfo(); ContextPtr prevCtx = thread->eglContext; if(releaseContext) { //releasing current context if(prevCtx.get()) { g_eglInfo->getIface(prevCtx->version())->flush(); if(!dpy->nativeType()->makeCurrent(NULL,NULL,NULL)) { RETURN_ERROR(EGL_FALSE,EGL_BAD_ACCESS); } thread->updateInfo(ContextPtr(),dpy,NULL,ShareGroupPtr(),dpy->getManager(prevCtx->version())); } } else { //assining new context VALIDATE_CONTEXT(context); VALIDATE_SURFACE(draw,newDrawSrfc); VALIDATE_SURFACE(read,newReadSrfc); EglSurface* newDrawPtr = newDrawSrfc.get(); EglSurface* newReadPtr = newReadSrfc.get(); ContextPtr newCtx = ctx; if (newCtx.get() && prevCtx.get()) { if (newCtx.get() == prevCtx.get()) { if (newDrawPtr == prevCtx->draw().get() && newReadPtr == prevCtx->read().get()) { // nothing to do return EGL_TRUE; } } else { // Make sure previous context is detached from surfaces releaseContext = true; } } //surfaces compatibility check if(!((*ctx->getConfig()).compatibleWith((*newDrawPtr->getConfig()))) || !((*ctx->getConfig()).compatibleWith((*newReadPtr->getConfig())))) { RETURN_ERROR(EGL_FALSE,EGL_BAD_MATCH); } EglOS::Display* nativeDisplay = dpy->nativeType(); EglOS::Surface* nativeRead = newReadPtr->native(); EglOS::Surface* nativeDraw = newDrawPtr->native(); //checking native window validity if(newReadPtr->type() == EglSurface::WINDOW && !nativeDisplay->isValidNativeWin(nativeRead)) { RETURN_ERROR(EGL_FALSE,EGL_BAD_NATIVE_WINDOW); } if(newDrawPtr->type() == EglSurface::WINDOW && !nativeDisplay->isValidNativeWin(nativeDraw)) { RETURN_ERROR(EGL_FALSE,EGL_BAD_NATIVE_WINDOW); } if(prevCtx.get()) { g_eglInfo->getIface(prevCtx->version())->flush(); } { android::base::AutoLock mutex(s_eglLock); if (!dpy->nativeType()->makeCurrent( newReadPtr->native(), newDrawPtr->native(), newCtx->nativeType())) { RETURN_ERROR(EGL_FALSE,EGL_BAD_ACCESS); } //TODO: handle the following errors // EGL_BAD_CURRENT_SURFACE , EGL_CONTEXT_LOST , EGL_BAD_ACCESS thread->updateInfo(newCtx,dpy,newCtx->getGlesContext(),newCtx->getShareGroup(),dpy->getManager(newCtx->version())); newCtx->setSurfaces(newReadSrfc,newDrawSrfc); g_eglInfo->getIface(newCtx->version())->initContext(newCtx->getGlesContext(), newCtx->getShareGroup(), dpy->nativeTextureDecompressionEnabled()); g_eglInfo->sweepDestroySurfaces(); } if (newDrawPtr->type() == EglSurface::PBUFFER && newReadPtr->type() == EglSurface::PBUFFER) { EglPbufferSurface* tmpPbSurfacePtr = static_cast(newDrawPtr); EglPbufferSurface* tmpReadPbSurfacePtr = static_cast(newReadPtr); EGLint width, height, readWidth, readHeight; GLint colorFormat, depthStencilFormat, multisamples; GLint readColorFormat, readDepthStencilFormat, readMultisamples; sGetPbufferSurfaceGLProperties( tmpPbSurfacePtr, &width, &height, &multisamples, &colorFormat, &depthStencilFormat); sGetPbufferSurfaceGLProperties( tmpReadPbSurfacePtr, &readWidth, &readHeight, &readMultisamples, &readColorFormat, &readDepthStencilFormat); newCtx->getGlesContext()->initDefaultFBO( width, height, colorFormat, depthStencilFormat, multisamples, &tmpPbSurfacePtr->glRboColor, &tmpPbSurfacePtr->glRboDepth, readWidth, readHeight, readColorFormat, readDepthStencilFormat, readMultisamples, &tmpReadPbSurfacePtr->glRboColor, &tmpReadPbSurfacePtr->glRboDepth); } // Initialize the GLES extension function table used in // eglGetProcAddress for the context's GLES version if not // yet initialized. We initialize it here to make sure we call the // GLES getProcAddress after when a context is bound. g_eglInfo->initClientExtFuncTable(newCtx->version()); } // release previous context surface binding if(prevCtx.get() && releaseContext) { prevCtx->setSurfaces(SurfacePtr(),SurfacePtr()); } return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay display, EGLContext context, EGLint attribute, EGLint *value) { VALIDATE_DISPLAY(display); VALIDATE_CONTEXT(context); if(!ctx->getAttrib(attribute,value)){ RETURN_ERROR(EGL_FALSE,EGL_BAD_ATTRIBUTE); } return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay display, EGLSurface surface) { VALIDATE_DISPLAY(display); VALIDATE_SURFACE(surface,Srfc); ThreadInfo* thread = getThreadInfo(); ContextPtr currentCtx = thread->eglContext; //if surface not window return if(Srfc->type() != EglSurface::WINDOW){ RETURN_ERROR(EGL_TRUE,EGL_SUCCESS); } if(!currentCtx.get() || !currentCtx->usingSurface(Srfc) || !dpy->nativeType()->isValidNativeWin(Srfc.get()->native())) { RETURN_ERROR(EGL_FALSE,EGL_BAD_SURFACE); } dpy->nativeType()->swapBuffers(Srfc->native()); return EGL_TRUE; } EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void) { MEM_TRACE("EMUGL"); android::base::AutoLock mutex(s_eglLock); ThreadInfo* thread = getThreadInfo(); EglDisplay* dpy = static_cast(thread->eglDisplay); ContextPtr ctx = thread->eglContext; if(dpy && ctx.get()){ // This double check is required because a context might still be current after it is destroyed - in which case // its handle should be invalid, that is EGL_NO_CONTEXT should be returned even though the context is current EGLContext c = (EGLContext)SafePointerFromUInt(ctx->getHndl()); if(dpy->getContext(c).get()) { return c; } } return EGL_NO_CONTEXT; } EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw) { MEM_TRACE("EMUGL"); android::base::AutoLock mutex(s_eglLock); if (!EglValidate::surfaceTarget(readdraw)) { return EGL_NO_SURFACE; } ThreadInfo* thread = getThreadInfo(); EglDisplay* dpy = static_cast(thread->eglDisplay); ContextPtr ctx = thread->eglContext; if(dpy && ctx.get()) { SurfacePtr surface = readdraw == EGL_READ ? ctx->read() : ctx->draw(); if(surface.get()) { // This double check is required because a surface might still be // current after it is destroyed - in which case its handle should // be invalid, that is EGL_NO_SURFACE should be returned even // though the surface is current. EGLSurface s = (EGLSurface)SafePointerFromUInt(surface->getHndl()); surface = dpy->getSurface(s); if(surface.get()) { return s; } } } return EGL_NO_SURFACE; } EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void) { MEM_TRACE("EMUGL"); ThreadInfo* thread = getThreadInfo(); return (thread->eglContext.get()) ? thread->eglDisplay : EGL_NO_DISPLAY; } EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api) { MEM_TRACE("EMUGL"); if(!EglValidate::supportedApi(api)) { RETURN_ERROR(EGL_FALSE,EGL_BAD_PARAMETER); } CURRENT_THREAD(); tls_thread->setApi(api); return EGL_TRUE; } EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void) { MEM_TRACE("EMUGL"); CURRENT_THREAD(); return tls_thread->getApi(); } EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread(void) { MEM_TRACE("EMUGL"); ThreadInfo* thread = getThreadInfo(); EglDisplay* dpy = static_cast(thread->eglDisplay); if (!dpy) { return EGL_TRUE; } if (!translator::egl::eglMakeCurrent(dpy,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) { return EGL_FALSE; } return dpy->releaseThread(); } EGLAPI void* EGLAPIENTRY eglGetProcAddress(const char *procname){ __eglMustCastToProperFunctionPointerType retVal = NULL; if(!strncmp(procname,"egl",3)) { //EGL proc for(int i=0;i < s_eglExtensionsSize;i++){ if(strcmp(procname,s_eglExtensions[i].name) == 0){ retVal = s_eglExtensions[i].address; break; } } } else { // Look at the clientAPI (GLES) supported extension // function table. retVal = ClientAPIExts::getProcAddress(procname); } return (void*)retVal; } /************************** KHR IMAGE *************************************************************/ ImagePtr getEGLImage(unsigned int imageId) { ThreadInfo* thread = getThreadInfo(); EglDisplay* dpy = static_cast(thread->eglDisplay); ContextPtr ctx = thread->eglContext; if (ctx.get()) { const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); return dpy->getImage(reinterpret_cast(imageId), iface->restoreTexture); } else { // Maybe this is a native image, so we don't need a current gl context. const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); return dpy->getImage(reinterpret_cast(imageId), iface->restoreTexture); } return nullptr; } EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR(EGLDisplay display, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) { VALIDATE_DISPLAY(display); if (target != EGL_GL_TEXTURE_2D_KHR) { // Create image from underlying and add to registry EGLImage image = dpy->createNativeImage(dpy->getHostDriverDisplay(), 0, target, buffer, attrib_list); if (image == EGL_NO_IMAGE_KHR) { return EGL_NO_IMAGE_KHR; } ImagePtr img( new EglImage() ); img->isNative = true; img->nativeImage = image; img->width = 0; img->height = 0; if (attrib_list) { const EGLint* current = attrib_list; while (EGL_NONE != *current) { switch (*current) { case EGL_WIDTH: img->width = current[1]; break; case EGL_HEIGHT: img->height = current[1]; break; case EGL_LINUX_DRM_FOURCC_EXT: // TODO: Translate drm fourcc to internal format // img->fourcc = current[1]; break; } current += 2; } } get_emugl_vm_operations().setSkipSnapshotSave(true); return dpy->addImageKHR(img); } ThreadInfo* thread = getThreadInfo(); ShareGroupPtr sg = thread->shareGroup; if (sg.get() != NULL) { NamedObjectPtr globalTexObject = sg->getNamedObject(NamedObjectType::TEXTURE, SafeUIntFromPointer(buffer)); if (!globalTexObject) return EGL_NO_IMAGE_KHR; ImagePtr img( new EglImage() ); if (img.get() != NULL) { auto objData = sg->getObjectData( NamedObjectType::TEXTURE, SafeUIntFromPointer(buffer)); if (!objData) return EGL_NO_IMAGE_KHR; TextureData *texData = (TextureData *)objData; if(!texData->width || !texData->height) return EGL_NO_IMAGE_KHR; img->width = texData->width; img->height = texData->height; img->border = texData->border; img->internalFormat = texData->internalFormat; img->globalTexObj = globalTexObject; img->format = texData->format; img->type = texData->type; img->texStorageLevels = texData->texStorageLevels; img->saveableTexture = texData->getSaveableTexture(); img->needRestore = false; img->sync = nullptr; return dpy->addImageKHR(img); } } return EGL_NO_IMAGE_KHR; } EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR(EGLDisplay display, EGLImageKHR image) { VALIDATE_DISPLAY(display); unsigned int imagehndl = SafeUIntFromPointer(image); ImagePtr img = getEGLImage(imagehndl); if (!img) return EGL_FALSE; const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); if (img->sync) { iface->deleteSync((GLsync)img->sync); img->sync = nullptr; } if (img->isNative && !img->isImported) { dpy->destroyNativeImage(dpy->getHostDriverDisplay(), img->nativeImage); } return dpy->destroyImageKHR(image) ? EGL_TRUE:EGL_FALSE; } EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { MEM_TRACE("EMUGL"); // swiftshader_indirect used to have a bug with eglCreateSyncKHR // but it seems to have been fixed now. // BUG: 65587659 // BUG: 246740239 if (!g_eglInfo->isEgl2EglSyncSafeToUse()) { return (EGLSyncKHR)0x42; } const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); GLsync res = iface->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); return (EGLSyncKHR)res; } EGLAPI EGLint EGLAPIENTRY eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { MEM_TRACE("EMUGL"); android::base::AutoLock mutex(s_eglLock); if (!g_eglInfo->isEgl2EglSyncSafeToUse()) { return EGL_CONDITION_SATISFIED_KHR; } const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); GLenum gl_wait_result = iface->clientWaitSync((GLsync)sync, GL_SYNC_FLUSH_COMMANDS_BIT, timeout); EGLint egl_wait_result; switch (gl_wait_result) { case GL_ALREADY_SIGNALED: case GL_CONDITION_SATISFIED: egl_wait_result = EGL_CONDITION_SATISFIED_KHR; break; case GL_TIMEOUT_EXPIRED: egl_wait_result = EGL_TIMEOUT_EXPIRED_KHR; break; case GL_WAIT_FAILED: egl_wait_result = EGL_FALSE; break; default: egl_wait_result = EGL_CONDITION_SATISFIED_KHR; } return egl_wait_result; } EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { MEM_TRACE("EMUGL"); if (!g_eglInfo->isEgl2EglSyncSafeToUse()) { return EGL_TRUE; } const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); iface->deleteSync((GLsync)sync); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttribKHR( EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value) { MEM_TRACE("EMUGL"); if (!g_eglInfo->isEgl2EglSyncSafeToUse()) { switch (attribute) { case EGL_SYNC_TYPE_KHR: *value = EGL_SYNC_FENCE_KHR; break; case EGL_SYNC_CONDITION_KHR: *value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR; break; case EGL_SYNC_STATUS_KHR: { *value = EGL_SIGNALED_KHR; break; default: return EGL_FALSE; } } return EGL_TRUE; } switch (attribute) { // Guest doesn't care about sync type (handled in guest), // but host side might care case EGL_SYNC_TYPE_KHR: *value = EGL_SYNC_FENCE_KHR; break; case EGL_SYNC_CONDITION_KHR: *value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR; break; case EGL_SYNC_STATUS_KHR: { const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); GLint status = -1; iface->getSynciv((GLsync)sync, GL_SYNC_STATUS, sizeof(GLint), nullptr, &status); switch (status) { case GL_UNSIGNALED: *value = EGL_UNSIGNALED_KHR; break; case GL_SIGNALED: *value = EGL_SIGNALED_KHR; break; default: // error, return EGL_FALSE return EGL_FALSE; } break; } default: return EGL_FALSE; } return EGL_TRUE; } EGLAPI EGLint EGLAPIENTRY eglGetMaxGLESVersion(EGLDisplay display) { // 0: es2 1: es3.0 2: es3.1 3: es3.2 VALIDATE_DISPLAY_RETURN(display, 0 /* gles2 */); return (EGLint)dpy->getMaxGlesVersion(); } EGLAPI EGLint EGLAPIENTRY eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { MEM_TRACE("EMUGL"); if (!g_eglInfo->isEgl2EglSyncSafeToUse()) { return EGL_TRUE; } const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); iface->waitSync((GLsync)sync, 0, -1); return EGL_TRUE; } EGLAPI void EGLAPIENTRY eglBlitFromCurrentReadBufferANDROID(EGLDisplay dpy, EGLImageKHR image) { MEM_TRACE("EMUGL"); const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); iface->blitFromCurrentReadBufferANDROID((GLeglImageOES)image); } // Creates a fence checkpoint for operations that have happened to |image|. // Other users of |image| can choose to wait on the resulting return fence so // that operations on |image| occur in the correct order on the GPU. For // example, we might render some objects or upload image data to |image| in // Thread A, and then in Thread B, read the results. It is not guaranteed that // the write operations on |image| have finished on the GPU when we start // reading, so we call eglSetImageFenceANDROID at the end of writing operations // in Thread A, and then wait on the fence in Thread B. EGLAPI void* EGLAPIENTRY eglSetImageFenceANDROID(EGLDisplay dpy, EGLImageKHR image) { MEM_TRACE("EMUGL"); unsigned int imagehndl = SafeUIntFromPointer(image); ImagePtr img = getEGLImage(imagehndl); const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); if (!img) return iface->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); if (img->sync) { iface->deleteSync((GLsync)img->sync); img->sync = nullptr; } GLsync res = iface->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); iface->flush(); img->sync = res; return (void*)res; } EGLAPI void EGLAPIENTRY eglWaitImageFenceANDROID(EGLDisplay dpy, void* fence) { MEM_TRACE("EMUGL"); const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); iface->waitSync((GLsync)fence, 0, -1); } EGLAPI void EGLAPIENTRY eglAddLibrarySearchPathANDROID(const char* path) { MEM_TRACE("EMUGL"); android::base::SharedLibrary::addLibrarySearchPath(path); } EGLAPI EGLBoolean EGLAPIENTRY eglQueryVulkanInteropSupportANDROID(void) { MEM_TRACE("EMUGL"); const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); return iface->vulkanInteropSupported() ? EGL_TRUE : EGL_FALSE; } EGLAPI EGLBoolean EGLAPIENTRY eglSetNativeTextureDecompressionEnabledANDROID(EGLDisplay display, EGLBoolean enabled) { MEM_TRACE("EMUGL"); VALIDATE_DISPLAY_RETURN(display, EGL_FALSE); dpy->setNativeTextureDecompressionEnabled(enabled == EGL_TRUE); return EGL_TRUE; } /*********************************************************************************/ EGLAPI EGLBoolean EGLAPIENTRY eglPreSaveContext(EGLDisplay display, EGLContext contex, EGLStreamKHR stream) { const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); assert(iface->saveTexture); if (!iface || !iface->saveTexture) return EGL_TRUE; VALIDATE_DISPLAY(display); VALIDATE_CONTEXT(contex); ctx->getShareGroup()->preSave(dpy->getGlobalNameSpace()); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglSaveContext(EGLDisplay display, EGLContext contex, EGLStreamKHR stream) { VALIDATE_DISPLAY(display); VALIDATE_CONTEXT(contex); ctx->onSave((android::base::Stream*)stream); return EGL_TRUE; } EGLAPI EGLContext EGLAPIENTRY eglLoadContext(EGLDisplay display, const EGLint *attrib_list, EGLStreamKHR stream) { return eglCreateOrLoadContext(display, (EGLConfig)0, EGL_NO_CONTEXT, attrib_list, (android::base::Stream*)stream); } EGLAPI EGLBoolean EGLAPIENTRY eglPostSaveContext(EGLDisplay display, EGLContext context, EGLStreamKHR stream) { VALIDATE_DISPLAY(display); VALIDATE_CONTEXT(context); ctx->postSave((android::base::Stream*)stream); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglSaveConfig(EGLDisplay display, EGLConfig config, EGLStreamKHR stream) { VALIDATE_DISPLAY(display); VALIDATE_CONFIG(config); android::base::Stream* stm = static_cast(stream); stm->putBe32(cfg->id()); return EGL_TRUE; } EGLAPI EGLConfig EGLAPIENTRY eglLoadConfig(EGLDisplay display, EGLStreamKHR stream) { VALIDATE_DISPLAY(display); android::base::Stream* stm = static_cast(stream); EGLint cfgId = stm->getBe32(); EglConfig* cfg = dpy->getConfig(cfgId); if (!cfg) { fprintf(stderr, "WARNING: EGL config mismatch, fallback to default configs\n"); cfg = dpy->getDefaultConfig(); } return static_cast(cfg); } EGLAPI EGLBoolean EGLAPIENTRY eglSaveAllImages(EGLDisplay display, EGLStreamKHR stream, const void* textureSaver) { const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); assert(iface->saveTexture); if (!iface || !iface->saveTexture) return true; VALIDATE_DISPLAY(display); android::base::Stream* stm = static_cast(stream); iface->preSaveTexture(); dpy->onSaveAllImages( stm, *static_cast(textureSaver), iface->saveTexture, iface->restoreTexture); iface->postSaveTexture(); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglLoadAllImages(EGLDisplay display, EGLStreamKHR stream, const void* textureLoader) { const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); assert(iface->createTexture); if (!iface || !iface->createTexture) return true; VALIDATE_DISPLAY(display); android::base::Stream* stm = static_cast(stream); dpy->onLoadAllImages( stm, *static_cast(textureLoader), iface->createTexture); return EGL_TRUE; } EGLAPI EGLBoolean EGLAPIENTRY eglPostLoadAllImages(EGLDisplay display, EGLStreamKHR stream) { VALIDATE_DISPLAY(display); android::base::Stream* stm = static_cast(stream); dpy->postLoadAllImages(stm); return true; } EGLAPI void EGLAPIENTRY eglUseOsEglApi(EGLBoolean enable, EGLBoolean nullEgl) { MEM_TRACE("EMUGL"); EglGlobalInfo::setEgl2Egl(enable, nullEgl == EGL_TRUE); EglGlobalInfo::setEgl2EglSyncSafeToUse(EGL_TRUE); } EGLAPI void EGLAPIENTRY eglSetMaxGLESVersion(EGLint version) { MEM_TRACE("EMUGL"); // The "version" here follows the convention of eglGetMaxGLESVesion // 0: es2 1: es3.0 2: es3.1 3: es3.2 GLESVersion glesVersion = GLES_2_0; switch (version) { case 0: glesVersion = GLES_2_0; break; case 1: glesVersion = GLES_3_0; break; case 2: case 3: // TODO: GLES 3.2 support? glesVersion = GLES_3_1; break; } if (g_eglInfo->getIface(GLES_1_1)) { g_eglInfo->getIface(GLES_1_1)->setMaxGlesVersion(glesVersion); } g_eglInfo->getIface(GLES_2_0)->setMaxGlesVersion(glesVersion); } EGLAPI void EGLAPIENTRY eglFillUsages(void* usages) { MEM_TRACE("EMUGL"); // TODO: Figure out better usage metrics interface // that doesn't require linking protobuf into Translator // if (g_eglInfo->getIface(GLES_1_1) && // g_eglInfo->getIface(GLES_1_1)->fillGLESUsages) { // g_eglInfo->getIface(GLES_1_1)->fillGLESUsages( // (android_studio::EmulatorGLESUsages*)usages); // } // if (g_eglInfo->getIface(GLES_2_0) && // g_eglInfo->getIface(GLES_2_0)->fillGLESUsages) { // g_eglInfo->getIface(GLES_2_0)->fillGLESUsages( // (android_studio::EmulatorGLESUsages*)usages); // } } EGLAPI EGLDisplay EGLAPIENTRY eglGetNativeDisplayANDROID(EGLDisplay display) { VALIDATE_DISPLAY_RETURN(display, (EGLDisplay)0); return dpy->getHostDriverDisplay(); } EGLAPI EGLContext EGLAPIENTRY eglGetNativeContextANDROID(EGLDisplay display, EGLContext context) { VALIDATE_DISPLAY_RETURN(display, (EGLContext)0); VALIDATE_CONTEXT_RETURN(context, (EGLContext)0); return dpy->getNativeContext(context); } EGLAPI EGLImage EGLAPIENTRY eglGetNativeImageANDROID(EGLDisplay display, EGLImage image) { VALIDATE_DISPLAY_RETURN(display, (EGLImage)0); unsigned int imagehndl = SafeUIntFromPointer(image); ImagePtr img = getEGLImage(imagehndl); if (!img || !img->isNative) return (EGLImage)0; return img->nativeImage; } EGLAPI EGLBoolean EGLAPIENTRY eglSetImageInfoANDROID(EGLDisplay display, EGLImage image, EGLint width, EGLint height, EGLint internalFormat) { VALIDATE_DISPLAY_RETURN(display, EGL_FALSE); unsigned int imagehndl = SafeUIntFromPointer(image); ImagePtr img = getEGLImage(imagehndl); if (!img) { fprintf(stderr, "%s: error: Could not find image %p\n", __func__, image); return EGL_FALSE; } img->width = width; img->height = height; img->internalFormat = internalFormat; img->format = getFormatFromInternalFormat(internalFormat); img->type = getTypeFromInternalFormat(internalFormat); return EGL_TRUE; } EGLImage eglImportImageANDROID(EGLDisplay display, EGLImage nativeImage) { VALIDATE_DISPLAY_RETURN(display, (EGLImage)0); ImagePtr img( new EglImage() ); img->isNative = true; img->isImported = true; img->nativeImage = nativeImage; return dpy->addImageKHR(img); } EGLint eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, const EGLAttrib* attribs) { return EglGlobalInfo::getInstance()->getOsEngine()->eglDebugMessageControlKHR(callback, attribs); } static const GLint kAuxiliaryContextAttribsCompat[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; static const GLint kAuxiliaryContextAttribsCore[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, EGL_NONE }; #define NAMESPACED_EGL(f) translator::egl::f static bool createAndBindAuxiliaryContext(EGLContext* context_out, EGLSurface* surface_out) { // create the context EGLDisplay dpy = NAMESPACED_EGL(eglGetDisplay)(EGL_DEFAULT_DISPLAY); NAMESPACED_EGL(eglBindAPI)(EGL_OPENGL_ES_API); static const GLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; EGLConfig config; int numConfigs; if (!NAMESPACED_EGL(eglChooseConfig)(dpy, configAttribs, &config, 1, &numConfigs) || numConfigs == 0) { fprintf(stderr, "%s: could not find gles 2 config!\n", __func__); return false; } static const EGLint pbufAttribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; EGLSurface surface = eglCreatePbufferSurface(dpy, config, pbufAttribs); if (!surface) { fprintf(stderr, "%s: could not create surface\n", __func__); return false; } EGLContext context = NAMESPACED_EGL(eglCreateContext)(dpy, config, EGL_NO_CONTEXT, isCoreProfile() ? kAuxiliaryContextAttribsCore : kAuxiliaryContextAttribsCompat); if (!NAMESPACED_EGL(eglMakeCurrent)(dpy, surface, surface, context)) { fprintf(stderr, "%s: eglMakeCurrent failed\n", __func__); return false; } if (context_out) *context_out = context; if (surface_out) *surface_out = surface; return true; } static bool unbindAndDestroyAuxiliaryContext(EGLContext context, EGLSurface surface) { // create the context EGLDisplay dpy = NAMESPACED_EGL(eglGetDisplay)(EGL_DEFAULT_DISPLAY); if (!NAMESPACED_EGL(eglMakeCurrent)( dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { fprintf(stderr, "%s: failure to unbind current context!\n", __func__); return false; } if (!eglDestroySurface(dpy, surface)) { fprintf(stderr, "%s: failure to destroy surface!\n", __func__); return false; } if (!eglDestroyContext(dpy, context)) { fprintf(stderr, "%s: failure to destroy context!\n", __func__); return false; } return true; } static bool bindAuxiliaryContext(EGLContext context, EGLSurface surface) { // create the context EGLDisplay dpy = NAMESPACED_EGL(eglGetDisplay)(EGL_DEFAULT_DISPLAY); if (!eglMakeCurrent(dpy, surface, surface, context)) { fprintf(stderr, "%s: eglMakeCurrent failed\n", __func__); return false; } return true; } static bool unbindAuxiliaryContext() { // create the context EGLDisplay dpy = NAMESPACED_EGL(eglGetDisplay)(EGL_DEFAULT_DISPLAY); if (!eglMakeCurrent( dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { fprintf(stderr, "%s: failure to unbind current context!\n", __func__); return false; } return true; } EGLAPI EGLint EGLAPIENTRY eglGetError(void) { MEM_TRACE("EMUGL"); CURRENT_THREAD(); EGLint err = tls_thread->getError(); tls_thread->setError(EGL_SUCCESS); return err; } } // namespace translator } // namespace egl