继续 EGL context 创建的分析。
eglInitialize()
来看 EGL10.eglInitialize() 的实现。com.google.android.gles_jni.EGLImpl 中,这个方法的实现如下:
public native boolean eglInitialize(EGLDisplay display, int[] major_minor);
它是一个本地层方法。其实际实现位于 frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp:
1static jfieldID gDisplay_EGLDisplayFieldID; 2. . . . . . 3static void nativeClassInit(JNIEnv *_env, jclass eglImplClass) 4{ 5. . . . . . 6 jclass display_class = _env->FindClass("com/google/android/gles_jni/EGLDisplayImpl"); 7 gDisplay_EGLDisplayFieldID = _env->GetFieldID(display_class, "mEGLDisplay", "J"); 8. . . . . . 9} 10 11. . . . . . 12static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) { 13 if (!o) return EGL_NO_DISPLAY; 14 return (EGLDisplay)env->GetLongField(o, gDisplay_EGLDisplayFieldID); 15} 16. . . . . . 17static jboolean jni_eglInitialize(JNIEnv *_env, jobject _this, jobject display, 18 jintArray major_minor) { 19 if (display == NULL || (major_minor != NULL && 20 _env->GetArrayLength(major_minor) < 2)) { 21 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 22 return JNI_FALSE; 23 } 24 25 EGLDisplay dpy = getDisplay(_env, display); 26 EGLBoolean success = eglInitialize(dpy, NULL, NULL); 27 if (success && major_minor) { 28 int len = _env->GetArrayLength(major_minor); 29 if (len) { 30 // we're exposing only EGL 1.0 31 jint* base = (jint *)_env->GetPrimitiveArrayCritical(major_minor, (jboolean *)0); 32 if (len >= 1) base[0] = 1; 33 if (len >= 2) base[1] = 0; 34 _env->ReleasePrimitiveArrayCritical(major_minor, base, 0); 35 } 36 } 37 return EglBoolToJBool(success); 38}
EGL10.eglInitialize() 以 EGLDisplay 对象及一个 int 数组为参数,其中 int 数组为出参,用于返回版本号,并通过方法返回值表示初始化是否成功。
在 jni_eglInitialize() 中,它通过传入的 Java EGLDisplay 对象获得本地层 Display 对象的句柄,执行 EGL 库 (EGL wrapper 库) 的 eglInitialize() 函数完成初始化,并返回版本号,版本号总是 1.0。
EGL 库 (EGL wrapper 库) 的 eglInitialize() 的定义如下:
1EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) 2{ 3 clearError(); 4 5 egl_display_ptr dp = get_display(dpy); 6 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 7 8 EGLBoolean res = dp->initialize(major, minor); 9 10 return res; 11}
在这个函数中先获得本地层 Display 对象的指针对象 egl_display_ptr,然后执行 egl_display_t::initialize(EGLint *major, EGLint *minor)。
egl_display_ptr 定义(位于 frameworks/native/opengl/libs/EGL/egl_display.h)如下:
1class egl_display_ptr { 2public: 3 explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) { 4 if (mDpy) { 5 if (CC_UNLIKELY(!mDpy->enter())) { 6 mDpy = NULL; 7 } 8 } 9 } 10 11 // We only really need a C++11 move constructor, not a copy constructor. 12 // A move constructor would save an enter()/leave() pair on every EGL API 13 // call. But enabling -std=c++0x causes lots of errors elsewhere, so I 14 // can't use a move constructor until those are cleaned up. 15 // 16 // egl_display_ptr(egl_display_ptr&& other) { 17 // mDpy = other.mDpy; 18 // other.mDpy = NULL; 19 // } 20 // 21 egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) { 22 if (mDpy) { 23 mDpy->enter(); 24 } 25 } 26 27 ~egl_display_ptr() { 28 if (mDpy) { 29 mDpy->leave(); 30 } 31 } 32 33 const egl_display_t* operator->() const { return mDpy; } 34 egl_display_t* operator->() { return mDpy; } 35 36 const egl_display_t* get() const { return mDpy; } 37 egl_display_t* get() { return mDpy; } 38 39 operator bool() const { return mDpy != NULL; } 40 41private: 42 egl_display_t* mDpy; 43 44 // non-assignable 45 egl_display_ptr& operator=(const egl_display_ptr&); 46};
这是 egl_display_t 对象的智能指针,该指针对象创建时执行 egl_display_t::enter(),对象销毁时执行 egl_display_t::leave()。
get_display()定义(位于 frameworks/native/opengl/libs/EGL/egl_display.h)如下:
1inline egl_display_ptr get_display(EGLDisplay dpy) { 2 return egl_display_ptr(egl_display_t::get(dpy)); 3}
egl_display_t::get(dpy) 定义(位于 frameworks/native/opengl/libs/EGL/egl_display.cpp)如下:
1egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS]; 2. . . . . . 3egl_display_t* egl_display_t::get(EGLDisplay dpy) { 4 uintptr_t index = uintptr_t(dpy)-1U; 5 if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) { 6 return nullptr; 7 } 8 return &sDisplay[index]; 9}
本地层 Display 对象句柄为本地层全局静态 egl_display_t 对象数组中的索引值加 1。
egl_display_t::initialize() 定义如下:
1EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { 2 3 { 4 Mutex::Autolock _rf(refLock); 5 6 refs++; 7 if (refs > 1) { 8 if (major != NULL) 9 *major = VERSION_MAJOR; 10 if (minor != NULL) 11 *minor = VERSION_MINOR; 12 while(!eglIsInitialized) refCond.wait(refLock); 13 return EGL_TRUE; 14 } 15 16 while(eglIsInitialized) refCond.wait(refLock); 17 } 18 19 { 20 Mutex::Autolock _l(lock); 21 22 setGLHooksThreadSpecific(&gHooksNoContext); 23 24 // initialize each EGL and 25 // build our own extension string first, based on the extension we know 26 // and the extension supported by our client implementation 27 28 egl_connection_t* const cnx = &gEGLImpl; 29 cnx->major = -1; 30 cnx->minor = -1; 31 if (cnx->dso) { 32 EGLDisplay idpy = disp.dpy; 33 if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) { 34 //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p", 35 // idpy, cnx->major, cnx->minor, cnx); 36 37 // display is now initialized 38 disp.state = egl_display_t::INITIALIZED; 39 40 // get the query-strings for this display for each implementation 41 disp.queryString.vendor = cnx->egl.eglQueryString(idpy, 42 EGL_VENDOR); 43 disp.queryString.version = cnx->egl.eglQueryString(idpy, 44 EGL_VERSION); 45 disp.queryString.extensions = cnx->egl.eglQueryString(idpy, 46 EGL_EXTENSIONS); 47 disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, 48 EGL_CLIENT_APIS); 49 50 } else { 51 ALOGW("eglInitialize(%p) failed (%s)", idpy, 52 egl_tls_t::egl_strerror(cnx->egl.eglGetError())); 53 } 54 } 55 56 // the query strings are per-display 57 mVendorString.setTo(sVendorString); 58 mVersionString.setTo(sVersionString); 59 mClientApiString.setTo(sClientApiString); 60 61 mExtensionString.setTo(gBuiltinExtensionString); 62 char const* start = gExtensionString; 63 do { 64 // length of the extension name 65 size_t len = strcspn(start, " "); 66 if (len) { 67 // NOTE: we could avoid the copy if we had strnstr. 68 const String8 ext(start, len); 69 if (findExtension(disp.queryString.extensions, ext.string(), 70 len)) { 71 mExtensionString.append(ext + " "); 72 } 73 // advance to the next extension name, skipping the space. 74 start += len; 75 start += (*start == ' ') ? 1 : 0; 76 } 77 } while (*start != '\0'); 78 79 egl_cache_t::get()->initialize(this); 80 81 char value[PROPERTY_VALUE_MAX]; 82 property_get("debug.egl.finish", value, "0"); 83 if (atoi(value)) { 84 finishOnSwap = true; 85 } 86 87 property_get("debug.egl.traceGpuCompletion", value, "0"); 88 if (atoi(value)) { 89 traceGpuCompletion = true; 90 } 91 92 if (major != NULL) 93 *major = VERSION_MAJOR; 94 if (minor != NULL) 95 *minor = VERSION_MINOR; 96 97 mHibernation.setDisplayValid(true); 98 } 99 100 { 101 Mutex::Autolock _rf(refLock); 102 eglIsInitialized = true; 103 refCond.broadcast(); 104 } 105 106 return EGL_TRUE; 107}
每次执行 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 设备而言,这些字符串的实际值如下:queryString.vendor = Qualcomm Inc. queryString.version = 1.4 queryString.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_attribs queryString.clientApi = OpenGL_ES
-
前面查询的字符串为图形硬件的特性。
mVendorString等则为 Android 图形系统的特性信息,设备生产商,EGL 版本,客户端版本和支持的 EGL 扩展等信息。这些信息如下。static 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)如下:
1extern char const * const gBuiltinExtensionString = 2 "EGL_KHR_get_all_proc_addresses " 3 "EGL_ANDROID_presentation_time " 4 "EGL_KHR_swap_buffers_with_damage " 5 "EGL_ANDROID_create_native_client_buffer " 6 "EGL_ANDROID_front_buffer_auto_refresh " 7#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 8 "EGL_ANDROID_get_frame_timestamps " 9#endif 10 ; 11extern char const * const gExtensionString = 12 "EGL_KHR_image " // mandatory 13 "EGL_KHR_image_base " // mandatory 14 "EGL_KHR_image_pixmap " 15 "EGL_KHR_lock_surface " 16#if (ENABLE_EGL_KHR_GL_COLORSPACE != 0) 17 "EGL_KHR_gl_colorspace " 18#endif 19 "EGL_KHR_gl_texture_2D_image " 20 "EGL_KHR_gl_texture_3D_image " 21 "EGL_KHR_gl_texture_cubemap_image " 22 "EGL_KHR_gl_renderbuffer_image " 23 "EGL_KHR_reusable_sync " 24 "EGL_KHR_fence_sync " 25 "EGL_KHR_create_context " 26 "EGL_KHR_config_attribs " 27 "EGL_KHR_surfaceless_context " 28 "EGL_KHR_stream " 29 "EGL_KHR_stream_fifo " 30 "EGL_KHR_stream_producer_eglsurface " 31 "EGL_KHR_stream_consumer_gltexture " 32 "EGL_KHR_stream_cross_process_fd " 33 "EGL_EXT_create_context_robustness " 34 "EGL_NV_system_time " 35 "EGL_ANDROID_image_native_buffer " // mandatory 36 "EGL_KHR_wait_sync " // strongly recommended 37 "EGL_ANDROID_recordable " // mandatory 38 "EGL_KHR_partial_update " // strongly recommended 39 "EGL_EXT_buffer_age " // strongly recommended with partial_update 40 "EGL_KHR_create_context_no_error " 41 "EGL_KHR_mutable_render_buffer " 42 "EGL_EXT_yuv_surface " 43 "EGL_EXT_protected_content " 44 ;
这里初始化获得的 EGL 扩展特性是最终暴露给应用程序的 EGL 扩展特性。(gBuiltinExtensionString + gExtensionString) 为 Android 图形系统可以识别并应用的 EGL 扩展,其中 (gBuiltinExtensionString) 是完全由 Android 的 EGL wrapper 库实现并总是可用的。其余的 (gExtensionString) 则依赖于图形硬件 EGL 驱动中的支持。其中的一些必须得到支持,因为它们由 Android 系统本身使用,它们是上面在注释中标记了 mandatory 的那些,CDD 对它们有做要求。系统 假设 设备总是支持那些强制的 EGL 扩展,如果那些扩展缺失的话,则设备运行可能会出问题。 实际暴露给应用程序的 EGL 扩展特性将是 Android 图形系统可识别的与图形硬件支持的交集,其中 Android 图形系统可识别的包括由 EGL wrapper 实现的与需要图形硬件支持的。 4. 初始化 egl_cache_t。 5. 根据调试有关的一些系统属性设置状态。 6. 返回主、次版本号。 7. 将 eglIsInitialized 置为 true 并发送广播出去通知其它线程,初始化结束。
eglChooseConfig()
接下来来看 eglChooseConfig()。在 EGLImpl 中,它同样为本地层方法:
public native boolean eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config);
eglChooseConfig() 方法的本地层实现(位于 frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp)如下:
1static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display, 2 jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) { 3 if (display == NULL 4 || !validAttribList(_env, attrib_list) 5 || (configs != NULL && _env->GetArrayLength(configs) < config_size) 6 || (num_config != NULL && _env->GetArrayLength(num_config) < 1)) { 7 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 8 return JNI_FALSE; 9 } 10 EGLDisplay dpy = getDisplay(_env, display); 11 EGLBoolean success = EGL_FALSE; 12 13 if (configs == NULL) { 14 config_size = 0; 15 } 16 EGLConfig nativeConfigs[config_size]; 17 18 int num = 0; 19 jint* attrib_base = beginNativeAttribList(_env, attrib_list); 20 success = eglChooseConfig(dpy, attrib_base, configs ? nativeConfigs : 0, config_size, &num); 21 endNativeAttributeList(_env, attrib_list, attrib_base); 22 23 if (num_config != NULL) { 24 _env->SetIntArrayRegion(num_config, 0, 1, (jint*) &num); 25 } 26 27 if (success && configs!=NULL) { 28 for (int i=0 ; i<num ; i++) { 29 jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, reinterpret_cast<jlong>(nativeConfigs[i])); 30 _env->SetObjectArrayElement(configs, i, obj); 31 } 32 } 33 return EglBoolToJBool(success); 34}
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 对象。static 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");
可以看一下这里用到的一些函数的定义:
1static bool validAttribList(JNIEnv *_env, jintArray attrib_list) { 2 if (attrib_list == NULL) { 3 return true; 4 } 5 jsize len = _env->GetArrayLength(attrib_list); 6 if (len < 1) { 7 return false; 8 } 9 jint item = 0; 10 _env->GetIntArrayRegion(attrib_list, len-1, 1, &item); 11 return item == EGL_NONE; 12} 13 14static jint* beginNativeAttribList(JNIEnv *_env, jintArray attrib_list) { 15 if (attrib_list != NULL) { 16 return _env->GetIntArrayElements(attrib_list, (jboolean *)0); 17 } else { 18 return(jint*) gNull_attrib_base; 19 } 20} 21 22static void endNativeAttributeList(JNIEnv *_env, jintArray attrib_list, jint* attrib_base) { 23 if (attrib_list != NULL) { 24 _env->ReleaseIntArrayElements(attrib_list, attrib_base, 0); 25 } 26}
EGL wrapper 库中 eglChooseConfig() 定义如下:
1egl_display_ptr validate_display(EGLDisplay dpy) { 2 egl_display_ptr dp = get_display(dpy); 3 if (!dp) 4 return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL)); 5 if (!dp->isReady()) 6 return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL)); 7 8 return dp; 9} 10. . . . . . 11EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, 12 EGLConfig *configs, EGLint config_size, 13 EGLint *num_config) 14{ 15 clearError(); 16 17 const egl_display_ptr dp = validate_display(dpy); 18 if (!dp) return EGL_FALSE; 19 20 if (num_config==0) { 21 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 22 } 23 24 EGLBoolean res = EGL_FALSE; 25 *num_config = 0; 26 27 egl_connection_t* const cnx = &gEGLImpl; 28 if (cnx->dso) { 29 if (attrib_list) { 30 char value[PROPERTY_VALUE_MAX]; 31 property_get("debug.egl.force_msaa", value, "false"); 32 33 if (!strcmp(value, "true")) { 34 size_t attribCount = 0; 35 EGLint attrib = attrib_list[0]; 36 37 // Only enable MSAA if the context is OpenGL ES 2.0 and 38 // if no caveat is requested 39 const EGLint *attribRendererable = NULL; 40 const EGLint *attribCaveat = NULL; 41 42 // Count the number of attributes and look for 43 // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT 44 while (attrib != EGL_NONE) { 45 attrib = attrib_list[attribCount]; 46 switch (attrib) { 47 case EGL_RENDERABLE_TYPE: 48 attribRendererable = &attrib_list[attribCount]; 49 break; 50 case EGL_CONFIG_CAVEAT: 51 attribCaveat = &attrib_list[attribCount]; 52 break; 53 } 54 attribCount++; 55 } 56 57 if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && 58 (!attribCaveat || attribCaveat[1] != EGL_NONE)) { 59 60 // Insert 2 extra attributes to force-enable MSAA 4x 61 EGLint aaAttribs[attribCount + 4]; 62 aaAttribs[0] = EGL_SAMPLE_BUFFERS; 63 aaAttribs[1] = 1; 64 aaAttribs[2] = EGL_SAMPLES; 65 aaAttribs[3] = 4; 66 67 memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); 68 69 EGLint numConfigAA; 70 EGLBoolean resAA = cnx->egl.eglChooseConfig( 71 dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); 72 73 if (resAA == EGL_TRUE && numConfigAA > 0) { 74 ALOGD("Enabling MSAA 4x"); 75 *num_config = numConfigAA; 76 return resAA; 77 } 78 } 79 } 80 } 81 82 res = cnx->egl.eglChooseConfig( 83 dp->disp.dpy, attrib_list, configs, config_size, num_config); 84 } 85 return res; 86}
在这里,如果为了调试强制使用 MSAA,即多重采样抗锯齿(MultiSampling Anti-Aliasing,简称MSAA),则会插入 2 个额外的属性来强制启用 MSAA 4x,然后调用图形硬件特有的实际 EGL 实现库的 eglChooseConfig() 完成设置。否则直接用设备特有的实际 EGL 实现库的 eglChooseConfig() 完成设置。
我们前面 在 Android 中使用 OpenGL 一文中的示例里,attrib_list 的实际值如下:
1 int[] mConfigSpec = { EGL10.EGL_RED_SIZE, 5, 2 EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, 3 EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
这个配置大概用于配置 OpenGL ES 渲染所用的颜色模式,深度大小等。
eglCreateWindowSurface()
然后来看 eglCreateWindowSurface()。在 EGLImpl 中,它有着如下这样的定义:
1 public EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list) { 2 Surface sur = null; 3 if (native_window instanceof SurfaceView) { 4 SurfaceView surfaceView = (SurfaceView)native_window; 5 sur = surfaceView.getHolder().getSurface(); 6 } else if (native_window instanceof SurfaceHolder) { 7 SurfaceHolder holder = (SurfaceHolder)native_window; 8 sur = holder.getSurface(); 9 } else if (native_window instanceof Surface) { 10 sur = (Surface) native_window; 11 } 12 13 long eglSurfaceId; 14 if (sur != null) { 15 eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list); 16 } else if (native_window instanceof SurfaceTexture) { 17 eglSurfaceId = _eglCreateWindowSurfaceTexture(display, config, 18 native_window, attrib_list); 19 } else { 20 throw new java.lang.UnsupportedOperationException( 21 "eglCreateWindowSurface() can only be called with an instance of " + 22 "Surface, SurfaceView, SurfaceHolder or SurfaceTexture at the moment."); 23 } 24 25 if (eglSurfaceId == 0) { 26 return EGL10.EGL_NO_SURFACE; 27 } 28 return new EGLSurfaceImpl( eglSurfaceId ); 29 } 30. . . . . . 31 private native long _eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list); 32 private native long _eglCreateWindowSurfaceTexture(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
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 类定义如下:
1public class EGLSurfaceImpl extends EGLSurface { 2 long mEGLSurface; 3 private long mNativePixelRef; 4 public EGLSurfaceImpl() { 5 mEGLSurface = 0; 6 mNativePixelRef = 0; 7 } 8 public EGLSurfaceImpl(long surface) { 9 mEGLSurface = surface; 10 mNativePixelRef = 0; 11 } 12 13 @Override 14 public boolean equals(Object o) { 15 if (this == o) return true; 16 if (o == null || getClass() != o.getClass()) return false; 17 18 EGLSurfaceImpl that = (EGLSurfaceImpl) o; 19 20 return mEGLSurface == that.mEGLSurface; 21 22 } 23 24 @Override 25 public int hashCode() { 26 /* 27 * Based on the algorithm suggested in 28 * http://developer.android.com/reference/java/lang/Object.html 29 */ 30 int result = 17; 31 result = 31 * result + (int) (mEGLSurface ^ (mEGLSurface >>> 32)); 32 return result; 33 } 34}
EGLSurfaceImpl 仅仅是本地层对象句柄的简单封装。
本地层方法 _eglCreateWindowSurface() 的实现如下:
1static jfieldID gConfig_EGLConfigFieldID; 2. . . . . . 3static inline EGLConfig getConfig(JNIEnv* env, jobject o) { 4 if (!o) return 0; 5 return (EGLConfig)env->GetLongField(o, gConfig_EGLConfigFieldID); 6} 7. . . . . . 8static void nativeClassInit(JNIEnv *_env, jclass eglImplClass) 9{ 10 jclass config_class = _env->FindClass("com/google/android/gles_jni/EGLConfigImpl"); 11 gConfig_class = (jclass) _env->NewGlobalRef(config_class); 12 gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(J)V"); 13 gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "J"); 14. . . . . . 15static jlong jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject display, 16 jobject config, jobject native_window, jintArray attrib_list) { 17 if (display == NULL || config == NULL 18 || !validAttribList(_env, attrib_list)) { 19 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 20 return JNI_FALSE; 21 } 22 EGLDisplay dpy = getDisplay(_env, display); 23 EGLContext cnf = getConfig(_env, config); 24 sp<ANativeWindow> window; 25 if (native_window == NULL) { 26not_valid_surface: 27 jniThrowException(_env, "java/lang/IllegalArgumentException", 28 "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface"); 29 return 0; 30 } 31 32 window = android_view_Surface_getNativeWindow(_env, native_window); 33 if (window == NULL) 34 goto not_valid_surface; 35 36 jint* base = beginNativeAttribList(_env, attrib_list); 37 EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base); 38 endNativeAttributeList(_env, attrib_list, base); 39 return reinterpret_cast<jlong>(sur); 40}
在这个函数中,首先从 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:
1sp<ANativeWindow> android_view_Surface_getNativeWindow(JNIEnv* env, jobject surfaceObj) { 2 return android_view_Surface_getSurface(env, surfaceObj); 3} 4 5sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { 6 sp<Surface> sur; 7 jobject lock = env->GetObjectField(surfaceObj, 8 gSurfaceClassInfo.mLock); 9 if (env->MonitorEnter(lock) == JNI_OK) { 10 sur = reinterpret_cast<Surface *>( 11 env->GetLongField(surfaceObj, gSurfaceClassInfo.mNativeObject)); 12 env->MonitorExit(lock); 13 } 14 env->DeleteLocalRef(lock); 15 return sur; 16}
得到的 ANativeWindow 实际为本地层的 Surface 类对象。
最后调用 EGL wrapper 库的 eglCreateWindowSurface() 创建 EGLSurface 并返回给调用者。
EGL wrapper 库的 eglCreateWindowSurface() 定义(位于 frameworks/native/opengl/libs/EGL/eglApi.cpp)如下:
1EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, 2 NativeWindowType window, 3 const EGLint *attrib_list) 4{ 5 clearError(); 6 7 egl_connection_t* cnx = NULL; 8 egl_display_ptr dp = validate_display_connection(dpy, cnx); 9 if (dp) { 10 EGLDisplay iDpy = dp->disp.dpy; 11 12 int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); 13 if (result != OK) { 14 ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " 15 "failed (%#x) (already connected to another API?)", 16 window, result); 17 return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); 18 } 19 20 // Set the native window's buffers format to match what this config requests. 21 // Whether to use sRGB gamma is not part of the EGLconfig, but is part 22 // of our native format. So if sRGB gamma is requested, we have to 23 // modify the EGLconfig's format before setting the native window's 24 // format. 25 26 // by default, just pick RGBA_8888 27 EGLint format = HAL_PIXEL_FORMAT_RGBA_8888; 28 android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN; 29 30 EGLint a = 0; 31 cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_ALPHA_SIZE, &a); 32 if (a > 0) { 33 // alpha-channel requested, there's really only one suitable format 34 format = HAL_PIXEL_FORMAT_RGBA_8888; 35 } else { 36 EGLint r, g, b; 37 r = g = b = 0; 38 cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE, &r); 39 cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g); 40 cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE, &b); 41 EGLint colorDepth = r + g + b; 42 if (colorDepth <= 16) { 43 format = HAL_PIXEL_FORMAT_RGB_565; 44 } else { 45 format = HAL_PIXEL_FORMAT_RGBX_8888; 46 } 47 } 48 49 // now select a corresponding sRGB format if needed 50 if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) { 51 for (const EGLint* attr = attrib_list; *attr != EGL_NONE; attr += 2) { 52 if (*attr == EGL_GL_COLORSPACE_KHR) { 53 if (ENABLE_EGL_KHR_GL_COLORSPACE) { 54 dataSpace = modifyBufferDataspace(dataSpace, *(attr+1)); 55 } else { 56 // Normally we'd pass through unhandled attributes to 57 // the driver. But in case the driver implements this 58 // extension but we're disabling it, we want to prevent 59 // it getting through -- support will be broken without 60 // our help. 61 ALOGE("sRGB window surfaces not supported"); 62 return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); 63 } 64 } 65 } 66 } 67 68 if (format != 0) { 69 int err = native_window_set_buffers_format(window, format); 70 if (err != 0) { 71 ALOGE("error setting native window pixel format: %s (%d)", 72 strerror(-err), err); 73 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 74 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); 75 } 76 } 77 78 if (dataSpace != 0) { 79 int err = native_window_set_buffers_data_space(window, dataSpace); 80 if (err != 0) { 81 ALOGE("error setting native window pixel dataSpace: %s (%d)", 82 strerror(-err), err); 83 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 84 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); 85 } 86 } 87 88 // the EGL spec requires that a new EGLSurface default to swap interval 89 // 1, so explicitly set that on the window here. 90 ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); 91 anw->setSwapInterval(anw, 1); 92 93 EGLSurface surface = cnx->egl.eglCreateWindowSurface( 94 iDpy, config, window, attrib_list); 95 if (surface != EGL_NO_SURFACE) { 96 egl_surface_t* s = new egl_surface_t(dp.get(), config, window, 97 surface, cnx); 98 return s; 99 } 100 101 // EGLSurface creation failed 102 native_window_set_buffers_format(window, 0); 103 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 104 } 105 return EGL_NO_SURFACE; 106}
eglCreateWindowSurface() 执行步骤如下: 第一步,获得 egl_connection_t 和 egl_display_ptr:
1egl_display_ptr validate_display(EGLDisplay dpy) { 2 egl_display_ptr dp = get_display(dpy); 3 if (!dp) 4 return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL)); 5 if (!dp->isReady()) 6 return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL)); 7 8 return dp; 9} 10 11egl_display_ptr validate_display_connection(EGLDisplay dpy, 12 egl_connection_t*& cnx) { 13 cnx = NULL; 14 egl_display_ptr dp = validate_display(dpy); 15 if (!dp) 16 return dp; 17 cnx = &gEGLImpl; 18 if (cnx->dso == 0) { 19 return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL)); 20 } 21 return dp; 22}
第二步,通过函数 native_window_api_connect() (定义位于 system/core/include/system/window.h) 为本地窗口连接 EGL API:
1/* 2 * native_window_api_connect(..., int api) 3 * connects an API to this window. only one API can be connected at a time. 4 * Returns -EINVAL if for some reason the window cannot be connected, which 5 * can happen if it's connected to some other API. 6 */ 7static inline int native_window_api_connect( 8 struct ANativeWindow* window, int api) 9{ 10 return window->perform(window, NATIVE_WINDOW_API_CONNECT, api); 11}
第三步,根据配置计算颜色模式。
第四步,如果 GPU 图形硬件支持 EGL_KHR_gl_colorspace 扩展,则计算得到色彩空间。
1// The EGL_KHR_gl_colorspace spec hasn't been ratified yet, so these haven't 2// been added to the Khronos egl.h. 3#define EGL_GL_COLORSPACE_KHR EGL_VG_COLORSPACE 4#define EGL_GL_COLORSPACE_SRGB_KHR EGL_VG_COLORSPACE_sRGB 5#define EGL_GL_COLORSPACE_LINEAR_KHR EGL_VG_COLORSPACE_LINEAR 6 7// Turn linear formats into corresponding sRGB formats when colorspace is 8// EGL_GL_COLORSPACE_SRGB_KHR, or turn sRGB formats into corresponding linear 9// formats when colorspace is EGL_GL_COLORSPACE_LINEAR_KHR. In any cases where 10// the modification isn't possible, the original dataSpace is returned. 11static android_dataspace modifyBufferDataspace( android_dataspace dataSpace, 12 EGLint colorspace) { 13 if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { 14 return HAL_DATASPACE_SRGB_LINEAR; 15 } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { 16 return HAL_DATASPACE_SRGB; 17 } 18 return dataSpace; 19} 20. . . . . . 21 // now select a corresponding sRGB format if needed 22 if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) { 23 for (const EGLint* attr = attrib_list; *attr != EGL_NONE; attr += 2) { 24 if (*attr == EGL_GL_COLORSPACE_KHR) { 25 if (ENABLE_EGL_KHR_GL_COLORSPACE) { 26 dataSpace = modifyBufferDataspace(dataSpace, *(attr+1)); 27 } else { 28 // Normally we'd pass through unhandled attributes to 29 // the driver. But in case the driver implements this 30 // extension but we're disabling it, we want to prevent 31 // it getting through -- support will be broken without 32 // our help. 33 ALOGE("sRGB window surfaces not supported"); 34 return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); 35 } 36 } 37 } 38 }
第五步,为 window (本地窗口) 设置颜色模式。比较奇怪,获得色彩模式与设置色彩模式之间,为什么要隔一段计算颜色空间的逻辑呢?
1/* 2 * native_window_set_buffers_format(..., int format) 3 * All buffers dequeued after this call will have the format specified. 4 * 5 * If the specified format is 0, the default buffer format will be used. 6 */ 7static inline int native_window_set_buffers_format( 8 struct ANativeWindow* window, 9 int format) 10{ 11 return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format); 12}
第六步,为 window 设置色彩空间。
1/* 2 * native_window_set_buffers_data_space(..., int dataSpace) 3 * All buffers queued after this call will be associated with the dataSpace 4 * parameter specified. 5 * 6 * dataSpace specifies additional information about the buffer that's dependent 7 * on the buffer format and the endpoints. For example, it can be used to convey 8 * the color space of the image data in the buffer, or it can be used to 9 * indicate that the buffers contain depth measurement data instead of color 10 * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been 11 * overridden by the consumer. 12 */ 13static inline int native_window_set_buffers_data_space( 14 struct ANativeWindow* window, 15 android_dataspace_t dataSpace) 16{ 17 return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE, 18 dataSpace); 19}
第七步,设置 Swap interval。
1 // the EGL spec requires that a new EGLSurface default to swap interval 2 // 1, so explicitly set that on the window here. 3 ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); 4 anw->setSwapInterval(anw, 1);
第八步,调用实际的设备 EGL 库接口创建 EGLSurface,并据此创建 egl_surface_t 返回给调用者。
EGLImpl 中的本地层方法 _eglCreateWindowSurfaceTexture() 的实现如下:
1static jlong jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display, 2 jobject config, jobject native_window, jintArray attrib_list) { 3 if (display == NULL || config == NULL 4 || !validAttribList(_env, attrib_list)) { 5 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 6 return 0; 7 } 8 EGLDisplay dpy = getDisplay(_env, display); 9 EGLContext cnf = getConfig(_env, config); 10 sp<ANativeWindow> window; 11 if (native_window == 0) { 12not_valid_surface: 13 jniThrowException(_env, "java/lang/IllegalArgumentException", 14 "Make sure the SurfaceTexture is valid"); 15 return 0; 16 } 17 18 sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(_env, native_window)); 19 window = new Surface(producer, true); 20 if (window == NULL) 21 goto not_valid_surface; 22 23 jint* base = beginNativeAttribList(_env, attrib_list); 24 EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base); 25 endNativeAttributeList(_env, attrib_list, base); 26 return reinterpret_cast<jlong>(sur); 27}
这个函数与 jni_eglCreateWindowSurface() 函数类似。只是它会从传入的 Java 对象 SurfaceTexture 获得本地层的 IGraphicBufferProducer 对象引用,然后利用该引用创建本地层 Surface。后面的流程就与 jni_eglCreateWindowSurface() 的流程就完全一样。
由此不难理解本地层的 Surface 是 IGraphicBufferProducer 的封装,并提供函数来方便操作 IGraphicBufferProducer。
eglCreateContext()
Android 的 OpenGL 应用,通过 EGL.eglCreateContext() 创建EGL context。在 EGLImpl 中该方法定义如下:
1 public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list) { 2 long eglContextId = _eglCreateContext(display, config, share_context, attrib_list); 3 if (eglContextId == 0) { 4 return EGL10.EGL_NO_CONTEXT; 5 } 6 return new EGLContextImpl( eglContextId ); 7 } 8. . . . . . 9 private native long _eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);
这个方方法调用本地层方法 _eglCreateContext() 创建本地层 EGL context 并获得其 ID,然后创建 EGLContextImpl 对象并返回给调用者。
EGLContextImpl 定义如下:
1public class EGLContextImpl extends EGLContext { 2 private GLImpl mGLContext; 3 long mEGLContext; 4 5 public EGLContextImpl(long ctx) { 6 mEGLContext = ctx; 7 mGLContext = new GLImpl(); 8 } 9 10 @Override 11 public GL getGL() { 12 return mGLContext; 13 } 14 15 @Override 16 public boolean equals(Object o) { 17 if (this == o) return true; 18 if (o == null || getClass() != o.getClass()) return false; 19 20 EGLContextImpl that = (EGLContextImpl) o; 21 22 return mEGLContext == that.mEGLContext; 23 } 24 25 @Override 26 public int hashCode() { 27 /* 28 * Based on the algorithm suggested in 29 * http://developer.android.com/reference/java/lang/Object.html 30 */ 31 int result = 17; 32 result = 31 * result + (int) (mEGLContext ^ (mEGLContext >>> 32)); 33 return result; 34 } 35}
它是对本地层的 EGL context ID 和 GLImpl 的简单封装。
本地层方法 _eglCreateContext() 实现如下:
1static jlong jni_eglCreateContext(JNIEnv *_env, jobject _this, jobject display, 2 jobject config, jobject share_context, jintArray attrib_list) { 3 if (display == NULL || config == NULL || share_context == NULL 4 || !validAttribList(_env, attrib_list)) { 5 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 6 return JNI_FALSE; 7 } 8 EGLDisplay dpy = getDisplay(_env, display); 9 EGLConfig cnf = getConfig(_env, config); 10 EGLContext shr = getContext(_env, share_context); 11 jint* base = beginNativeAttribList(_env, attrib_list); 12 EGLContext ctx = eglCreateContext(dpy, cnf, shr, base); 13 endNativeAttributeList(_env, attrib_list, base); 14 return reinterpret_cast<jlong>(ctx); 15}
这个方法从传入的 Java 对象中获得相应的本地层对象,随后通过 EGL wrapper 库的 eglCreateContext() 创建本地层 EGL context 对象,并将其 ID 返回个调用者。
EGL wrapper 库中 eglCreateContext() 的定义如下:
1EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, 2 EGLContext share_list, const EGLint *attrib_list) 3{ 4 clearError(); 5 6 egl_connection_t* cnx = NULL; 7 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 8 if (dp) { 9 if (share_list != EGL_NO_CONTEXT) { 10 if (!ContextRef(dp.get(), share_list).get()) { 11 return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); 12 } 13 egl_context_t* const c = get_context(share_list); 14 share_list = c->context; 15 } 16 EGLContext context = cnx->egl.eglCreateContext( 17 dp->disp.dpy, config, share_list, attrib_list); 18 if (context != EGL_NO_CONTEXT) { 19 // figure out if it's a GLESv1 or GLESv2 20 int version = 0; 21 if (attrib_list) { 22 while (*attrib_list != EGL_NONE) { 23 GLint attr = *attrib_list++; 24 GLint value = *attrib_list++; 25 if (attr == EGL_CONTEXT_CLIENT_VERSION) { 26 if (value == 1) { 27 version = egl_connection_t::GLESv1_INDEX; 28 } else if (value == 2 || value == 3) { 29 version = egl_connection_t::GLESv2_INDEX; 30 } 31 } 32 }; 33 } 34 egl_context_t* c = new egl_context_t(dpy, context, config, cnx, 35 version); 36 return c; 37 } 38 } 39 return EGL_NO_CONTEXT; 40}
创建 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 类对象。该类定义如下:
1class egl_context_t: public egl_object_t { 2protected: 3 ~egl_context_t() {} 4public: 5 typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref; 6 7 egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, 8 egl_connection_t const* cnx, int version); 9 10 void onLooseCurrent(); 11 void onMakeCurrent(EGLSurface draw, EGLSurface read); 12 13 EGLDisplay dpy; 14 EGLContext context; 15 EGLConfig config; 16 EGLSurface read; 17 EGLSurface draw; 18 egl_connection_t const* cnx; 19 int version; 20 String8 gl_extensions; 21 Vector<String8> tokenized_gl_extensions; 22};
此时这个 egl_context_t 还无法实际使用,它还没有关联 EGLSurface。
eglMakeCurrent()
eglMakeCurrent() 为 EGLContext 关联 EGLSurface,并为当前线程启用该 EGLContext。EGLImpl 中 eglMakeCurrent() 定义如下:
public native boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
这是一个本地层方法,其本地层实现如下:
1static jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display, jobject draw, jobject read, jobject context) { 2 if (display == NULL || draw == NULL || read == NULL || context == NULL) { 3 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 4 return JNI_FALSE; 5 } 6 EGLDisplay dpy = getDisplay(_env, display); 7 EGLSurface sdr = getSurface(_env, draw); 8 EGLSurface srd = getSurface(_env, read); 9 EGLContext ctx = getContext(_env, context); 10 return EglBoolToJBool(eglMakeCurrent(dpy, sdr, srd, ctx)); 11}
这个实现也很直接:它从传入的 Java 对象参数中获得它们本地层对象,然后调用 EGL wrapper 库的 eglMakeCurrent() 并将结果返回给调用者。
EGL wrapper 库中 eglMakeCurrent() 定义如下:
1EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, 2 EGLSurface read, EGLContext ctx) 3{ 4 clearError(); 5 6 egl_display_ptr dp = validate_display(dpy); 7 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 8 9 // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not 10 // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is 11 // a valid but uninitialized display. 12 if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || 13 (draw != EGL_NO_SURFACE) ) { 14 if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, EGL_FALSE); 15 } 16 17 // get a reference to the object passed in 18 ContextRef _c(dp.get(), ctx); 19 SurfaceRef _d(dp.get(), draw); 20 SurfaceRef _r(dp.get(), read); 21 22 // validate the context (if not EGL_NO_CONTEXT) 23 if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { 24 // EGL_NO_CONTEXT is valid 25 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 26 } 27 28 // these are the underlying implementation's object 29 EGLContext impl_ctx = EGL_NO_CONTEXT; 30 EGLSurface impl_draw = EGL_NO_SURFACE; 31 EGLSurface impl_read = EGL_NO_SURFACE; 32 33 // these are our objects structs passed in 34 egl_context_t * c = NULL; 35 egl_surface_t const * d = NULL; 36 egl_surface_t const * r = NULL; 37 38 // these are the current objects structs 39 egl_context_t * cur_c = get_context(getContext()); 40 41 if (ctx != EGL_NO_CONTEXT) { 42 c = get_context(ctx); 43 impl_ctx = c->context; 44 } else { 45 // no context given, use the implementation of the current context 46 if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { 47 // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); 48 return setError(EGL_BAD_MATCH, EGL_FALSE); 49 } 50 if (cur_c == NULL) { 51 // no current context 52 // not an error, there is just no current context. 53 return EGL_TRUE; 54 } 55 } 56 57 // retrieve the underlying implementation's draw EGLSurface 58 if (draw != EGL_NO_SURFACE) { 59 if (!_d.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE); 60 d = get_surface(draw); 61 impl_draw = d->surface; 62 } 63 64 // retrieve the underlying implementation's read EGLSurface 65 if (read != EGL_NO_SURFACE) { 66 if (!_r.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE); 67 r = get_surface(read); 68 impl_read = r->surface; 69 } 70 71 72 EGLBoolean result = dp->makeCurrent(c, cur_c, 73 draw, read, ctx, 74 impl_draw, impl_read, impl_ctx); 75 76 if (result == EGL_TRUE) { 77 if (c) { 78 setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 79 egl_tls_t::setContext(ctx); 80 _c.acquire(); 81 _r.acquire(); 82 _d.acquire(); 83 } else { 84 setGLHooksThreadSpecific(&gHooksNoContext); 85 egl_tls_t::setContext(EGL_NO_CONTEXT); 86 } 87 } else { 88 // this will ALOGE the error 89 egl_connection_t* const cnx = &gEGLImpl; 90 result = setError(cnx->egl.eglGetError(), EGL_FALSE); 91 } 92 return result; 93}
eglMakeCurrent() 首先获得线程当前关联的 EGL context:
1static inline EGLContext getContext() { return egl_tls_t::getContext(); } 2. . . . . . 3 // these are the current objects structs 4 egl_context_t * cur_c = get_context(getContext());
在 EGL wrapper 这一级,通过线程局部存储保存当前线程关联的 egl_context_t:
1EGLContext egl_tls_t::getContext() { 2 if (sKey == TLS_KEY_NOT_INITIALIZED) { 3 return EGL_NO_CONTEXT; 4 } 5 egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey); 6 if (!tls) return EGL_NO_CONTEXT; 7 return tls->ctx; 8}
frameworks/native/opengl/libs/EGL/egl_object.h 中 get_context() 的定义如下:
1template<typename NATIVE, typename EGL> 2static inline NATIVE* egl_to_native_cast(EGL arg) { 3 return reinterpret_cast<NATIVE*>(arg); 4} 5. . . . . . 6static inline 7egl_context_t* get_context(EGLContext context) { 8 return egl_to_native_cast<egl_context_t>(context); 9}
eglMakeCurrent() 接口有两个主要的功能:一是为一个有效的 EGLContext 关联 Surface,并把该 EGLContext 关联到当前线程,此时对 Surface 没有特别要求,这也就意味着 EGLContext 可以在不关联 Surface 被设置为当前 EGLContext;二是当传入的 EGLContext 为空时,则将当前线程关联的 EGLContext 接触关联,且当 EGLContext 为空时,传入的 Surface 必须为 EGL_NO_SURFACE。
eglMakeCurrent() 通过 egl_display_t::makeCurrent() 执行底层图形硬件 EGL 库实现级别的 make current:
1EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, 2 EGLSurface draw, EGLSurface read, EGLContext /*ctx*/, 3 EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx) 4{ 5 EGLBoolean result; 6 7 // by construction, these are either 0 or valid (possibly terminated) 8 // it should be impossible for these to be invalid 9 ContextRef _cur_c(cur_c); 10 SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); 11 SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); 12 13 { // scope for the lock 14 Mutex::Autolock _l(lock); 15 if (c) { 16 result = c->cnx->egl.eglMakeCurrent( 17 disp.dpy, impl_draw, impl_read, impl_ctx); 18 if (result == EGL_TRUE) { 19 c->onMakeCurrent(draw, read); 20 if (!cur_c) { 21 mHibernation.incWakeCount(HibernationMachine::STRONG); 22 } 23 } 24 } else { 25 result = cur_c->cnx->egl.eglMakeCurrent( 26 disp.dpy, impl_draw, impl_read, impl_ctx); 27 if (result == EGL_TRUE) { 28 cur_c->onLooseCurrent(); 29 mHibernation.decWakeCount(HibernationMachine::STRONG); 30 } 31 } 32 } 33 34 if (result == EGL_TRUE) { 35 // This cannot be called with the lock held because it might end-up 36 // calling back into EGL (in particular when a surface is destroyed 37 // it calls ANativeWindow::disconnect 38 _cur_c.release(); 39 _cur_r.release(); 40 _cur_d.release(); 41 } 42 43 return result; 44}
这里调用底层的图形硬件 EGL 库实现的 eglMakeCurrent() 完成操作,并根据需要回调 egl_context_t 的函数。随后释放当前的 context。
如果传入的 EGLContext 有效,且当前已经关联了一个 EGLContext,则新的替换旧的,但是旧的 egl_context_t 的回调 onLooseCurrent() 没有被调到。
如果是要为当前线程关联 EGLContext 的话,则设置线程局部的 GL Hooks 为 EGLContext 的 OpenGL ES 版本所对应的 Hooks,并在线程局部存储中保存 EGLContext,然后增加 EGLContext 和 Surface 的引用计数:
1void setGLHooksThreadSpecific(gl_hooks_t const *value) { 2 setGlThreadSpecific(value); 3} 4. . . . . . 5void setGlThreadSpecific(gl_hooks_t const *value) { 6 gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); 7 tls_hooks[TLS_SLOT_OPENGL_API] = value; 8}
egl_tls_t::setContext(ctx) 定义如下:
1egl_tls_t* egl_tls_t::getTLS() { 2 egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); 3 if (tls == 0) { 4 tls = new egl_tls_t; 5 pthread_setspecific(sKey, tls); 6 } 7 return tls; 8} 9. . . . . . 10void egl_tls_t::setContext(EGLContext ctx) { 11 validateTLSKey(); 12 getTLS()->ctx = ctx; 13}
如果是要清除当前线程关联的 EGLContext 的话,则置线程局部的 GL Hooks 为 gHooksNoContext,并设置当前线程关联的 EGLContext 为 EGL_NO_CONTEXT。
猜测在设备特有的 EGL 库实现一级,无论是软件实现,还是硬件实现,都存在着另外的线程局部存储变量来保存那一级的 EGLContext 数据。
自此之后,就可以使用 OpenGL ES 的接口来渲染图形了。
Done.