What is OpenGL?
Open Graphics Library (OpenGL) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics. The API is typically used to interact with a graphics processing unit (GPU), to achieve hardware-accelerated rendering.
OpenGL is a set of interfaces independent of programming language and platform, mainly for rendering 2D and 3D graphics. Generally, this set of interfaces is used to interact with GPU, and GPU is used for rendering hardware acceleration. To put it bluntly, OpenGL is a group of function names, similar to the interface in java, which can not be used directly.
What is OpenGL ES?
Android includes support for high performance 2D and 3D graphics with the Open Graphics Library (OpenGL®), specifically, the OpenGL ES API. OpenGL is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is a flavor of the OpenGL specification intended for embedded devices.
OpenGL ES is designed for embedded devices. Of course, mobile phones are also embedded devices. Then there must be some differences in the function interfaces between OpenGL ES and OpenGL, because there is still a gap in the hardware processing capacity between embedded devices and PCs, otherwise the mobile phone will get stuck.
Since OpenGL ES is just a set of function interfaces, how to use it? We must first implement these function interfaces, and android provides two types of implementations: software implementation and hardware implementation.
a. Hardware implementation. The function interfaces mentioned above are mainly used to deal with GPU hardware. Therefore, various hardware manufacturers will provide relevant implementations, such as the adobe solution of Qualcomm platform;
b. For software implementation, android also provides a set of software implementation of OpenGL ES, that is, without GPU, it completely uses software to realize the related functions of drawing, that is, libagl. The code is in frameworks\native\opengl\libagl and its makefile,
//The software implementation is finally compiled and saved in system\lib\egl\libGLES_android.so LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl LOCAL_MODULE:= libGLES_android
So far, there has been a specific implementation of OpenGL ES, but due to the platform independence of its implementation, it can not be used on android, and EGL must be used.
EGL
EGL - Native Platform Interface EGL? is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system. It handles graphics context management, surface/buffer binding, and rendering synchronization and enables high-performance, accelerated, mixed-mode 2D and 3D rendering using other Khronos APIs.
So what is EGL? EGL is the interface between OpenGL ES and the underlying native window system.
EGL is a complement to OpenGL ES. EGL is used for getting surfaces to render to using functions like eglCreateWindowSurface, and you can then draw to that surface with OpenGL ES. Its role is similar to GLX/WGL/CGL.
Whether or not EGL can give you a context that supports OpenGL ES 2.0 may vary by platform, but if the Android device supports ES 2.0 and EGL, you should be able to get such a context from EGL. Take a look at the EGL_RENDERABLE_TYPE attribute and the EGL_OPENGL_ES2_BIT when requesting an EGLConfig.
http://www.khronos.org/files/egl-1-4-quick-reference-card.pdf
On Android, EGL improves OpenGL ES. The EGL function similar to eglCreateWindowSurface can be used to create a surface for render ing. With this surface, you can use the OpenGL ES function to draw pictures on this surface.
example
Let's take a look at a practical example. Generally, when egl and OpenGL ES are used, they will first use egl function (beginning with egl) to create opengl local environment, and then use opengl function (beginning with gl) to draw pictures.
The following is the implementation of boot animation. First, create a local environment,
status_t BootAnimation::readyToRun() { // Create SurfaceControl // create the native surface sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"), dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); SurfaceComposerClient::openGlobalTransaction(); // Set layerstack control->setLayer(0x40000000); SurfaceComposerClient::closeGlobalTransaction(); //Get Surface sp<Surface> s = control->getSurface(); // initialize opengl and egl const EGLint attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE }; EGLint w, h, dummy; EGLint numConfigs; EGLConfig config; EGLSurface surface; EGLContext context; //Call eglGetDisplay EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); eglChooseConfig(display, attribs, &config, 1, &numConfigs); //Call eglCreateWindowSurface to convert the Surface s into a local window, surface = eglCreateWindowSurface(display, config, s.get(), NULL); context = eglCreateContext(display, config, NULL, NULL); eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); //The surface generated after eglMakeCurrent can be drawn using opengl if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) return NO_INIT; return NO_ERROR; }
egl create the environment, call gl related commands to draw, note that eglSwapBuffers(mDisplay, mSurface) function is a very important function, it will trigger queueBuffer and dequeueBuffer, and the picture will be painted one by one.
bool BootAnimation::android() { initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png"); // clear screen glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); //Calling eglSwapBuffers will trigger queuebuffer and dequeuebuffer, //The queuebuffer gives the drawn buffer to the surface linker for processing, //dequeuebuffer creates a new buffer for drawing eglSwapBuffers(mDisplay, mSurface); glEnable(GL_TEXTURE_2D); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); const GLint xc = (mWidth - mAndroid[0].w) / 2; const GLint yc = (mHeight - mAndroid[0].h) / 2; const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), updateRect.height()); // Blend state glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); const nsecs_t startTime = systemTime(); do { nsecs_t now = systemTime(); double time = now - startTime; float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w; GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w; GLint x = xc - offset; glDisable(GL_SCISSOR_TEST); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h); glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h); glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); if (res == EGL_FALSE) break; // 12fps: don't animate too fast to preserve CPU const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); if (sleepTime > 0) usleep(sleepTime); checkExit(); } while (!exitPending()); glDeleteTextures(1, &mAndroid[0].name); glDeleteTextures(1, &mAndroid[1].name); return false; }
EGL loading OpenGL ES Library
From the above example, we found that after calling the egl function, we can directly call the gl function to draw a picture. Why? Have the opengl related libraries been loaded during the egl * function call?
Libraries involved
Firstly, due to the large number of libraries involved, list them first (for Qualcomm platform, only the first four are native),
//It is an egl Library in android, which is used to load specific implementations system\lib\libEGL.so //wrapper implemented in opengl system\lib\libGLESv1_CM.so system\lib\libGLESv2.so //opengl software implementation, namely agl system\lib\egl\libGLES_android.so //Implementation of egl system\vendor\lib\egl\libEGL_adreno.so //Hardware implementation of opengl system\vendor\lib\egl\libGLESv1_CM_adreno.so system\vendor\lib\egl\libGLESv2_adreno.so
Library loading
Continue to take bootanimation as an example. In the makefile of bootanimation, the libEGL library is used,
// frameworks\base\cmds\bootanimation\Android.mk LOCAL_SHARED_LIBRARIES := \ libcutils \ liblog \ libandroidfw \ libutils \ libbinder \ libui \ libskia \ libEGL \ libGLESv1_CM \ libgui LOCAL_MODULE:= bootanimation
In frameworks \ native \ opengl \ LIBS \ Android In MK, libegl is defined So, no special storage path is specified in the makefile, so the final generated library is saved in system \ lib \ libegl So, this library is used to load specific EGL and opengl implementations. It acts as a bridge and needs to be separated from the reservoir area implemented by EGL.
//frameworks\native\opengl\libs\Android.mk include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ EGL/egl_tls.cpp \ EGL/egl_cache.cpp \ EGL/egl_display.cpp \ EGL/egl_object.cpp \ EGL/egl.cpp \ EGL/eglApi.cpp \ EGL/trace.cpp \ EGL/getProcAddress.cpp.arm \ EGL/Loader.cpp \ # LOCAL_MODULE:= libEGL include $(BUILD_SHARED_LIBRARY)
Next, let's look at eglGetDisplay(). This function is in system \ lib \ libegl So.
EGLDisplay eglGetDisplay(EGLNativeDisplayType display) { clearError(); uint32_t index = uint32_t(display); if (index >= NUM_DISPLAYS) { return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); } //From the function name, it is to load the relevant implementation if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); } EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display); return dpy; } EGLBoolean egl_init_drivers() { EGLBoolean res; pthread_mutex_lock(&sInitDriverMutex); res = egl_init_drivers_locked(); pthread_mutex_unlock(&sInitDriverMutex); return res; } static EGLBoolean egl_init_drivers_locked() { if (sEarlyInitState) { // initialized by static ctor. should be set here. return EGL_FALSE; } // get our driver loader Loader& loader(Loader::getInstance()); // dynamically load our EGL implementation egl_connection_t* cnx = &gEGLImpl; if (cnx->dso == 0) { //gHooks saves the function implementation starting with gl cnx->hooks[egl_connection_t::GLESv1_INDEX] = &gHooks[egl_connection_t::GLESv1_INDEX]; cnx->hooks[egl_connection_t::GLESv2_INDEX] = &gHooks[egl_connection_t::GLESv2_INDEX]; cnx->dso = loader.open(cnx); } return cnx->dso ? EGL_TRUE : EGL_FALSE; egl_connection_t gEGLImpl; gl_hooks_t gHooks[2]; struct egl_connection_t { enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 }; inline egl_connection_t() : dso(0) { } void * dso; gl_hooks_t * hooks[2]; EGLint major; EGLint minor; //Save egl implementation egl_t egl; void* libGles1; void* libGles2; }; // It still works like this. I haven't used it before // EGL / EGL_ entries. The in file include s all EGL entries struct egl_t { #include "EGL/egl_entries.in" }; //Set entries In file include struct gl_hooks_t { struct gl_t { #include "entries.in" } gl; struct gl_ext_t { __eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS]; } ext; };
EGL/http://egl_entries.in The file contains the following entries, starting with egl, which are all function declarations,
EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
http://entries.in All entries in the file start with gl and are function declarations,
GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program)
As can be seen from the following macro, after the conversion, the above entry is the return value, function name and function parameter type.
#undef GL_ENTRY #undef EGL_ENTRY #define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); #define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
So,
struct egl_t { EGLDisplay eglGetDisplay(NativeDisplayType ); ....... }; struct gl_t { void glActiveShaderProgramEXT(GLuint pipeline, GLuint program); } gl;
Let's look at CNx - > DSO = loader open(cnx),
void* Loader::open(egl_connection_t* cnx) { void* dso; driver_t* hnd = 0; //First, kind is GLES, mask is EGL, GLESv1_CM ,GLESv2 //Look for libgles under / vendor/lib/egl and / system/lib/egl So or libgles_ * so //Only system \ lib \ EGL \ libgles can be found here_ android. So, software implementation of opengl //However, the code continue s directly without software implementation //So dso returns null dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2); if (dso) { hnd = new driver_t(dso); } else { // Always load EGL first // kind is EGL and mask is EGL // You can find system\vendor\lib\egl\libEGL_adreno.so // Then fill in the egl correlation function dso = load_driver("EGL", cnx, EGL); if (dso) { hnd = new driver_t(dso); //Found system\vendor\lib\egl\libGLESv1_CM_adreno.so library, //Filling gl correlation function hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM ); //System \ vendor \ lib \ EGL \ libglesv2 found_ adreno. So library, //Filling gl correlation function hnd->set( load_driver("GLESv2", cnx, GLESv2), GLESv2 ); } } LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation"); //The above is the implementation of opengle, and the following CNx - > libgles2 and libGles1 are the wrapper s of the implementation of gles // Just open / system / lib / libglesv2 So and / system/lib/libGLESv1_CM.so these two libraries and return the address to cnx cnx->libGles2 = load_wrapper("/system/lib/libGLESv2.so"); cnx->libGles1 = load_wrapper("/system/lib/libGLESv1_CM.so"); LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1, "couldn't load system OpenGL ES wrapper libraries"); return (void*)hnd; } void *Loader::load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask) { class MatchFile { public: static String8 find(const char* kind) { String8 result; String8 pattern; pattern.appendFormat("lib%s", kind); //Look in the path below const char* const searchPaths[] = { "/vendor/lib/egl", "/system/lib/egl" }; // first, we search for the exact name of the GLES userspace // driver in both locations. // i.e.: // libGLES.so, or: // libEGL.so, libGLESv1_CM.so, libGLESv2.so for (size_t i=0 ; i<NELEM(searchPaths) ; i++) { if (find(result, pattern, searchPaths[i], true)) { return result; } } // for compatibility with the old "egl.cfg" naming convention // we look for files that match: // libGLES_*.so, or: // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so pattern.append("_"); for (size_t i=0 ; i<NELEM(searchPaths) ; i++) { if (find(result, pattern, searchPaths[i], false)) { return result; } } // we didn't find the driver. gah. result.clear(); return result; } private: static bool find(String8& result, const String8& pattern, const char* const search, bool exact) { // in the emulator case, we just return the hardcoded name // of the software renderer. if (checkGlesEmulationStatus() == 0) { ALOGD("Emulator without GPU support detected. " "Fallback to software renderer."); result.setTo("/system/lib/egl/libGLES_android.so"); return true; } if (exact) { String8 absolutePath; absolutePath.appendFormat("%s/%s.so", search, pattern.string()); if (!access(absolutePath.string(), R_OK)) { result = absolutePath; return true; } return false; } DIR* d = opendir(search); if (d != NULL) { struct dirent cur; struct dirent* e; while (readdir_r(d, &cur, &e) == 0 && e) { if (e->d_type == DT_DIR) { continue; } //system\lib\egl\libGLES_android.so skip software implementation directly //Too slow, but there is no gpu or overlay in a project, //You can only use agl to synthesize layer s. if (!strcmp(e->d_name, "libGLES_android.so")) { // always skip the software renderer continue; } if (strstr(e->d_name, pattern.string()) == e->d_name) { if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) { result.clear(); result.appendFormat("%s/%s", search, e->d_name); closedir(d); return true; } } } closedir(d); } return false; } }; String8 absolutePath = MatchFile::find(kind); if (absolutePath.isEmpty()) { // this happens often, we don't want to log an error return 0; } const char* const driver_absolute_path = absolutePath.string(); //Open the library found void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); if (dso == 0) { const char* err = dlerror(); ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown"); return 0; } ALOGD("loaded %s", driver_absolute_path); //mask is EGL if (mask & EGL) { //First, find the eglGetProcAddress function in the library getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); ALOGE_IF(!getProcAddress, "can't find eglGetProcAddress() in %s", driver_absolute_path); egl_t* egl = &cnx->egl; __eglMustCastToProperFunctionPointerType* curr = (__eglMustCastToProperFunctionPointerType*)egl; //egl_names is the entry of EGL char const * const * api = egl_names; while (*api) { char const * name = *api; //Find functions in entry __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); //If not found, use eglGetProcAddress to find it again if (f == NULL) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); //If it is still not found, it is set to 0 if (f == NULL) { f = (__eglMustCastToProperFunctionPointerType)0; } } *curr++ = f; api++; } } if (mask & GLESv1_CM) { init_api(dso, gl_names, (__eglMustCastToProperFunctionPointerType*) &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, getProcAddress); } if (mask & GLESv2) { init_api(dso, gl_names, (__eglMustCastToProperFunctionPointerType*) &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, getProcAddress); } return dso; }
Summary
a. For processes that need to use opengl functions, first load system \ lib \ libegl So shared library, call the eglGetDisplay() function in the library to load the specific OpenGL implementation;
b. If there is only opengl software implementation in the system, that is, only system \ lib \ egl \ libgles can be found_ android. So, then both egl and opengl are implemented in libGLES_android.so library. Although this library is no longer used in 4.4 (directly ignored in the code), I have contacted a platform with low hardware requirements, no gpu, no overlay, and can only synthesize layer s through agl, so it is still valuable;
c. If there is a GPU in the system, find the EGL implementation library system\vendor\lib\egl\libEGL_adreno.so, opengl implementation library system\vendor\lib\egl\libGLESv1_CM_adreno.so,system\vendor\lib\egl\libGLESv2_adreno.so, which uses the hardware implementation of opengl.
d. When using opengl functions, first use egl functions to build the local environment, and then use OpenGL to draw.
e. There are two main uses of opengl in android: the upper layer uses opengl render, that is, drawing; opengl is used in the lower layer surface linker to synthesize layers. Later, we will continue to analyze the synthesis of layers, which will also involve fence.