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.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;");
jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "(I)Landroid/view/Surface;");
/* Run the application. */
@ -810,12 +810,12 @@ void Platform_PreCreateMove( void )
/*
========================
Android_RunEvents
Android_ProcessEvents
Execute all events from queue
========================
*/
void Platform_RunEvents( void )
static void Android_ProcessEvents( void )
{
int i;
@ -859,7 +859,7 @@ void Platform_RunEvents( void )
{
SNDDMA_Activate( true );
// (*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
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
}
@ -867,7 +867,7 @@ void Platform_RunEvents( void )
if( events.queue[i].arg )
{
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.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, 1 );
Con_DPrintf("resize event\n");
Android_UpdateSurface( false );
Android_UpdateSurface( true );
Android_UpdateSurface( surface_pause );
Android_UpdateSurface( surface_active );
SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval
VID_SetMode();
}
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;
case event_joyadd:
@ -939,14 +939,14 @@ void Platform_RunEvents( void )
#endif
// disable sound during call/screen-off
SNDDMA_Activate( false );
host.status = HOST_SLEEP;
//host.status = HOST_SLEEP;
// stop blocking UI thread
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify );
break;
case event_onresume:
// re-enable sound after onPause
host.status = HOST_FRAME;
//host.status = HOST_FRAME;
SNDDMA_Activate( true );
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
break;
@ -995,4 +995,16 @@ void Platform_RunEvents( void )
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

View File

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

View File

@ -236,18 +236,39 @@ destroy old surface, recreate and make context current
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 );
host.status = HOST_SLEEP;
if( eglstate.surface )
{
egl.DestroySurface( eglstate.dpy, eglstate.surface );
eglstate.surface = EGL_NO_SURFACE;
}
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 )
@ -261,8 +282,10 @@ qboolean EGL_UpdateSurface( void *window )
Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() );
return false;
}
Con_DPrintf( S_NOTE "restored current context\n" );
host.status = HOST_FRAME;
if( !dummy)
host.status = HOST_FRAME;
return true;
@ -321,6 +344,11 @@ qboolean EGL_CreateContext( void )
eglstate.valid = true;
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;
}

View File

@ -12,6 +12,7 @@ extern struct eglstate_s
EGLContext context;
EGLConfig cfg;
EGLint numCfg;
qboolean support_surfaceless_context;
const char *extensions;
int gl_attribs[REF_GL_ATTRIBUTES_COUNT];
@ -49,7 +50,7 @@ void * EGL_GetProcAddress( const char *name );
void EGL_Terminate( void );
qboolean EGL_ImportContext( void );
qboolean EGL_CreateContext( void );
qboolean EGL_UpdateSurface( void *window );
qboolean EGL_UpdateSurface( void *window, qboolean dummy );
int EGL_GetAttribute( int attr, int *val );
size_t EGL_GenerateContextConfig( 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
========================
*/
void Android_UpdateSurface( qboolean active )
void Android_UpdateSurface( surfacestate_t state )
{
vid_android.nativeegl = false;
Con_Printf("1\n");
if( glw_state.software || ( eglstate.valid && !eglstate.imported ) )
qboolean active = state == surface_active;
if( glw_state.software || ( eglstate.valid && !eglstate.imported ))
{
Con_Printf("2\n");
if( vid_android.window && !active )
if( vid_android.window )
{
EGL_UpdateSurface( NULL, false );
nw.release( vid_android.window );
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;
jobject surf;
if( 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);
vid_android.window = nw.fromSurface(jni.env, surf);
Con_DPrintf("NativeWindow %p\n", vid_android.window);
if( surf )
{
vid_android.window = nw.fromSurface(jni.env, surf);
Con_DPrintf("NativeWindow %p\n", vid_android.window);
if( eglstate.valid )
egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format );
if( eglstate.valid )
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;
}
return;
}
Con_Printf("3\n");
if( !vid_android.has_context )
return;
//if( ( active && host.status == HOST_FRAME ) || !active )
if( !active )
{
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;
}
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, (int)state );
host.status = HOST_FRAME; // active ? HOST_FRAME : HOST_SLEEP;
// 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 )
{
uint arg;
// Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" );
// return false;
Android_UpdateSurface( true );
if( !nw.release )
{
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 ) )
return false;
}
@ -339,7 +343,7 @@ qboolean VID_SetMode( void )
vid_android.has_context = vid_android.nativeegl = EGL_CreateContext();
if( vid_android.has_context )
Android_UpdateSurface( true );
Android_UpdateSurface( surface_active );
else
return false;
}