forked from PojavLauncherTeam/PojavLauncher
-
Notifications
You must be signed in to change notification settings - Fork 244
Expand file tree
/
Copy pathgl_bridge.c
More file actions
227 lines (202 loc) · 9.76 KB
/
Copy pathgl_bridge.c
File metadata and controls
227 lines (202 loc) · 9.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#include <EGL/egl.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <stdbool.h>
#include <environ/environ.h>
#include "gl_bridge.h"
#include "egl_loader.h"
#define TAG __FILE_NAME__
#include <unistd.h>
#include <log.h>
//
// Created by maks on 17.09.2022.
//
static __thread gl_render_window_t* currentBundle;
static EGLDisplay g_EglDisplay;
bool gl_init() {
if(!dlsym_EGL()) return false;
g_EglDisplay = eglGetDisplay_p(EGL_DEFAULT_DISPLAY);
if (g_EglDisplay == EGL_NO_DISPLAY) {
LOGE("%s", "eglGetDisplay_p(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY");
return false;
}
if (eglInitialize_p(g_EglDisplay, 0, 0) != EGL_TRUE) {
LOGE("eglInitialize_p() failed: %04x", eglGetError_p());
return false;
}
return true;
}
gl_render_window_t* gl_get_current() {
return currentBundle;
}
static void gl4esi_get_display_dimensions(int* width, int* height) {
if(currentBundle == NULL) goto zero;
EGLSurface surface = currentBundle->surface;
// Fetch dimensions from the EGL surface - the most reliable way
EGLBoolean result_width = eglQuerySurface_p(g_EglDisplay, surface, EGL_WIDTH, width);
EGLBoolean result_height = eglQuerySurface_p(g_EglDisplay, surface, EGL_HEIGHT, height);
if(!result_width || !result_height) goto zero;
return;
zero:
// No idea what to do, but feeding gl4es incorrect or non-initialized dimensions may be
// a bad idea. Set to zero in case of errors.
*width = 0;
*height = 0;
}
gl_render_window_t* gl_init_context(gl_render_window_t *share) {
gl_render_window_t* bundle = malloc(sizeof(gl_render_window_t));
memset(bundle, 0, sizeof(gl_render_window_t));
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_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE };
EGLint num_configs = 0;
if (eglChooseConfig_p(g_EglDisplay, egl_attributes, NULL, 0, &num_configs) != EGL_TRUE) {
LOGE("eglChooseConfig_p() failed: %04x", eglGetError_p());
free(bundle);
return NULL;
}
if (num_configs == 0) {
LOGE("%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);
{
EGLBoolean bindResult;
if (strncmp(getenv("AMETHYST_RENDERER"), "opengles3_desktopgl", 19) == 0) {
printf("EGLBridge: Binding to desktop OpenGL\n");
bindResult = eglBindAPI_p(EGL_OPENGL_API);
} else {
printf("EGLBridge: Binding to OpenGL ES\n");
bindResult = eglBindAPI_p(EGL_OPENGL_ES_API);
}
if (!bindResult) printf("EGLBridge: bind failed: %p\n", eglGetError_p());
}
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->config, share == NULL ? EGL_NO_CONTEXT : share->context, egl_context_attributes);
if (bundle->context == EGL_NO_CONTEXT) {
LOGE("eglCreateContext_p() finished with error: %04x", eglGetError_p());
free(bundle);
return NULL;
}
return bundle;
}
void gl_swap_surface(gl_render_window_t* bundle) {
/*
* In some cases (see MinecraftGLSurface.start(), android kills the surface automatically for
* us, if we try to release/destroy it, we SIGSEGV. Check if we are -19x-19 or some other
* invalid value and skip the release because Android decided to handle releasing it for us.
* This goes against every piece of documentation I have ever seen but who actually reads those?
*
* Some drivers take forever to properly destroy the surface, they do it part at a time or
* some other garbage while SIGSEGVing us if we try releasing while they're in the middle of
* turning the surface dead. This makes the width and height make it look valid when it actually
* isn't so we wait for them and hope there is no race condition of both us and Android trying
* to release the surface. This seems driver dependent as AVD and Waydroid do not need 0.75s
* to set the bloody height and width to their proper values. They just do it, instantly.
*/
usleep(750000); // An overkill amount of time to wait for a surface to finish dying
int32_t nativeWindowWidth = ANativeWindow_getWidth(pojav_environ->pojavWindow);
int32_t nativeWindowHeight = ANativeWindow_getHeight(pojav_environ->pojavWindow);
if ((nativeWindowWidth > 0) || (nativeWindowHeight > 0)) {
LOGI("Native surface dimensions (%d x %d)\n",
nativeWindowWidth, nativeWindowHeight);
if (bundle->nativeSurface != NULL) {
ANativeWindow_release(bundle->nativeSurface);
}
if (bundle->surface != NULL) eglDestroySurface_p(g_EglDisplay, bundle->surface);
} else {
LOGW("Native surface dimensions (%d x %d) are invalid! Assuming given nativeSurface is bad.\n",
nativeWindowWidth, nativeWindowHeight);
}
if(bundle->newNativeSurface != NULL) {
LOGI("Switching to new native surface");
bundle->nativeSurface = bundle->newNativeSurface;
bundle->newNativeSurface = NULL;
ANativeWindow_acquire(bundle->nativeSurface);
ANativeWindow_setBuffersGeometry(bundle->nativeSurface, 0, 0, bundle->format);
bundle->surface = eglCreateWindowSurface_p(g_EglDisplay, bundle->config, bundle->nativeSurface, NULL);
}else{
LOGI("No new native surface, switching to 1x1 pbuffer");
bundle->nativeSurface = NULL;
const EGLint pbuffer_attrs[] = {EGL_WIDTH, 1 , EGL_HEIGHT, 1, EGL_NONE};
bundle->surface = eglCreatePbufferSurface_p(g_EglDisplay, bundle->config, pbuffer_attrs);
}
//eglMakeCurrent_p(g_EglDisplay, bundle->surface, bundle->surface, bundle->context);
}
void gl_make_current(gl_render_window_t* bundle) {
if(bundle == NULL) {
if(eglMakeCurrent_p(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
currentBundle = NULL;
}
return;
}
bool hasSetMainWindow = false;
if(pojav_environ->mainWindowBundle == NULL) {
pojav_environ->mainWindowBundle = (basic_render_window_t*)bundle;
LOGI("Main window bundle is now %p", pojav_environ->mainWindowBundle);
pojav_environ->mainWindowBundle->newNativeSurface = pojav_environ->pojavWindow;
hasSetMainWindow = true;
}
LOGI("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
gl_swap_surface(bundle);
}
if(eglMakeCurrent_p(g_EglDisplay, bundle->surface, bundle->surface, bundle->context)) {
currentBundle = bundle;
}else {
if(hasSetMainWindow) {
pojav_environ->mainWindowBundle->newNativeSurface = NULL;
gl_swap_surface((gl_render_window_t*)pojav_environ->mainWindowBundle);
pojav_environ->mainWindowBundle = NULL;
}
LOGE("eglMakeCurrent returned with error: %04x", eglGetError_p());
}
}
void gl_swap_buffers() {
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
gl_swap_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);
currentBundle->newNativeSurface = NULL;
gl_swap_surface(currentBundle);
eglMakeCurrent_p(g_EglDisplay, currentBundle->surface, currentBundle->surface, currentBundle->context);
LOGI("The window has died, awaiting window change");
}
}
void gl_setup_window() {
if(pojav_environ->mainWindowBundle != NULL) {
LOGI("Main window bundle is not NULL, changing state");
pojav_environ->mainWindowBundle->state = STATE_RENDERER_NEW_WINDOW;
pojav_environ->mainWindowBundle->newNativeSurface = pojav_environ->pojavWindow;
}
}
void gl_swap_interval(int swapInterval) {
if(pojav_environ->force_vsync) swapInterval = 1;
eglSwapInterval_p(g_EglDisplay, swapInterval);
}
JNIEXPORT void JNICALL
Java_org_lwjgl_opengl_PojavRendererInit_nativeInitGl4esInternals(JNIEnv *env, jclass clazz,
jobject function_provider) {
LOGI("GL4ES internals initializing...");
jclass funcProviderClass = (*env)->GetObjectClass(env, function_provider);
jmethodID method_getFunctionAddress = (*env)->GetMethodID(env, funcProviderClass, "getFunctionAddress", "(Ljava/lang/CharSequence;)J");
#define GETSYM(N) ((*env)->CallLongMethod(env, function_provider, method_getFunctionAddress, (*env)->NewStringUTF(env, N)));
void (*set_getmainfbsize)(void (*new_getMainFBSize)(int* width, int* height)) = (void*)GETSYM("set_getmainfbsize");
if(set_getmainfbsize != NULL) {
LOGI("GL4ES internals initialized dimension callback");
set_getmainfbsize(gl4esi_get_display_dimensions);
}
#undef GETSYM
}