platform/android: prevent rendering without context, fix android_sleep using surfaceless mode or dummy surface if possible

This commit is contained in:
mittorn 2023-10-30 03:10:44 +03:00 committed by Alibek Omarov
parent d423c437ed
commit bb55de5963
5 changed files with 103 additions and 50 deletions

View File

@ -261,7 +261,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array )
jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I");
jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z");
jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I"); jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I");
jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "()Landroid/view/Surface;"); jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "(I)Landroid/view/Surface;");
/* Run the application. */ /* Run the application. */
@ -810,12 +810,12 @@ void Platform_PreCreateMove( void )
/* /*
======================== ========================
Android_RunEvents Android_ProcessEvents
Execute all events from queue Execute all events from queue
======================== ========================
*/ */
void Platform_RunEvents( void ) static void Android_ProcessEvents( void )
{ {
int i; int i;
@ -859,7 +859,7 @@ void Platform_RunEvents( void )
{ {
SNDDMA_Activate( true ); SNDDMA_Activate( true );
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 );
Android_UpdateSurface( true ); Android_UpdateSurface( surface_active );
SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
} }
@ -867,7 +867,7 @@ void Platform_RunEvents( void )
if( events.queue[i].arg ) if( events.queue[i].arg )
{ {
SNDDMA_Activate( false ); SNDDMA_Activate( false );
Android_UpdateSurface( false ); Android_UpdateSurface( !android_sleep->value ? surface_dummy : surface_pause );
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 );
} }
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify );
@ -880,14 +880,14 @@ void Platform_RunEvents( void )
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 );
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 );
Con_DPrintf("resize event\n"); Con_DPrintf("resize event\n");
Android_UpdateSurface( false ); Android_UpdateSurface( surface_pause );
Android_UpdateSurface( true ); Android_UpdateSurface( surface_active );
SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval
VID_SetMode(); VID_SetMode();
} }
else else
{ {
Con_DPrintf("resize skip %d %d %d %d %d", jni.width, jni.height, refState.width, refState.height, host.status ); Con_DPrintf("resize skip %d %d %d %d %d\n", jni.width, jni.height, refState.width, refState.height, host.status );
} }
break; break;
case event_joyadd: case event_joyadd:
@ -939,14 +939,14 @@ void Platform_RunEvents( void )
#endif #endif
// disable sound during call/screen-off // disable sound during call/screen-off
SNDDMA_Activate( false ); SNDDMA_Activate( false );
host.status = HOST_SLEEP; //host.status = HOST_SLEEP;
// stop blocking UI thread // stop blocking UI thread
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify );
break; break;
case event_onresume: case event_onresume:
// re-enable sound after onPause // re-enable sound after onPause
host.status = HOST_FRAME; //host.status = HOST_FRAME;
SNDDMA_Activate( true ); SNDDMA_Activate( true );
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
break; break;
@ -995,4 +995,16 @@ void Platform_RunEvents( void )
pthread_mutex_lock( &events.framemutex ); pthread_mutex_lock( &events.framemutex );
} }
void Platform_RunEvents( void )
{
Android_ProcessEvents();
// do not allow running frames while android_sleep is 1, but surface/context not restored
while( android_sleep->value && host.status == HOST_SLEEP )
{
usleep( 20000 );
Android_ProcessEvents();
}
}
#endif // XASH_DEDICATED #endif // XASH_DEDICATED

View File

@ -32,6 +32,14 @@ extern struct jnimethods_s
int width, height; int width, height;
} jni; } jni;
typedef enum surfacestate_e
{
surface_pause,
surface_active,
surface_dummy,
} surfacestate_t;
extern struct jnimouse_s extern struct jnimouse_s
{ {
@ -41,6 +49,6 @@ extern struct jnimouse_s
// //
// vid_android.c // vid_android.c
// //
void Android_UpdateSurface( qboolean active ); void Android_UpdateSurface( surfacestate_t state );
#endif // ANDROID_PRIV_H #endif // ANDROID_PRIV_H

View File

@ -236,18 +236,39 @@ destroy old surface, recreate and make context current
must be called with valid context must be called with valid context
========================= =========================
*/ */
qboolean EGL_UpdateSurface( void *window ) qboolean EGL_UpdateSurface( void *window, qboolean dummy )
{ {
if( !eglstate.valid )
return false;
egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
host.status = HOST_SLEEP; host.status = HOST_SLEEP;
if( eglstate.surface ) if( eglstate.surface )
{
egl.DestroySurface( eglstate.dpy, eglstate.surface ); egl.DestroySurface( eglstate.dpy, eglstate.surface );
eglstate.surface = EGL_NO_SURFACE;
}
if( !window ) if( !window )
{ {
Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" );
return false; if( dummy && eglstate.support_surfaceless_context )
{
if( egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglstate.context ))
{
Con_Reportf( S_NOTE "EGL_UpdateSurface: using surfaceless mode\n" );
return true;
}
else
{
egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
eglstate.support_surfaceless_context = false;
}
Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" );
}
return false; // let platform fallback to dummy surface or pause engine
} }
if(( eglstate.surface = egl.CreateWindowSurface( eglstate.dpy, eglstate.cfg, window, NULL )) == EGL_NO_SURFACE ) if(( eglstate.surface = egl.CreateWindowSurface( eglstate.dpy, eglstate.cfg, window, NULL )) == EGL_NO_SURFACE )
@ -261,8 +282,10 @@ qboolean EGL_UpdateSurface( void *window )
Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() ); Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() );
return false; return false;
} }
Con_DPrintf( S_NOTE "restored current context\n" ); Con_DPrintf( S_NOTE "restored current context\n" );
host.status = HOST_FRAME; if( !dummy)
host.status = HOST_FRAME;
return true; return true;
@ -321,6 +344,11 @@ qboolean EGL_CreateContext( void )
eglstate.valid = true; eglstate.valid = true;
eglstate.imported = false; eglstate.imported = false;
// now check if it's safe to use surfaceless context here without surface fallback
if( eglstate.extensions && Q_strstr( eglstate.extensions, "EGL_KHR_surfaceless_context" ))
eglstate.support_surfaceless_context = true;
return true; return true;
} }

View File

@ -12,6 +12,7 @@ extern struct eglstate_s
EGLContext context; EGLContext context;
EGLConfig cfg; EGLConfig cfg;
EGLint numCfg; EGLint numCfg;
qboolean support_surfaceless_context;
const char *extensions; const char *extensions;
int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; int gl_attribs[REF_GL_ATTRIBUTES_COUNT];
@ -49,7 +50,7 @@ void * EGL_GetProcAddress( const char *name );
void EGL_Terminate( void ); void EGL_Terminate( void );
qboolean EGL_ImportContext( void ); qboolean EGL_ImportContext( void );
qboolean EGL_CreateContext( void ); qboolean EGL_CreateContext( void );
qboolean EGL_UpdateSurface( void *window ); qboolean EGL_UpdateSurface( void *window, qboolean dummy );
int EGL_GetAttribute( int attr, int *val ); int EGL_GetAttribute( int attr, int *val );
size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size ); size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size );
size_t EGL_GenerateConfig( EGLint *attribs, size_t size ); size_t EGL_GenerateConfig( EGLint *attribs, size_t size );

View File

@ -119,63 +119,63 @@ Android_UpdateSurface
Check if we may use native EGL without jni calls Check if we may use native EGL without jni calls
======================== ========================
*/ */
void Android_UpdateSurface( qboolean active ) void Android_UpdateSurface( surfacestate_t state )
{ {
vid_android.nativeegl = false; vid_android.nativeegl = false;
Con_Printf("1\n"); qboolean active = state == surface_active;
if( glw_state.software || ( eglstate.valid && !eglstate.imported ) )
if( glw_state.software || ( eglstate.valid && !eglstate.imported ))
{ {
Con_Printf("2\n"); if( vid_android.window )
if( vid_android.window && !active )
{ {
EGL_UpdateSurface( NULL, false );
nw.release( vid_android.window ); nw.release( vid_android.window );
vid_android.window = NULL; vid_android.window = NULL;
} }
if( state == surface_dummy && glw_state.software )
return;
// first, ask EGL for surfaceless mode
if( state == surface_dummy && EGL_UpdateSurface( NULL, true ))
{
vid_android.nativeegl = true;
return;
}
if( active ) if( state != surface_pause )
{ {
EGLint format = WINDOW_FORMAT_RGB_565; EGLint format = WINDOW_FORMAT_RGB_565;
jobject surf; jobject surf;
if( vid_android.window ) if( vid_android.window )
nw.release( vid_android.window ); nw.release( vid_android.window );
surf = (*jni.env)->CallStaticObjectMethod(jni.env, jni.actcls, jni.getSurface); surf = (*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.getSurface, state );
Con_DPrintf("Surface handle %p\n", surf); Con_DPrintf("Surface handle %p\n", surf);
vid_android.window = nw.fromSurface(jni.env, surf); if( surf )
Con_DPrintf("NativeWindow %p\n", vid_android.window); {
vid_android.window = nw.fromSurface(jni.env, surf);
Con_DPrintf("NativeWindow %p\n", vid_android.window);
if( eglstate.valid ) if( eglstate.valid )
egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format ); egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format );
nw.setBuffersGeometry(vid_android.window, 0, 0, format ); nw.setBuffersGeometry(vid_android.window, 0, 0, format );
(*jni.env)->DeleteLocalRef( jni.env, surf ); (*jni.env)->DeleteLocalRef( jni.env, surf );
}
} }
if( Sys_CheckParm( "-egl" )) if( eglstate.valid && !eglstate.imported )
{ {
EGL_UpdateSurface( vid_android.window ); EGL_UpdateSurface( vid_android.window, state == surface_dummy );
vid_android.nativeegl = true; vid_android.nativeegl = true;
} }
return; return;
} }
Con_Printf("3\n");
if( !vid_android.has_context ) if( !vid_android.has_context )
return; return;
//if( ( active && host.status == HOST_FRAME ) || !active ) (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, (int)state );
if( !active ) host.status = HOST_FRAME; // active ? HOST_FRAME : HOST_SLEEP;
{
Con_Printf("toggleEGL 0\n");
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 );
}
host.status = HOST_SLEEP;
if( active )
{
Con_Printf("toggleEGL 1\n");
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 );
host.status = HOST_FRAME;
}
// todo: check opengl context here and set HOST_SLEEP if not // todo: check opengl context here and set HOST_SLEEP if not
@ -257,9 +257,13 @@ qboolean R_Init_Video( const int type )
if( glw_state.software ) if( glw_state.software )
{ {
uint arg; uint arg;
// Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" );
// return false; if( !nw.release )
Android_UpdateSurface( true ); {
Con_Reportf( S_ERROR "Native software mode unavailiable\n" );
return false;
}
Android_UpdateSurface( surface_active );
if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) ) if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) )
return false; return false;
} }
@ -339,7 +343,7 @@ qboolean VID_SetMode( void )
vid_android.has_context = vid_android.nativeegl = EGL_CreateContext(); vid_android.has_context = vid_android.nativeegl = EGL_CreateContext();
if( vid_android.has_context ) if( vid_android.has_context )
Android_UpdateSurface( true ); Android_UpdateSurface( surface_active );
else else
return false; return false;
} }