首先來看看Android官方對EGL的解釋:
OpenGL ES 定義了一個渲染圖形的 API,但沒有定義窗口系統。為了讓 GLES 能夠適合各種平台,GLES 將與知道如何通過操作系統創建和訪問窗口的庫結合使用。用於 Android 的庫稱為 EGL。如果要繪制紋理多邊形,應使用 GLES 調用;如果要在屏幕上進行渲染,應使用 EGL 調用。
OpenGL ES 是Android繪圖API,但OpenGL ES是平台通用的,在特定設備上使用需要一個中間層做適配,這個中間層就是EGL。
EGL架構
- Display(EGLDisplay) 是對實際顯示設備的抽象。
- Surface(EGLSurface)是對用來存儲圖像的內存區域 FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer。
- Context (EGLContext) 存儲 OpenGL ES繪圖的一些狀態信息。
Android中的OpenGL 與EGL
Android 2.0版本之后圖形系統的底層渲染均由OpenGL負責,OpenGL除了負責處理3D API調用,還需負責管理顯示內存及處理Android SurfaceFlinger或上層應用對其發出的2D API調用請求。
本地代碼:
frameworks/native/opengl/libs/EGL
Android EGL框架,負責加載OpenGL函數庫和EGL本地實現。
frameworks/native/opengl/libagl
Android提供的OpenGL軟件庫JNI代碼:
frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp
EGL本地代碼的JNI調用接口
frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.cpp
frameworks/base/core/jni/android_opengl_GLESXXX.cpp
OpenGL功能函數的JNI調用接口Java代碼:
frameworks/base/opengl/java/javax/microedition/khronos/egl
frameworks/base/opengl/java/javax/microedition/khronos/opengles
frameworks/base/opengl/java/com/google/android/gles_jni/
frameworks/base/opengl/java/android/opengl
EGL和OpenGL的Java層接口,提供給應用開發者,通過JNI方式調用底層函數。
首先從Native代碼入手: frameworks/native/opengl/libs/EGL,該目錄下文件如圖所示:
frameworks/native/opengl/libs/EGL
依次解析該目錄下各個文件的作用,由於文件缺少注釋,只能從代碼解讀含義:
eglApi.cpp:提供暴露給上層的API。包含EGL對象的創建、配置、銷毀等操作。
初始化EGL
OpenGL ES的初始化過程(EGL初始化)如下圖所示意:
Display → Config → Surface
↑
Context
↑
Application → OpenGL Command
獲取Display。
獲得Display要調用EGLboolean eglGetDisplay(NativeDisplay dpy),參數一般為 EGL_DEFAULT_DISPLAY 。初始化egl。
調用 EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor),該函數會進行一些內部初始化工作,並傳回EGL版本號(major.minor)。
開機時打出的信息:
如下信息可以由const char * eglQueryString (EGLDisplay dpy, EGLint name);給出,name可以是EGL_VENDOR, EGL_VERSION, 或者EGL_EXTENSIONS 。該函數常用來查詢當前版本EGL實現了哪些擴展,方便向下兼容。
SurfaceFlinger: EGL information:
SurfaceFlinger: vendor : Android
SurfaceFlinger: version : 1.4 Android META-EGL
SurfaceFlinger: extensions: EGL_KHR_get_all_proc_addresses EGL_ANDROID_presentation_time EGL_KHR_swap_buffers_with_damage EGL_ANDROID_get_native_client_buffer EGL_ANDROID_front_buffer_auto_refresh EGL_ANDROID_get_frame_timestamps EGL_KHR_image EGL_KHR_image_base EGL_KHR_gl_colorspace EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_gl_renderbuffer_image EGL_KHR_fence_sync EGL_KHR_create_context EGL_KHR_config_attribs EGL_KHR_surfaceless_context EGL_EXT_create_context_robustness EGL_ANDROID_image_native_buffer EGL_KHR_wait_sync EGL_ANDROID_recordable EGL_KHR_partial_update EGL_KHR_mutable_render_buffer EGL_IMG_context_priority
SurfaceFlinger: Client API: OpenGL_ES
SurfaceFlinger: EGLSurface: 8-8-8-8, config=0x785f32d008
SurfaceFlinger: OpenGL ES informations:
SurfaceFlinger: vendor : ARM
SurfaceFlinger: renderer : Mali-T860
SurfaceFlinger: version : OpenGL ES 3.2 v1.r18p0-00cet0.e348142bb0bcdf18abb600a2670c56a1
SurfaceFlinger: extensions: GL_EXT_debug_marker GL_ARM_rgba8 GL_ARM_mali_shader_binary GL_OES_depth24 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_EXT_read_format_bgra GL_OES_compressed_paletted_texture GL_OES_compressed_ETC1_RGB8_texture GL_OES_standard_derivatives GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_EGL_image_external_essl3 GL_OES_EGL_sync GL_OES_texture_npot GL_OES_vertex_half_float GL_OES_required_internalformat GL_OES_vertex_array_object GL_OES_mapbuffer GL_EXT_texture_format_BGRA8888 GL_EXT_texture_rg GL_EXT_texture_type_2_10_10_10_REV GL_OES_fbo_render_mipmap GL_OES_element_index_uint GL_EXT_shadow_samplers GL_OES_texture_compression_astc GL_KHR_texture_compression_astc_ldr GL_KHR_texture_compression_astc_hdr GL_KHR_texture_compression_astc_sliced_3d GL_KHR_debug GL_EXT_occlusion_query_boolean GL_EXT_disjoint_timer_query GL_EXT_blend_minmax GL_EXT_discard_framebuffer GL_OES_get_prog...
SurfaceFlinger: GL_MAX_TEXTURE_SIZE = 8192
SurfaceFlinger: GL_MAX_VIEWPORT_DIMS = 8192
選擇Config。
Config實際指的是FrameBuffer的參數,
一般用EGLboolean eglChooseConfig(EGLDisplay dpy, const EGLint * attr_list, EGLConfig * config, EGLint config_size, EGLint *num_config)
,其中attr_list是以EGL_NONE結束的參數數組,通常以id,value依次存放,對於個別標識性的屬性可以只有 id,沒有value。另一個辦法是用EGLboolean eglGetConfigs(EGLDisplay dpy, EGLConfig * config, EGLint config_size, EGLint *num_config) 來獲得所有config。
這兩個函數都會返回不多於config_size個Config,結果保存在config[]中,系統的總Config個數保存 在num_config中。
可以利用eglGetConfig()中間兩個參數為0來查詢系統支持的Config總個數。Config有眾多的Attribute,這些Attribute決定FrameBuffer的格式和能力,通過eglGetConfigAttrib ()來讀取,但不能修改。構造Surface
Surface實際上就是一個FrameBuffer,也就是渲染目的地,通過EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg, NativeWindow win, EGLint *cfg_attr)
來創建一個可實際顯示的Surface。
系統通常還支持另外兩種Surface:PixmapSurface和PBufferSurface,這兩種都不是可顯示的Surface,PixmapSurface是保存在系統內存中的位圖,PBuffer則是保存在顯存中的幀。
對於這兩種surface,Android系統中,支持PBufferSurface。Surface也有一些attribute,基本上都可以顧名思義,
EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER
EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET
EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL
通過eglSurfaceAttrib()設置、eglQuerySurface()讀取。
創建Context
OpenGL ES的pipeline從程序的角度看就是一個狀態機,有當前的顏色、紋理坐標、變換矩陣、絢染模式等一大堆狀態,這些狀態作用於OpenGL API程序提交的頂點坐標等圖元從而形成幀緩沖內的像素。在OpenGL的編程接口中,Context就代表這個狀態機,OpenGL API程序的主要工作就是向Context提供圖元、設置狀態,偶爾也從Context里獲取一些信息。
可以用EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)
來創建一個Context。EGL變量之間的綁定
boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)
該接口將申請到的display,draw(surface)和 context進行了綁定。也就是說,在context下的OpenGLAPI指令將draw(surface)作為其渲染最終目的地。而display作為draw(surface)的前端顯示。調用后,當前線程使用的EGLContex為context。繪制。
應用程序通過OpenGL API進行繪制,一幀完成之后,調用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)
來顯示。
If
surface
is a window surface,eglSwapBuffers
posts its color buffer to the associated native window.
The contents of ancillary buffers are always undefined after callingeglSwapBuffers
. The contents of the color buffer are left unchanged if the value of theEGL_SWAP_BEHAVIOR
attribute ofsurface
isEGL_BUFFER_PRESERVED
, and are undefined if the value isEGL_BUFFER_DESTROYED
. The value ofEGL_SWAP_BEHAVIOR
can be set for some surfaces using eglSurfaceAttrib.eglSwapBuffers
performs an implicit flush operation on the context (glFlush
for an OpenGL ES or OpenGL context,vgFlush
for an OpenVG context) bound tosurface
before swapping. Subsequent client API commands may be issued on that context immediately after callingeglSwapBuffers
, but are not executed until the buffer exchange is completed.
Ifsurface
is a pixel buffer or a pixmap,eglSwapBuffers
has no effect, and no error is generated.
看看frameworks/native/opengl/libagl/egl.cpp對eglSwapBuffers的實現:
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
egl_surface_t* d = static_cast<egl_surface_t*>(draw);
...
// post the surface
d->swapBuffers();
// if it's bound to a context, update the buffer
if (d->ctx != EGL_NO_CONTEXT) {
d->bindDrawSurface((ogles_context_t*)d->ctx);
// if this surface is also the read surface of the context
// it is bound to, make sure to update the read buffer as well.
// The EGL spec is a little unclear about this.
egl_context_t* c = egl_context_t::context(d->ctx);
if (c->read == draw) {
d->bindReadSurface((ogles_context_t*)d->ctx);
}
}
return EGL_TRUE;
}
- eglSwapBuffers之后