// // Copyright 2020 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // egl_stubs.cpp: Stubs for EGL entry points. // #include "libGLESv2/egl_stubs_autogen.h" #include "common/angle_version_info.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/EGLSync.h" #include "libANGLE/Surface.h" #include "libANGLE/Thread.h" #include "libANGLE/capture/capture_egl.h" #include "libANGLE/capture/frame_capture_utils_autogen.h" #include "libANGLE/capture/gl_enum_utils_autogen.h" #include "libANGLE/queryutils.h" #include "libANGLE/validationEGL.h" #include "libGLESv2/global_state.h" #include "libGLESv2/proc_table_egl.h" namespace egl { namespace { bool CompareProc(const ProcEntry &a, const char *b) { return strcmp(a.first, b) < 0; } void ClipConfigs(const std::vector &filteredConfigs, EGLConfig *outputConfigs, EGLint configSize, EGLint *numConfigs) { EGLint resultSize = static_cast(filteredConfigs.size()); if (outputConfigs) { resultSize = std::max(std::min(resultSize, configSize), 0); for (EGLint i = 0; i < resultSize; i++) { outputConfigs[i] = const_cast(filteredConfigs[i]); } } *numConfigs = resultSize; } } // anonymous namespace EGLBoolean BindAPI(Thread *thread, EGLenum api) { thread->setAPI(api); thread->setSuccess(); return EGL_TRUE; } EGLBoolean BindTexImage(Thread *thread, Display *display, Surface *eglSurface, EGLint buffer) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglBindTexImage", GetDisplayIfValid(display), EGL_FALSE); gl::Context *context = thread->getContext(); if (context && !context->isContextLost()) { gl::TextureType type = egl_gl::EGLTextureTargetToTextureType(eglSurface->getTextureTarget()); gl::Texture *textureObject = context->getTextureByType(type); ANGLE_EGL_TRY_RETURN(thread, eglSurface->bindTexImage(context, textureObject, buffer), "eglBindTexImage", GetSurfaceIfValid(display, eglSurface), EGL_FALSE); ANGLE_CAPTURE_EGL(EGLBindTexImage, thread, eglSurface, buffer); } thread->setSuccess(); return EGL_TRUE; } EGLBoolean ChooseConfig(Thread *thread, Display *display, const AttributeMap &attribMap, EGLConfig *configs, EGLint config_size, EGLint *num_config) { ClipConfigs(display->chooseConfig(attribMap), configs, config_size, num_config); thread->setSuccess(); return EGL_TRUE; } EGLint ClientWaitSync(Thread *thread, Display *display, Sync *syncObject, EGLint flags, EGLTime timeout) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglClientWaitSync", GetDisplayIfValid(display), EGL_FALSE); gl::Context *currentContext = thread->getContext(); EGLint syncStatus = EGL_FALSE; ANGLE_EGL_TRY_RETURN( thread, syncObject->clientWait(display, currentContext, flags, timeout, &syncStatus), "eglClientWaitSync", GetSyncIfValid(display, syncObject), EGL_FALSE); thread->setSuccess(); return syncStatus; } EGLBoolean CopyBuffers(Thread *thread, Display *display, Surface *eglSurface, EGLNativePixmapType target) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCopyBuffers", GetDisplayIfValid(display), EGL_FALSE); UNIMPLEMENTED(); // FIXME thread->setSuccess(); return 0; } EGLContext CreateContext(Thread *thread, Display *display, Config *configuration, gl::Context *sharedGLContext, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateContext", GetDisplayIfValid(display), EGL_NO_CONTEXT); gl::Context *context = nullptr; ANGLE_EGL_TRY_RETURN(thread, display->createContext(configuration, sharedGLContext, thread->getAPI(), attributes, &context), "eglCreateContext", GetDisplayIfValid(display), EGL_NO_CONTEXT); thread->setSuccess(); return static_cast(context); } EGLImage CreateImage(Thread *thread, Display *display, gl::Context *context, EGLenum target, EGLClientBuffer buffer, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateImage", GetDisplayIfValid(display), EGL_FALSE); Image *image = nullptr; Error error = display->createImage(context, target, buffer, attributes, &image); if (error.isError()) { thread->setError(error, "eglCreateImage", GetDisplayIfValid(display)); return EGL_NO_IMAGE; } ANGLE_CAPTURE_EGL(EGLCreateImage, thread, context, target, buffer, attributes, image); thread->setSuccess(); return static_cast(image); } EGLSurface CreatePbufferFromClientBuffer(Thread *thread, Display *display, EGLenum buftype, EGLClientBuffer buffer, Config *configuration, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePbufferFromClientBuffer", GetDisplayIfValid(display), EGL_NO_SURFACE); Surface *surface = nullptr; ANGLE_EGL_TRY_RETURN(thread, display->createPbufferFromClientBuffer(configuration, buftype, buffer, attributes, &surface), "eglCreatePbufferFromClientBuffer", GetDisplayIfValid(display), EGL_NO_SURFACE); return static_cast(surface); } EGLSurface CreatePbufferSurface(Thread *thread, Display *display, Config *configuration, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePbufferSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); Surface *surface = nullptr; ANGLE_EGL_TRY_RETURN(thread, display->createPbufferSurface(configuration, attributes, &surface), "eglCreatePbufferSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); ANGLE_CAPTURE_EGL(EGLCreatePbufferSurface, thread, attributes, surface); return static_cast(surface); } EGLSurface CreatePixmapSurface(Thread *thread, Display *display, Config *configuration, EGLNativePixmapType pixmap, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); Surface *surface = nullptr; ANGLE_EGL_TRY_RETURN(thread, display->createPixmapSurface(configuration, pixmap, attributes, &surface), "eglCreatePixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); thread->setSuccess(); return static_cast(surface); } EGLSurface CreatePlatformPixmapSurface(Thread *thread, Display *display, Config *configuration, void *pixmap, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePlatformPixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); Surface *surface = nullptr; EGLNativePixmapType nativePixmap = reinterpret_cast(pixmap); ANGLE_EGL_TRY_RETURN( thread, display->createPixmapSurface(configuration, nativePixmap, attributes, &surface), "eglCreatePlatformPixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); thread->setSuccess(); return static_cast(surface); } EGLSurface CreatePlatformWindowSurface(Thread *thread, Display *display, Config *configuration, void *win, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePlatformWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); Surface *surface = nullptr; EGLNativeWindowType nativeWindow = reinterpret_cast(win); ANGLE_EGL_TRY_RETURN( thread, display->createWindowSurface(configuration, nativeWindow, attributes, &surface), "eglPlatformCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); return static_cast(surface); } EGLSync CreateSync(Thread *thread, Display *display, EGLenum type, const AttributeMap &attributes) { gl::Context *currentContext = thread->getContext(); ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateSync", GetDisplayIfValid(display), EGL_FALSE); Sync *syncObject = nullptr; ANGLE_EGL_TRY_RETURN(thread, display->createSync(currentContext, type, attributes, &syncObject), "eglCreateSync", GetDisplayIfValid(display), EGL_NO_SYNC); thread->setSuccess(); return static_cast(syncObject); } EGLSurface CreateWindowSurface(Thread *thread, Display *display, Config *configuration, EGLNativeWindowType win, const AttributeMap &attributes) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); Surface *surface = nullptr; ANGLE_EGL_TRY_RETURN(thread, display->createWindowSurface(configuration, win, attributes, &surface), "eglCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE); return static_cast(surface); } EGLBoolean DestroyContext(Thread *thread, Display *display, gl::Context *context) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroyContext", GetDisplayIfValid(display), EGL_FALSE); ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread); ANGLE_EGL_TRY_RETURN(thread, display->destroyContext(thread, context), "eglDestroyContext", GetContextIfValid(display, context), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean DestroyImage(Thread *thread, Display *display, Image *img) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroyImage", GetDisplayIfValid(display), EGL_FALSE); display->destroyImage(img); ANGLE_CAPTURE_EGL(EGLDestroyImage, thread, display, img); thread->setSuccess(); return EGL_TRUE; } EGLBoolean DestroySurface(Thread *thread, Display *display, Surface *eglSurface) { ANGLE_CAPTURE_EGL(EGLDestroySurface, thread, display, eglSurface); ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroySurface", GetDisplayIfValid(display), EGL_FALSE); ANGLE_EGL_TRY_RETURN(thread, display->destroySurface(eglSurface), "eglDestroySurface", GetSurfaceIfValid(display, eglSurface), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean DestroySync(Thread *thread, Display *display, Sync *syncObject) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroySync", GetDisplayIfValid(display), EGL_FALSE); display->destroySync(syncObject); thread->setSuccess(); return EGL_TRUE; } EGLBoolean GetConfigAttrib(Thread *thread, Display *display, Config *configuration, EGLint attribute, EGLint *value) { QueryConfigAttrib(configuration, attribute, value); thread->setSuccess(); return EGL_TRUE; } EGLBoolean GetConfigs(Thread *thread, Display *display, EGLConfig *configs, EGLint config_size, EGLint *num_config) { ClipConfigs(display->getConfigs(AttributeMap()), configs, config_size, num_config); thread->setSuccess(); return EGL_TRUE; } EGLContext GetCurrentContext(Thread *thread) { gl::Context *context = thread->getContext(); thread->setSuccess(); return static_cast(context); } EGLDisplay GetCurrentDisplay(Thread *thread) { thread->setSuccess(); if (thread->getContext() != nullptr) { return thread->getContext()->getDisplay(); } return EGL_NO_DISPLAY; } EGLSurface GetCurrentSurface(Thread *thread, EGLint readdraw) { if (readdraw == EGL_READ) { thread->setSuccess(); return thread->getCurrentReadSurface(); } else if (readdraw == EGL_DRAW) { thread->setSuccess(); return thread->getCurrentDrawSurface(); } else { thread->setError(EglBadParameter(), "eglGetCurrentSurface", nullptr); return EGL_NO_SURFACE; } } EGLDisplay GetDisplay(Thread *thread, EGLNativeDisplayType display_id) { return Display::GetDisplayFromNativeDisplay(EGL_PLATFORM_ANGLE_ANGLE, display_id, AttributeMap()); } EGLint GetError(Thread *thread) { EGLint error = thread->getError(); thread->setSuccess(); return error; } EGLDisplay GetPlatformDisplay(Thread *thread, EGLenum platform, void *native_display, const AttributeMap &attribMap) { switch (platform) { case EGL_PLATFORM_ANGLE_ANGLE: case EGL_PLATFORM_GBM_KHR: case EGL_PLATFORM_WAYLAND_EXT: { return Display::GetDisplayFromNativeDisplay( platform, gl::bitCast(native_display), attribMap); } case EGL_PLATFORM_DEVICE_EXT: { Device *eglDevice = static_cast(native_display); return Display::GetDisplayFromDevice(eglDevice, attribMap); } default: { UNREACHABLE(); return EGL_NO_DISPLAY; } } } __eglMustCastToProperFunctionPointerType GetProcAddress(Thread *thread, const char *procname) { const ProcEntry *entry = std::lower_bound(&g_procTable[0], &g_procTable[g_numProcs], procname, CompareProc); thread->setSuccess(); if (entry == &g_procTable[g_numProcs] || strcmp(entry->first, procname) != 0) { return nullptr; } return entry->second; } EGLBoolean GetSyncAttrib(Thread *thread, Display *display, Sync *syncObject, EGLint attribute, EGLAttrib *value) { EGLint valueExt; ANGLE_EGL_TRY_RETURN(thread, GetSyncAttrib(display, syncObject, attribute, &valueExt), "eglGetSyncAttrib", GetSyncIfValid(display, syncObject), EGL_FALSE); *value = valueExt; thread->setSuccess(); return EGL_TRUE; } EGLBoolean Initialize(Thread *thread, Display *display, EGLint *major, EGLint *minor) { ANGLE_EGL_TRY_RETURN(thread, display->initialize(), "eglInitialize", GetDisplayIfValid(display), EGL_FALSE); if (major) { *major = kEglMajorVersion; } if (minor) { *minor = kEglMinorVersion; } thread->setSuccess(); return EGL_TRUE; } EGLBoolean MakeCurrent(Thread *thread, Display *display, Surface *drawSurface, Surface *readSurface, gl::Context *context) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglMakeCurrent", GetDisplayIfValid(display), EGL_FALSE); ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread); Surface *previousDraw = thread->getCurrentDrawSurface(); Surface *previousRead = thread->getCurrentReadSurface(); gl::Context *previousContext = thread->getContext(); // Only call makeCurrent if the context or surfaces have changed. if (previousDraw != drawSurface || previousRead != readSurface || previousContext != context) { ANGLE_EGL_TRY_RETURN( thread, display->makeCurrent(thread, previousContext, drawSurface, readSurface, context), "eglMakeCurrent", GetContextIfValid(display, context), EGL_FALSE); ANGLE_CAPTURE_EGL(EGLMakeCurrent, thread, drawSurface, readSurface, context); } thread->setSuccess(); return EGL_TRUE; } EGLenum QueryAPI(Thread *thread) { EGLenum API = thread->getAPI(); thread->setSuccess(); return API; } EGLBoolean QueryContext(Thread *thread, Display *display, gl::Context *context, EGLint attribute, EGLint *value) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglQueryContext", GetDisplayIfValid(display), EGL_FALSE); QueryContextAttrib(context, attribute, value); thread->setSuccess(); return EGL_TRUE; } const char *QueryString(Thread *thread, Display *display, EGLint name) { if (display) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglQueryString", GetDisplayIfValid(display), nullptr); } const char *result = nullptr; switch (name) { case EGL_CLIENT_APIS: result = display->getClientAPIString().c_str(); break; case EGL_EXTENSIONS: if (display == EGL_NO_DISPLAY) { result = Display::GetClientExtensionString().c_str(); } else { result = display->getExtensionString().c_str(); } break; case EGL_VENDOR: result = display->getVendorString().c_str(); break; case EGL_VERSION: { static const char *sVersionString = MakeStaticString(std::string("1.5 (ANGLE ") + angle::GetANGLEVersionString() + ")"); result = sVersionString; break; } default: UNREACHABLE(); break; } thread->setSuccess(); return result; } EGLBoolean QuerySurface(Thread *thread, Display *display, Surface *eglSurface, EGLint attribute, EGLint *value) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglQuerySurface", GetDisplayIfValid(display), EGL_FALSE); ANGLE_EGL_TRY_RETURN( thread, QuerySurfaceAttrib(display, thread->getContext(), eglSurface, attribute, value), "eglQuerySurface", GetSurfaceIfValid(display, eglSurface), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean ReleaseTexImage(Thread *thread, Display *display, Surface *eglSurface, EGLint buffer) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglReleaseTexImage", GetDisplayIfValid(display), EGL_FALSE); gl::Context *context = thread->getContext(); if (context && !context->isContextLost()) { gl::Texture *texture = eglSurface->getBoundTexture(); if (texture) { ANGLE_EGL_TRY_RETURN(thread, eglSurface->releaseTexImage(thread->getContext(), buffer), "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface), EGL_FALSE); ANGLE_CAPTURE_EGL(EGLReleaseTexImage, thread, eglSurface, buffer); } } thread->setSuccess(); return EGL_TRUE; } EGLBoolean ReleaseThread(Thread *thread) { ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread); Surface *previousDraw = thread->getCurrentDrawSurface(); Surface *previousRead = thread->getCurrentReadSurface(); gl::Context *previousContext = thread->getContext(); Display *previousDisplay = thread->getDisplay(); if (previousDisplay != EGL_NO_DISPLAY) { ANGLE_EGL_TRY_RETURN(thread, previousDisplay->prepareForCall(), "eglReleaseThread", GetDisplayIfValid(previousDisplay), EGL_FALSE); // Only call makeCurrent if the context or surfaces have changed. if (previousDraw != EGL_NO_SURFACE || previousRead != EGL_NO_SURFACE || previousContext != EGL_NO_CONTEXT) { ANGLE_EGL_TRY_RETURN( thread, previousDisplay->makeCurrent(thread, previousContext, nullptr, nullptr, nullptr), "eglReleaseThread", nullptr, EGL_FALSE); } ANGLE_EGL_TRY_RETURN(thread, previousDisplay->releaseThread(), "eglReleaseThread", GetDisplayIfValid(previousDisplay), EGL_FALSE); } thread->setSuccess(); return EGL_TRUE; } EGLBoolean SurfaceAttrib(Thread *thread, Display *display, Surface *eglSurface, EGLint attribute, EGLint value) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglSurfaceAttrib", GetDisplayIfValid(display), EGL_FALSE); ANGLE_EGL_TRY_RETURN(thread, SetSurfaceAttrib(eglSurface, attribute, value), "eglSurfaceAttrib", GetDisplayIfValid(display), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean SwapBuffers(Thread *thread, Display *display, Surface *eglSurface) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglSwapBuffers", GetDisplayIfValid(display), EGL_FALSE); ANGLE_EGL_TRY_RETURN(thread, eglSurface->swap(thread->getContext()), "eglSwapBuffers", GetSurfaceIfValid(display, eglSurface), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean SwapInterval(Thread *thread, Display *display, EGLint interval) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglSwapInterval", GetDisplayIfValid(display), EGL_FALSE); Surface *drawSurface = static_cast(thread->getCurrentDrawSurface()); const Config *surfaceConfig = drawSurface->getConfig(); EGLint clampedInterval = std::min(std::max(interval, surfaceConfig->minSwapInterval), surfaceConfig->maxSwapInterval); drawSurface->setSwapInterval(clampedInterval); thread->setSuccess(); return EGL_TRUE; } EGLBoolean Terminate(Thread *thread, Display *display) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglTerminate", GetDisplayIfValid(display), EGL_FALSE); ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread); ANGLE_EGL_TRY_RETURN(thread, display->terminate(thread, Display::TerminateReason::Api), "eglTerminate", GetDisplayIfValid(display), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean WaitClient(Thread *thread) { Display *display = thread->getDisplay(); if (display == nullptr) { // EGL spec says this about eglWaitClient - // If there is no current context for the current rendering API, // the function has no effect but still returns EGL_TRUE. return EGL_TRUE; } gl::Context *context = thread->getContext(); ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitClient", GetDisplayIfValid(display), EGL_FALSE); ANGLE_EGL_TRY_RETURN(thread, display->waitClient(context), "eglWaitClient", GetContextIfValid(display, context), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean WaitGL(Thread *thread) { Display *display = thread->getDisplay(); if (display == nullptr) { // EGL spec says this about eglWaitGL - // eglWaitGL is ignored if there is no current EGL rendering context for OpenGL ES. return EGL_TRUE; } ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitGL", GetDisplayIfValid(display), EGL_FALSE); // eglWaitGL like calling eglWaitClient with the OpenGL ES API bound. Since we only implement // OpenGL ES we can do the call directly. ANGLE_EGL_TRY_RETURN(thread, display->waitClient(thread->getContext()), "eglWaitGL", GetDisplayIfValid(display), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean WaitNative(Thread *thread, EGLint engine) { Display *display = thread->getDisplay(); if (display == nullptr) { // EGL spec says this about eglWaitNative - // eglWaitNative is ignored if there is no current EGL rendering context. return EGL_TRUE; } ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitNative", GetDisplayIfValid(display), EGL_FALSE); ANGLE_EGL_TRY_RETURN(thread, display->waitNative(thread->getContext(), engine), "eglWaitNative", GetThreadIfValid(thread), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } EGLBoolean WaitSync(Thread *thread, Display *display, Sync *syncObject, EGLint flags) { ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitSync", GetDisplayIfValid(display), EGL_FALSE); gl::Context *currentContext = thread->getContext(); ANGLE_EGL_TRY_RETURN(thread, syncObject->serverWait(display, currentContext, flags), "eglWaitSync", GetSyncIfValid(display, syncObject), EGL_FALSE); thread->setSuccess(); return EGL_TRUE; } } // namespace egl