engine: android: implement egl context management

This commit is contained in:
Alibek Omarov 2019-05-12 03:06:55 +03:00
parent a1391f3d0b
commit 85c2126775
4 changed files with 271 additions and 121 deletions

View File

@ -20,6 +20,8 @@ GNU General Public License for more details.
#include "platform/android/android_priv.h"
#include "errno.h"
#include <pthread.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#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;
}
}

View File

@ -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

View File

@ -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 );
}

View File

@ -5,6 +5,12 @@
#include "filesystem.h"
#include "platform/android/android_priv.h"
#include "vid_common.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
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 )