diff --git a/engine/platform/android/android.c b/engine/platform/android/android.c index 98fc080b..186cdb8f 100644 --- a/engine/platform/android/android.c +++ b/engine/platform/android/android.c @@ -20,6 +20,8 @@ GNU General Public License for more details. #include "platform/android/android_priv.h" #include "errno.h" #include +#include +#include #ifndef JNICALL #define JNICALL // a1ba: workaround for my IDE, where Java files are not included @@ -86,7 +88,8 @@ typedef enum event_type event_onpause, event_ondestroy, event_onresume, - event_onfocuschange + event_onfocuschange, + event_setwindow, } eventtype_t; typedef struct touchevent_s @@ -139,6 +142,7 @@ typedef struct event_s joyaxis_t axis; joybutton_t button; keyevent_t key; + ANativeWindow *window; }; } event_t; @@ -243,14 +247,9 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) jni.env = env; jni.actcls = (*env)->FindClass(env, "in/celest/xash3d/XashActivity"); - jni.swapBuffers = (*env)->GetStaticMethodID(env, jni.actcls, "swapBuffers", "()V"); - jni.toggleEGL = (*env)->GetStaticMethodID(env, jni.actcls, "toggleEGL", "(I)V"); jni.enableTextInput = (*env)->GetStaticMethodID(env, jni.actcls, "showKeyboard", "(I)V"); jni.vibrate = (*env)->GetStaticMethodID(env, jni.actcls, "vibrate", "(I)V" ); jni.messageBox = (*env)->GetStaticMethodID(env, jni.actcls, "messageBox", "(Ljava/lang/String;Ljava/lang/String;)V"); - jni.createGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "createGLContext", "(I)Z"); - jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); - jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); jni.notify = (*env)->GetStaticMethodID(env, jni.actcls, "engineThreadNotify", "()V"); jni.setTitle = (*env)->GetStaticMethodID(env, jni.actcls, "setTitle", "(Ljava/lang/String;)V"); jni.setIcon = (*env)->GetStaticMethodID(env, jni.actcls, "setIcon", "(Ljava/lang/String;)V"); @@ -554,6 +553,24 @@ DECLARE_JNI_INTERFACE( int, nativeTestWritePermission, jstring jPath ) return ret; } +DECLARE_JNI_INTERFACE( void, nativeSetSurface, jobject surface ) +{ + Android_Lock(); + if( surface ) + { + negl.window = ANativeWindow_fromSurface( env, surface ); + } + else + { + if( negl.window ) + { + ANativeWindow_release( negl.window ); + negl.window = NULL; + } + } + Android_Unlock(); +} + JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) { return JNI_VERSION_1_6; @@ -819,8 +836,8 @@ void Platform_RunEvents() { host.status = HOST_FRAME; SNDDMA_Activate( true ); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); - Android_UpdateSurface(); + // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + // Android_UpdateSurface(); SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; @@ -830,8 +847,8 @@ void Platform_RunEvents() { host.status = HOST_NOFOCUS; SNDDMA_Activate( false ); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); - negl.valid = false; + // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); + // negl.valid = false; } break; @@ -839,9 +856,9 @@ void Platform_RunEvents() // reinitialize EGL and change engine screen size if( host.status == HOST_NORMAL && ( refState.width != jni.width || refState.height != jni.height ) ) { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); - Android_UpdateSurface(); + // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); + // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + // Android_UpdateSurface(); SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval VID_SetMode(); } @@ -911,6 +928,9 @@ void Platform_RunEvents() host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; break; + case event_setwindow: + negl.window = events.queue[i].window; + break; } } diff --git a/engine/platform/android/android_priv.h b/engine/platform/android/android_priv.h index 5cee6465..e928c6d1 100644 --- a/engine/platform/android/android_priv.h +++ b/engine/platform/android/android_priv.h @@ -12,14 +12,9 @@ extern struct jnimethods_s jclass actcls; JavaVM *vm; JNIEnv *env; - jmethodID swapBuffers; - jmethodID toggleEGL; jmethodID enableTextInput; jmethodID vibrate; jmethodID messageBox; - jmethodID createGLContext; - jmethodID getGLAttribute; - jmethodID deleteGLContext; jmethodID notify; jmethodID setTitle; jmethodID setIcon; @@ -34,8 +29,14 @@ extern struct jnimethods_s extern struct nativeegl_s { qboolean valid; + void *window; EGLDisplay dpy; EGLSurface surface; + EGLContext context; + EGLConfig cfg; + EGLint numCfg; + + const char *extensions; } negl; extern struct jnimouse_s diff --git a/engine/platform/android/lib_android.c b/engine/platform/android/lib_android.c index 4e070e45..72cfadb2 100644 --- a/engine/platform/android/lib_android.c +++ b/engine/platform/android/lib_android.c @@ -47,15 +47,17 @@ void *ANDROID_LoadLibrary( const char *dllname ) return pHandle; COM_PushLibraryError( dlerror() ); + + return NULL; } void *ANDROID_GetProcAddress( void *hInstance, const char *name ) { - void *p = dlsym_weak( hInstance, name ); + void *p = dlsym( hInstance, name ); if( p ) return p; - return dlsym( hInstance, name ); + return dlsym_weak( hInstance, name ); } diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 6be35796..764f342b 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -5,6 +5,12 @@ #include "filesystem.h" #include "platform/android/android_priv.h" #include "vid_common.h" +#include +#include + +static int gl_attribs[REF_GL_ATTRIBUTES_COUNT] = { 0 }; +static qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT] = { 0 }; +static EGLint gl_api = EGL_OPENGL_ES_API; /* ======================== @@ -13,10 +19,7 @@ Android_SwapInterval */ static void Android_SwapInterval( int interval ) { - // there is no eglSwapInterval in EGL10/EGL11 classes, - // so only native backend supported - if( negl.valid ) - eglSwapInterval( negl.dpy, interval ); + eglSwapInterval( negl.dpy, interval ); } /* @@ -51,91 +54,6 @@ static void Android_GetScreenRes( int *width, int *height ) *width=jni.width, *height=jni.height; } -/* -======================== -Android_UpdateSurface - -Check if we may use native EGL without jni calls -======================== -*/ -void Android_UpdateSurface( void ) -{ - negl.valid = false; - - if( Sys_CheckParm("-nonativeegl") ) - return; //disabled by user - - negl.dpy = eglGetCurrentDisplay(); - - if( negl.dpy == EGL_NO_DISPLAY ) - return; - - negl.surface = eglGetCurrentSurface(EGL_DRAW); - - if( negl.surface == EGL_NO_SURFACE ) - return; - - // now check if swapBuffers does not give error - if( eglSwapBuffers( negl.dpy, negl.surface ) == EGL_FALSE ) - return; - - // double check - if( eglGetError() != EGL_SUCCESS ) - return; - - __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "native EGL enabled" ); - - negl.valid = true; -} - -/* -======================== -Android_GetGLAttribute -======================== -*/ -static int Android_GetGLAttribute( int eglAttr ) -{ - int ret = (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getGLAttribute, eglAttr ); - // Con_Reportf( "Android_GetGLAttribute( %i ) => %i\n", eglAttr, ret ); - return ret; -} - -/* -======================== -Android_InitGL -======================== -*/ -qboolean Android_InitGL() -{ - int colorBits[3]; - qboolean result; - - // result = (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.createGLContext, (int)gl_stencilbits->value ); - - /*colorBits[0] = Android_GetGLAttribute( EGL_RED_SIZE ); - colorBits[1] = Android_GetGLAttribute( EGL_GREEN_SIZE ); - colorBits[2] = Android_GetGLAttribute( EGL_BLUE_SIZE ); - glConfig.color_bits = colorBits[0] + colorBits[1] + colorBits[2]; - glConfig.alpha_bits = Android_GetGLAttribute( EGL_ALPHA_SIZE ); - glConfig.depth_bits = Android_GetGLAttribute( EGL_DEPTH_SIZE ); - glConfig.stencil_bits = Android_GetGLAttribute( EGL_STENCIL_SIZE ); - glState.stencilEnabled = glConfig.stencil_bits ? true : false;*/ - - Android_UpdateSurface(); - - return result; -} - -/* -======================== -Android_ShutdownGL -======================== -*/ -void Android_ShutdownGL() -{ - (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.deleteGLContext ); -} - /* ======================== Android_SwapBuffers @@ -145,15 +63,7 @@ Update screen. Use native EGL if possible */ void GL_SwapBuffers() { - if( negl.valid ) - { - eglSwapBuffers( negl.dpy, negl.surface ); - } - else - { - // nanoGL_Flush(); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.swapBuffers ); - } + eglSwapBuffers( negl.dpy, negl.surface ); } qboolean R_Init_Video( const int type ) @@ -161,6 +71,9 @@ qboolean R_Init_Video( const int type ) string safe; qboolean retval; + memset( gl_attribs, 0, sizeof( gl_attribs )); + memset( gl_attribs_set, 0, sizeof( gl_attribs_set )); + if( FS_FileExists( GI->iconpath, true ) ) { if( host.rodir[0] ) @@ -218,7 +131,13 @@ qboolean R_Init_Video( const int type ) void R_Free_Video( void ) { - // GL_DeleteContext (); + eglMakeCurrent(negl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + eglDestroyContext(negl.dpy, negl.context); + + eglDestroySurface(negl.dpy, negl.surface); + + eglTerminate(negl.dpy); // VID_DestroyWindow (); @@ -227,10 +146,177 @@ void R_Free_Video( void ) ref.dllFuncs.GL_ClearExtensions(); } +#define COPY_ATTR_IF_SET( refattr, attr ) \ + if( gl_attribs_set[refattr] ) \ + { \ + attribs[i++] = attr; \ + attribs[i++] = gl_attribs[refattr]; \ + } -qboolean VID_SetMode( void ) +static const EGLint *VID_GenerateConfig( void ) { + int i = 0; + static EGLint attribs[32 + 1]; + + memset( attribs, 0, sizeof( attribs )); + + COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE ); + COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE ); + COPY_ATTR_IF_SET( REF_GL_BLUE_SIZE, EGL_BLUE_SIZE ); + COPY_ATTR_IF_SET( REF_GL_ALPHA_SIZE, EGL_ALPHA_SIZE ); + COPY_ATTR_IF_SET( REF_GL_DEPTH_SIZE, EGL_DEPTH_SIZE ); + COPY_ATTR_IF_SET( REF_GL_STENCIL_SIZE, EGL_STENCIL_SIZE ); + COPY_ATTR_IF_SET( REF_GL_MULTISAMPLEBUFFERS, EGL_SAMPLE_BUFFERS ); + COPY_ATTR_IF_SET( REF_GL_MULTISAMPLESAMPLES, EGL_SAMPLES ); + + if( gl_attribs_set[REF_GL_ACCELERATED_VISUAL] ) + { + attribs[i++] = EGL_CONFIG_CAVEAT; + attribs[i++] = gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE; + } + + // BigGL support + attribs[i++] = EGL_RENDERABLE_TYPE; + gl_api = EGL_OPENGL_ES_API; + + if( gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] && + !( gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES )) + { + attribs[i++] = EGL_OPENGL_BIT; + gl_api = EGL_OPENGL_API; + } + else if( gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && + gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) + { + attribs[i++] = EGL_OPENGL_ES2_BIT; + } + else + { + attribs[i++] = EGL_OPENGL_ES_BIT; + } + + attribs[i] = EGL_NONE; + + return attribs; +} + +static const EGLint *VID_GenerateContextConfig( void ) +{ + int i = 0; + static EGLint attribs[32 + 1]; + + memset( attribs, 0, sizeof( attribs )); + + if( Q_strcmp( negl.extensions, " EGL_KHR_create_context ") ) + { + if( gl_attribs_set[REF_GL_CONTEXT_FLAGS] ) + { + attribs[i++] = 0x30FC; // EGL_CONTEXT_FLAGS_KHR + attribs[i++] = gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1); + } + + if( gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) + { + int val = gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; + + if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) ) + { + attribs[i++] = 0x30FD; // EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + attribs[i++] = val; + } + } + + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MINOR_VERSION, 0x30FB ); + } + else + { + // without extension we can set only major version + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + } + + + attribs[i] = EGL_NONE; + + return attribs; +} + +qboolean VID_SetMode( void ) +{ + EGLint format; + const EGLint *attribs = VID_GenerateConfig(); + const EGLint *contextAttribs = VID_GenerateContextConfig(); + R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + + negl.valid = false; + + if( ( negl.dpy = eglGetDisplay( EGL_DEFAULT_DISPLAY )) == EGL_NO_DISPLAY ) + { + Con_Reportf( S_ERROR "eglGetDisplay returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !eglInitialize( negl.dpy, NULL, NULL )) + { + Con_Reportf( S_ERROR "eglInitialize returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !(negl.extensions = eglQueryString( negl.dpy, EGL_EXTENSIONS ) ) ) + { + Con_Reportf( S_ERROR "eglQueryString(EGL_EXTENSIONS) returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !eglChooseConfig( negl.dpy, attribs, &negl.cfg, 1, &negl.numCfg )) + { + Con_Reportf( S_ERROR "eglChooseConfig returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_NATIVE_VISUAL_ID, &format) ) + { + Con_Reportf( S_ERROR "eglGetConfigAttrib returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( ANativeWindow_setBuffersGeometry( negl.window, 0, 0, format ) ) + { + Con_Reportf( S_ERROR "ANativeWindow_setBuffersGeometry returned error\n" ); + return false; + } + + if( !( negl.surface = eglCreateWindowSurface( negl.dpy, negl.cfg, negl.window, NULL ))) + { + Con_Reportf( S_ERROR "eglCreateWindowSurface returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !( negl.context = eglCreateContext( negl.dpy, negl.cfg, NULL, contextAttribs ))) + { + Con_Reportf( S_ERROR "eglCreateContext returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !eglMakeCurrent( negl.dpy, negl.surface, negl.surface, negl.context )) + { + Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", eglGetError() ); + return false; + } + + if( !eglBindAPI( gl_api )) + { + Con_Reportf( S_ERROR "eglBindAPI returned error: 0x%x\n", eglGetError() ); + return false; + } + + __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "native EGL enabled" ); + + negl.valid = true; + + ref.dllFuncs.GL_OnContextCreated(); + return true; } @@ -252,12 +338,53 @@ rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) int GL_SetAttribute( int attr, int val ) { + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) + return -1; + gl_attribs[attr] = val; + gl_attribs_set[attr] = true; + return 0; } int GL_GetAttribute( int attr, int *val ) { + EGLBoolean ret; + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) + return -1; + + if( !val ) + return -1; + + switch( attr ) + { + case REF_GL_RED_SIZE: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_RED_SIZE, val ); + break; + case REF_GL_GREEN_SIZE: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_GREEN_SIZE, val ); + break; + case REF_GL_BLUE_SIZE: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_BLUE_SIZE, val ); + break; + case REF_GL_ALPHA_SIZE: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_ALPHA_SIZE, val ); + break; + case REF_GL_DEPTH_SIZE: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_DEPTH_SIZE, val ); + break; + case REF_GL_STENCIL_SIZE: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_STENCIL_SIZE, val ); + break; + case REF_GL_MULTISAMPLESAMPLES: + ret = eglGetConfigAttrib( negl.dpy, negl.cfg, EGL_SAMPLES, val ); + break; + } + + if( !ret ) + return -1; + + return 0; } int R_MaxVideoModes() @@ -272,7 +399,7 @@ vidmode_t* R_GetVideoMode( int num ) void* GL_GetProcAddress( const char *name ) // RenderAPI requirement { - return NULL; // not implemented, only static for now + return eglGetProcAddress( name ); } void GL_UpdateSwapInterval( void )