eglInitialize()
来看 EGL10.eglInitialize()
的实现。com.google.android.gles_jni.EGLImpl
中,这个方法的实现如下:
它是一个本地层方法。其实际实现位于 frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp
:
EGL10.eglInitialize()
以 EGLDisplay
对象及一个 int
数组为参数,其中 int
数组为出参,用于返回版本号,并通过方法返回值表示初始化是否成功。
在 jni_eglInitialize()
中,它通过传入的 Java EGLDisplay
对象获得本地层 Display 对象的句柄,执行 EGL 库 (EGL wrapper 库) 的 eglInitialize()
函数完成初始化,并返回版本号,版本号总是 1.0
。
EGL 库 (EGL wrapper 库) 的 eglInitialize()
的定义如下:
在这个函数中先获得本地层 Display 对象的指针对象 egl_display_ptr
,然后执行 egl_display_t::initialize(EGLint *major, EGLint *minor)
。
egl_display_ptr
定义(位于 frameworks/native/opengl/libs/EGL/egl_display.h
)如下:
这是 egl_display_t
对象的智能指针,该指针对象创建时执行 egl_display_t::enter()
,对象销毁时执行 egl_display_t::leave()
。
get_display()
定义(位于 frameworks/native/opengl/libs/EGL/egl_display.h
)如下:
egl_display_t::get(dpy)
定义(位于 frameworks/native/opengl/libs/EGL/egl_display.cpp
)如下:
本地层 Display 对象句柄为本地层全局静态 egl_display_t
对象数组中的索引值加 1。
egl_display_t::initialize()
定义如下:
每次执行 egl_display_t::initialize()
初始化时,都会递增 refs
,每次执行 egl_display_t::terminate()
终止时,则会递减它。在 egl_display_t
对象创建时,refs
被初始化为 0。
在 egl_display_t::initialize()
中,会首先处理初始化/终止的同步。增加 refs
之后,当它大于 1 时,表明对象已经被初始化了,可以直接返回,但如果 eglIsInitialized
为 false
表明,有另一个线程在初始化,但初始化还没有完成,这需要等待初始化的完成。
增加 refs
之后,当它等于 1 时,表明对象还没有初始化过,或者已经被终止,若 eglIsInitialized
为 true
则表明终止还没有结束,此时则需要等待终止过程结束。
对于 refs
的同步,反正在这里都会同步地等待,为什么不把 refLock
锁的锁定范围定位整个函数呢?即锁定整个 egl_display_t::initialize()
函数或 egl_display_t::terminate()
。
处理初始化/终止的同步之后,才开始了正式的初始化。
- 首先设置线程特有 GL Hooks 为
gHooksNoContext
。 执行设备特有的实际 EGL 库实现的
eglInitialize()
函数来初始化,并从实际 EGL 库实现中查询一些与图形硬件特性有关的字符串,包括图形硬件的生产商,支持的 EGL 版本,支持的扩展和客户端 API。
对于 Google Pixel 设备而言,这些字符串的实际值如下:1234queryString.vendor = Qualcomm Inc.queryString.version = 1.4queryString.extensions = EGL_QUALCOMM_shared_image EGL_KHR_image EGL_KHR_image_base EGL_QCOM_create_image EGL_QCOM_gpu_perf EGL_KHR_lock_surface EGL_KHR_lock_surface2 EGL_KHR_lock_surface3 EGL_KHR_fence_sync EGL_KHR_wait_sync EGL_KHR_cl_event EGL_KHR_cl_event2 EGL_KHR_reusable_sync EGL_IMG_context_priority EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_gl_texture_3D_image EGL_KHR_gl_renderbuffer_image EGL_EXT_create_context_robustness EGL_EXT_yuv_surface EGL_ANDROID_blob_cache EGL_KHR_create_context EGL_KHR_gl_colorspace EGL_KHR_surfaceless_context EGL_KHR_create_context_no_error EGL_KHR_get_all_proc_addresses EGL_QCOM_lock_image2 EGL_KHR_partial_update EGL_EXT_protected_content EGL_KHR_mutable_render_buffer EGL_ANDROID_recordable EGL_ANDROID_native_fence_sync EGL_ANDROID_image_native_buffer EGL_ANDROID_framebuffer_target EGL_ANDROID_image_crop EGL_IMG_image_plane_attribsqueryString.clientApi = OpenGL_ES前面查询的字符串为图形硬件的特性。
mVendorString
等则为 Android 图形系统的特性信息,设备生产商,EGL 版本,客户端版本和支持的 EGL 扩展等信息。这些信息如下。123456static char const * const sVendorString = "Android";static char const * const sVersionString = "1.4 Android META-EGL";static char const * const sClientApiString = "OpenGL_ES";extern char const * const gBuiltinExtensionString;extern char const * const gExtensionString;
gBuiltinExtensionString
和 gExtensionString
定义(位于 frameworks/native/opengl/libs/EGL/eglApi.cpp
)如下:
这里初始化获得的 EGL 扩展特性是最终暴露给应用程序的 EGL 扩展特性。(gBuiltinExtensionString + gExtensionString) 为 Android 图形系统可以识别并应用的 EGL 扩展,其中 (gBuiltinExtensionString) 是完全由 Android 的 EGL wrapper 库实现并总是可用的。其余的 (gExtensionString) 则依赖于图形硬件 EGL 驱动中的支持。其中的一些必须得到支持,因为它们由 Android 系统本身使用,它们是上面在注释中标记了 mandatory
的那些,CDD 对它们有做要求。系统 假设 设备总是支持那些强制的 EGL 扩展,如果那些扩展缺失的话,则设备运行可能会出问题。
实际暴露给应用程序的 EGL 扩展特性将是 Android 图形系统可识别的与图形硬件支持的交集,其中 Android 图形系统可识别的包括由 EGL wrapper 实现的与需要图形硬件支持的。
- 初始化
egl_cache_t
。 - 根据调试有关的一些系统属性设置状态。
- 返回主、次版本号。
- 将
eglIsInitialized
置为true
并发送广播出去通知其它线程,初始化结束。
eglChooseConfig()
接下来来看 eglChooseConfig()
。在 EGLImpl
中,它同样为本地层方法:
eglChooseConfig()
方法的本地层实现(位于 frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp
)如下:
eglChooseConfig()
接收 EGLDisplay
和 int[]
的 attrib_list
作为入参,接收 EGLConfig[]
的 configs
及 int[]
的 num_config
接收返回值,而 int
型的 config_size
则用来表示 configs
的长度。
jni_eglChooseConfig()
做的事情如下:
- 检查参数有效性。参数无效的时候,抛出异常并返回。
- 参数转换。将 Java 对象转换为本地层所用的结构。
- 执行 EGL wrapper 库的
eglChooseConfig()
。 - 返回值给 Java 层。对于
int[]
的num_config
,通过 JNI 函数更新其内容。对于EGLConfig[]
的configs
,则会先构造 Java 对象。12345678910static jclass gConfig_class;static jmethodID gConfig_ctorID;. . . . . .static void nativeClassInit(JNIEnv *_env, jclass eglImplClass){jclass config_class = _env->FindClass("com/google/android/gles_jni/EGLConfigImpl");gConfig_class = (jclass) _env->NewGlobalRef(config_class);gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(J)V");gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "J");
可以看一下这里用到的一些函数的定义:
EGL wrapper 库中 eglChooseConfig()
定义如下:
在这里,如果为了调试强制使用 MSAA,即多重采样抗锯齿(MultiSampling Anti-Aliasing,简称MSAA),则会插入 2 个额外的属性来强制启用 MSAA 4x,然后调用图形硬件特有的实际 EGL 实现库的 eglChooseConfig()
完成设置。否则直接用设备特有的实际 EGL 实现库的 eglChooseConfig()
完成设置。
我们前面 在 Android 中使用 OpenGL 一文中的示例里,attrib_list
的实际值如下:
这个配置大概用于配置 OpenGL ES 渲染所用的颜色模式,深度大小等。
eglCreateWindowSurface()
然后来看 eglCreateWindowSurface()
。在 EGLImpl
中,它有着如下这样的定义:
eglCreateWindowSurface()
根据传入的本地窗口创建 EGLSurface
。
Android 中可以作为本地窗口传入的有 SurfaceView
对象,SurfaceView
的 SurfaceHolder
或 Surface
对象,或者 TextureView
的 SurfaceTexture
。
传入的 native_window
如果是 Surface
对象,则调用本地层方法 _eglCreateWindowSurface()
创建本地层 EGL Surface。如果传入的
native_window
是 SurfaceView
或 SurfaceHolder
,则会先从中获得 Surface
对象,然后调用本地层方法 _eglCreateWindowSurface()
创建本地层 EGL Surface。
如果传入的 native_window
是 SurfaceTexture
,则会调用本地层方法 _eglCreateWindowSurfaceTexture()
创建本地层 EGL Surface。
有了地层 EGL Surface 之后,则创建对象 EGLSurfaceImpl
封装本地层 EGL Surface 并返回给调用者。
EGLSurfaceImpl
类定义如下:
EGLSurfaceImpl
仅仅是本地层对象句柄的简单封装。
本地层方法 _eglCreateWindowSurface()
的实现如下:
在这个函数中,首先从 Display 和 Config 的 Java 对象中获得其相应的本地层对象的句柄。上面的代码中用 EGLContext cnf
来保存 getConfig(_env, config)
的返回值。怀疑这是代码作者的 bug,只是由于 EGLContext
和 EGLConfig
都是 `void 的
typedef`,所以才没有出现实际的问题。*
然后通过 android_view_Surface_getNativeWindow()
(定义位于 frameworks/base/core/jni/android_view_Surface.cpp
)从 Java 层的 Surface
对象获得本地层的 ANativeWindow
:
得到的 ANativeWindow
实际为本地层的 Surface
类对象。
最后调用 EGL wrapper 库的 eglCreateWindowSurface()
创建 EGLSurface
并返回给调用者。
EGL wrapper 库的 eglCreateWindowSurface()
定义(位于 frameworks/native/opengl/libs/EGL/eglApi.cpp
)如下:
eglCreateWindowSurface()
执行步骤如下:
第一步,获得 egl_connection_t
和 egl_display_ptr
:
第二步,通过函数 native_window_api_connect()
(定义位于 system/core/include/system/window.h
) 为本地窗口连接 EGL API:
第三步,根据配置计算颜色模式。
第四步,如果 GPU 图形硬件支持 EGL_KHR_gl_colorspace
扩展,则计算得到色彩空间。
第五步,为 window (本地窗口) 设置颜色模式。比较奇怪,获得色彩模式与设置色彩模式之间,为什么要隔一段计算颜色空间的逻辑呢?
第六步,为 window 设置色彩空间。
第七步,设置 Swap interval。
第八步,调用实际的设备 EGL 库接口创建 EGLSurface
,并据此创建 egl_surface_t
返回给调用者。
EGLImpl
中的本地层方法 _eglCreateWindowSurfaceTexture()
的实现如下:
这个函数与 jni_eglCreateWindowSurface()
函数类似。只是它会从传入的 Java 对象 SurfaceTexture
获得本地层的 IGraphicBufferProducer
对象引用,然后利用该引用创建本地层 Surface
。后面的流程就与 jni_eglCreateWindowSurface()
的流程就完全一样。
由此不难理解本地层的 Surface
是 IGraphicBufferProducer
的封装,并提供函数来方便操作 IGraphicBufferProducer
。
eglCreateContext()
Android 的 OpenGL 应用,通过 EGL.eglCreateContext()
创建EGL context。在 EGLImpl
中该方法定义如下:
这个方方法调用本地层方法 _eglCreateContext()
创建本地层 EGL context 并获得其 ID,然后创建 EGLContextImpl
对象并返回给调用者。
EGLContextImpl
定义如下:
它是对本地层的 EGL context ID 和 GLImpl
的简单封装。
本地层方法 _eglCreateContext()
实现如下:
这个方法从传入的 Java 对象中获得相应的本地层对象,随后通过 EGL wrapper 库的 eglCreateContext()
创建本地层 EGL context 对象,并将其 ID 返回个调用者。
EGL wrapper 库中 eglCreateContext()
的定义如下:
创建 EGLContext 分为如下几步来完成:
- 获得共享 Context。(EGL 的共享 context 是个什么概念?)
- 根据共享 context 及传入的 EGLDisplay 等,通过图形硬件特有的实际 EGL 实现库的
eglCreateContext()
创建EGLContext
。 - 获取传入的
attrib_list
中的 OpenGL ES 版本信息。 - 根据前面创建的
EGLContext
和 OpenGL ES 版本信息创建egl_context_t
。
在 EGL Wrapper 库这一级,EGL context 即为 egl_context_t
类对象。该类定义如下:
此时这个 egl_context_t
还无法实际使用,它还没有关联 EGLSurface
。
eglMakeCurrent()
eglMakeCurrent()
为 EGLContext
关联 EGLSurface
,并为当前线程启用该 EGLContext
。EGLImpl
中 eglMakeCurrent()
定义如下:
这是一个本地层方法,其本地层实现如下:
这个实现也很直接:它从传入的 Java 对象参数中获得它们本地层对象,然后调用 EGL wrapper 库的 eglMakeCurrent()
并将结果返回给调用者。
EGL wrapper 库中 eglMakeCurrent()
定义如下:
eglMakeCurrent()
首先获得线程当前关联的 EGL context:
在 EGL wrapper 这一级,通过线程局部存储保存当前线程关联的 egl_context_t
:
frameworks/native/opengl/libs/EGL/egl_object.h
中 get_context()
的定义如下:
eglMakeCurrent()
接口有两个主要的功能:一是为一个有效的 EGLContext 关联 Surface,并把该 EGLContext 关联到当前线程,此时对 Surface 没有特别要求,这也就意味着 EGLContext 可以在不关联 Surface 被设置为当前 EGLContext;二是当传入的 EGLContext 为空时,则将当前线程关联的 EGLContext 接触关联,且当 EGLContext 为空时,传入的 Surface 必须为 EGL_NO_SURFACE
。
eglMakeCurrent()
通过 egl_display_t::makeCurrent()
执行底层图形硬件 EGL 库实现级别的 make current:
这里调用底层的图形硬件 EGL 库实现的 eglMakeCurrent()
完成操作,并根据需要回调 egl_context_t
的函数。随后释放当前的 context。
如果传入的 EGLContext 有效,且当前已经关联了一个 EGLContext,则新的替换旧的,但是旧的 egl_context_t
的回调 onLooseCurrent()
没有被调到。
如果是要为当前线程关联 EGLContext 的话,则设置线程局部的 GL Hooks 为 EGLContext 的 OpenGL ES 版本所对应的 Hooks,并在线程局部存储中保存 EGLContext
,然后增加 EGLContext 和 Surface 的引用计数:
egl_tls_t::setContext(ctx)
定义如下:
如果是要清除当前线程关联的 EGLContext 的话,则置线程局部的 GL Hooks 为 gHooksNoContext
,并设置当前线程关联的 EGLContext
为 EGL_NO_CONTEXT
。
猜测在设备特有的 EGL 库实现一级,无论是软件实现,还是硬件实现,都存在着另外的线程局部存储变量来保存那一级的 EGLContext 数据。
自此之后,就可以使用 OpenGL ES 的接口来渲染图形了。
打赏
Done.
Android OpenGL 图形系统分析系列文章
在 Android 中使用 OpenGL
Android 图形驱动初始化
EGL Context 创建
Android 图形系统之图形缓冲区分配
Android 图形系统之gralloc