diff --git a/meson.build b/meson.build index eae1c965..297b363f 100644 --- a/meson.build +++ b/meson.build @@ -88,7 +88,6 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/widgets/android_widget_EditText.c', 'src/api-impl-jni/widgets/android_widget_ImageButton.c', 'src/api-impl-jni/widgets/android_widget_ScrollView.c', - 'src/api-impl-jni/widgets/android_opengl_GLSurfaceView.c', 'src/api-impl-jni/widgets/android_widget_ImageView.c', 'src/api-impl-jni/widgets/android_widget_FrameLayout.c', 'src/api-impl-jni/widgets/WrapperWidget.c', diff --git a/src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c b/src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c index 30c24e85..e631e231 100644 --- a/src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c +++ b/src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c @@ -3,6 +3,8 @@ #include "../defines.h" #include "../util.h" +#include "../../libandroid/native_window.h" + #include "../generated_headers/com_google_android_gles_jni_EGLImpl.h" // helpers from android source (TODO: either use GetIntArrayElements, or figure out if GetPrimitiveArrayCritical is superior and use it everywhere if so) @@ -67,3 +69,59 @@ JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1egl return ret; } + +JNIEXPORT jlong JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglCreateWindowSurface(JNIEnv *env, jobject this, jlong display, jlong config, jobject surface, jintArray _attrib_list) +{ + struct ANativeWindow *native_window = ANativeWindow_fromSurface(env, surface); + EGLint *attrib_list = get_int_array_crit(env, _attrib_list); + EGLSurface ret = bionic_eglCreateWindowSurface(_PTR(display), _PTR(config), native_window, attrib_list); + release_int_array_crit(env, _attrib_list, attrib_list); + return _INTPTR(ret); +} + +JNIEXPORT jlong JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglGetDisplay(JNIEnv *env, jobject this, jobject display) +{ + return _INTPTR(bionic_eglGetDisplay(0)); // FIXME: why is display passed as an Object??? how do we get an integer from that +} + +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglInitialize(JNIEnv *env, jobject this, jlong display, jintArray _major_minor) +{ + EGLint *major_minor = get_int_array_crit(env, _major_minor); + bool ret = eglInitialize(_PTR(display), &major_minor[0], &major_minor[1]); + release_int_array_crit(env, _major_minor, major_minor); + return ret; +} + +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglGetConfigAttrib(JNIEnv *env, jobject this, jlong display, jlong config, jint attribute, jintArray _value) +{ + EGLint *value = get_int_array_crit(env, _value); + bool ret = eglGetConfigAttrib(_PTR(display), _PTR(config), attribute, &value[0]); + release_int_array_crit(env, _value, value); + return ret; +} + +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglMakeCurrent(JNIEnv *env, jobject this, jlong display, jlong draw_surface, jlong read_surface, jlong context) +{ + return eglMakeCurrent(_PTR(display), _PTR(draw_surface), _PTR(read_surface), _PTR(context)); +} + +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglSwapBuffers(JNIEnv *env, jobject this, jlong display, jlong surface) +{ + return eglSwapBuffers(_PTR(display), _PTR(surface)); +} + +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglDestroySurface(JNIEnv *env, jobject this, jlong display, jlong surface) +{ + struct ANativeWindow *native_window = g_hash_table_lookup(egl_surface_hashtable, _PTR(surface)); + + bool ret = eglDestroySurface(_PTR(display), _PTR(surface)); + /* ANativeWindow_fromSurface starts the refcounter at 1, so this will destroy the native window */ + ANativeWindow_release(native_window); + + return ret; +} + +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglDestroyContext(JNIEnv *env, jobject this, jlong display, jlong context) +{ + return eglDestroyContext(_PTR(display), _PTR(context)); +} diff --git a/src/api-impl-jni/generated_headers/android_opengl_GLSurfaceView.h b/src/api-impl-jni/generated_headers/android_opengl_GLSurfaceView.h deleted file mode 100644 index b341f9b4..00000000 --- a/src/api-impl-jni/generated_headers/android_opengl_GLSurfaceView.h +++ /dev/null @@ -1,221 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class android_opengl_GLSurfaceView */ - -#ifndef _Included_android_opengl_GLSurfaceView -#define _Included_android_opengl_GLSurfaceView -#ifdef __cplusplus -extern "C" { -#endif -#undef android_opengl_GLSurfaceView_NO_ID -#define android_opengl_GLSurfaceView_NO_ID -1L -#undef android_opengl_GLSurfaceView_NOT_FOCUSABLE -#define android_opengl_GLSurfaceView_NOT_FOCUSABLE 0L -#undef android_opengl_GLSurfaceView_FOCUSABLE -#define android_opengl_GLSurfaceView_FOCUSABLE 1L -#undef android_opengl_GLSurfaceView_FOCUSABLE_MASK -#define android_opengl_GLSurfaceView_FOCUSABLE_MASK 1L -#undef android_opengl_GLSurfaceView_FITS_SYSTEM_WINDOWS -#define android_opengl_GLSurfaceView_FITS_SYSTEM_WINDOWS 2L -#undef android_opengl_GLSurfaceView_VISIBLE -#define android_opengl_GLSurfaceView_VISIBLE 0L -#undef android_opengl_GLSurfaceView_INVISIBLE -#define android_opengl_GLSurfaceView_INVISIBLE 4L -#undef android_opengl_GLSurfaceView_GONE -#define android_opengl_GLSurfaceView_GONE 8L -#undef android_opengl_GLSurfaceView_VISIBILITY_MASK -#define android_opengl_GLSurfaceView_VISIBILITY_MASK 12L -#undef android_opengl_GLSurfaceView_ENABLED -#define android_opengl_GLSurfaceView_ENABLED 0L -#undef android_opengl_GLSurfaceView_DISABLED -#define android_opengl_GLSurfaceView_DISABLED 32L -#undef android_opengl_GLSurfaceView_ENABLED_MASK -#define android_opengl_GLSurfaceView_ENABLED_MASK 32L -#undef android_opengl_GLSurfaceView_WILL_NOT_DRAW -#define android_opengl_GLSurfaceView_WILL_NOT_DRAW 128L -#undef android_opengl_GLSurfaceView_DRAW_MASK -#define android_opengl_GLSurfaceView_DRAW_MASK 128L -#undef android_opengl_GLSurfaceView_SCROLLBARS_NONE -#define android_opengl_GLSurfaceView_SCROLLBARS_NONE 0L -#undef android_opengl_GLSurfaceView_SCROLLBARS_HORIZONTAL -#define android_opengl_GLSurfaceView_SCROLLBARS_HORIZONTAL 256L -#undef android_opengl_GLSurfaceView_SCROLLBARS_VERTICAL -#define android_opengl_GLSurfaceView_SCROLLBARS_VERTICAL 512L -#undef android_opengl_GLSurfaceView_SCROLLBARS_MASK -#define android_opengl_GLSurfaceView_SCROLLBARS_MASK 768L -#undef android_opengl_GLSurfaceView_FILTER_TOUCHES_WHEN_OBSCURED -#define android_opengl_GLSurfaceView_FILTER_TOUCHES_WHEN_OBSCURED 1024L -#undef android_opengl_GLSurfaceView_OPTIONAL_FITS_SYSTEM_WINDOWS -#define android_opengl_GLSurfaceView_OPTIONAL_FITS_SYSTEM_WINDOWS 2048L -#undef android_opengl_GLSurfaceView_FADING_EDGE_NONE -#define android_opengl_GLSurfaceView_FADING_EDGE_NONE 0L -#undef android_opengl_GLSurfaceView_FADING_EDGE_HORIZONTAL -#define android_opengl_GLSurfaceView_FADING_EDGE_HORIZONTAL 4096L -#undef android_opengl_GLSurfaceView_FADING_EDGE_VERTICAL -#define android_opengl_GLSurfaceView_FADING_EDGE_VERTICAL 8192L -#undef android_opengl_GLSurfaceView_FADING_EDGE_MASK -#define android_opengl_GLSurfaceView_FADING_EDGE_MASK 12288L -#undef android_opengl_GLSurfaceView_CLICKABLE -#define android_opengl_GLSurfaceView_CLICKABLE 16384L -#undef android_opengl_GLSurfaceView_DRAWING_CACHE_ENABLED -#define android_opengl_GLSurfaceView_DRAWING_CACHE_ENABLED 32768L -#undef android_opengl_GLSurfaceView_SAVE_DISABLED -#define android_opengl_GLSurfaceView_SAVE_DISABLED 65536L -#undef android_opengl_GLSurfaceView_SAVE_DISABLED_MASK -#define android_opengl_GLSurfaceView_SAVE_DISABLED_MASK 65536L -#undef android_opengl_GLSurfaceView_WILL_NOT_CACHE_DRAWING -#define android_opengl_GLSurfaceView_WILL_NOT_CACHE_DRAWING 131072L -#undef android_opengl_GLSurfaceView_FOCUSABLE_IN_TOUCH_MODE -#define android_opengl_GLSurfaceView_FOCUSABLE_IN_TOUCH_MODE 262144L -#undef android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_LOW -#define android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_LOW 524288L -#undef android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_HIGH -#define android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_HIGH 1048576L -#undef android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_AUTO -#define android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_AUTO 0L -#undef android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_MASK -#define android_opengl_GLSurfaceView_DRAWING_CACHE_QUALITY_MASK 1572864L -#undef android_opengl_GLSurfaceView_LONG_CLICKABLE -#define android_opengl_GLSurfaceView_LONG_CLICKABLE 2097152L -#undef android_opengl_GLSurfaceView_DUPLICATE_PARENT_STATE -#define android_opengl_GLSurfaceView_DUPLICATE_PARENT_STATE 4194304L -#undef android_opengl_GLSurfaceView_SCROLLBARS_INSIDE_OVERLAY -#define android_opengl_GLSurfaceView_SCROLLBARS_INSIDE_OVERLAY 0L -#undef android_opengl_GLSurfaceView_SCROLLBARS_INSIDE_INSET -#define android_opengl_GLSurfaceView_SCROLLBARS_INSIDE_INSET 16777216L -#undef android_opengl_GLSurfaceView_SCROLLBARS_OUTSIDE_OVERLAY -#define android_opengl_GLSurfaceView_SCROLLBARS_OUTSIDE_OVERLAY 33554432L -#undef android_opengl_GLSurfaceView_SCROLLBARS_OUTSIDE_INSET -#define android_opengl_GLSurfaceView_SCROLLBARS_OUTSIDE_INSET 50331648L -#undef android_opengl_GLSurfaceView_SCROLLBARS_INSET_MASK -#define android_opengl_GLSurfaceView_SCROLLBARS_INSET_MASK 16777216L -#undef android_opengl_GLSurfaceView_SCROLLBARS_OUTSIDE_MASK -#define android_opengl_GLSurfaceView_SCROLLBARS_OUTSIDE_MASK 33554432L -#undef android_opengl_GLSurfaceView_SCROLLBARS_STYLE_MASK -#define android_opengl_GLSurfaceView_SCROLLBARS_STYLE_MASK 50331648L -#undef android_opengl_GLSurfaceView_KEEP_SCREEN_ON -#define android_opengl_GLSurfaceView_KEEP_SCREEN_ON 67108864L -#undef android_opengl_GLSurfaceView_SOUND_EFFECTS_ENABLED -#define android_opengl_GLSurfaceView_SOUND_EFFECTS_ENABLED 134217728L -#undef android_opengl_GLSurfaceView_HAPTIC_FEEDBACK_ENABLED -#define android_opengl_GLSurfaceView_HAPTIC_FEEDBACK_ENABLED 268435456L -#undef android_opengl_GLSurfaceView_PARENT_SAVE_DISABLED -#define android_opengl_GLSurfaceView_PARENT_SAVE_DISABLED 536870912L -#undef android_opengl_GLSurfaceView_PARENT_SAVE_DISABLED_MASK -#define android_opengl_GLSurfaceView_PARENT_SAVE_DISABLED_MASK 536870912L -#undef android_opengl_GLSurfaceView_FOCUSABLES_ALL -#define android_opengl_GLSurfaceView_FOCUSABLES_ALL 0L -#undef android_opengl_GLSurfaceView_FOCUSABLES_TOUCH_MODE -#define android_opengl_GLSurfaceView_FOCUSABLES_TOUCH_MODE 1L -#undef android_opengl_GLSurfaceView_FOCUS_BACKWARD -#define android_opengl_GLSurfaceView_FOCUS_BACKWARD 1L -#undef android_opengl_GLSurfaceView_FOCUS_FORWARD -#define android_opengl_GLSurfaceView_FOCUS_FORWARD 2L -#undef android_opengl_GLSurfaceView_FOCUS_LEFT -#define android_opengl_GLSurfaceView_FOCUS_LEFT 17L -#undef android_opengl_GLSurfaceView_FOCUS_UP -#define android_opengl_GLSurfaceView_FOCUS_UP 33L -#undef android_opengl_GLSurfaceView_FOCUS_RIGHT -#define android_opengl_GLSurfaceView_FOCUS_RIGHT 66L -#undef android_opengl_GLSurfaceView_FOCUS_DOWN -#define android_opengl_GLSurfaceView_FOCUS_DOWN 130L -#undef android_opengl_GLSurfaceView_MEASURED_SIZE_MASK -#define android_opengl_GLSurfaceView_MEASURED_SIZE_MASK 16777215L -#undef android_opengl_GLSurfaceView_MEASURED_STATE_MASK -#define android_opengl_GLSurfaceView_MEASURED_STATE_MASK -16777216L -#undef android_opengl_GLSurfaceView_MEASURED_HEIGHT_STATE_SHIFT -#define android_opengl_GLSurfaceView_MEASURED_HEIGHT_STATE_SHIFT 16L -#undef android_opengl_GLSurfaceView_MEASURED_STATE_TOO_SMALL -#define android_opengl_GLSurfaceView_MEASURED_STATE_TOO_SMALL 16777216L -#undef android_opengl_GLSurfaceView_PFLAG2_DRAG_CAN_ACCEPT -#define android_opengl_GLSurfaceView_PFLAG2_DRAG_CAN_ACCEPT 1L -#undef android_opengl_GLSurfaceView_PFLAG2_DRAG_HOVERED -#define android_opengl_GLSurfaceView_PFLAG2_DRAG_HOVERED 2L -#undef android_opengl_GLSurfaceView_LAYOUT_DIRECTION_LTR -#define android_opengl_GLSurfaceView_LAYOUT_DIRECTION_LTR 0L -#undef android_opengl_GLSurfaceView_LAYOUT_DIRECTION_RTL -#define android_opengl_GLSurfaceView_LAYOUT_DIRECTION_RTL 1L -#undef android_opengl_GLSurfaceView_LAYOUT_DIRECTION_INHERIT -#define android_opengl_GLSurfaceView_LAYOUT_DIRECTION_INHERIT 2L -#undef android_opengl_GLSurfaceView_LAYOUT_DIRECTION_LOCALE -#define android_opengl_GLSurfaceView_LAYOUT_DIRECTION_LOCALE 3L -#undef android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT -#define android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT 2L -#undef android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_MASK -#define android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_MASK 12L -#undef android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL -#define android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL 16L -#undef android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_RESOLVED -#define android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_RESOLVED 32L -#undef android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK -#define android_opengl_GLSurfaceView_PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK 48L -#undef android_opengl_GLSurfaceView_STATUS_BAR_HIDDEN -#define android_opengl_GLSurfaceView_STATUS_BAR_HIDDEN 1L -#undef android_opengl_GLSurfaceView_STATUS_BAR_VISIBLE -#define android_opengl_GLSurfaceView_STATUS_BAR_VISIBLE 0L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_FULLSCREEN -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_FULLSCREEN 4L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_HIDE_NAVIGATION -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_HIDE_NAVIGATION 2L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_IMMERSIVE -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_IMMERSIVE 2048L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_IMMERSIVE_STICKY -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_IMMERSIVE_STICKY 4096L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 1024L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 512L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LAYOUT_STABLE -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LAYOUT_STABLE 256L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LOW_PROFILE -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_LOW_PROFILE 1L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_VISIBLE -#define android_opengl_GLSurfaceView_SYSTEM_UI_FLAG_VISIBLE 0L -#undef android_opengl_GLSurfaceView_SYSTEM_UI_LAYOUT_FLAGS -#define android_opengl_GLSurfaceView_SYSTEM_UI_LAYOUT_FLAGS 1536L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_CENTER -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_CENTER 4L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_GRAVITY -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_GRAVITY 1L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_INHERIT -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_INHERIT 0L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_TEXT_END -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_TEXT_END 3L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_TEXT_START -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_TEXT_START 2L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_VIEW_END -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_VIEW_END 6L -#undef android_opengl_GLSurfaceView_TEXT_ALIGNMENT_VIEW_START -#define android_opengl_GLSurfaceView_TEXT_ALIGNMENT_VIEW_START 5L -#undef android_opengl_GLSurfaceView_TEXT_DIRECTION_ANY_RTL -#define android_opengl_GLSurfaceView_TEXT_DIRECTION_ANY_RTL 2L -#undef android_opengl_GLSurfaceView_TEXT_DIRECTION_FIRST_STRONG -#define android_opengl_GLSurfaceView_TEXT_DIRECTION_FIRST_STRONG 1L -#undef android_opengl_GLSurfaceView_TEXT_DIRECTION_INHERIT -#define android_opengl_GLSurfaceView_TEXT_DIRECTION_INHERIT 0L -#undef android_opengl_GLSurfaceView_TEXT_DIRECTION_LOCALE -#define android_opengl_GLSurfaceView_TEXT_DIRECTION_LOCALE 5L -#undef android_opengl_GLSurfaceView_TEXT_DIRECTION_LTR -#define android_opengl_GLSurfaceView_TEXT_DIRECTION_LTR 3L -#undef android_opengl_GLSurfaceView_TEXT_DIRECTION_RTL -#define android_opengl_GLSurfaceView_TEXT_DIRECTION_RTL 4L -/* - * Class: android_opengl_GLSurfaceView - * Method: native_constructor - * Signature: (Landroid/content/Context;Landroid/util/AttributeSet;)J - */ -JNIEXPORT jlong JNICALL Java_android_opengl_GLSurfaceView_native_1constructor - (JNIEnv *, jobject, jobject, jobject); - -/* - * Class: android_opengl_GLSurfaceView - * Method: native_set_renderer - * Signature: (Landroid/opengl/GLSurfaceView/Renderer;)V - */ -JNIEXPORT void JNICALL Java_android_opengl_GLSurfaceView_native_1set_1renderer - (JNIEnv *, jobject, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/api-impl-jni/generated_headers/com_google_android_gles_jni_EGLImpl.h b/src/api-impl-jni/generated_headers/com_google_android_gles_jni_EGLImpl.h index e02630f0..4b51fe1c 100644 --- a/src/api-impl-jni/generated_headers/com_google_android_gles_jni_EGLImpl.h +++ b/src/api-impl-jni/generated_headers/com_google_android_gles_jni_EGLImpl.h @@ -23,6 +23,70 @@ JNIEXPORT jlong JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglCre JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglChooseConfig (JNIEnv *, jobject, jlong, jintArray, jlongArray, jint, jintArray); +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglCreateWindowSurface + * Signature: (JJLandroid/view/Surface;[I)J + */ +JNIEXPORT jlong JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglCreateWindowSurface + (JNIEnv *, jobject, jlong, jlong, jobject, jintArray); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglGetDisplay + * Signature: (Ljava/lang/Object;)J + */ +JNIEXPORT jlong JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglGetDisplay + (JNIEnv *, jobject, jobject); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglInitialize + * Signature: (J[I)Z + */ +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglInitialize + (JNIEnv *, jobject, jlong, jintArray); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglGetConfigAttrib + * Signature: (JJI[I)Z + */ +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglGetConfigAttrib + (JNIEnv *, jobject, jlong, jlong, jint, jintArray); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglMakeCurrent + * Signature: (JJJJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglMakeCurrent + (JNIEnv *, jobject, jlong, jlong, jlong, jlong); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglSwapBuffers + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglSwapBuffers + (JNIEnv *, jobject, jlong, jlong); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglDestroySurface + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglDestroySurface + (JNIEnv *, jobject, jlong, jlong); + +/* + * Class: com_google_android_gles_jni_EGLImpl + * Method: native_eglDestroyContext + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_google_android_gles_1jni_EGLImpl_native_1eglDestroyContext + (JNIEnv *, jobject, jlong, jlong); + #ifdef __cplusplus } #endif diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 04e654c1..4f23d1dd 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -87,24 +87,15 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.canvas.class = _REF((*env)->FindClass(env, "android/graphics/Canvas")); handle_cache.canvas.constructor = _METHOD(handle_cache.canvas.class, "", "(JJ)V"); - handle_cache.renderer.class = _REF((*env)->FindClass(env, "android/opengl/GLSurfaceView$Renderer")); - handle_cache.renderer.onSurfaceCreated = _METHOD(handle_cache.renderer.class, "onSurfaceCreated", "(Ljavax/microedition/khronos/opengles/GL10;Ljavax/microedition/khronos/egl/EGLConfig;)V"); - handle_cache.renderer.onSurfaceChanged = _METHOD(handle_cache.renderer.class, "onSurfaceChanged", "(Ljavax/microedition/khronos/opengles/GL10;II)V"); - handle_cache.renderer.onDrawFrame = _METHOD(handle_cache.renderer.class, "onDrawFrame", "(Ljavax/microedition/khronos/opengles/GL10;)V"); - - handle_cache.gl_surface_view.class = _REF((*env)->FindClass(env, "android/opengl/GLSurfaceView")); - handle_cache.gl_surface_view.onTouchEvent = _METHOD(handle_cache.gl_surface_view.class, "onTouchEvent", "(Landroid/view/MotionEvent;)Z"); - handle_cache.gl_surface_view.wrap_EGLContextFactory_createContext = _METHOD(handle_cache.gl_surface_view.class, "wrap_EGLContextFactory_createContext", "(JJ)J"); - handle_cache.gl_surface_view.wrap_EGLConfigChooser_chooseConfig = _METHOD(handle_cache.gl_surface_view.class, "wrap_EGLConfigChooser_chooseConfig", "(J)J"); - handle_cache.audio_track_periodic_listener.class = _REF((*env)->FindClass(env, "android/media/AudioTrack$OnPlaybackPositionUpdateListener")); handle_cache.audio_track_periodic_listener.onPeriodicNotification = _METHOD(handle_cache.audio_track_periodic_listener.class, "onPeriodicNotification", "(Landroid/media/AudioTrack;)V"); handle_cache.input_queue_callback.class = _REF((*env)->FindClass(env, "android/view/InputQueue$Callback")); handle_cache.input_queue_callback.onInputQueueCreated = _METHOD(handle_cache.input_queue_callback.class, "onInputQueueCreated", "(Landroid/view/InputQueue;)V"); - handle_cache.surface_holder_callback.class = _REF((*env)->FindClass(env, "android/view/SurfaceHolder$Callback")); - handle_cache.surface_holder_callback.surfaceCreated = _METHOD(handle_cache.surface_holder_callback.class, "surfaceCreated", "(Landroid/view/SurfaceHolder;)V"); + handle_cache.surface_view.class = _REF((*env)->FindClass(env, "android/view/SurfaceView")); + handle_cache.surface_view.surfaceCreated = _METHOD(handle_cache.surface_view.class, "surfaceCreated", "()V"); + handle_cache.surface_view.surfaceChanged = _METHOD(handle_cache.surface_view.class, "surfaceChanged", "(III)V"); handle_cache.view.class = _REF((*env)->FindClass(env, "android/view/View")); if((*env)->ExceptionCheck(env)) diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h index 0dbf46d9..180b9e24 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -41,18 +41,6 @@ struct handle_cache { jclass class; jmethodID constructor; } canvas; - struct { - jclass class; - jmethodID onSurfaceCreated; - jmethodID onSurfaceChanged; - jmethodID onDrawFrame; - } renderer; - struct { - jclass class; - jmethodID onTouchEvent; - jmethodID wrap_EGLContextFactory_createContext; - jmethodID wrap_EGLConfigChooser_chooseConfig; - } gl_surface_view; struct { jclass class; jmethodID onPeriodicNotification; @@ -64,7 +52,8 @@ struct handle_cache { struct { jclass class; jmethodID surfaceCreated; - } surface_holder_callback; + jmethodID surfaceChanged; + } surface_view; struct { jclass class; jmethodID setLayoutParams; diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index badc5a22..03822813 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -65,7 +65,6 @@ static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *e if(d->num_clicks == 0) break; case GDK_TOUCH_UPDATE: - printf("d->num_clicks: %u\n", d->num_clicks); gdk_event_get_widget_relative_position(event, widget, &x, &y); call_ontouch_callback(MOTION_EVENT_ACTION_MOVE, x, y, d); break; diff --git a/src/api-impl-jni/widgets/android_opengl_GLSurfaceView.c b/src/api-impl-jni/widgets/android_opengl_GLSurfaceView.c deleted file mode 100644 index cdfd89a5..00000000 --- a/src/api-impl-jni/widgets/android_opengl_GLSurfaceView.c +++ /dev/null @@ -1,498 +0,0 @@ -#define GL_GLEXT_PROTOTYPES - -#include -#include - -#include -#include -#include - -#include -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif - -#include "../defines.h" -#include "../util.h" - -#include "WrapperWidget.h" - -#include "../generated_headers/android_opengl_GLSurfaceView.h" - -#define SOURCE_TOUCHSCREEN 4098 - -// for whatever reason, some Mesa builds don't export the OES function (which we use in order to have GLESv1 support) -GL_APICALL void GL_APIENTRY _glEGLImageTargetTexture2DOES_load(GLenum target, GLeglImageOES image); -static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC _glEGLImageTargetTexture2DOES = &_glEGLImageTargetTexture2DOES_load; - -GL_APICALL void GL_APIENTRY _glEGLImageTargetTexture2DOES_load(GLenum target, GLeglImageOES image) -{ - _glEGLImageTargetTexture2DOES = - (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); - - _glEGLImageTargetTexture2DOES(target, image); -} - -GL_APICALL void GL_APIENTRY __attribute__((weak)) glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) -{ - _glEGLImageTargetTexture2DOES(target, image); -} -// end of OES workaround - -//#define FIXME__WIDTH 540 -//#define FIXME__HEIGHT 960 - -// FIXME: what do we do here? we should probably take these from the size of the GLArea, but how do we change the sizes of all the textures and buffers etc when the GLArea changes size? -// for now, borrow the initial app window width/height -extern int FIXME__WIDTH; -extern int FIXME__HEIGHT; - -// mainly frame markers (very obtrusive, not recommended for most builds) -#ifdef DEBUG_GLAREA -#define d_printf(...) printf(__VA_ARGS__) -#else -#define d_printf(...) -#endif - -#define ___GL_TRACE___(message) glDebugMessageInsert(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_MARKER, 0xdeadbeef, GL_DEBUG_SEVERITY_NOTIFICATION, strlen(message), message) - -JNIEXPORT jlong JNICALL Java_android_opengl_GLSurfaceView_native_1constructor(JNIEnv *env, jobject this, jobject context, jobject attrs) -{ - GtkWidget *wrapper = g_object_ref(wrapper_widget_new()); - gtk_widget_set_vexpand(wrapper, TRUE); - GtkWidget *gl_area = gtk_gl_area_new(); - wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), gl_area); - wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this); - return _INTPTR(gl_area); -} - -//two triangles put together to make a square -const float positions[] = { - -1, -1, 0, - -1, 1, 0, - 1, -1, 0, - 1, -1, 0, - -1, 1, 0, - 1, 1, 0 -}; - -const char *vertexStr = "#version 320 es\n" - "in vec3 pos;\n" - "out vec2 texCoords;\n" - "void main(){\n" - " texCoords = vec2(pos.x, pos.y);\n" - " gl_Position = vec4((pos.x * 2.0) - 1.0, (pos.y * 2.0) - 1.0, pos.z, 1.0);\n" - "}\n"; - -const char *fragmentStr = "#version 320 es\n" - "out highp vec4 outputColor;\n" - "in highp vec2 texCoords;\n" - "uniform sampler2D texSampler;\n" - "void main(){\n" - " outputColor = texture(texSampler, texCoords);\n" - "}\n"; - -struct render_priv { - GLuint program; - GLuint vao; - int texUnit; - GLuint texBufferdObject; - GLuint sampler; - -/* --- */ - EGLSurface eglSurface; - EGLContext eglContext; - - GLuint FramebufferName; - GLuint renderedTexture; - GLuint texture_id; - - EGLImage egl_image; -}; - -static void check_shader_compile_error(GLuint shader) -{ - GLint compileResult; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); - - if(compileResult != GL_TRUE) { - GLint log_size = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_size); - - GLchar *info_log = malloc(log_size); - glGetShaderInfoLog(shader, log_size, &log_size, info_log); - fprintf(stderr, "\nError compiling one of the shaders used by GLSurfaceView:\n%s\n---\nsince this error shouldn't ever happen, we fail hard\n", info_log); - free(info_log); - - exit(1); - } -} - -static void check_program_link_error(GLuint program) -{ - GLint link_status = 0; - glGetProgramiv(program, GL_LINK_STATUS, &link_status); - - if(link_status != GL_TRUE) { - GLint log_size = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_size); - - GLchar *info_log = malloc(log_size); - glGetProgramInfoLog(program, log_size, &log_size, info_log); - fprintf(stderr, "\nError linking the program used by GLSurfaceView:\n%s\n---\nsince this error shouldn't ever happen, we fail hard\n", info_log); - free(info_log); - - exit(1); - } -} - -#define check_egl_error() \ - do { \ - EGLint error_ = eglGetError(); \ - if(error_ != EGL_SUCCESS) \ - fprintf(stderr, "\nError in %s (%s:%d): \n" \ - "eglGetError reported 0x%x\n" \ - "this might be because we need to implement more stuff, so we will continue from here\n", \ - __func__, __FILE__, __LINE__, error_); \ - } while(0) - -// TODO: use this where appropriate -#define check_gl_error() \ - do { \ - GLenum error_ = glGetError(); \ - if(error_ != GL_NO_ERROR) \ - fprintf(stderr, "\nError in %s (%s:%d): \n" \ - "glGetError reported 0x%x\n" \ - "this might be because we need to implement more stuff, so we will continue from here\n", \ - __func__, __FILE__, __LINE__, error_); \ - } while(0) - - -struct jni_gl_callback_data { JavaVM *jvm; jobject this; jobject renderer; bool first_time;}; -static void on_realize(GtkGLArea *gl_area, struct jni_gl_callback_data *d) -{ -// --- - // compensate for offset between the widget coordinates and the surface coordinates - double off_x; - double off_y; - - GtkWidget *window = GTK_WIDGET(gtk_widget_get_native(gl_area)); - gtk_native_get_surface_transform(GTK_NATIVE(window), &off_x, &off_y); - - FIXME__WIDTH -= off_x; - FIXME__HEIGHT -= off_y; -// --- - - gtk_gl_area_make_current(gl_area); - - struct render_priv *render_priv = g_object_get_data(G_OBJECT(gl_area), "render_priv"); - - JNIEnv *env; - (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); - - EGLNativeDisplayType display_id = EGL_DEFAULT_DISPLAY; -#ifdef GDK_WINDOWING_WAYLAND - GdkDisplay *gdk_dpy = gdk_display_get_default(); - if (GDK_IS_WAYLAND_DISPLAY(gdk_dpy)) { - display_id = (EGLNativeDisplayType)gdk_wayland_display_get_wl_display(gdk_dpy); - } -#endif - EGLDisplay eglDisplay = eglGetDisplay(display_id); - EGLDisplay old_eglDisplay = eglGetCurrentDisplay(); - - d_printf("GTK version: >%d__%d__%d<\n", gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version()); - d_printf("OpenGL version: >%s<\n", glGetString(GL_VERSION)); - - glGenTextures(1, &render_priv->texture_id); - - // vertex shader - - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vertexStr, NULL); - glCompileShader(vertexShader); - - check_shader_compile_error(vertexShader); - - // fragment shader - - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fragmentStr, NULL); - glCompileShader(fragmentShader); - - check_shader_compile_error(fragmentShader); - - // program - - render_priv->program = glCreateProgram(); - - glAttachShader(render_priv->program, vertexShader); - glAttachShader(render_priv->program, fragmentShader); - glLinkProgram(render_priv->program); - - check_program_link_error(render_priv->program); - - glUseProgram(render_priv->program); - - // the triangles - - glGenVertexArrays(1, &render_priv->vao); - glBindVertexArray(render_priv->vao); - - GLuint positionBufferObject; - glGenBuffers(1, &positionBufferObject); - glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*6*3,positions, GL_STREAM_DRAW); - - GLuint posIndex = glGetAttribLocation(render_priv->program, "pos"); - glEnableVertexAttribArray(posIndex); - glVertexAttribPointer(posIndex, 3, GL_FLOAT, GL_FALSE, 0, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - glUseProgram(0); - - glUseProgram(render_priv->program); - - // the texture we will be rendering to - - glGenTextures(1, &render_priv->texBufferdObject); - glBindTexture(GL_TEXTURE_2D, render_priv->texBufferdObject); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glBindTexture(GL_TEXTURE_2D, 0); - - // texture sampler for our triangles - - glGenSamplers(1, &render_priv->sampler); - glSamplerParameteri(render_priv->sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(render_priv->sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(render_priv->sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLuint samplerUniform = glGetUniformLocation(render_priv->program, "texSampler"); - glUniform1i(samplerUniform, render_priv->texUnit); - glUseProgram(0); - - // EGL setup - - eglInitialize(eglDisplay, 0, 0); - eglBindAPI(EGL_OPENGL_ES_API); - - EGLConfig eglConfig; - - // a roundabout way to let the app define the parameter list, and then choose the best fit out of all the returned configs - eglConfig = (EGLConfig)_PTR((*env)->CallLongMethod(env, d->this, handle_cache.gl_surface_view.wrap_EGLConfigChooser_chooseConfig, _INTPTR(eglDisplay))); - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - check_egl_error(); - - // set up the pbuffer (TODO: is there any way to dynamically change the width/height?) - - EGLint pbufferAttribs[5]; - pbufferAttribs[0] = EGL_WIDTH; - pbufferAttribs[1] = FIXME__WIDTH; - pbufferAttribs[2] = EGL_HEIGHT; - pbufferAttribs[3] = FIXME__HEIGHT; - pbufferAttribs[4] = EGL_NONE; - - render_priv->eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, pbufferAttribs); - check_egl_error(); - - // a roundabout way to run eglCreateContext with the atrribute list that the app chose - render_priv->eglContext = (EGLContext)_PTR((*env)->CallLongMethod(env, d->this, handle_cache.gl_surface_view.wrap_EGLContextFactory_createContext, _INTPTR(eglDisplay), _INTPTR(eglConfig))); - check_egl_error(); - - // save the EGL state before we change it, so we can switch back later - EGLContext old_eglContext = eglGetCurrentContext(); - EGLSurface old_read_surface = eglGetCurrentSurface(EGL_READ); - EGLSurface old_draw_surface = eglGetCurrentSurface(EGL_DRAW); - - bool result = eglMakeCurrent(eglDisplay, render_priv->eglSurface, render_priv->eglSurface, render_priv->eglContext); - check_egl_error(); - - // set up the framebuffer - glGenFramebuffers(1, &render_priv->FramebufferName); - glBindFramebuffer(GL_FRAMEBUFFER, render_priv->FramebufferName); - check_gl_error(); - - glGenTextures(1, &render_priv->renderedTexture); - glBindTexture(GL_TEXTURE_2D, render_priv->renderedTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FIXME__WIDTH, FIXME__HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - check_gl_error(); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - check_gl_error(); - - GLuint depthrenderbuffer; - glGenRenderbuffers(1, &depthrenderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, FIXME__WIDTH, FIXME__HEIGHT); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer); - check_gl_error(); - - // Set "renderedTexture" as our colour attachement #0 - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_priv->renderedTexture, 0); - check_gl_error(); - - // Set the list of draw buffers. - GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; - glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers - check_gl_error(); - - if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - fprintf(stderr, "Error: glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE\n"); - - // create the EGL Image which we will use to access the rendered-to texture from Gtk's EGL context - render_priv->egl_image = eglCreateImage(eglDisplay, render_priv->eglContext, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(intptr_t)render_priv->renderedTexture, NULL); - check_egl_error(); - - // the GLSurfaceView implements SurfaceHolder.Callback, and the app may expect that we call the `surfaceCreated` method - (*env)->CallVoidMethod(env, d->this, handle_cache.surface_holder_callback.surfaceCreated, NULL); - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - - // Here we call the app's onSurfaceCreated callback. This is the android API's equivalent of the `realize` callback that we are currently in. - ___GL_TRACE___("---- calling onSurfaceCreated"); - (*env)->CallVoidMethod(env, d->renderer, handle_cache.renderer.onSurfaceCreated, _GET_OBJ_FIELD(d->this, "java_gl_wrapper", "Ljavax/microedition/khronos/opengles/GL10;"), NULL); // FIXME passing NULL only works if the app doesn't use these parameters - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - ___GL_TRACE___("---- returned from calling onSurfaceCreated"); - - // This should be called from Gtk's `resize` callback. However, until we figure out how to resize the pbuffer, the framebuffer, and the texture, we can't afford to pass the actual widget's dimensions here - ___GL_TRACE___("---- calling onSurfaceChanged"); - (*env)->CallVoidMethod(env, d->renderer, handle_cache.renderer.onSurfaceChanged, NULL, FIXME__WIDTH, FIXME__HEIGHT); // FIXME put this in the right callback (and pass the actual dimensions) - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - ___GL_TRACE___("---- returned from calling onSurfaceChanged"); - - // restore the EGL context so that Gtk doesn't get confused - - result = eglMakeCurrent(old_eglDisplay, old_read_surface, old_draw_surface, old_eglContext); - check_egl_error(); -} - -static gboolean render(GtkGLArea *gl_area, GdkGLContext *context, struct jni_gl_callback_data *d) -{ - struct render_priv *render_priv = g_object_get_data(G_OBJECT(gl_area), "render_priv"); - - JNIEnv *env; - (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); - - EGLNativeDisplayType display_id = EGL_DEFAULT_DISPLAY; -#ifdef GDK_WINDOWING_WAYLAND - GdkDisplay *gdk_dpy = gdk_display_get_default(); - if (GDK_IS_WAYLAND_DISPLAY(gdk_dpy)) { - display_id = (EGLNativeDisplayType)gdk_wayland_display_get_wl_display(gdk_dpy); - } -#endif - EGLDisplay eglDisplay = eglGetDisplay(display_id); - EGLDisplay old_eglDisplay = eglGetCurrentDisplay(); - - // save the EGL state before we change it, so we can switch back later - EGLContext old_eglContext = eglGetCurrentContext(); - EGLSurface old_read_surface = eglGetCurrentSurface(EGL_READ); - EGLSurface old_draw_surface = eglGetCurrentSurface(EGL_DRAW); - - bool result = eglMakeCurrent(eglDisplay, render_priv->eglSurface, render_priv->eglSurface, render_priv->eglContext); - check_egl_error(); - - // bind the framebuffer that we are rendering into - check_gl_error(); - glBindFramebuffer(GL_FRAMEBUFFER, render_priv->FramebufferName); - check_gl_error(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_priv->renderedTexture, 0); - check_gl_error(); - - if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - fprintf(stderr, "Error: glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE\n"); - - d_printf("OpenGL version before calling onDrawFrame: >%s<\n", glGetString(GL_VERSION)); - d_printf("\n\n\n---- calling onDrawFrame\n\n"); - // this marks the part where the app is doing the rendering (as opposed to Gtk) for easier orientation in a trace (e.g apitrace) - // TODO: make a program that extracts just these fragments from a trace - // TODO: add a unique identifier of this GLArea (worst case the pointer to this widget) -check_gl_error(); - ___GL_TRACE___("---- calling onDrawFrame"); - - // Here we call the app's onDrawFrame callback. This is the android API's equivalent of the `render` callback that we are currently in. - (*env)->CallVoidMethod(env, d->renderer, handle_cache.renderer.onDrawFrame, NULL); // FIXME passing NULL only works if the app doesn't use this parameter - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - - ___GL_TRACE___("---- returned from calling onDrawFrame"); -check_gl_error(); - d_printf("\n---- returned from calling onDrawFrame\n\n\n\n"); - - eglSwapBuffers(eglDisplay, render_priv->eglSurface); - - // switch the EGL context back to Gtk's one - result = eglMakeCurrent(old_eglDisplay, old_read_surface, old_draw_surface, old_eglContext); - check_egl_error(); - - gtk_gl_area_make_current(gl_area); - - d_printf("OpenGL version after restore: >%s<\n", glGetString(GL_VERSION)); - d_printf("\n\n\n---- drawing the texture containing our frame\n\n"); - ___GL_TRACE___("---- drawing the texture containing our frame"); - - // draw on a black background (TODO: isn't this pointless?) - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClearDepth(1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // draw two triangles (a rectangle) painted with the texture that we were rendering into - glUseProgram(render_priv->program); - glBindVertexArray(render_priv->vao); - - glActiveTexture(GL_TEXTURE0 + render_priv->texUnit); - glBindTexture(GL_TEXTURE_2D, render_priv->texBufferdObject); - - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, render_priv->egl_image); - glBindSampler(render_priv->texUnit, render_priv->sampler); - - glDrawArrays(GL_TRIANGLES, 0, 6); - - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); - glUseProgram(0); - - ___GL_TRACE___("---- end of drawing the texture containing our frame"); - d_printf("\n---- end of drawing the texture containing our frame\n\n\n\n"); - - return TRUE; -} - -extern gboolean tick_callback(GtkWidget* widget, GdkFrameClock* frame_clock, gpointer user_data); - -JNIEXPORT void JNICALL Java_android_opengl_GLSurfaceView_native_1set_1renderer(JNIEnv *env, jobject this, jobject renderer) -{ - GtkWidget *gl_area = GTK_WIDGET(_PTR(_GET_LONG_FIELD(this, "widget"))); - - gtk_gl_area_set_has_stencil_buffer(GTK_GL_AREA(gl_area), true); // FIXME don't assume what the app wants - gtk_gl_area_set_has_depth_buffer(GTK_GL_AREA(gl_area), true); - - gtk_gl_area_set_use_es(GTK_GL_AREA(gl_area), true); // FIXME - - struct render_priv *render_priv = malloc(sizeof(struct render_priv)); - render_priv->texUnit = 0; - render_priv->sampler = 0; - render_priv->FramebufferName = 0; - - g_object_set_data(G_OBJECT(gl_area), "render_priv", render_priv); - - JavaVM *jvm; - (*env)->GetJavaVM(env, &jvm); - - struct jni_gl_callback_data *gl_callback_data = malloc(sizeof(struct jni_gl_callback_data)); - gl_callback_data->jvm = jvm; - gl_callback_data->this = _REF(this); - gl_callback_data->renderer = _REF(renderer); - gl_callback_data->first_time = 1; - - g_signal_connect(gl_area, "render", G_CALLBACK(render), gl_callback_data); - g_signal_connect(gl_area, "realize", G_CALLBACK(on_realize), gl_callback_data); - - gtk_widget_add_tick_callback(gl_area, tick_callback, NULL, NULL); - - //FIXME - gtk_widget_set_hexpand(gtk_widget_get_parent(GTK_WIDGET(gl_area)), true); -} diff --git a/src/api-impl-jni/widgets/android_view_SurfaceView.c b/src/api-impl-jni/widgets/android_view_SurfaceView.c index 5ee84cc2..27439f38 100644 --- a/src/api-impl-jni/widgets/android_view_SurfaceView.c +++ b/src/api-impl-jni/widgets/android_view_SurfaceView.c @@ -78,9 +78,16 @@ static void on_resize(GtkWidget* self, gint width, gint height, struct jni_callb // TODO: are there cases where returning RGBA_8888 is a bad idea? // NOTE: we want to call the private method of android.view.SurfaceView, not the related method with this name in the API - (*env)->CallVoidMethod(env, d->this, _METHOD(d->this_class, "surfaceChanged", "(Landroid/view/SurfaceHolder;III)V"), - _GET_OBJ_FIELD(d->this, "mSurfaceHolder", "Landroid/view/SurfaceHolder;"), 1 /*RGBA_8888*/, - width, height); + (*env)->CallVoidMethod(env, d->this, handle_cache.surface_view.surfaceChanged, 1 /*RGBA_8888*/, width, height); +} + +static void on_realize(GtkWidget* self, struct jni_callback_data *d) +{ + JNIEnv *env; + (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); + + // NOTE: we want to call the private method of android.view.SurfaceView, not the related method with this name in the API + (*env)->CallVoidMethod(env, d->this, handle_cache.surface_view.surfaceCreated); } JNIEXPORT jlong JNICALL Java_android_view_SurfaceView_native_1constructor(JNIEnv *env, jobject this, jobject context, jobject attrs) @@ -89,6 +96,7 @@ JNIEXPORT jlong JNICALL Java_android_view_SurfaceView_native_1constructor(JNIEnv GtkWidget *dummy = surface_view_widget_new(); gtk_widget_set_name(dummy, "dummy widget for SurfaceView"); wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), dummy); + wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this); // TODO: is this correct for all usecases? how do we know when it's not? gtk_widget_set_hexpand(wrapper, true); gtk_widget_set_vexpand(wrapper, true); @@ -102,6 +110,7 @@ JNIEXPORT jlong JNICALL Java_android_view_SurfaceView_native_1constructor(JNIEnv callback_data->this_class = _REF((*env)->FindClass(env, "android/view/SurfaceView")); g_signal_connect(dummy, "resize", G_CALLBACK(on_resize), callback_data); + g_signal_connect(dummy, "realize", G_CALLBACK(on_realize), callback_data); return _INTPTR(dummy); } diff --git a/src/api-impl/android/media/MediaPlayer.java b/src/api-impl/android/media/MediaPlayer.java index 1bc43848..2196a780 100644 --- a/src/api-impl/android/media/MediaPlayer.java +++ b/src/api-impl/android/media/MediaPlayer.java @@ -33,6 +33,7 @@ public class MediaPlayer { public void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener dummy) {} public void setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener dummy) {} public void setAudioStreamType(int dummy) {} + public void pause() {} public void prepareAsync() {} public void reset() {} public void release() {} diff --git a/src/api-impl/android/opengl/GLSurfaceView.java b/src/api-impl/android/opengl/GLSurfaceView.java index c86df7fa..9cceebaa 100644 --- a/src/api-impl/android/opengl/GLSurfaceView.java +++ b/src/api-impl/android/opengl/GLSurfaceView.java @@ -1,82 +1,348 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.opengl; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Rect; +import android.content.pm.ConfigurationInfo; +//import android.os.SystemProperties; import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.Surface; +import android.util.Log; import android.view.SurfaceHolder; -import android.view.View; -import com.google.android.gles_jni.EGLImpl; +import android.view.SurfaceView; +import java.io.Writer; +import java.lang.ref.WeakReference; +import java.util.ArrayList; import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; -public class GLSurfaceView extends View implements SurfaceHolder.Callback { // TODO: have this extend SurfaceView? - EGLContextFactory context_factory = new default_ContextFactory(); - EGLConfigChooser config_chooser = new boolean_ConfigChooser(true); - EGL10 java_egl_wrapper = (EGL10)EGLContext.getEGL(); - GL10 java_gl_wrapper = (GL10)EGLContext.getGL(); - int opengl_version = 1; +/** + * An implementation of SurfaceView that uses the dedicated surface for + * displaying OpenGL rendering. + *

+ * A GLSurfaceView provides the following features: + *

+ *

    + *
  • Manages a surface, which is a special piece of memory that can be + * composited into the Android view system. + *
  • Manages an EGL display, which enables OpenGL to render into a surface. + *
  • Accepts a user-provided Renderer object that does the actual rendering. + *
  • Renders on a dedicated thread to decouple rendering performance from the + * UI thread. + *
  • Supports both on-demand and continuous rendering. + *
  • Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls. + *
+ * + *
+ *

Developer Guides

+ *

For more information about how to use OpenGL, read the + * OpenGL developer guide.

+ *
+ * + *

Using GLSurfaceView

+ *

+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the + * View system input event methods. If your application does not need to override event + * methods then GLSurfaceView can be used as-is. For the most part + * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. + * For example, unlike a regular View, drawing is delegated to a separate Renderer object which + * is registered with the GLSurfaceView + * using the {@link #setRenderer(Renderer)} call. + *

+ *

Initializing GLSurfaceView

+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}. + * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or + * more of these methods before calling setRenderer: + *
    + *
  • {@link #setDebugFlags(int)} + *
  • {@link #setEGLConfigChooser(boolean)} + *
  • {@link #setEGLConfigChooser(EGLConfigChooser)} + *
  • {@link #setEGLConfigChooser(int, int, int, int, int, int)} + *
  • {@link #setGLWrapper(GLWrapper)} + *
+ *

+ *

Specifying the android.view.Surface

+ * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent + * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). + * The exact format of a TRANSLUCENT surface is device dependent, but it will be + * a 32-bit-per-pixel surface with 8 bits per component. + *

+ *

Choosing an EGL Configuration

+ * A given Android device may support multiple EGLConfig rendering configurations. + * The available configurations may differ in how may channels of data are present, as + * well as how many bits are allocated to each channel. Therefore, the first thing + * GLSurfaceView has to do when starting to render is choose what EGLConfig to use. + *

+ * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format, + * with at least a 16-bit depth buffer and no stencil. + *

+ * If you would prefer a different EGLConfig + * you can override the default behavior by calling one of the + * setEGLConfigChooser methods. + *

+ *

Debug Behavior

+ * You can optionally modify the behavior of GLSurfaceView by calling + * one or more of the debugging methods {@link #setDebugFlags(int)}, + * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but + * typically they are called before setRenderer so that they take effect immediately. + *

+ *

Setting a Renderer

+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}. + * The renderer is + * responsible for doing the actual OpenGL rendering. + *

+ *

Rendering Mode

+ * Once the renderer is set, you can control whether the renderer draws + * continuously or on-demand by calling + * {@link #setRenderMode}. The default is continuous rendering. + *

+ *

Activity Life-cycle

+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients + * are required to call {@link #onPause()} when the activity pauses and + * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to + * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate + * the OpenGL display. + *

+ *

Handling events

+ *

+ * To handle an event you will typically subclass GLSurfaceView and override the + * appropriate method, just as you would with any other View. However, when handling + * the event, you may need to communicate with the Renderer object + * that's running in the rendering thread. You can do this using any + * standard Java cross-thread communication mechanism. In addition, + * one relatively easy way to communicate with your renderer is + * to call + * {@link #queueEvent(Runnable)}. For example: + *

+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ *     private MyRenderer mMyRenderer;
+ *
+ *     public void start() {
+ *         mMyRenderer = ...;
+ *         setRenderer(mMyRenderer);
+ *     }
+ *
+ *     public boolean onKeyDown(int keyCode, KeyEvent event) {
+ *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ *             queueEvent(new Runnable() {
+ *                 // This method will be called on the rendering
+ *                 // thread:
+ *                 public void run() {
+ *                     mMyRenderer.handleDpadCenter();
+ *                 }});
+ *             return true;
+ *         }
+ *         return super.onKeyDown(keyCode, event);
+ *     }
+ * }
+ * 
+ * + */ +public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { + private final static String TAG = "GLSurfaceView"; + private final static boolean LOG_ATTACH_DETACH = true; + private final static boolean LOG_THREADS = true; + private final static boolean LOG_PAUSE_RESUME = true; + private final static boolean LOG_SURFACE = true; + private final static boolean LOG_RENDERER = true; + private final static boolean LOG_RENDERER_DRAW_FRAME = false; + private final static boolean LOG_EGL = false; + /** + * The renderer only renders + * when the surface is created, or when {@link #requestRender} is called. + * + * @see #getRenderMode() + * @see #setRenderMode(int) + * @see #requestRender() + */ + public final static int RENDERMODE_WHEN_DIRTY = 0; + /** + * The renderer is called + * continuously to re-render the scene. + * + * @see #getRenderMode() + * @see #setRenderMode(int) + */ + public final static int RENDERMODE_CONTINUOUSLY = 1; - // from SurfaceHolder.Callback - public void surfaceCreated(SurfaceHolder surfaceHolder) {} - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} - public void surfaceDestroyed(SurfaceHolder holder) {} + /** + * Check glError() after every GL call and throw an exception if glError indicates + * that an error has occurred. This can be used to help track down which OpenGL ES call + * is causing an error. + * + * @see #getDebugFlags + * @see #setDebugFlags + */ + public final static int DEBUG_CHECK_GL_ERROR = 1; - public GLSurfaceView(Context context, AttributeSet attrs) { - super(context, attrs); - } + /** + * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". + * + * @see #getDebugFlags + * @see #setDebugFlags + */ + public final static int DEBUG_LOG_GL_CALLS = 2; + /** + * Standard View constructor. In order to render something, you + * must call {@link #setRenderer} to register a renderer. + */ public GLSurfaceView(Context context) { super(context); + init(); + } + + /** + * Standard View constructor. In order to render something, you + * must call {@link #setRenderer} to register a renderer. + */ + public GLSurfaceView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); } @Override - protected native long native_constructor(Context context, AttributeSet attrs); - - // Resumes the rendering thread, re-creating the OpenGL context if necessary. It is the counterpart to onPause(). This method should typically be called in Activity.onStart. Must not be called before a renderer has been set. - // TODO: make use of this - public void onResume() {} - - public void setEGLConfigChooser(EGLConfigChooser chooser) { - config_chooser = chooser; + protected void finalize() throws Throwable { + try { + if (mGLThread != null) { + // GLThread may still be running if this view was never + // attached to a window. + mGLThread.requestExitAndWait(); + } + } finally { + super.finalize(); + } } - public void setEGLConfigChooser(boolean needDepth) { - config_chooser = new boolean_ConfigChooser(needDepth); + private void init() { + // Install a SurfaceHolder.Callback so we get notified when the + // underlying surface is created and destroyed + SurfaceHolder holder = getHolder(); + holder.addCallback(this); + // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment + // this statement if back-porting to 2.2 or older: + // holder.setFormat(PixelFormat.RGB_565); + // + // setType is not needed for SDK 2.0 or newer. Uncomment this + // statement if back-porting this code to older SDKs. + // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); } - public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize) { - // setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, - // blueSize, alphaSize, depthSize, stencilSize)); + /** + * Set the glWrapper. If the glWrapper is not null, its + * {@link GLWrapper#wrap(GL)} method is called + * whenever a surface is created. A GLWrapper can be used to wrap + * the GL object that's passed to the renderer. Wrapping a GL + * object enables examining and modifying the behavior of the + * GL calls made by the renderer. + *

+ * Wrapping is typically used for debugging purposes. + *

+ * The default value is null. + * @param glWrapper the new GLWrapper + */ + public void setGLWrapper(GLWrapper glWrapper) { + mGLWrapper = glWrapper; } - public void setEGLContextFactory(EGLContextFactory factory) { - context_factory = factory; + /** + * Set the debug flags to a new value. The value is + * constructed by OR-together zero or more + * of the DEBUG_CHECK_* constants. The debug flags take effect + * whenever a surface is created. The default value is zero. + * @param debugFlags the new debug flags + * @see #DEBUG_CHECK_GL_ERROR + * @see #DEBUG_LOG_GL_CALLS + */ + public void setDebugFlags(int debugFlags) { + mDebugFlags = debugFlags; } - public void setEGLContextClientVersion(int version) { - opengl_version = version; + /** + * Get the current value of the debug flags. + * @return the current value of the debug flags. + */ + public int getDebugFlags() { + return mDebugFlags; } - public void setPreserveEGLContextOnPause(boolean preserveOnPause) {} - - public synchronized boolean shouldTerminateEGLWhenPausing() { - return false; + /** + * Control whether the EGL context is preserved when the GLSurfaceView is paused and + * resumed. + *

+ * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. + * Whether the EGL context is actually preserved or not depends upon whether the + * Android device that the program is running on can support an arbitrary number of EGL + * contexts or not. Devices that can only support a limited number of EGL contexts must + * release the EGL context in order to allow multiple applications to share the GPU. + *

+ * If set to false, the EGL context will be released when the GLSurfaceView is paused, + * and recreated when the GLSurfaceView is resumed. + *

+ * + * The default is false. + * + * @param preserveOnPause preserve the EGL context when paused + */ + public void setPreserveEGLContextOnPause(boolean preserveOnPause) { + mPreserveEGLContextOnPause = preserveOnPause; } - private native void native_set_renderer(Renderer renderer); + /** + * @return true if the EGL context will be preserved when paused + */ + public boolean getPreserveEGLContextOnPause() { + return mPreserveEGLContextOnPause; + } + /** + * Set the renderer associated with this view. Also starts the thread that + * will call the renderer, which in turn causes the rendering to start. + *

This method should be called once and only once in the life-cycle of + * a GLSurfaceView. + *

The following GLSurfaceView methods can only be called before + * setRenderer is called: + *

    + *
  • {@link #setEGLConfigChooser(boolean)} + *
  • {@link #setEGLConfigChooser(EGLConfigChooser)} + *
  • {@link #setEGLConfigChooser(int, int, int, int, int, int)} + *
+ *

+ * The following GLSurfaceView methods can only be called after + * setRenderer is called: + *

    + *
  • {@link #getRenderMode()} + *
  • {@link #onPause()} + *
  • {@link #onResume()} + *
  • {@link #queueEvent(Runnable)} + *
  • {@link #requestRender()} + *
  • {@link #setRenderMode(int)} + *
+ * + * @param renderer the renderer to use to perform OpenGL drawing. + */ public void setRenderer(Renderer renderer) { - System.out.println("setRenderer(" + renderer + ") called"); - - native_set_renderer(renderer); -/* checkRenderThreadState(); + checkRenderThreadState(); if (mEGLConfigChooser == null) { mEGLConfigChooser = new SimpleEGLConfigChooser(true); } @@ -88,100 +354,1564 @@ public class GLSurfaceView extends View implements SurfaceHolder.Callback { // T } mRenderer = renderer; mGLThread = new GLThread(mThisWeakRef); - mGLThread.start();*/ + mGLThread.start(); } + /** + * Install a custom EGLContextFactory. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If this method is not called, then by default + * a context will be created with no shared context and + * with a null attribute list. + */ + public void setEGLContextFactory(EGLContextFactory factory) { + checkRenderThreadState(); + mEGLContextFactory = factory; + } + + /** + * Install a custom EGLWindowSurfaceFactory. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If this method is not called, then by default + * a window surface will be created with a null attribute list. + */ + public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { + checkRenderThreadState(); + mEGLWindowSurfaceFactory = factory; + } + + /** + * Install a custom EGLConfigChooser. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If no setEGLConfigChooser method is called, then by default the + * view will choose an EGLConfig that is compatible with the current + * android.view.Surface, with a depth buffer depth of + * at least 16 bits. + * @param configChooser + */ + public void setEGLConfigChooser(EGLConfigChooser configChooser) { + checkRenderThreadState(); + mEGLConfigChooser = configChooser; + } + + /** + * Install a config chooser which will choose a config + * as close to 16-bit RGB as possible, with or without an optional depth + * buffer as close to 16-bits as possible. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If no setEGLConfigChooser method is called, then by default the + * view will choose an RGB_888 surface with a depth buffer depth of + * at least 16 bits. + * + * @param needDepth + */ + public void setEGLConfigChooser(boolean needDepth) { + setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); + } + + /** + * Install a config chooser which will choose a config + * with at least the specified depthSize and stencilSize, + * and exactly the specified redSize, greenSize, blueSize and alphaSize. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If no setEGLConfigChooser method is called, then by default the + * view will choose an RGB_888 surface with a depth buffer depth of + * at least 16 bits. + * + */ + public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, + int alphaSize, int depthSize, int stencilSize) { + setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, + blueSize, alphaSize, depthSize, stencilSize)); + } + + /** + * Inform the default EGLContextFactory and default EGLConfigChooser + * which EGLContext client version to pick. + *

Use this method to create an OpenGL ES 2.0-compatible context. + * Example: + *

+	 *     public MyView(Context context) {
+	 *         super(context);
+	 *         setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
+	 *         setRenderer(new MyRenderer());
+	 *     }
+	 * 
+ *

Note: Activities which require OpenGL ES 2.0 should indicate this by + * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's + * AndroidManifest.xml file. + *

If this method is called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

This method only affects the behavior of the default EGLContexFactory and the + * default EGLConfigChooser. If + * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied + * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context. + * If + * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied + * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config. + * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0 + */ + public void setEGLContextClientVersion(int version) { + checkRenderThreadState(); + mEGLContextClientVersion = version; + } + + /** + * Set the rendering mode. When renderMode is + * RENDERMODE_CONTINUOUSLY, the renderer is called + * repeatedly to re-render the scene. When renderMode + * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface + * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. + *

+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance + * by allowing the GPU and CPU to idle when the view does not need to be updated. + *

+ * This method can only be called after {@link #setRenderer(Renderer)} + * + * @param renderMode one of the RENDERMODE_X constants + * @see #RENDERMODE_CONTINUOUSLY + * @see #RENDERMODE_WHEN_DIRTY + */ + public void setRenderMode(int renderMode) { + mGLThread.setRenderMode(renderMode); + } + + /** + * Get the current rendering mode. May be called + * from any thread. Must not be called before a renderer has been set. + * @return the current rendering mode. + * @see #RENDERMODE_CONTINUOUSLY + * @see #RENDERMODE_WHEN_DIRTY + */ + public int getRenderMode() { + return mGLThread.getRenderMode(); + } + + /** + * Request that the renderer render a frame. + * This method is typically used when the render mode has been set to + * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. + * May be called + * from any thread. Must not be called before a renderer has been set. + */ + public void requestRender() { + mGLThread.requestRender(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceCreated(SurfaceHolder holder) { + mGLThread.surfaceCreated(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceDestroyed(SurfaceHolder holder) { + // Surface will be destroyed when we return + mGLThread.surfaceDestroyed(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + mGLThread.onWindowResize(w, h); + } + + /** + * Inform the view that the activity is paused. The owner of this view must + * call this method when the activity is paused. Calling this method will + * pause the rendering thread. + * Must not be called before a renderer has been set. + */ + public void onPause() { + mGLThread.onPause(); + } + + /** + * Inform the view that the activity is resumed. The owner of this view must + * call this method when the activity is resumed. Calling this method will + * recreate the OpenGL display and resume the rendering + * thread. + * Must not be called before a renderer has been set. + */ + public void onResume() { + mGLThread.onResume(); + } + + /** + * Queue a runnable to be run on the GL rendering thread. This can be used + * to communicate with the Renderer on the rendering thread. + * Must not be called before a renderer has been set. + * @param r the runnable to be run on the GL rendering thread. + */ + public void queueEvent(Runnable r) { + mGLThread.queueEvent(r); + } + + /** + * This method is used as part of the View class and is not normally + * called or subclassed by clients of GLSurfaceView. + */ + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (LOG_ATTACH_DETACH) { + Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); + } + if (mDetached && (mRenderer != null)) { + int renderMode = RENDERMODE_CONTINUOUSLY; + if (mGLThread != null) { + renderMode = mGLThread.getRenderMode(); + } + mGLThread = new GLThread(mThisWeakRef); + if (renderMode != RENDERMODE_CONTINUOUSLY) { + mGLThread.setRenderMode(renderMode); + } + mGLThread.start(); + } + mDetached = false; + } + + /** + * This method is used as part of the View class and is not normally + * called or subclassed by clients of GLSurfaceView. + * Must not be called before a renderer has been set. + */ +// @Override FIXME + protected void onDetachedFromWindow() { + if (LOG_ATTACH_DETACH) { + Log.d(TAG, "onDetachedFromWindow"); + } + if (mGLThread != null) { + mGLThread.requestExitAndWait(); + } + mDetached = true; +// super.onDetachedFromWindow(); + } + + // ---------------------------------------------------------------------- + + /** + * An interface used to wrap a GL interface. + *

Typically + * used for implementing debugging and tracing on top of the default + * GL interface. You would typically use this by creating your own class + * that implemented all the GL methods by delegating to another GL instance. + * Then you could add your own behavior before or after calling the + * delegate. All the GLWrapper would do was instantiate and return the + * wrapper GL instance: + *

+	 * class MyGLWrapper implements GLWrapper {
+	 *     GL wrap(GL gl) {
+	 *         return new MyGLImplementation(gl);
+	 *     }
+	 *     static class MyGLImplementation implements GL,GL10,GL11,... {
+	 *         ...
+	 *     }
+	 * }
+	 * 
+ * @see #setGLWrapper(GLWrapper) + */ + public interface GLWrapper { + /** + * Wraps a gl interface in another gl interface. + * @param gl a GL interface that is to be wrapped. + * @return either the input argument or another GL object that wraps the input argument. + */ + GL wrap(GL gl); + } + + /** + * A generic renderer interface. + *

+ * The renderer is responsible for making OpenGL calls to render a frame. + *

+ * GLSurfaceView clients typically create their own classes that implement + * this interface, and then call {@link GLSurfaceView#setRenderer} to + * register the renderer with the GLSurfaceView. + *

+ * + *

+ *

Developer Guides

+ *

For more information about how to use OpenGL, read the + * OpenGL developer guide.

+ *
+ * + *

Threading

+ * The renderer will be called on a separate thread, so that rendering + * performance is decoupled from the UI thread. Clients typically need to + * communicate with the renderer from the UI thread, because that's where + * input events are received. Clients can communicate using any of the + * standard Java techniques for cross-thread communication, or they can + * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method. + *

+ *

EGL Context Lost

+ * There are situations where the EGL rendering context will be lost. This + * typically happens when device wakes up after going to sleep. When + * the EGL context is lost, all OpenGL resources (such as textures) that are + * associated with that context will be automatically deleted. In order to + * keep rendering correctly, a renderer must recreate any lost resources + * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method + * is a convenient place to do this. + * + * + * @see #setRenderer(Renderer) + */ public interface Renderer { + /** + * Called when the surface is created or recreated. + *

+ * Called when the rendering thread + * starts and whenever the EGL context is lost. The EGL context will typically + * be lost when the Android device awakes after going to sleep. + *

+ * Since this method is called at the beginning of rendering, as well as + * every time the EGL context is lost, this method is a convenient place to put + * code to create resources that need to be created when the rendering + * starts, and that need to be recreated when the EGL context is lost. + * Textures are an example of a resource that you might want to create + * here. + *

+ * Note that when the EGL context is lost, all OpenGL resources associated + * with that context will be automatically deleted. You do not need to call + * the corresponding "glDelete" methods such as glDeleteTextures to + * manually delete these lost resources. + *

+ * @param gl the GL interface. Use instanceof to + * test if the interface supports GL11 or higher interfaces. + * @param config the EGLConfig of the created surface. Can be used + * to create matching pbuffers. + */ void onSurfaceCreated(GL10 gl, EGLConfig config); + + /** + * Called when the surface changed size. + *

+ * Called after the surface is created and whenever + * the OpenGL ES surface size changes. + *

+ * Typically you will set your viewport here. If your camera + * is fixed then you could also set your projection matrix here: + *

+		 * void onSurfaceChanged(GL10 gl, int width, int height) {
+		 *     gl.glViewport(0, 0, width, height);
+		 *     // for a fixed camera, set the projection too
+		 *     float ratio = (float) width / height;
+		 *     gl.glMatrixMode(GL10.GL_PROJECTION);
+		 *     gl.glLoadIdentity();
+		 *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+		 * }
+		 * 
+ * @param gl the GL interface. Use instanceof to + * test if the interface supports GL11 or higher interfaces. + * @param width + * @param height + */ void onSurfaceChanged(GL10 gl, int width, int height); + + /** + * Called to draw the current frame. + *

+ * This method is responsible for drawing the current frame. + *

+ * The implementation of this method typically looks like this: + *

+		 * void onDrawFrame(GL10 gl) {
+		 *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+		 *     //... other gl calls to render the scene ...
+		 * }
+		 * 
+ * @param gl the GL interface. Use instanceof to + * test if the interface supports GL11 or higher interfaces. + */ void onDrawFrame(GL10 gl); } - private long wrap_EGLConfigChooser_chooseConfig(long egl_display) { - if (config_chooser != null) { - EGLConfig egl_config = config_chooser.chooseConfig(java_egl_wrapper, new EGLDisplay(egl_display)); - return egl_config.native_egl_config; - } else { - throw new java.lang.NullPointerException("FIXME: EGLConfigChooser is NULL (time to provide a default implementation?)"); + /** + * An interface for customizing the eglCreateContext and eglDestroyContext calls. + *

+ * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} + */ + public interface EGLContextFactory { + EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); + void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); + } + + private class DefaultContextFactory implements EGLContextFactory { + private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { + int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, + EGL10.EGL_NONE}; + + return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, + mEGLContextClientVersion != 0 ? attrib_list : null); + } + + public void destroyContext(EGL10 egl, EGLDisplay display, + EGLContext context) { + if (!egl.eglDestroyContext(display, context)) { + Log.e("DefaultContextFactory", "display:" + display + " context: " + context); + if (LOG_THREADS) { + Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId()); + } + EglHelper.throwEglException("eglDestroyContext", egl.eglGetError()); + } } } - private long wrap_EGLContextFactory_createContext(long egl_display, long egl_config) { - if (context_factory != null) { - EGLContext egl_context = context_factory.createContext(java_egl_wrapper, new EGLDisplay(egl_display), new EGLConfig(egl_config)); - return egl_context.native_egl_context; - } else { - throw new java.lang.NullPointerException("FIXME: EGLContextFactory is NULL (time to provide a default implementation?)"); + /** + * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. + *

+ * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} + */ + public interface EGLWindowSurfaceFactory { + /** + * @return null if the surface cannot be constructed. + */ + EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, + Object nativeWindow); + void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); + } + + private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { + + public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, + EGLConfig config, Object nativeWindow) { + EGLSurface result = null; + try { + result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); + } catch (IllegalArgumentException e) { + // This exception indicates that the surface flinger surface + // is not valid. This can happen if the surface flinger surface has + // been torn down, but the application has not yet been + // notified via SurfaceHolder.Callback.surfaceDestroyed. + // In theory the application should be notified first, + // but in practice sometimes it is not. See b/4588890 + Log.e(TAG, "eglCreateWindowSurface", e); + } + return result; + } + + public void destroySurface(EGL10 egl, EGLDisplay display, + EGLSurface surface) { + egl.eglDestroySurface(display, surface); } } - public void queueEvent(Runnable r) { - // just run it synchronously, why not - r.run(); + /** + * An interface for choosing an EGLConfig configuration from a list of + * potential configurations. + *

+ * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)} + */ + public interface EGLConfigChooser { + /** + * Choose a configuration from the list. Implementors typically + * implement this method by calling + * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the + * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. + * @param egl the EGL10 for the current display. + * @param display the current display. + * @return the chosen configuration. + */ + EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); } - private class DummySurfaceHolder implements SurfaceHolder { - public void addCallback(Callback callback) {} - public void removeCallback(Callback callback) {} - public boolean isCreating() { return false; } - public void setType(int type) {} - public void setFixedSize(int width, int height) {} - public void setSizeFromLayout() {} - public void setFormat(int format) {} - public void setKeepScreenOn(boolean screenOn) {} - public Canvas lockCanvas() { return null; } - public Canvas lockCanvas(Rect dirty) { return null; } - public void unlockCanvasAndPost(Canvas canvas) {} - public Rect getSurfaceFrame() { return null; } - public Surface getSurface() { return null; } - } - - public SurfaceHolder getHolder() { - return (SurfaceHolder) new DummySurfaceHolder(); - } - - private static class boolean_ConfigChooser implements GLSurfaceView.EGLConfigChooser { - // TODO - what happens if we actually allow for 16 bits per color? - private static int[] config_attribs_no_depth = {EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_ALPHA_SIZE, 0, EGL10.EGL_DEPTH_SIZE, 0, EGL10.EGL_STENCIL_SIZE, 0, EGL10.EGL_NONE}; - private static int[] config_attribs_depth = {EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_ALPHA_SIZE, 0, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_STENCIL_SIZE, 0, EGL10.EGL_NONE}; - - private boolean want_depth; - - public boolean_ConfigChooser(boolean depth) { - want_depth = depth; + private abstract class BaseConfigChooser + implements EGLConfigChooser { + public BaseConfigChooser(int[] configSpec) { + mConfigSpec = filterConfigSpec(configSpec); } public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int[] num_config = new int[1]; - egl.eglChooseConfig(display, want_depth ? config_attribs_depth : config_attribs_no_depth, null, 0, num_config); - int numConfigs = num_config[0]; - if (numConfigs <= 0) { - throw new IllegalArgumentException("boolean_ConfigChooser: no configs match"); + if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, + num_config)) { + throw new IllegalArgumentException("eglChooseConfig failed"); } + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + throw new IllegalArgumentException( + "No configs match configSpec"); + } + EGLConfig[] configs = new EGLConfig[numConfigs]; - egl.eglChooseConfig(display, want_depth ? config_attribs_depth : config_attribs_no_depth, configs, numConfigs, num_config); - return configs[0]; // TODO - something smarter here? + if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, + num_config)) { + throw new IllegalArgumentException("eglChooseConfig#2 failed"); + } + EGLConfig config = chooseConfig(egl, display, configs); + if (config == null) { + throw new IllegalArgumentException("No config chosen"); + } + return config; + } + + abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs); + + protected int[] mConfigSpec; + + private int[] filterConfigSpec(int[] configSpec) { + if (mEGLContextClientVersion != 2) { + return configSpec; + } + /* We know none of the subclasses define EGL_RENDERABLE_TYPE. + * And we know the configSpec is well formed. + */ + int len = configSpec.length; + int[] newConfigSpec = new int[len + 2]; + System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); + newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; + newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ + newConfigSpec[len + 1] = EGL10.EGL_NONE; + return newConfigSpec; } } - private static class default_ContextFactory implements GLSurfaceView.EGLContextFactory { - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - return egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, new int[] {EGL10.EGL_CONTEXT_CLIENT_VERSION, 2 /*opengl_version*/, EGL10.EGL_NONE}); + /** + * Choose a configuration with exactly the specified r,g,b,a sizes, + * and at least the specified depth and stencil sizes. + */ + private class ComponentSizeChooser extends BaseConfigChooser { + public ComponentSizeChooser(int redSize, int greenSize, int blueSize, + int alphaSize, int depthSize, int stencilSize) { + super(new int[] { + EGL10.EGL_RED_SIZE, redSize, + EGL10.EGL_GREEN_SIZE, greenSize, + EGL10.EGL_BLUE_SIZE, blueSize, + EGL10.EGL_ALPHA_SIZE, alphaSize, + EGL10.EGL_DEPTH_SIZE, depthSize, + EGL10.EGL_STENCIL_SIZE, stencilSize, + EGL10.EGL_NONE}); + mValue = new int[1]; + mRedSize = redSize; + mGreenSize = greenSize; + mBlueSize = blueSize; + mAlphaSize = alphaSize; + mDepthSize = depthSize; + mStencilSize = stencilSize; } - public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { - egl.eglDestroyContext(display, context); + @Override + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + for (EGLConfig config : configs) { + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + if ((d >= mDepthSize) && (s >= mStencilSize)) { + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) && (a == mAlphaSize)) { + return config; + } + } + } + return null; + } + + private int findConfigAttrib(EGL10 egl, EGLDisplay display, + EGLConfig config, int attribute, int defaultValue) { + + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { + return mValue[0]; + } + return defaultValue; + } + + private int[] mValue; + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; + } + + /** + * This class will choose a RGB_888 surface with + * or without a depth buffer. + * + */ + private class SimpleEGLConfigChooser extends ComponentSizeChooser { + public SimpleEGLConfigChooser(boolean withDepthBuffer) { + super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0); } } - // interfaces + /** + * An EGL helper class. + */ - public interface EGLConfigChooser { - EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); + private static class EglHelper { + public EglHelper(WeakReference glSurfaceViewWeakRef) { + mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; + } + + /** + * Initialize EGL for a given configuration spec. + * @param configSpec + */ + public void start() { + if (LOG_EGL) { + Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); + } + /* + * Get an EGL instance + */ + mEgl = (EGL10)EGLContext.getEGL(); + + /* + * Get to the default display. + */ + mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + + if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { + throw new RuntimeException("eglGetDisplay failed"); + } + + /* + * We can now initialize EGL for that display + */ + int[] version = new int[2]; + if (!mEgl.eglInitialize(mEglDisplay, version)) { + throw new RuntimeException("eglInitialize failed"); + } + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view == null) { + mEglConfig = null; + mEglContext = null; + } else { + mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); + + /* + * Create an EGL context. We want to do this as rarely as we can, because an + * EGL context is a somewhat heavy object. + */ + mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); + } + if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { + mEglContext = null; + throwEglException("createContext"); + } + if (LOG_EGL) { + Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); + } + + mEglSurface = null; + } + + /** + * Create an egl surface for the current SurfaceHolder surface. If a surface + * already exists, destroy it before creating the new surface. + * + * @return true if the surface was created successfully. + */ + public boolean createSurface() { + if (LOG_EGL) { + Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); + } + /* + * Check preconditions. + */ + if (mEgl == null) { + throw new RuntimeException("egl not initialized"); + } + if (mEglDisplay == null) { + throw new RuntimeException("eglDisplay not initialized"); + } + if (mEglConfig == null) { + throw new RuntimeException("mEglConfig not initialized"); + } + + /* + * The window size has changed, so we need to create a new + * surface. + */ + destroySurfaceImp(); + + /* + * Create an EGL surface we can render into. + */ + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, + mEglDisplay, mEglConfig, view.getHolder()); + } else { + mEglSurface = null; + } + + if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { + int error = mEgl.eglGetError(); + if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { + Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); + } + return false; + } + + /* + * Before we can issue GL commands, we need to make sure + * the context is current and bound to a surface. + */ + if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + /* + * Could not make the context current, probably because the underlying + * SurfaceView surface has been destroyed. + */ + logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); + return false; + } + + return true; + } + + /** + * Create a GL object for the current EGL context. + * @return + */ + GL createGL() { + + GL gl = mEglContext.getGL(); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + if (view.mGLWrapper != null) { + gl = view.mGLWrapper.wrap(gl); + } + + if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { +//FIXME +/* int configFlags = 0; + Writer log = null; + if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { + configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; + } + if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { + log = new LogWriter(); + } + gl = GLDebugHelper.wrap(gl, configFlags, log);*/ + } + } + return gl; + } + + /** + * Display the current render surface. + * @return the EGL error code from eglSwapBuffers. + */ + public int swap() { + if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { + return mEgl.eglGetError(); + } + return EGL10.EGL_SUCCESS; + } + + public void destroySurface() { + if (LOG_EGL) { + Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId()); + } + destroySurfaceImp(); + } + + private void destroySurfaceImp() { + if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { + mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_CONTEXT); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); + } + mEglSurface = null; + } + } + + public void finish() { + if (LOG_EGL) { + Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); + } + if (mEglContext != null) { + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); + } + mEglContext = null; + } + if (mEglDisplay != null) { + mEgl.eglTerminate(mEglDisplay); + mEglDisplay = null; + } + } + + private void throwEglException(String function) { + throwEglException(function, mEgl.eglGetError()); + } + + public static void throwEglException(String function, int error) { + String message = formatEglError(function, error); + if (LOG_THREADS) { + Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " + message); + } + throw new RuntimeException(message); + } + + public static void logEglErrorAsWarning(String tag, String function, int error) { + Log.w(tag, formatEglError(function, error)); + } + + public static String formatEglError(String function, int error) { + return function + " failed: " + "FIXME"/*EGLLogWrapper.getErrorString(error)*/; + } + + private WeakReference mGLSurfaceViewWeakRef; + EGL10 mEgl; + EGLDisplay mEglDisplay; + EGLSurface mEglSurface; + EGLConfig mEglConfig; + EGLContext mEglContext; } - public interface EGLContextFactory { - EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); - // void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); + /** + * A generic GL Thread. Takes care of initializing EGL and GL. Delegates + * to a Renderer instance to do the actual drawing. Can be configured to + * render continuously or on request. + * + * All potentially blocking synchronization is done through the + * sGLThreadManager object. This avoids multiple-lock ordering issues. + * + */ + static class GLThread extends Thread { + GLThread(WeakReference glSurfaceViewWeakRef) { + super(); + mWidth = 0; + mHeight = 0; + mRequestRender = true; + mRenderMode = RENDERMODE_CONTINUOUSLY; + mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; + } + + @Override + public void run() { + setName("GLThread " + getId()); + if (LOG_THREADS) { + Log.i("GLThread", "starting tid=" + getId()); + } + + try { + guardedRun(); + } catch (InterruptedException e) { + // fall thru and exit normally + } finally { + sGLThreadManager.threadExiting(this); + } + } + + /* + * This private method should only be called inside a + * synchronized(sGLThreadManager) block. + */ + private void stopEglSurfaceLocked() { + if (mHaveEglSurface) { + mHaveEglSurface = false; + mEglHelper.destroySurface(); + } + } + + /* + * This private method should only be called inside a + * synchronized(sGLThreadManager) block. + */ + private void stopEglContextLocked() { + if (mHaveEglContext) { + mEglHelper.finish(); + mHaveEglContext = false; + sGLThreadManager.releaseEglContextLocked(this); + } + } + private void guardedRun() throws InterruptedException { + mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); + mHaveEglContext = false; + mHaveEglSurface = false; + try { + GL10 gl = null; + boolean createEglContext = false; + boolean createEglSurface = false; + boolean createGlInterface = false; + boolean lostEglContext = false; + boolean sizeChanged = false; + boolean wantRenderNotification = false; + boolean doRenderNotification = false; + boolean askedToReleaseEglContext = false; + int w = 0; + int h = 0; + Runnable event = null; + + while (true) { + synchronized (sGLThreadManager) { + while (true) { + if (mShouldExit) { + return; + } + + if (!mEventQueue.isEmpty()) { + event = mEventQueue.remove(0); + break; + } + + // Update the pause state. + boolean pausing = false; + if (mPaused != mRequestPaused) { + pausing = mRequestPaused; + mPaused = mRequestPaused; + sGLThreadManager.notifyAll(); + if (LOG_PAUSE_RESUME) { + Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId()); + } + } + + // Do we need to give up the EGL context? + if (mShouldReleaseEglContext) { + if (LOG_SURFACE) { + Log.i("GLThread", "releasing EGL context because asked to tid=" + getId()); + } + stopEglSurfaceLocked(); + stopEglContextLocked(); + mShouldReleaseEglContext = false; + askedToReleaseEglContext = true; + } + + // Have we lost the EGL context? + if (lostEglContext) { + stopEglSurfaceLocked(); + stopEglContextLocked(); + lostEglContext = false; + } + + // When pausing, release the EGL surface: + if (pausing && mHaveEglSurface) { + if (LOG_SURFACE) { + Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); + } + stopEglSurfaceLocked(); + } + + // When pausing, optionally release the EGL Context: + if (pausing && mHaveEglContext) { + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + boolean preserveEglContextOnPause = view == null ? false : view.mPreserveEGLContextOnPause; + if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) { + stopEglContextLocked(); + if (LOG_SURFACE) { + Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); + } + } + } + + // When pausing, optionally terminate EGL: + if (pausing) { + if (sGLThreadManager.shouldTerminateEGLWhenPausing()) { + mEglHelper.finish(); + if (LOG_SURFACE) { + Log.i("GLThread", "terminating EGL because paused tid=" + getId()); + } + } + } + + // Have we lost the SurfaceView surface? + if ((!mHasSurface) && (!mWaitingForSurface)) { + if (LOG_SURFACE) { + Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); + } + if (mHaveEglSurface) { + stopEglSurfaceLocked(); + } + mWaitingForSurface = true; + mSurfaceIsBad = false; + sGLThreadManager.notifyAll(); + } + + // Have we acquired the surface view surface? + if (mHasSurface && mWaitingForSurface) { + if (LOG_SURFACE) { + Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); + } + mWaitingForSurface = false; + sGLThreadManager.notifyAll(); + } + + if (doRenderNotification) { + if (LOG_SURFACE) { + Log.i("GLThread", "sending render notification tid=" + getId()); + } + wantRenderNotification = false; + doRenderNotification = false; + mRenderComplete = true; + sGLThreadManager.notifyAll(); + } + + // Ready to draw? + if (readyToDraw()) { + + // If we don't have an EGL context, try to acquire one. + if (!mHaveEglContext) { + if (askedToReleaseEglContext) { + askedToReleaseEglContext = false; + } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) { + try { + mEglHelper.start(); + } catch (RuntimeException t) { + sGLThreadManager.releaseEglContextLocked(this); + throw t; + } + mHaveEglContext = true; + createEglContext = true; + + sGLThreadManager.notifyAll(); + } + } + + if (mHaveEglContext && !mHaveEglSurface) { + mHaveEglSurface = true; + createEglSurface = true; + createGlInterface = true; + sizeChanged = true; + } + + if (mHaveEglSurface) { + if (mSizeChanged) { + sizeChanged = true; + w = mWidth; + h = mHeight; + wantRenderNotification = true; + if (LOG_SURFACE) { + Log.i("GLThread", + "noticing that we want render notification tid=" + getId()); + } + + // Destroy and recreate the EGL surface. + createEglSurface = true; + + mSizeChanged = false; + } + mRequestRender = false; + sGLThreadManager.notifyAll(); + break; + } + } + + // By design, this is the only place in a GLThread thread where we wait(). + if (LOG_THREADS) { + Log.i("GLThread", "waiting tid=" + getId() + " mHaveEglContext: " + mHaveEglContext + " mHaveEglSurface: " + mHaveEglSurface + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface + " mPaused: " + mPaused + " mHasSurface: " + mHasSurface + " mSurfaceIsBad: " + mSurfaceIsBad + " mWaitingForSurface: " + mWaitingForSurface + " mWidth: " + mWidth + " mHeight: " + mHeight + " mRequestRender: " + mRequestRender + " mRenderMode: " + mRenderMode); + } + sGLThreadManager.wait(); + } + } // end of synchronized(sGLThreadManager) + + if (event != null) { + event.run(); + event = null; + continue; + } + + if (createEglSurface) { + if (LOG_SURFACE) { + Log.w("GLThread", "egl createSurface"); + } + if (mEglHelper.createSurface()) { + synchronized (sGLThreadManager) { + mFinishedCreatingEglSurface = true; + sGLThreadManager.notifyAll(); + } + } else { + synchronized (sGLThreadManager) { + mFinishedCreatingEglSurface = true; + mSurfaceIsBad = true; + sGLThreadManager.notifyAll(); + } + continue; + } + createEglSurface = false; + } + + if (createGlInterface) { + gl = (GL10)mEglHelper.createGL(); + + sGLThreadManager.checkGLDriver(gl); + createGlInterface = false; + } + + if (createEglContext) { + if (LOG_RENDERER) { + Log.w("GLThread", "onSurfaceCreated"); + } + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); + } + createEglContext = false; + } + + if (sizeChanged) { + if (LOG_RENDERER) { + Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); + } + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mRenderer.onSurfaceChanged(gl, w, h); + } + sizeChanged = false; + } + + if (LOG_RENDERER_DRAW_FRAME) { + Log.w("GLThread", "onDrawFrame tid=" + getId()); + } + { + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mRenderer.onDrawFrame(gl); + } + } + int swapError = mEglHelper.swap(); + switch (swapError) { + case EGL10.EGL_SUCCESS: + break; + case EGL11.EGL_CONTEXT_LOST: + if (LOG_SURFACE) { + Log.i("GLThread", "egl context lost tid=" + getId()); + } + lostEglContext = true; + break; + default: + // Other errors typically mean that the current surface is bad, + // probably because the SurfaceView surface has been destroyed, + // but we haven't been notified yet. + // Log the error to help developers understand why rendering stopped. + EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); + + synchronized (sGLThreadManager) { + mSurfaceIsBad = true; + sGLThreadManager.notifyAll(); + } + break; + } + + if (wantRenderNotification) { + doRenderNotification = true; + } + } + + } finally { + /* + * clean-up everything... + */ + synchronized (sGLThreadManager) { + stopEglSurfaceLocked(); + stopEglContextLocked(); + } + } + } + + public boolean ableToDraw() { + return mHaveEglContext && mHaveEglSurface && readyToDraw(); + } + + private boolean readyToDraw() { + return (!mPaused) && mHasSurface && (!mSurfaceIsBad) && (mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); + } + + public void setRenderMode(int renderMode) { + if (!((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY))) { + throw new IllegalArgumentException("renderMode"); + } + synchronized (sGLThreadManager) { + mRenderMode = renderMode; + sGLThreadManager.notifyAll(); + } + } + + public int getRenderMode() { + synchronized (sGLThreadManager) { + return mRenderMode; + } + } + + public void requestRender() { + synchronized (sGLThreadManager) { + mRequestRender = true; + sGLThreadManager.notifyAll(); + } + } + + public void surfaceCreated() { + synchronized (sGLThreadManager) { + if (LOG_THREADS) { + Log.i("GLThread", "surfaceCreated tid=" + getId()); + } + mHasSurface = true; + mFinishedCreatingEglSurface = false; + sGLThreadManager.notifyAll(); + while (mWaitingForSurface && !mFinishedCreatingEglSurface && !mExited) { + try { + sGLThreadManager.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void surfaceDestroyed() { + synchronized (sGLThreadManager) { + if (LOG_THREADS) { + Log.i("GLThread", "surfaceDestroyed tid=" + getId()); + } + mHasSurface = false; + sGLThreadManager.notifyAll(); + while ((!mWaitingForSurface) && (!mExited)) { + try { + sGLThreadManager.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void onPause() { + synchronized (sGLThreadManager) { + if (LOG_PAUSE_RESUME) { + Log.i("GLThread", "onPause tid=" + getId()); + } + mRequestPaused = true; + sGLThreadManager.notifyAll(); + while ((!mExited) && (!mPaused)) { + if (LOG_PAUSE_RESUME) { + Log.i("Main thread", "onPause waiting for mPaused."); + } + try { + sGLThreadManager.wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void onResume() { + synchronized (sGLThreadManager) { + if (LOG_PAUSE_RESUME) { + Log.i("GLThread", "onResume tid=" + getId()); + } + mRequestPaused = false; + mRequestRender = true; + mRenderComplete = false; + sGLThreadManager.notifyAll(); + while ((!mExited) && mPaused && (!mRenderComplete)) { + if (LOG_PAUSE_RESUME) { + Log.i("Main thread", "onResume waiting for !mPaused."); + } + try { + sGLThreadManager.wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void onWindowResize(int w, int h) { + synchronized (sGLThreadManager) { + mWidth = w; + mHeight = h; + mSizeChanged = true; + mRequestRender = true; + mRenderComplete = false; + sGLThreadManager.notifyAll(); + + // Wait for thread to react to resize and render a frame + while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) { + if (LOG_SURFACE) { + Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId()); + } + try { + sGLThreadManager.wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void requestExitAndWait() { + // don't call this from GLThread thread or it is a guaranteed + // deadlock! + synchronized (sGLThreadManager) { + mShouldExit = true; + sGLThreadManager.notifyAll(); + while (!mExited) { + try { + sGLThreadManager.wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void requestReleaseEglContextLocked() { + mShouldReleaseEglContext = true; + sGLThreadManager.notifyAll(); + } + + /** + * Queue an "event" to be run on the GL rendering thread. + * @param r the runnable to be run on the GL rendering thread. + */ + public void queueEvent(Runnable r) { + if (r == null) { + throw new IllegalArgumentException("r must not be null"); + } + synchronized (sGLThreadManager) { + mEventQueue.add(r); + sGLThreadManager.notifyAll(); + } + } + + // Once the thread is started, all accesses to the following member + // variables are protected by the sGLThreadManager monitor + private boolean mShouldExit; + private boolean mExited; + private boolean mRequestPaused; + private boolean mPaused; + private boolean mHasSurface; + private boolean mSurfaceIsBad; + private boolean mWaitingForSurface; + private boolean mHaveEglContext; + private boolean mHaveEglSurface; + private boolean mFinishedCreatingEglSurface; + private boolean mShouldReleaseEglContext; + private int mWidth; + private int mHeight; + private int mRenderMode; + private boolean mRequestRender; + private boolean mRenderComplete; + private ArrayList mEventQueue = new ArrayList(); + private boolean mSizeChanged = true; + + // End of member variables protected by the sGLThreadManager monitor. + + private EglHelper mEglHelper; + + /** + * Set once at thread construction time, nulled out when the parent view is garbage + * called. This weak reference allows the GLSurfaceView to be garbage collected while + * the GLThread is still alive. + */ + private WeakReference mGLSurfaceViewWeakRef; } + + static class LogWriter extends Writer { + + @Override + public void close() { + flushBuilder(); + } + + @Override + public void flush() { + flushBuilder(); + } + + @Override + public void write(char[] buf, int offset, int count) { + for (int i = 0; i < count; i++) { + char c = buf[offset + i]; + if (c == '\n') { + flushBuilder(); + } else { + mBuilder.append(c); + } + } + } + + private void flushBuilder() { + if (mBuilder.length() > 0) { + Log.v("GLSurfaceView", mBuilder.toString()); + mBuilder.delete(0, mBuilder.length()); + } + } + + private StringBuilder mBuilder = new StringBuilder(); + } + + private void checkRenderThreadState() { + if (mGLThread != null) { + throw new IllegalStateException( + "setRenderer has already been called for this instance."); + } + } + + private static class GLThreadManager { + private static String TAG = "GLThreadManager"; + + public synchronized void threadExiting(GLThread thread) { + if (LOG_THREADS) { + Log.i("GLThread", "exiting tid=" + thread.getId()); + } + thread.mExited = true; + if (mEglOwner == thread) { + mEglOwner = null; + } + notifyAll(); + } + + /* + * Tries once to acquire the right to use an EGL + * context. Does not block. Requires that we are already + * in the sGLThreadManager monitor when this is called. + * + * @return true if the right to use an EGL context was acquired. + */ + public boolean tryAcquireEglContextLocked(GLThread thread) { + if (mEglOwner == thread || mEglOwner == null) { + mEglOwner = thread; + notifyAll(); + return true; + } + checkGLESVersion(); + if (mMultipleGLESContextsAllowed) { + return true; + } + // Notify the owning thread that it should release the context. + // TODO: implement a fairness policy. Currently + // if the owning thread is drawing continuously it will just + // reacquire the EGL context. + if (mEglOwner != null) { + mEglOwner.requestReleaseEglContextLocked(); + } + return false; + } + + /* + * Releases the EGL context. Requires that we are already in the + * sGLThreadManager monitor when this is called. + */ + public void releaseEglContextLocked(GLThread thread) { + if (mEglOwner == thread) { + mEglOwner = null; + } + notifyAll(); + } + + public synchronized boolean shouldReleaseEGLContextWhenPausing() { + // Release the EGL context when pausing even if + // the hardware supports multiple EGL contexts. + // Otherwise the device could run out of EGL contexts. + return mLimitedGLESContexts; + } + + public synchronized boolean shouldTerminateEGLWhenPausing() { + checkGLESVersion(); + return !mMultipleGLESContextsAllowed; + } + + public synchronized void checkGLDriver(GL10 gl) { + if (!mGLESDriverCheckComplete) { + checkGLESVersion(); + String renderer = gl.glGetString(GL10.GL_RENDERER); + if (mGLESVersion < kGLES_20) { + mMultipleGLESContextsAllowed = + !renderer.startsWith(kMSM7K_RENDERER_PREFIX); + notifyAll(); + } + mLimitedGLESContexts = !mMultipleGLESContextsAllowed; + if (LOG_SURFACE) { + Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = " + mMultipleGLESContextsAllowed + " mLimitedGLESContexts = " + mLimitedGLESContexts); + } + mGLESDriverCheckComplete = true; + } + } + + private void checkGLESVersion() { + if (!mGLESVersionCheckComplete) { +/* mGLESVersion = SystemProperties.getInt( + "ro.opengles.version", + ConfigurationInfo.GL_ES_VERSION_UNDEFINED);*/ + if (mGLESVersion >= kGLES_20) { + mMultipleGLESContextsAllowed = true; + } + if (LOG_SURFACE) { + Log.w(TAG, "checkGLESVersion mGLESVersion =" + + + " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed); + } + mGLESVersionCheckComplete = true; + } + } + + /** + * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides + * support for hardware-accelerated views, therefore multiple EGL contexts are + * supported on all Android 3.0+ EGL drivers. + */ + private boolean mGLESVersionCheckComplete; + private int mGLESVersion = 0x30001; // 3.1 + private boolean mGLESDriverCheckComplete; + private boolean mMultipleGLESContextsAllowed; + private boolean mLimitedGLESContexts; + private static final int kGLES_20 = 0x20000; + private static final String kMSM7K_RENDERER_PREFIX = + "Q3Dimension MSM7500 "; + private GLThread mEglOwner; + } + + private static final GLThreadManager sGLThreadManager = new GLThreadManager(); + + private final WeakReference mThisWeakRef = + new WeakReference(this); + private GLThread mGLThread; + private Renderer mRenderer; + private boolean mDetached; + private EGLConfigChooser mEGLConfigChooser; + private EGLContextFactory mEGLContextFactory; + private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; + private GLWrapper mGLWrapper; + private int mDebugFlags; + private int mEGLContextClientVersion; + private boolean mPreserveEGLContextOnPause; } diff --git a/src/api-impl/android/view/SurfaceView.java b/src/api-impl/android/view/SurfaceView.java index 0cf75402..d40af83a 100644 --- a/src/api-impl/android/view/SurfaceView.java +++ b/src/api-impl/android/view/SurfaceView.java @@ -23,12 +23,18 @@ public class SurfaceView extends View { mSurface.widget = this.widget; } - private void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + private void surfaceChanged(int format, int width, int height) { for (SurfaceHolder.Callback c : mCallbacks) { c.surfaceChanged(mSurfaceHolder, format, width, height); } } + private void surfaceCreated() { + for (SurfaceHolder.Callback c : mCallbacks) { + c.surfaceCreated(mSurfaceHolder); + } + } + @Override protected native long native_constructor(Context context, AttributeSet attrs); diff --git a/src/api-impl/com/google/android/gles_jni/EGLImpl.java b/src/api-impl/com/google/android/gles_jni/EGLImpl.java index 81347780..973ea8ba 100644 --- a/src/api-impl/com/google/android/gles_jni/EGLImpl.java +++ b/src/api-impl/com/google/android/gles_jni/EGLImpl.java @@ -1,5 +1,24 @@ +/* + * parts of this file Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.android.gles_jni; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; import javax.microedition.khronos.egl.*; public class EGLImpl implements EGL10 { @@ -36,21 +55,78 @@ public class EGLImpl implements EGL10 { return ret; } + public EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list) { + Surface sur = null; + if (native_window instanceof SurfaceView) { + SurfaceView surfaceView = (SurfaceView)native_window; + sur = surfaceView.getHolder().getSurface(); + } else if (native_window instanceof SurfaceHolder) { + SurfaceHolder holder = (SurfaceHolder)native_window; + sur = holder.getSurface(); + } else if (native_window instanceof Surface) { + sur = (Surface)native_window; + } + + long eglSurfaceId; + if (sur != null) { + eglSurfaceId = native_eglCreateWindowSurface(display.native_egl_display, config.native_egl_config, sur, attrib_list); + } /*else if (native_window instanceof SurfaceTexture) { + eglSurfaceId = native_eglCreateWindowSurfaceTexture(display, config, + native_window, attrib_list); + }*/ else { + throw new java.lang.UnsupportedOperationException( + "eglCreateWindowSurface() can only be called with an instance of " + + + "Surface, SurfaceView, SurfaceHolder or [FIXME]SurfaceTexture at the moment."); + } + + if (eglSurfaceId == 0) { + return EGL10.EGL_NO_SURFACE; + } + return new EGLSurfaceImpl(eglSurfaceId); + } + + public EGLDisplay eglGetDisplay(Object display) { + long native_display = native_eglGetDisplay(display); + if (native_display == 0) + return EGL10.EGL_NO_DISPLAY; + + return new EGLDisplay(native_display); + } + + public boolean eglInitialize(EGLDisplay display, int[] major_minor) { + return native_eglInitialize(display.native_egl_display, major_minor); + } + + public boolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, int attribute, int[] value) { + return native_eglGetConfigAttrib(display.native_egl_display, config.native_egl_config, attribute, value); + } + + public boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context) { + return native_eglMakeCurrent(display.native_egl_display, ((EGLSurfaceImpl)draw).mEGLSurface, ((EGLSurfaceImpl)read).mEGLSurface, context.native_egl_context); + } + + public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) { + return native_eglSwapBuffers(display.native_egl_display, ((EGLSurfaceImpl)surface).mEGLSurface); + } + + public boolean eglDestroySurface(EGLDisplay display, EGLSurface surface) { + return native_eglDestroySurface(display.native_egl_display, ((EGLSurfaceImpl)surface).mEGLSurface); + } + + public boolean eglDestroyContext(EGLDisplay display, EGLContext context) { + return native_eglDestroyContext(display.native_egl_display, context.native_egl_context); + } + + /* STUBS */ public boolean eglCopyBuffers(EGLDisplay display, EGLSurface surface, Object native_pixmap) { return false; } public EGLSurface eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, int[] attrib_list) { return null; } public EGLSurface eglCreatePixmapSurface(EGLDisplay display, EGLConfig config, Object native_pixmap, int[] attrib_list) { return null; } - public EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list) { return null; } - public boolean eglDestroyContext(EGLDisplay display, EGLContext context) { return false; } - public boolean eglDestroySurface(EGLDisplay display, EGLSurface surface) { return false; } - public boolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, int attribute, int[] value) { return false; } public boolean eglGetConfigs(EGLDisplay display, EGLConfig[] configs, int config_size, int[] num_config) { return false; } public EGLContext eglGetCurrentContext() { return null; } public EGLDisplay eglGetCurrentDisplay() { return null; } public EGLSurface eglGetCurrentSurface(int readdraw) { return null; } - public EGLDisplay eglGetDisplay(Object native_display) { return null; } public int eglGetError() { return EGL_SUCCESS; } // don't let yourself get fooled, this is also a stub :P - public boolean eglInitialize(EGLDisplay display, int[] major_minor) { return false; } - public boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context) { return false; } public boolean eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value) { return false; } public String eglQueryString(EGLDisplay display, int name) { return "FIXME"; } public boolean eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value) { return false; } @@ -58,8 +134,16 @@ public class EGLImpl implements EGL10 { * @hide * */ public boolean eglReleaseThread() { return false; } - public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) { return false; } public boolean eglTerminate(EGLDisplay display) { return false; } public boolean eglWaitGL() { return false; } public boolean eglWaitNative(int engine, Object bindTarget) { return false; } + + private native long native_eglCreateWindowSurface(long native_egl_display, long native_egl_config, Surface surface, int[] attrib_list); + private native long native_eglGetDisplay(Object native_display); + private native boolean native_eglInitialize(long native_egl_display, int[] major_minor); + private native boolean native_eglGetConfigAttrib(long native_egl_display, long native_edl_config, int attribute, int[] value); + private native boolean native_eglMakeCurrent(long native_egl_display, long native_draw_surface, long native_read_surface, long native_context); + private native boolean native_eglSwapBuffers(long native_egl_display, long native_surface); + private native boolean native_eglDestroySurface(long native_egl_display, long native_surface); + private native boolean native_eglDestroyContext(long native_egl_display, long native_context); } diff --git a/src/api-impl/com/google/android/gles_jni/EGLSurfaceImpl.java b/src/api-impl/com/google/android/gles_jni/EGLSurfaceImpl.java new file mode 100644 index 00000000..928a0ad1 --- /dev/null +++ b/src/api-impl/com/google/android/gles_jni/EGLSurfaceImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gles_jni; + +import javax.microedition.khronos.egl.*; + +public class EGLSurfaceImpl extends EGLSurface { + public long mEGLSurface; + private long mNativePixelRef; + public EGLSurfaceImpl() { + mEGLSurface = 0; + mNativePixelRef = 0; + } + public EGLSurfaceImpl(long surface) { + mEGLSurface = surface; + mNativePixelRef = 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + EGLSurfaceImpl that = (EGLSurfaceImpl)o; + + return mEGLSurface == that.mEGLSurface; + } +} diff --git a/src/api-impl/javax/microedition/khronos/egl/EGL10.java b/src/api-impl/javax/microedition/khronos/egl/EGL10.java index c92a7a86..670d8d94 100644 --- a/src/api-impl/javax/microedition/khronos/egl/EGL10.java +++ b/src/api-impl/javax/microedition/khronos/egl/EGL10.java @@ -91,9 +91,9 @@ public interface EGL10 extends EGL { int EGL_WINDOW_BIT = 0x04; Object EGL_DEFAULT_DISPLAY = null; - EGLDisplay EGL_NO_DISPLAY = null; // new com.google.android.gles_jni.EGLDisplayImpl(0); - EGLContext EGL_NO_CONTEXT = null; // new com.google.android.gles_jni.EGLContextImpl(0); - EGLSurface EGL_NO_SURFACE = null; // new com.google.android.gles_jni.EGLSurfaceImpl(0); + EGLDisplay EGL_NO_DISPLAY = new javax.microedition.khronos.egl.EGLDisplay(0); + EGLContext EGL_NO_CONTEXT = new javax.microedition.khronos.egl.EGLContext(0); + EGLSurface EGL_NO_SURFACE = new com.google.android.gles_jni.EGLSurfaceImpl(0); boolean eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config); boolean eglCopyBuffers(EGLDisplay display, EGLSurface surface, Object native_pixmap); diff --git a/src/api-impl/meson.build b/src/api-impl/meson.build index de6cd531..ea370ae2 100644 --- a/src/api-impl/meson.build +++ b/src/api-impl/meson.build @@ -440,6 +440,7 @@ hax_jar = jar('hax', [ 'com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java', 'com/android/org/conscrypt/SSLParametersImpl.java', 'com/google/android/gles_jni/EGLImpl.java', + 'com/google/android/gles_jni/EGLSurfaceImpl.java', 'com/google/android/gles_jni/GLImpl.java', 'com/google/android/vending/expansion/downloader/IDownloaderClient.java', 'com/google/android/vending/expansion/downloader/impl/DownloaderService.java', diff --git a/src/libandroid/native_window.c b/src/libandroid/native_window.c index c23ab34b..7b4dc985 100644 --- a/src/libandroid/native_window.c +++ b/src/libandroid/native_window.c @@ -468,21 +468,28 @@ EGLDisplay bionic_eglGetDisplay(NativeDisplayType native_display) } } +EGLSurface egl_surface_hashtable; + EGLSurface bionic_eglCreateWindowSurface(EGLDisplay display, EGLConfig config, struct ANativeWindow *native_window, EGLint const *attrib_list) { // better than crashing (TODO: check if apps try to use the NULL value anyway) if(!native_window) return NULL; + if(!egl_surface_hashtable) + egl_surface_hashtable = g_hash_table_new(NULL, NULL); + PrintConfigAttributes(display, config); - EGLSurface ret = eglCreateWindowSurface(display, config, native_window->egl_window, attrib_list); + EGLSurface surface = eglCreateWindowSurface(display, config, native_window->egl_window, attrib_list); printf("EGL::: native_window->egl_window: %ld\n", native_window->egl_window); printf("EGL::: eglGetError: %d\n", eglGetError()); - printf("EGL::: ret: %p\n", ret); + printf("EGL::: ret: %p\n", surface); - return ret; + g_hash_table_insert(egl_surface_hashtable, surface, native_window); + + return surface; } // FIXME 1.5: this most likely belongs elsewhere diff --git a/src/libandroid/native_window.h b/src/libandroid/native_window.h index 5c83c386..8a4c3116 100644 --- a/src/libandroid/native_window.h +++ b/src/libandroid/native_window.h @@ -14,4 +14,9 @@ struct ANativeWindow { int refcount; }; +extern EGLSurface egl_surface_hashtable; + struct ANativeWindow *ANativeWindow_fromSurface(JNIEnv* env, jobject surface); +EGLSurface bionic_eglCreateWindowSurface(EGLDisplay display, EGLConfig config, struct ANativeWindow *native_window, EGLint const *attrib_list); +EGLDisplay bionic_eglGetDisplay(NativeDisplayType native_display); +void ANativeWindow_release(struct ANativeWindow *native_window); diff --git a/src/main-executable/main.c b/src/main-executable/main.c index 68bd8188..ae5cf979 100644 --- a/src/main-executable/main.c +++ b/src/main-executable/main.c @@ -47,10 +47,6 @@ char *get_app_data_dir() return app_data_dir; } -// FIXME: used by hacks in GLSurfaceView -int FIXME__WIDTH; -int FIXME__HEIGHT; - char *construct_classpath(char *prefix, char **cp_array, size_t len) { size_t result_len = strlen(prefix); @@ -335,9 +331,6 @@ static void open(GtkApplication *app, GFile** files, gint nfiles, const gchar* h jclass context_class = (*env)->FindClass(env, "android/content/Context"); _SET_STATIC_OBJ_FIELD(context_class, "apk_path", "Ljava/lang/String;", _JSTRING(apk_classpath)); - FIXME__WIDTH = d->window_width; - FIXME__HEIGHT = d->window_height; - window = gtk_application_window_new(app); if(getenv("ATL_DISABLE_WINDOW_DECORATIONS"))