| /* |
| * 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 <GLcommon/GLESmacros.h> |
| #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 <EGL/egl.h> |
| #include <EGL/eglext.h> |
| |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #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 eglSetProgramBinaryLinkStatusEnabledANDROID(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 }, |
| {"eglSetProgramBinaryLinkStatusEnabledANDROID", |
| (__eglMustCastToProperFunctionPointerType)eglSetProgramBinaryLinkStatusEnabledANDROID }, |
| }; |
| |
| 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 alpha_mask_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<EGLint> 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); |
| } |
| alpha_mask_size = attrib_list[i+1]; |
| 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<EGLConfig>(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,alpha_mask_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<EglPbufferSurface*>(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(), |
| dpy->programBinaryLinkStatusEnabled()); |
| g_eglInfo->sweepDestroySurfaces(); |
| } |
| |
| if (newDrawPtr->type() == EglSurface::PBUFFER && |
| newReadPtr->type() == EglSurface::PBUFFER) { |
| |
| EglPbufferSurface* tmpPbSurfacePtr = |
| static_cast<EglPbufferSurface*>(newDrawPtr); |
| EglPbufferSurface* tmpReadPbSurfacePtr = |
| static_cast<EglPbufferSurface*>(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<EglDisplay*>(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<EglDisplay*>(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<EglDisplay*>(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<EglDisplay*>(thread->eglDisplay); |
| ContextPtr ctx = thread->eglContext; |
| if (ctx.get()) { |
| const GLESiface* iface = g_eglInfo->getIface(GLES_2_0); |
| return dpy->getImage(reinterpret_cast<EGLImageKHR>(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<EGLImageKHR>(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 eglSetProgramBinaryLinkStatusEnabledANDROID(EGLDisplay display, EGLBoolean enabled) { |
| MEM_TRACE("EMUGL"); |
| VALIDATE_DISPLAY_RETURN(display, EGL_FALSE); |
| dpy->setProgramBinaryLinkStatusEnabled(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<android::base::Stream*>(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<android::base::Stream*>(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<EGLConfig>(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<android::base::Stream*>(stream); |
| iface->preSaveTexture(); |
| dpy->onSaveAllImages( |
| stm, |
| *static_cast<const android::snapshot::ITextureSaverPtr*>(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<android::base::Stream*>(stream); |
| dpy->onLoadAllImages( |
| stm, |
| *static_cast<const android::snapshot::ITextureLoaderPtr*>(textureLoader), |
| iface->createTexture); |
| return EGL_TRUE; |
| } |
| |
| EGLAPI EGLBoolean EGLAPIENTRY eglPostLoadAllImages(EGLDisplay display, EGLStreamKHR stream) { |
| VALIDATE_DISPLAY(display); |
| android::base::Stream* stm = static_cast<android::base::Stream*>(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 |