Path: blob/v3_openjdk/app_pojavlauncher/src/main/jni/egl_bridge.c
2128 views
#include <jni.h>1#include <assert.h>2#include <dlfcn.h>34#include <stdbool.h>5#include <stdint.h>6#include <stdio.h>7#include <stdlib.h>8#include <sys/types.h>9#include <unistd.h>1011#include <EGL/egl.h>12#include <GL/osmesa.h>13#include "ctxbridges/osmesa_loader.h"14#include "driver_helper/nsbypass.h"1516#ifdef GLES_TEST17#include <GLES2/gl2.h>18#endif1920#include <android/native_window.h>21#include <android/native_window_jni.h>22#include <android/rect.h>23#include <string.h>24#include <environ/environ.h>25#include <android/dlext.h>26#include "utils.h"27#include "ctxbridges/bridge_tbl.h"28#include "ctxbridges/osm_bridge.h"2930#define GLFW_CLIENT_API 0x2200131/* Consider GLFW_NO_API as Vulkan API */32#define GLFW_NO_API 033#define GLFW_OPENGL_API 0x300013435// This means that the function is an external API and that it will be used36#define EXTERNAL_API __attribute__((used))37// This means that you are forced to have this function/variable for ABI compatibility38#define ABI_COMPAT __attribute__((unused))394041struct PotatoBridge {4243/* EGLContext */ void* eglContext;44/* EGLDisplay */ void* eglDisplay;45/* EGLSurface */ void* eglSurface;46/*47void* eglSurfaceRead;48void* eglSurfaceDraw;49*/50};51EGLConfig config;52struct PotatoBridge potatoBridge;5354#include "ctxbridges/egl_loader.h"55#include "ctxbridges/osmesa_loader.h"5657#define RENDERER_GL4ES 158#define RENDERER_VK_ZINK 259#define RENDERER_VULKAN 46061EXTERNAL_API void pojavTerminate() {62printf("EGLBridge: Terminating\n");6364switch (pojav_environ->config_renderer) {65case RENDERER_GL4ES: {66eglMakeCurrent_p(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);67eglDestroySurface_p(potatoBridge.eglDisplay, potatoBridge.eglSurface);68eglDestroyContext_p(potatoBridge.eglDisplay, potatoBridge.eglContext);69eglTerminate_p(potatoBridge.eglDisplay);70eglReleaseThread_p();7172potatoBridge.eglContext = EGL_NO_CONTEXT;73potatoBridge.eglDisplay = EGL_NO_DISPLAY;74potatoBridge.eglSurface = EGL_NO_SURFACE;75} break;7677//case RENDERER_VIRGL:78case RENDERER_VK_ZINK: {79// Nothing to do here80} break;81}82}8384JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setupBridgeWindow(JNIEnv* env, ABI_COMPAT jclass clazz, jobject surface) {85pojav_environ->pojavWindow = ANativeWindow_fromSurface(env, surface);86if(br_setup_window != NULL) br_setup_window();87}888990JNIEXPORT void JNICALL91Java_net_kdt_pojavlaunch_utils_JREUtils_releaseBridgeWindow(ABI_COMPAT JNIEnv *env, ABI_COMPAT jclass clazz) {92ANativeWindow_release(pojav_environ->pojavWindow);93}9495EXTERNAL_API void* pojavGetCurrentContext() {96return br_get_current();97}9899//#define ADRENO_POSSIBLE100#ifdef ADRENO_POSSIBLE101void* load_turnip_vulkan() {102if(getenv("POJAV_LOAD_TURNIP") == NULL) return NULL;103const char* native_dir = getenv("POJAV_NATIVEDIR");104const char* cache_dir = getenv("TMPDIR");105if(!linker_ns_load(native_dir)) return NULL;106void* linkerhook = linker_ns_dlopen("liblinkerhook.so", RTLD_LOCAL | RTLD_NOW);107if(linkerhook == NULL) return NULL;108void* turnip_driver_handle = linker_ns_dlopen("libvulkan_freedreno.so", RTLD_LOCAL | RTLD_NOW);109if(turnip_driver_handle == NULL) {110printf("AdrenoSupp: Failed to load Turnip!\n%s\n", dlerror());111dlclose(linkerhook);112return NULL;113}114void* dl_android = linker_ns_dlopen("libdl_android.so", RTLD_LOCAL | RTLD_LAZY);115if(dl_android == NULL) {116dlclose(linkerhook);117dlclose(turnip_driver_handle);118return NULL;119}120void* android_get_exported_namespace = dlsym(dl_android, "android_get_exported_namespace");121void (*linkerhook_pass_handles)(void*, void*, void*) = dlsym(linkerhook, "app__pojav_linkerhook_pass_handles");122if(linkerhook_pass_handles == NULL || android_get_exported_namespace == NULL) {123dlclose(dl_android);124dlclose(linkerhook);125dlclose(turnip_driver_handle);126return NULL;127}128linkerhook_pass_handles(turnip_driver_handle, android_dlopen_ext, android_get_exported_namespace);129void* libvulkan = linker_ns_dlopen_unique(cache_dir, "libvulkan.so", RTLD_LOCAL | RTLD_NOW);130return libvulkan;131}132#endif133134static void set_vulkan_ptr(void* ptr) {135char envval[64];136sprintf(envval, "%"PRIxPTR, (uintptr_t)ptr);137setenv("VULKAN_PTR", envval, 1);138}139140void load_vulkan() {141if(android_get_device_api_level() >= 28) { // the loader does not support below that142#ifdef ADRENO_POSSIBLE143void* result = load_turnip_vulkan();144if(result != NULL) {145printf("AdrenoSupp: Loaded Turnip, loader address: %p\n", result);146set_vulkan_ptr(result);147return;148}149#endif150}151printf("OSMDroid: loading vulkan regularly...\n");152void* vulkan_ptr = dlopen("libvulkan.so", RTLD_LAZY | RTLD_LOCAL);153printf("OSMDroid: loaded vulkan, ptr=%p\n", vulkan_ptr);154set_vulkan_ptr(vulkan_ptr);155}156157int pojavInitOpenGL() {158// Only affects GL4ES as of now159const char *forceVsync = getenv("FORCE_VSYNC");160if (strcmp(forceVsync, "true") == 0)161pojav_environ->force_vsync = true;162163// NOTE: Override for now.164const char *renderer = getenv("POJAV_RENDERER");165if (strncmp("opengles", renderer, 8) == 0) {166pojav_environ->config_renderer = RENDERER_GL4ES;167set_gl_bridge_tbl();168} else if (strcmp(renderer, "vulkan_zink") == 0) {169pojav_environ->config_renderer = RENDERER_VK_ZINK;170load_vulkan();171setenv("GALLIUM_DRIVER","zink",1);172set_osm_bridge_tbl();173}174if(br_init()) {175br_setup_window();176}177return 0;178}179180extern void updateMonitorSize(int width, int height);181182EXTERNAL_API int pojavInit() {183pojav_environ->glfwThreadVmEnv = get_attached_env(pojav_environ->runtimeJavaVMPtr);184if(pojav_environ->glfwThreadVmEnv == NULL) {185printf("Failed to attach Java-side JNIEnv to GLFW thread\n");186return 0;187}188ANativeWindow_acquire(pojav_environ->pojavWindow);189pojav_environ->savedWidth = ANativeWindow_getWidth(pojav_environ->pojavWindow);190pojav_environ->savedHeight = ANativeWindow_getHeight(pojav_environ->pojavWindow);191ANativeWindow_setBuffersGeometry(pojav_environ->pojavWindow,pojav_environ->savedWidth,pojav_environ->savedHeight,AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM);192updateMonitorSize(pojav_environ->savedWidth, pojav_environ->savedHeight);193pojavInitOpenGL();194return 1;195}196197EXTERNAL_API void pojavSetWindowHint(int hint, int value) {198if (hint != GLFW_CLIENT_API) return;199switch (value) {200case GLFW_NO_API:201pojav_environ->config_renderer = RENDERER_VULKAN;202/* Nothing to do: initialization is handled in Java-side */203// pojavInitVulkan();204break;205case GLFW_OPENGL_API:206/* Nothing to do: initialization is called in pojavCreateContext */207// pojavInitOpenGL();208break;209default:210printf("GLFW: Unimplemented API 0x%x\n", value);211abort();212}213}214215EXTERNAL_API void pojavSwapBuffers() {216br_swap_buffers();217}218219220EXTERNAL_API void pojavMakeCurrent(void* window) {221br_make_current((basic_render_window_t*)window);222}223224EXTERNAL_API void* pojavCreateContext(void* contextSrc) {225if (pojav_environ->config_renderer == RENDERER_VULKAN) {226return (void *) pojav_environ->pojavWindow;227}228return br_init_context((basic_render_window_t*)contextSrc);229}230231void* maybe_load_vulkan() {232// We use the env var because233// 1. it's easier to do that234// 2. it won't break if something will try to load vulkan and osmesa simultaneously235if(getenv("VULKAN_PTR") == NULL) load_vulkan();236return (void*) strtoul(getenv("VULKAN_PTR"), NULL, 0x10);237}238239EXTERNAL_API JNIEXPORT jlong JNICALL240Java_org_lwjgl_vulkan_VK_getVulkanDriverHandle(ABI_COMPAT JNIEnv *env, ABI_COMPAT jclass thiz) {241printf("EGLBridge: LWJGL-side Vulkan loader requested the Vulkan handle\n");242return (jlong) maybe_load_vulkan();243}244245EXTERNAL_API void pojavSwapInterval(int interval) {246br_swap_interval(interval);247}248249250251