From 1261e4a7ea572caef602cced1d948b6792d460af Mon Sep 17 00:00:00 2001 From: Boulay Mathias Date: Mon, 3 Oct 2022 19:48:54 +0200 Subject: [PATCH] Changes - Moved all gl4es+egl management into gl_bridge.c and gl_bridge.h - Also, reimplemented it from scratch with surface swapping support --- .../kdt/pojavlaunch/MinecraftGLSurface.java | 10 +- app_pojavlauncher/src/main/jni/Android.mk | 1 + app_pojavlauncher/src/main/jni/egl_bridge.c | 73 ++----- app_pojavlauncher/src/main/jni/gl_bridge.c | 189 ++++++++++++++++++ app_pojavlauncher/src/main/jni/gl_bridge.h | 26 +++ 5 files changed, 246 insertions(+), 53 deletions(-) create mode 100644 app_pojavlauncher/src/main/jni/gl_bridge.c create mode 100644 app_pojavlauncher/src/main/jni/gl_bridge.h diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java index b40284f4f..364d014ee 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java @@ -143,7 +143,10 @@ public class MinecraftGLSurface extends View { private boolean isCalled = false; @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { - if(isCalled) return; + if(isCalled) { + JREUtils.setupBridgeWindow(surfaceView.getHolder().getSurface()); + return; + } isCalled = true; realStart(surfaceView.getHolder().getSurface()); @@ -168,7 +171,10 @@ public class MinecraftGLSurface extends View { private boolean isCalled = false; @Override public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { - if(isCalled) return; + if(isCalled) { + JREUtils.setupBridgeWindow(new Surface(surface)); + return; + } isCalled = true; realStart(new Surface(textureView.getSurfaceTexture())); diff --git a/app_pojavlauncher/src/main/jni/Android.mk b/app_pojavlauncher/src/main/jni/Android.mk index 60f17eb7d..48e830cf0 100644 --- a/app_pojavlauncher/src/main/jni/Android.mk +++ b/app_pojavlauncher/src/main/jni/Android.mk @@ -33,6 +33,7 @@ LOCAL_MODULE := pojavexec # -DGLES_TEST LOCAL_SRC_FILES := \ egl_bridge.c \ + gl_bridge.c \ input_bridge_v3.c \ jre_launcher.c \ utils.c diff --git a/app_pojavlauncher/src/main/jni/egl_bridge.c b/app_pojavlauncher/src/main/jni/egl_bridge.c index 687cf8550..bb6b73371 100644 --- a/app_pojavlauncher/src/main/jni/egl_bridge.c +++ b/app_pojavlauncher/src/main/jni/egl_bridge.c @@ -21,6 +21,7 @@ #include #include #include "utils.h" +#include "gl_bridge.h" // region OSMESA internals struct pipe_screen; @@ -668,6 +669,9 @@ void pojavTerminate() { JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setupBridgeWindow(JNIEnv* env, jclass clazz, jobject surface) { potatoBridge.androidWindow = ANativeWindow_fromSurface(env, surface); + if(config_renderer == RENDERER_GL4ES) { + gl_setup_window(potatoBridge.androidWindow); + } char *ptrStr; asprintf(&ptrStr, "%ld", (long) potatoBridge.androidWindow); setenv("POJAV_WINDOW_PTR", ptrStr, 1); @@ -813,14 +817,20 @@ int pojavInit() { loadSymbolsVirGL(); } else if (strncmp("opengles", renderer, 8) == 0) { config_renderer = RENDERER_GL4ES; - loadSymbols(); + //loadSymbols(); } else if (strcmp(renderer, "vulkan_zink") == 0) { config_renderer = RENDERER_VK_ZINK; setenv("GALLIUM_DRIVER","zink",1); loadSymbols(); } - - if (config_renderer == RENDERER_GL4ES || config_renderer == RENDERER_VIRGL) { + if(config_renderer == RENDERER_GL4ES) { + if(gl_init()) { + gl_setup_window(potatoBridge.androidWindow); + return 1; + } + return 0; + } + if (config_renderer == RENDERER_VIRGL) { if (potatoBridge.eglDisplay == NULL || potatoBridge.eglDisplay == EGL_NO_DISPLAY) { potatoBridge.eglDisplay = eglGetDisplay_p(EGL_DEFAULT_DISPLAY); if (potatoBridge.eglDisplay == EGL_NO_DISPLAY) { @@ -938,12 +948,7 @@ void pojavSwapBuffers() { } switch (config_renderer) { case RENDERER_GL4ES: { - if (!eglSwapBuffers_p(potatoBridge.eglDisplay, eglGetCurrentSurface_p(EGL_DRAW))) { - if (eglGetError_p() == EGL_BAD_SURFACE) { - stopSwapBuffers = true; - closeGLFWWindow(); - } - } + gl_swap_buffers(); } break; case RENDERER_VIRGL: { @@ -993,46 +998,9 @@ void pojavMakeCurrent(void* window) { // printf("OSMDroid: skipped context reset\n"); // return JNI_TRUE; //} - - if (config_renderer == RENDERER_GL4ES) { - EGLContext *currCtx = eglGetCurrentContext_p(); - printf("EGLBridge: Comparing: thr=%d, this=%p, curr=%p\n", gettid(), window, currCtx); - if (currCtx == NULL || window == 0) { - /*if (window != 0x0 && potatoBridge.eglContextOld != NULL && potatoBridge.eglContextOld != (void *) window) { - // Create new pbuffer per thread - // TODO get window size for 2nd+ window! - int surfaceWidth, surfaceHeight; - eglQuerySurface(potatoBridge.eglDisplay, potatoBridge.eglSurface, EGL_WIDTH, &surfaceWidth); - eglQuerySurface(potatoBridge.eglDisplay, potatoBridge.eglSurface, EGL_HEIGHT, &surfaceHeight); - int surfaceAttr[] = { - EGL_WIDTH, surfaceWidth, - EGL_HEIGHT, surfaceHeight, - EGL_NONE - }; - potatoBridge.eglSurface = eglCreatePbufferSurface(potatoBridge.eglDisplay, config, surfaceAttr); - printf("EGLBridge: created pbuffer surface %p for context %p\n", potatoBridge.eglSurface, window); - }*/ - //potatoBridge.eglContextOld = (void *) window; - // eglMakeCurrent(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - printf("EGLBridge: Making current on window %p on thread %d\n", window, gettid()); - egl_make_current((void *)window); - - // Test -#ifdef GLES_TEST - glClearColor(0.4f, 0.4f, 0.4f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(potatoBridge.eglDisplay, potatoBridge.eglSurface); - printf("First frame error: %p\n", eglGetError()); -#endif - - // idk this should convert or just `return success;`... - return; //success == EGL_TRUE ? JNI_TRUE : JNI_FALSE; - } else { - // (*env)->ThrowNew(env,(*env)->FindClass(env,"java/lang/Exception"),"Trace exception"); - return; - } + if(config_renderer == RENDERER_GL4ES) { + gl_make_current((render_bundle_t*)window); } - if (config_renderer == RENDERER_VK_ZINK || config_renderer == RENDERER_VIRGL) { printf("OSMDroid: making current\n"); OSMesaMakeCurrent_p((OSMesaContext)window,gbuffer,GL_UNSIGNED_BYTE,savedWidth,savedHeight); @@ -1078,7 +1046,7 @@ Java_org_lwjgl_glfw_GLFW_nativeEglDetachOnCurrentThread(JNIEnv *env, jclass claz void* pojavCreateContext(void* contextSrc) { if (config_renderer == RENDERER_GL4ES) { - const EGLint ctx_attribs[] = { + /*const EGLint ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, atoi(getenv("LIBGL_ES")), EGL_NONE }; @@ -1086,7 +1054,8 @@ void* pojavCreateContext(void* contextSrc) { potatoBridge.eglContext = ctx; printf("EGLBridge: Created CTX pointer = %p\n",ctx); //(*env)->ThrowNew(env,(*env)->FindClass(env,"java/lang/Exception"),"Trace exception"); - return (long)ctx; + return (long)ctx;*/ + return gl_init_context(contextSrc); } if (config_renderer == RENDERER_VK_ZINK || config_renderer == RENDERER_VIRGL) { @@ -1119,7 +1088,9 @@ Java_org_lwjgl_opengl_GL_getNativeWidthHeight(JNIEnv *env, jobject thiz) { } void pojavSwapInterval(int interval) { switch (config_renderer) { - case RENDERER_GL4ES: + case RENDERER_GL4ES: { + gl_swap_interval(interval); + } break; case RENDERER_VIRGL: { eglSwapInterval_p(potatoBridge.eglDisplay, interval); } break; diff --git a/app_pojavlauncher/src/main/jni/gl_bridge.c b/app_pojavlauncher/src/main/jni/gl_bridge.c new file mode 100644 index 000000000..95f06f8ae --- /dev/null +++ b/app_pojavlauncher/src/main/jni/gl_bridge.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "gl_bridge.h" + +// +// Created by maks on 17.09.2022. +// + +#define STATE_RENDERER_ALIVE 0 +#define STATE_RENDERER_NEW_WINDOW 1 +static char* g_LogTag = "GLBridge"; + +EGLBoolean (*eglMakeCurrent_p) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); +EGLBoolean (*eglDestroyContext_p) (EGLDisplay dpy, EGLContext ctx); +EGLBoolean (*eglDestroySurface_p) (EGLDisplay dpy, EGLSurface surface); +EGLBoolean (*eglTerminate_p) (EGLDisplay dpy); +EGLBoolean (*eglReleaseThread_p) (void); +EGLContext (*eglGetCurrentContext_p) (void); +EGLDisplay (*eglGetDisplay_p) (NativeDisplayType display); +EGLBoolean (*eglInitialize_p) (EGLDisplay dpy, EGLint *major, EGLint *minor); +EGLBoolean (*eglChooseConfig_p) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); +EGLBoolean (*eglGetConfigAttrib_p) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); +EGLBoolean (*eglBindAPI_p) (EGLenum api); +EGLSurface (*eglCreatePbufferSurface_p) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); +EGLSurface (*eglCreateWindowSurface_p) (EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list); +EGLBoolean (*eglSwapBuffers_p) (EGLDisplay dpy, EGLSurface draw); +EGLint (*eglGetError_p) (void); +EGLContext (*eglCreateContext_p) (EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list); +EGLBoolean (*eglSwapInterval_p) (EGLDisplay dpy, EGLint interval); +EGLSurface (*eglGetCurrentSurface_p) (EGLint readdraw); + +struct ANativeWindow* newWindow; +static __thread render_bundle_t* currentBundle; +static render_bundle_t* mainWindowBundle; +EGLDisplay g_EglDisplay; + +void gl_dlsym_EGL() { + void* dl_handle = NULL; + if(getenv("POJAVEXEC_EGL")) dl_handle = dlopen(getenv("POJAVEXEC_EGL"), RTLD_LAZY); + if(dl_handle == NULL) dl_handle = dlopen("libEGL.so", RTLD_LAZY); + if(dl_handle == NULL) abort(); + eglBindAPI_p = dlsym(dl_handle,"eglBindAPI"); + eglChooseConfig_p = dlsym(dl_handle, "eglChooseConfig"); + eglCreateContext_p = dlsym(dl_handle, "eglCreateContext"); + eglCreatePbufferSurface_p = dlsym(dl_handle, "eglCreatePbufferSurface"); + eglCreateWindowSurface_p = dlsym(dl_handle, "eglCreateWindowSurface"); + eglDestroyContext_p = dlsym(dl_handle, "eglDestroyContext"); + eglDestroySurface_p = dlsym(dl_handle, "eglDestroySurface"); + eglGetConfigAttrib_p = dlsym(dl_handle, "eglGetConfigAttrib"); + eglGetCurrentContext_p = dlsym(dl_handle, "eglGetCurrentContext"); + eglGetDisplay_p = dlsym(dl_handle, "eglGetDisplay"); + eglGetError_p = dlsym(dl_handle, "eglGetError"); + eglInitialize_p = dlsym(dl_handle, "eglInitialize"); + eglMakeCurrent_p = dlsym(dl_handle, "eglMakeCurrent"); + eglSwapBuffers_p = dlsym(dl_handle, "eglSwapBuffers"); + eglReleaseThread_p = dlsym(dl_handle, "eglReleaseThread"); + eglSwapInterval_p = dlsym(dl_handle, "eglSwapInterval"); + eglTerminate_p = dlsym(dl_handle, "eglTerminate"); + eglGetCurrentSurface_p = dlsym(dl_handle,"eglGetCurrentSurface"); +} + +bool gl_init() { + gl_dlsym_EGL(); + g_EglDisplay = eglGetDisplay_p(EGL_DEFAULT_DISPLAY); + if (g_EglDisplay == EGL_NO_DISPLAY) { + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", + "eglGetDisplay_p(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY"); + return false; + } + if (eglInitialize_p(g_EglDisplay, 0, 0) != EGL_TRUE) { + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglInitialize_p() failed: %04x", + eglGetError_p()); + return false; + } + eglSwapInterval_p(g_EglDisplay, 1); + return true; +} + +render_bundle_t* gl_init_context(render_bundle_t *share) { + render_bundle_t* bundle = malloc(sizeof(render_bundle_t)); + const EGLint egl_attributes[] = { EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; + EGLint num_configs = 0; + if (eglChooseConfig_p(g_EglDisplay, egl_attributes, NULL, 0, &num_configs) != EGL_TRUE) { + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglChooseConfig_p() failed: %04x", + eglGetError_p()); + free(bundle); + return NULL; + } + if (num_configs == 0) { + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", + "eglChooseConfig_p() found no matching config"); + free(bundle); + return NULL; + } + + // Get the first matching config + eglChooseConfig_p(g_EglDisplay, egl_attributes, &bundle->config, 1, &num_configs); + eglGetConfigAttrib_p(g_EglDisplay, bundle->config, EGL_NATIVE_VISUAL_ID, &bundle->format); + + int libgl_es = strtol(getenv("LIBGL_ES"), NULL, 0); + if(libgl_es < 0 || libgl_es > INT16_MAX) libgl_es = 2; + const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, libgl_es, EGL_NONE }; + bundle->context = eglCreateContext_p(g_EglDisplay, bundle->context, share == NULL ? EGL_NO_CONTEXT : share->context, egl_context_attributes); + + if (bundle->context == EGL_NO_CONTEXT) { + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglCreateContext_p() finished with error: %04x", + eglGetError_p()); + free(bundle); + return NULL; + } + return bundle; +} + +void gl_set_android_surface(render_bundle_t* bundle) { + if(bundle->nativeSurface != NULL) { + ANativeWindow_release(bundle->nativeSurface); + } + if(bundle->newNativeSurface != NULL) { + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "Switching to new native surface"); + bundle->nativeSurface = bundle->newNativeSurface; + ANativeWindow_acquire(bundle->nativeSurface); + ANativeWindow_setBuffersGeometry(bundle->nativeSurface, 0, 0, bundle->format); + bundle->surface = eglCreateWindowSurface_p(g_EglDisplay, bundle->config, bundle->nativeSurface, NULL); + }else{ + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "No new native surface, disabling surfaces"); + bundle->nativeSurface = NULL; + bundle->surface = NULL; + } + //eglMakeCurrent_p(g_EglDisplay, bundle->surface, bundle->surface, bundle->context); +} + +void gl_make_current(render_bundle_t* bundle) { + if(bundle == NULL) { + __android_log_print(ANDROID_LOG_FATAL, g_LogTag, "Cannot make a NULL bundle current!"); + abort(); + } + __android_log_print(ANDROID_LOG_INFO, g_LogTag, "Making current, surface=%p, nativeSurface=%p, newNativeSurface=%p", bundle->surface, bundle->nativeSurface, bundle->newNativeSurface); + if(bundle->surface == NULL) { //it likely will be on the first run + if(bundle->nativeSurface || bundle->newNativeSurface) gl_set_android_surface(bundle); + } + if(eglMakeCurrent_p(g_EglDisplay, bundle->surface, bundle->surface, bundle->context)) { + currentBundle = bundle; + }else __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglMakeCurrent returned with error: %04x", eglGetError_p()); + +} + +void gl_swap_buffers() { + if(mainWindowBundle == NULL) { + mainWindowBundle = currentBundle; + __android_log_print(ANDROID_LOG_INFO, g_LogTag, "Main window bundle is now %p", mainWindowBundle); + mainWindowBundle->state = STATE_RENDERER_NEW_WINDOW; + mainWindowBundle->newNativeSurface = newWindow; + } + if(currentBundle->state == STATE_RENDERER_NEW_WINDOW) { + eglMakeCurrent_p(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); //detach everything to destroy the old EGLSurface + if(currentBundle->surface != NULL) eglDestroySurface_p(g_EglDisplay, currentBundle->surface); + gl_set_android_surface(currentBundle); + eglMakeCurrent_p(g_EglDisplay, currentBundle->surface, currentBundle->surface, currentBundle->context); + currentBundle->state = STATE_RENDERER_ALIVE; + } + if(currentBundle->surface != NULL) + if(!eglSwapBuffers_p(g_EglDisplay, currentBundle->surface) && eglGetError_p() == EGL_BAD_SURFACE) { + eglMakeCurrent_p(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface_p(g_EglDisplay, currentBundle->surface); + currentBundle->surface = NULL; + __android_log_print(ANDROID_LOG_INFO, g_LogTag, "The window has died, awaiting window change"); + eglMakeCurrent_p(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, currentBundle->context); + } + +} + +void gl_setup_window(struct ANativeWindow* window) { + if(mainWindowBundle != NULL) { + mainWindowBundle->state = STATE_RENDERER_NEW_WINDOW; + mainWindowBundle->newNativeSurface = window; + }else{ + newWindow = window; + } +} + +void gl_swap_interval(int swapInterval) { + eglSwapInterval_p(g_EglDisplay, swapInterval); +} diff --git a/app_pojavlauncher/src/main/jni/gl_bridge.h b/app_pojavlauncher/src/main/jni/gl_bridge.h new file mode 100644 index 000000000..5c7a28aa1 --- /dev/null +++ b/app_pojavlauncher/src/main/jni/gl_bridge.h @@ -0,0 +1,26 @@ +// +// Created by maks on 17.09.2022. +// + +#ifndef POJAVLAUNCHER_GL_BRIDGE_H +#define POJAVLAUNCHER_GL_BRIDGE_H + +typedef struct { + char state; + EGLConfig config; + EGLint format; + EGLContext context; + EGLSurface surface; + struct ANativeWindow *nativeSurface; + struct ANativeWindow *newNativeSurface; +} render_bundle_t; + +bool gl_init(); +render_bundle_t* gl_init_context(render_bundle_t* share); +void gl_make_current(render_bundle_t* bundle); +void gl_swap_buffers(); +void gl_setup_window(struct ANativeWindow* window); +void gl_swap_interval(int swapInterval); + + +#endif //POJAVLAUNCHER_GL_BRIDGE_H