diff --git a/engine/platform/android/android.c b/engine/platform/android/android.c index e1fdb80f..56fd6faa 100644 --- a/engine/platform/android/android.c +++ b/engine/platform/android/android.c @@ -20,8 +20,7 @@ GNU General Public License for more details. #include "platform/android/android_priv.h" #include "errno.h" #include -#include -#include +#include #ifndef JNICALL #define JNICALL // a1ba: workaround for my IDE, where Java files are not included @@ -89,7 +88,6 @@ typedef enum event_type event_ondestroy, event_onresume, event_onfocuschange, - event_setwindow, } eventtype_t; typedef struct touchevent_s @@ -142,7 +140,6 @@ typedef struct event_s joyaxis_t axis; joybutton_t button; keyevent_t key; - ANativeWindow *window; }; } event_t; @@ -163,7 +160,6 @@ static struct { } events = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }; struct jnimethods_s jni; -struct nativeegl_s negl; struct jnimouse_s jnimouse; #define Android_Lock() pthread_mutex_lock(&events.mutex); @@ -265,6 +261,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I"); + jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "()Landroid/view/Surface;"); /* Run the application. */ @@ -570,24 +567,6 @@ 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; @@ -863,10 +842,10 @@ void Platform_RunEvents( void ) // destroy EGL surface when hiding application if( !events.queue[i].arg ) { - 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( true ); + host.status = HOST_FRAME; 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; @@ -876,8 +855,8 @@ void Platform_RunEvents( void ) { host.status = HOST_NOFOCUS; SNDDMA_Activate( false ); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); - negl.valid = false; + Android_UpdateSurface( false ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); } break; @@ -885,9 +864,9 @@ void Platform_RunEvents( void ) // reinitialize EGL and change engine screen size if( host.status == HOST_FRAME &&( 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( true ); SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval VID_SetMode(); } @@ -957,9 +936,6 @@ void Platform_RunEvents( void ) 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 7ffebd63..3af27197 100644 --- a/engine/platform/android/android_priv.h +++ b/engine/platform/android/android_priv.h @@ -5,7 +5,6 @@ #include #include #include -#include extern struct jnimethods_s { @@ -29,21 +28,10 @@ extern struct jnimethods_s jmethodID getGLAttribute; jmethodID deleteGLContext; jmethodID getSelectedPixelFormat; + jmethodID getSurface; int width, height; } jni; -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 { @@ -53,6 +41,6 @@ extern struct jnimouse_s // // vid_android.c // -void Android_UpdateSurface( void ); +void Android_UpdateSurface( qboolean active ); #endif // ANDROID_PRIV_H diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index e0e1f763..dc707108 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -9,11 +9,84 @@ #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; -static void *libgles1, *libgles2; -static qboolean gles1; +static struct vid_android_s +{ + int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; + qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT]; + EGLint gl_api; + qboolean gles1; + void *libgles1, *libgles2; + qboolean has_context; + ANativeWindow* window; +} vid_android; + +static struct nw_s +{ + void (*release)(ANativeWindow* window); + int32_t (*getWidth)(ANativeWindow* window); + int32_t (*getHeight)(ANativeWindow* window); + int32_t (*getFormat)(ANativeWindow* window); + int32_t (*setBuffersGeometry)(ANativeWindow* window, int32_t width, int32_t height, int32_t format); + int32_t (*lock)(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); + int32_t (*unlockAndPost)(ANativeWindow* window); + ANativeWindow* (*fromSurface)(JNIEnv* env, jobject surface); +} nw; + +#define NW_FF(x) {"ANativeWindow_"#x, (void*)&nw.x} + + +static dllfunc_t android_funcs[] = +{ + NW_FF(release), + NW_FF(getWidth), + NW_FF(getHeight), + NW_FF(getFormat), + NW_FF(setBuffersGeometry), + NW_FF(lock), + NW_FF(unlockAndPost), + NW_FF(fromSurface), + { NULL, NULL } +}; +#undef NW_FF +dll_info_t android_info = { "libandroid.so", android_funcs, false }; + +static struct egl_s +{ + EGLSurface (*GetCurrentSurface)(EGLint readdraw); + EGLDisplay (*GetCurrentDisplay)(void); + EGLint (*GetError)(void); + EGLBoolean (*SwapBuffers)(EGLDisplay dpy, EGLSurface surface); + EGLBoolean (*SwapInterval)(EGLDisplay dpy, EGLint interval); + void *(*GetProcAddress)(const char *procname); +} egl; +#undef GetProcAddress +#define EGL_FF(x) {"egl"#x, (void*)&egl.x} +static dllfunc_t egl_funcs[] = +{ + EGL_FF(SwapInterval), + EGL_FF(SwapBuffers), + EGL_FF(GetError), + EGL_FF(GetCurrentDisplay), + EGL_FF(GetCurrentSurface), + EGL_FF(GetProcAddress), + { NULL, NULL } +}; +#undef EGL_FF +dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; + +static struct nativeegl_s +{ + qboolean valid; + void *window; + EGLDisplay dpy; + EGLSurface surface; + EGLContext context; + EGLConfig cfg; + EGLint numCfg; + + const char *extensions; +} negl; + /* ======================== Android_SwapInterval @@ -22,7 +95,7 @@ Android_SwapInterval static void Android_SwapInterval( int interval ) { if( negl.valid ) - eglSwapInterval( negl.dpy, interval ); + egl.SwapInterval( negl.dpy, interval ); } /* @@ -68,7 +141,7 @@ void GL_SwapBuffers( void ) { if( negl.valid ) { - eglSwapBuffers( negl.dpy, negl.surface ); + egl.SwapBuffers( negl.dpy, negl.surface ); } else { @@ -83,29 +156,64 @@ Android_UpdateSurface Check if we may use native EGL without jni calls ======================== */ -void Android_UpdateSurface( void ) +void Android_UpdateSurface( qboolean active ) { negl.valid = false; - if( !Sys_CheckParm("-nativeegl") ) + if( nw.release ) + { + if( vid_android.window && !active ) + { + nw.release( vid_android.window ); + vid_android.window = NULL; + } + + if( active ) + { + jobject surf; + if( vid_android.window ) + nw.release( vid_android.window ); + surf = (*jni.env)->CallStaticObjectMethod(jni.env, jni.actcls, jni.getSurface); + Con_Printf("s %p\n", surf); + vid_android.window = nw.fromSurface(jni.env, surf); + Con_Printf("w %p\n", vid_android.window); + nw.setBuffersGeometry(vid_android.window, 0, 0, WINDOW_FORMAT_RGB_565 ); + (*jni.env)->DeleteLocalRef( jni.env, surf ); + } + return; + } + + if( !vid_android.has_context ) + return; + + if( ( active && host.status == HOST_FRAME ) || !active ) + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); + + if( active ) + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + + if( !Sys_CheckParm("-nativeegl") || !active ) return; // enabled by user - negl.dpy = eglGetCurrentDisplay(); + if( !egl.GetCurrentDisplay ) + return; + + negl.dpy = egl.GetCurrentDisplay(); if( negl.dpy == EGL_NO_DISPLAY ) return; - negl.surface = eglGetCurrentSurface(EGL_DRAW); + negl.surface = egl.GetCurrentSurface(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 ) + if( egl.SwapBuffers( negl.dpy, negl.surface ) == EGL_FALSE ) return; // double check - if( eglGetError() != EGL_SUCCESS ) + if( egl.GetError() != EGL_SUCCESS ) return; __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "native EGL enabled" ); @@ -148,9 +256,6 @@ qboolean R_Init_Video( const int type ) break; } - 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] ) @@ -174,11 +279,11 @@ qboolean R_Init_Video( const int type ) break; case REF_GL: glw_state.software = false; + Sys_LoadLibrary( &egl_info ); + if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) ) glw_state.safe = bound( SAFE_NO, Q_atoi( safe ), SAFE_DONTCARE ); - // refdll can request some attributes - ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); break; default: Host_Error( "Can't initialize unknown context type %d!\n", type ); @@ -187,8 +292,13 @@ qboolean R_Init_Video( const int type ) if( glw_state.software ) { - Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" ); - return false; + uint arg; +// Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" ); +// return false; + Sys_LoadLibrary( &android_info ); + Android_UpdateSurface( true ); + if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) ) + return false; } while( !(retval = VID_SetMode()) ) @@ -221,15 +331,17 @@ void R_Free_Video( void ) // VID_DestroyWindow (); // R_FreeVideoModes(); - + Sys_FreeLibrary( &android_info ); + Sys_FreeLibrary( &egl_info ); + vid_android.has_context = false; ref.dllFuncs.GL_ClearExtensions(); } #define COPY_ATTR_IF_SET( refattr, attr ) \ - if( gl_attribs_set[refattr] ) \ + if( vid_android.gl_attribs_set[refattr] ) \ { \ attribs[i++] = attr; \ - attribs[i++] = gl_attribs[refattr]; \ + attribs[i++] = vid_android.gl_attribs[refattr]; \ } static size_t VID_GenerateConfig( EGLint *attribs, size_t size ) @@ -237,7 +349,12 @@ static size_t VID_GenerateConfig( EGLint *attribs, size_t size ) size_t i = 0; memset( attribs, 0, size * sizeof( EGLint ) ); - gles1 = false; + vid_android.gles1 = false; + memset( vid_android.gl_attribs, 0, sizeof( vid_android.gl_attribs )); + memset( vid_android.gl_attribs_set, 0, sizeof( vid_android.gl_attribs_set )); + + // refdll can request some attributes + ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE ); COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE ); @@ -248,31 +365,31 @@ static size_t VID_GenerateConfig( EGLint *attribs, size_t 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] ) + if( vid_android.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; + attribs[i++] = vid_android.gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE; } // BigGL support attribs[i++] = EGL_RENDERABLE_TYPE; - gl_api = EGL_OPENGL_ES_API; + vid_android.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 )) + if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] && + !( vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES )) { attribs[i++] = EGL_OPENGL_BIT; - gl_api = EGL_OPENGL_API; + vid_android.gl_api = EGL_OPENGL_API; } - else if( gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && - gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) + else if( vid_android.gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && + vid_android.gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) { attribs[i++] = EGL_OPENGL_ES2_BIT; } else { - attribs[i++] = EGL_OPENGL_ES_BIT; - gles1 = true; + i--; // erase EGL_RENDERABLE_TYPE + vid_android.gles1 = true; } attribs[i++] = EGL_NONE; @@ -288,15 +405,15 @@ static size_t VID_GenerateContextConfig( EGLint *attribs, size_t size ) /*if( Q_strcmp( negl.extensions, " EGL_KHR_create_context ") ) { - if( gl_attribs_set[REF_GL_CONTEXT_FLAGS] ) + if( vid_android.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); + attribs[i++] = vid_android.gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1); } - if( gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) + if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) { - int val = gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; + int val = vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) ) { @@ -325,9 +442,16 @@ qboolean VID_SetMode( void ) jintArray attribs, contextAttribs; static EGLint nAttribs[32+1], nContextAttribs[32+1]; const size_t attribsSize = ARRAYSIZE( nAttribs ); + size_t s1, s2; - size_t s1 = VID_GenerateConfig(nAttribs, attribsSize); - size_t s2 = VID_GenerateContextConfig(nContextAttribs, attribsSize); + if( vid_android.has_context ) + { + R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + return true; + } + + s1 = VID_GenerateConfig(nAttribs, attribsSize); + s2 = VID_GenerateContextConfig(nContextAttribs, attribsSize); attribs = (*jni.env)->NewIntArray( jni.env, s1 ); contextAttribs = (*jni.env)->NewIntArray( jni.env, s2 ); @@ -337,8 +461,12 @@ qboolean VID_SetMode( void ) R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + if( glw_state.software ) + return true; + if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.createGLContext, attribs, contextAttribs ) ) { + vid_android.has_context = true; return true; } @@ -387,8 +515,8 @@ 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; + vid_android.gl_attribs[attr] = val; + vid_android.gl_attribs_set[attr] = true; return 0; } @@ -445,23 +573,26 @@ void* GL_GetProcAddress( const char *name ) // RenderAPI requirement void *gles; void *addr; - if( gles1 ) + if( vid_android.gles1 ) { - if( !libgles1 ) - libgles1 = dlopen("libGLESv1_CM.so", RTLD_NOW); - gles = libgles1; + if( !vid_android.libgles1 ) + vid_android.libgles1 = dlopen("libGLESv1_CM.so", RTLD_NOW); + gles = vid_android.libgles1; } else { - if( !libgles2 ) - libgles2 = dlopen("libGLESv2.so", RTLD_NOW); - gles = libgles2; + if( !vid_android.libgles2 ) + vid_android.libgles2 = dlopen("libGLESv2.so", RTLD_NOW); + gles = vid_android.libgles2; } if( gles && ( addr = dlsym(gles, name ) ) ) return addr; - return eglGetProcAddress( name ); + if( !egl.GetProcAddress ) + return NULL; + + return egl.GetProcAddress( name ); } void GL_UpdateSwapInterval( void ) @@ -481,15 +612,47 @@ void GL_UpdateSwapInterval( void ) void *SW_LockBuffer( void ) { - return NULL; + ANativeWindow_Buffer buffer; + if( !nw.lock || !vid_android.window ) + return NULL; + if( nw.lock( vid_android.window, &buffer, NULL ) ) + return NULL; + if( buffer.width < refState.width || buffer.height < refState.height ) + return NULL; + return buffer.bits; } void SW_UnlockBuffer( void ) { - + if( nw.unlockAndPost ) + nw.unlockAndPost( vid_android.window ); } qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b ) { - return false; + ANativeWindow_Buffer buffer; + int lock; + if( !nw.lock || !vid_android.window ) + return false; + nw.unlockAndPost( vid_android.window ); + + if( ( lock = nw.lock( vid_android.window, &buffer, NULL ) ) ) + { + Con_Printf("SW_CreateBuffer: lock %d\n", lock ); + return false; + } + nw.unlockAndPost( vid_android.window ); + Con_Printf("SW_CreateBuffer: buffer %d %d %x %d\n", buffer.width, buffer.height, buffer.format, buffer.stride ); + if( width > buffer.width || height > buffer.height ) + return false; + if( buffer.format != WINDOW_FORMAT_RGB_565 ) + return false; + Con_Printf("SW_CreateBuffer: ok\n"); + *stride = buffer.stride; + + *bpp = 2; + *r = (((1 << 5) - 1) << (5+6)); + *g = (((1 << 6) - 1) << (5)); + *b = (((1 << 5) - 1) << (0)); + return true; } diff --git a/engine/wscript b/engine/wscript index 9c6726dd..847919ab 100644 --- a/engine/wscript +++ b/engine/wscript @@ -42,7 +42,7 @@ def configure(conf): if not conf.check_cc( fragment='int main(){ int i = socket();}', lib = 'wattcpwl', mandatory=False ): conf.define('XASH_NO_NETWORK',1) elif conf.env.DEST_OS == 'android': # Android doesn't need SDL2 - for i in ['android', 'log', 'EGL']: + for i in ['log']: conf.check_cc(lib = i) elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support @@ -130,7 +130,7 @@ def build(bld): is_cxx_link = True if bld.env.DEST_OS == 'android': - libs += ['LOG', 'EGL', 'ANDROID'] + libs += ['LOG'] source += bld.path.ant_glob(['platform/android/*.cpp', 'platform/android/*.c', 'platform/linux/*.c']) # add client files