- Moved all gl4es+egl management into gl_bridge.c and gl_bridge.h
- Also, reimplemented it from scratch with surface swapping support
This commit is contained in:
Boulay Mathias 2022-10-03 19:48:54 +02:00
parent 424e6bb630
commit 1261e4a7ea
5 changed files with 246 additions and 53 deletions

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#include <android/rect.h>
#include <string.h>
#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;

View File

@ -0,0 +1,189 @@
#include <EGL/egl.h>
#include <android/log.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <malloc.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <stdbool.h>
#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);
}

View File

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