diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java index 60c216b58..5eb1338e6 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java @@ -60,6 +60,7 @@ public class LauncherPreferences { public static boolean PREF_DUMP_SHADERS = false; public static float PREF_DEADZONE_SCALE = 1f; public static boolean PREF_BIG_CORE_AFFINITY = false; + public static boolean PREF_ZINK_PREFER_SYSTEM_DRIVER = false; @@ -103,6 +104,7 @@ public class LauncherPreferences { PREF_DUMP_SHADERS = DEFAULT_PREF.getBoolean("dump_shaders", false); PREF_DEADZONE_SCALE = DEFAULT_PREF.getInt("gamepad_deadzone_scale", 100)/100f; PREF_BIG_CORE_AFFINITY = DEFAULT_PREF.getBoolean("bigCoreAffinity", false); + PREF_ZINK_PREFER_SYSTEM_DRIVER = DEFAULT_PREF.getBoolean("zinkPreferSystemDriver", false); String argLwjglLibname = "-Dorg.lwjgl.opengl.libname="; for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java index 1676de8b1..d17f12e3d 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java @@ -7,6 +7,7 @@ import static net.kdt.pojavlaunch.Tools.NATIVE_LIB_DIR; import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; import static net.kdt.pojavlaunch.Tools.shareLog; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_DUMP_SHADERS; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_ZINK_PREFER_SYSTEM_DRIVER; import android.app.*; import android.content.*; @@ -184,6 +185,8 @@ public class JREUtils { if(PREF_DUMP_SHADERS) envMap.put("LIBGL_VGPU_DUMP", "1"); + if(PREF_ZINK_PREFER_SYSTEM_DRIVER) + envMap.put("POJAV_ZINK_PREFER_SYSTEM_DRIVER", "1"); // The OPEN GL version is changed according @@ -444,7 +447,7 @@ public class JREUtils { case "opengles2_5": case "opengles3": renderLibrary = "libgl4es_114.so"; break; - case "vulkan_zink": renderLibrary = "libOSMesa_8.so"; break; + case "vulkan_zink": renderLibrary = "libOSMesa.so"; break; case "opengles3_desktopgl_angle_vulkan" : renderLibrary = "libtinywrapper.so"; break; default: Log.w("RENDER_LIBRARY", "No renderer selected, defaulting to opengles2"); diff --git a/app_pojavlauncher/src/main/jni/Android.mk b/app_pojavlauncher/src/main/jni/Android.mk index f13adf8da..dc3b31f9e 100644 --- a/app_pojavlauncher/src/main/jni/Android.mk +++ b/app_pojavlauncher/src/main/jni/Android.mk @@ -51,9 +51,23 @@ LOCAL_SRC_FILES := \ environ/environ.c \ input_bridge_v3.c \ jre_launcher.c \ - utils.c + utils.c \ + driver_helper/nsbypass.c +ifeq ($(TARGET_ARCH_ABI),arm64-v8a) +LOCAL_CFLAGS += -DADRENO_POSSIBLE +LOCAL_LDLIBS += -lEGL -lGLESv2 +endif include $(BUILD_SHARED_LIBRARY) +#ifeq ($(TARGET_ARCH_ABI),arm64-v8a) +include $(CLEAR_VARS) +LOCAL_MODULE := linkerhook +LOCAL_SRC_FILES := driver_helper/hook.c +LOCAL_LDFLAGS := -z global +include $(BUILD_SHARED_LIBRARY) +#endif + + include $(CLEAR_VARS) LOCAL_MODULE := istdio LOCAL_SHARED_LIBRARIES := xhook diff --git a/app_pojavlauncher/src/main/jni/ctxbridges/osmesa_loader.c b/app_pojavlauncher/src/main/jni/ctxbridges/osmesa_loader.c index c81c1977f..331e3c9a2 100644 --- a/app_pojavlauncher/src/main/jni/ctxbridges/osmesa_loader.c +++ b/app_pojavlauncher/src/main/jni/ctxbridges/osmesa_loader.c @@ -21,7 +21,7 @@ void (*glReadPixels_p) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum void dlsym_OSMesa() { char* main_path = NULL; char* alt_path = NULL; - if(asprintf(&main_path, "%s/libOSMesa_8.so", getenv("POJAV_NATIVEDIR")) == -1 || + if(asprintf(&main_path, "%s/libOSMesa.so", getenv("POJAV_NATIVEDIR")) == -1 || asprintf(&alt_path, "%s/libOSMesa.so.8", getenv("POJAV_NATIVEDIR")) == -1) { abort(); } diff --git a/app_pojavlauncher/src/main/jni/driver_helper/hook.c b/app_pojavlauncher/src/main/jni/driver_helper/hook.c new file mode 100644 index 000000000..1a544d48c --- /dev/null +++ b/app_pojavlauncher/src/main/jni/driver_helper/hook.c @@ -0,0 +1,43 @@ +// +// Created by maks on 05.06.2023. +// +#include +#include +#include + +typedef void* (*android_dlopen_ext_t)(const char *filename, int flags, const android_dlextinfo *extinfo); +typedef struct android_namespace_t* (*android_get_exported_namespace_t)(const char* name); + +static void* ready_handle; +static android_dlopen_ext_t original_func; +static android_get_exported_namespace_t android_get_exported_namespace; + +static const char *sphal_namespaces[3] = { + "sphal", "vendor", "default" +}; + + +__attribute__((visibility("default"))) void app__pojav_linkerhook_set_data(void* data, void* data1, void* data2) { + ready_handle = data; + original_func = data1; + android_get_exported_namespace = data2; +} + +__attribute__((visibility("default"))) void *android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) { + if(!strstr(filename, "vulkan.")) + return original_func(filename, flags, extinfo); + return ready_handle; +} + +__attribute__((visibility("default"))) void *android_load_sphal_library(const char *filename, int flags) { + if(strstr(filename, "vulkan.")) return ready_handle; + struct android_namespace_t* androidNamespace; + for(int i = 0; i < 3; i++) { + androidNamespace = android_get_exported_namespace(sphal_namespaces[i]); + if(androidNamespace != NULL) break; + } + android_dlextinfo info; + info.flags = ANDROID_DLEXT_USE_NAMESPACE; + info.library_namespace = androidNamespace; + return original_func(filename, flags, &info); +} \ No newline at end of file diff --git a/app_pojavlauncher/src/main/jni/driver_helper/nsbypass.c b/app_pojavlauncher/src/main/jni/driver_helper/nsbypass.c new file mode 100644 index 000000000..b26cf4f59 --- /dev/null +++ b/app_pojavlauncher/src/main/jni/driver_helper/nsbypass.c @@ -0,0 +1,165 @@ +// +// Created by maks on 05.06.2023. +// +#include "nsbypass.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* upper 6 bits of an ARM64 instruction are the instruction name */ +#define OP_MS 0b11111100000000000000000000000000 +/* Branch Label instruction opcode and immediate mask */ +#define BL_OP 0b10010100000000000000000000000000 +#define BL_IM 0b00000011111111111111111111111111 +/* Library search path */ +#define SEARCH_PATH "/system/lib64" +#define ELF_EHDR Elf64_Ehdr +#define ELF_SHDR Elf64_Shdr +#define ELF_HALF Elf64_Half +#define ELF_XWORD Elf64_Xword +#define ELF_DYN Elf64_Dyn + +//#define ADRENO_POSSIBLE + +typedef void* (*loader_dlopen_t)(const char* filename, int flags, const void* caller_addr); + +typedef struct android_namespace_t* (*ld_android_create_namespace_t)( + const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, + const char* permitted_when_isolated_path, struct android_namespace_t* parent, const void* caller_addr); + +typedef struct android_namespace_t* (*ld_android_get_exported_namespace_t)(const char* name, const void* caller_addr); + +static ld_android_create_namespace_t android_create_namespace; +static ld_android_get_exported_namespace_t android_get_exported_namespace; +static struct android_namespace_t* namespace; + +struct android_namespace_t* local_android_create_namespace( + const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, + const char* permitted_when_isolated_path, struct android_namespace_t* parent) { + void* caller = __builtin_return_address(0); + return android_create_namespace(name, ld_library_path, default_library_path, type, permitted_when_isolated_path, parent, caller); +} + +struct android_namespace_t* ns_android_get_exported_namespace( + const char* name) { + void* caller = __builtin_return_address(0); + return android_get_exported_namespace(name, caller); +} + + +bool linker_ns_load(const char* lib_search_path) { +#ifndef ADRENO_POSSIBLE + return false; +#endif + uint32_t *dlext_bl_addr = (uint32_t*)&dlopen; + while((*dlext_bl_addr & OP_MS) != + BL_OP) dlext_bl_addr++; //walk through the function until we find the label that we need to go to + __android_log_print(ANDROID_LOG_INFO, "nsbypass", "found branch label: %u", *dlext_bl_addr); + loader_dlopen_t loader_dlopen; + loader_dlopen = (loader_dlopen_t)(((char *) dlext_bl_addr) + (*dlext_bl_addr & BL_IM)*4); + mprotect(loader_dlopen, PAGE_SIZE, PROT_WRITE | PROT_READ | PROT_EXEC); // reprotecting the function removes protection from indirect jumps + void* ld_android_handle = loader_dlopen("ld-android.so", RTLD_LAZY, &dlopen); + __android_log_print(ANDROID_LOG_INFO, "nsbypass", "ld-android.so handle: %p", ld_android_handle); + if(ld_android_handle == NULL) return false; + android_create_namespace = dlsym(ld_android_handle, "__loader_android_create_namespace"); + android_get_exported_namespace = dlsym(ld_android_handle, "__loader_android_get_exported_namespace"); + if(android_create_namespace == NULL || android_get_exported_namespace == NULL) { + dlclose(ld_android_handle); + return false; + } + char full_path[strlen(SEARCH_PATH) + strlen(lib_search_path) + 2 + 1]; + sprintf(full_path, "%s:%s", SEARCH_PATH, lib_search_path); + namespace = local_android_create_namespace("pojav-driver", + full_path, + full_path, + 3 /* TYPE_SHAFED | TYPE_ISOLATED */, + "/system/:/data/:/vendor/:/apex/", NULL); + //dlclose(ld_android_handle); + return true; +} + +void* linker_ns_dlopen(const char* name, int flag) { +#ifndef ADRENO_POSSIBLE + return NULL; +#endif + android_dlextinfo dlextinfo; + dlextinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + dlextinfo.library_namespace = namespace; + return android_dlopen_ext(name, flag, &dlextinfo); +} + +bool patch_elf_soname(int patchfd, int realfd, uint16_t patchid) { + struct stat realstat; + if(fstat(realfd, &realstat)) return false; + if(ftruncate(patchfd, realstat.st_size) == -1) return false; + char* target = mmap(NULL, realstat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, patchfd, 0); + if(!target) return false; + if(read(realfd, target, realstat.st_size) != realstat.st_size) { + munmap(target, realstat.st_size); + return false; + } + close(realfd); + + + ELF_EHDR *ehdr = (ELF_EHDR*)target; + ELF_SHDR *shdr = (ELF_SHDR*)(target + ehdr->e_shoff); + for(ELF_HALF i = 0; i < ehdr->e_shnum; i++) { + ELF_SHDR *hdr = &shdr[i]; + if(hdr->sh_type == SHT_DYNAMIC) { + char* strtab = target + shdr[hdr->sh_link].sh_offset; + ELF_DYN *dynEntries = (ELF_DYN*)(target + hdr->sh_offset); + for(ELF_XWORD k = 0; k < (hdr->sh_size / hdr->sh_entsize);k++) { + ELF_DYN* dynEntry = &dynEntries[k]; + if(dynEntry->d_tag == DT_SONAME) { + char* soname = strtab + dynEntry->d_un.d_val; + char sprb[4]; + snprintf(sprb, 4, "%03x", patchid); + memcpy(soname, sprb, 3); + munmap(target, realstat.st_size); + return true; + } + } + } + } + return false; +} + +void* linker_ns_dlopen_unique(const char* tmpdir, const char* name, int flags) { +#ifndef ADRENO_POSSIBLE + return NULL; +#endif + char pathbuf[PATH_MAX]; + static uint16_t patch_id; + int patch_fd, real_fd; + snprintf(pathbuf,PATH_MAX,"%s/%d_p.so", tmpdir, patch_id); + patch_fd = open(pathbuf, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if(patch_fd == -1) return NULL; + snprintf(pathbuf,PATH_MAX,"%s/%s", SEARCH_PATH, name); + real_fd = open(pathbuf, O_RDONLY); + if(real_fd == -1) { + close(patch_fd); + return NULL; + } + if(!patch_elf_soname(patch_fd, real_fd, patch_id)) { + close(patch_fd); + close(real_fd); + return NULL; + } + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE | ANDROID_DLEXT_USE_LIBRARY_FD; + extinfo.library_fd = patch_fd; + extinfo.library_namespace = namespace; + snprintf(pathbuf, PATH_MAX, "/proc/self/fd/%d", patch_fd); + return android_dlopen_ext(pathbuf, flags, &extinfo); +} diff --git a/app_pojavlauncher/src/main/jni/driver_helper/nsbypass.h b/app_pojavlauncher/src/main/jni/driver_helper/nsbypass.h new file mode 100644 index 000000000..7b6ac9b06 --- /dev/null +++ b/app_pojavlauncher/src/main/jni/driver_helper/nsbypass.h @@ -0,0 +1,16 @@ + +// +// Created by maks on 05.06.2023. +// + +#ifndef POJAVLAUNCHER_NSBYPASS_H +#define POJAVLAUNCHER_NSBYPASS_H + +#include + +bool linker_ns_load(const char* lib_search_path); +void* linker_ns_dlopen(const char* name, int flag); +void* linker_ns_dlopen_unique(const char* tmpdir, const char* name, int flag); +struct android_namespace_t* ns_android_get_exported_namespace(const char*); + +#endif //POJAVLAUNCHER_NSBYPASS_H diff --git a/app_pojavlauncher/src/main/jni/egl_bridge.c b/app_pojavlauncher/src/main/jni/egl_bridge.c index 22eeed98b..91f1e5b62 100644 --- a/app_pojavlauncher/src/main/jni/egl_bridge.c +++ b/app_pojavlauncher/src/main/jni/egl_bridge.c @@ -11,6 +11,8 @@ #include #include +#include "ctxbridges/osmesa_loader.h" +#include "driver_helper/nsbypass.h" #ifdef GLES_TEST #include @@ -21,6 +23,7 @@ #include #include #include +#include #include "utils.h" #include "ctxbridges/gl_bridge.h" @@ -46,8 +49,6 @@ struct PotatoBridge potatoBridge; void* gbuffer; -void* egl_make_current(void* window); - void pojav_openGLOnLoad() { } void pojav_openGLOnUnload() { @@ -146,6 +147,79 @@ bool loadSymbols() { } } +//#define ADRENO_POSSIBLE +#ifdef ADRENO_POSSIBLE +//Checks if your graphics are Adreno. Returns true if your graphics are Adreno, false otherwise or if there was an error +bool checkAdrenoGraphics() { + EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if(eglDisplay == EGL_NO_DISPLAY || eglInitialize(eglDisplay, NULL, NULL) != EGL_TRUE) return false; + 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_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; + EGLint num_configs = 0; + if(eglChooseConfig(eglDisplay, egl_attributes, NULL, 0, &num_configs) != EGL_TRUE || num_configs == 0) { + eglTerminate(eglDisplay); + return false; + } + EGLConfig eglConfig; + eglChooseConfig(eglDisplay, egl_attributes, &eglConfig, 1, &num_configs); + const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; + EGLContext context = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, egl_context_attributes); + if(context == EGL_NO_CONTEXT) { + eglTerminate(eglDisplay); + return false; + } + if(eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context) != EGL_TRUE) { + eglDestroyContext(eglDisplay, context); + eglTerminate(eglDisplay); + } + const char* vendor = glGetString(GL_VENDOR); + const char* renderer = glGetString(GL_RENDERER); + bool is_adreno = false; + if(strcmp(vendor, "Qualcomm") == 0 && strstr(renderer, "Adreno") != NULL) { + is_adreno = true; // TODO: check for Turnip support + } + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(eglDisplay, context); + eglTerminate(eglDisplay); + return is_adreno; +} +void* load_turnip_vulkan() { + if(!checkAdrenoGraphics()) return NULL; + const char* native_dir = getenv("POJAV_NATIVEDIR"); + const char* cache_dir = getenv("TMPDIR"); + linker_ns_load(native_dir); + void* linkerhook = linker_ns_dlopen("liblinkerhook.so", RTLD_LOCAL | RTLD_NOW); + void* turnip_driver_handle = linker_ns_dlopen("libvulkan.adr.so", RTLD_LOCAL | RTLD_NOW); + void (*linkerhook_set_data)(void*, void*, void*) = dlsym(linkerhook, "app__pojav_linkerhook_set_data"); + linkerhook_set_data(turnip_driver_handle, &android_dlopen_ext, &ns_android_get_exported_namespace); + void* libvulkan = linker_ns_dlopen_unique(cache_dir, "libvulkan.so", RTLD_LOCAL | RTLD_NOW); + return libvulkan; +} +#endif + +static void set_vulkan_ptr(void* ptr) { + char envval[64]; + sprintf(envval, "%"PRIxPTR, (uintptr_t)ptr); + setenv("VULKAN_PTR", envval, 1); + return; +} + +void load_vulkan() { + if(getenv("POJAV_ZINK_PREFER_SYSTEM_DRIVER") == NULL) { +#ifdef ADRENO_POSSIBLE + void* result = load_turnip_vulkan(); + if(result != NULL) { + printf("AdrenoSupp: Loaded Turnip, loader address: %p\n", result); + set_vulkan_ptr(result); + return; + } +#endif + } + printf("OSMDroid: loading vulkan regularly...\n"); + void* vulkan_ptr = dlopen("libvulkan.so", RTLD_LAZY | RTLD_LOCAL); + printf("OSMDroid: loaded vulkan, ptr=%p", vulkan_ptr); + set_vulkan_ptr(vulkan_ptr); +} + int pojavInit() { ANativeWindow_acquire(pojav_environ->pojavWindow); pojav_environ->savedWidth = ANativeWindow_getWidth(pojav_environ->pojavWindow); @@ -164,6 +238,7 @@ int pojavInit() { //loadSymbols(); } else if (strcmp(renderer, "vulkan_zink") == 0) { pojav_environ->config_renderer = RENDERER_VK_ZINK; + load_vulkan(); setenv("GALLIUM_DRIVER","zink",1); loadSymbols(); } @@ -221,20 +296,6 @@ void pojavSwapBuffers() { } } -void* egl_make_current(void* window) { - EGLBoolean success = eglMakeCurrent_p( - potatoBridge.eglDisplay, - window==0 ? (EGLSurface *) 0 : potatoBridge.eglSurface, - window==0 ? (EGLSurface *) 0 : potatoBridge.eglSurface, - /* window==0 ? EGL_NO_CONTEXT : */ (EGLContext *) window - ); - - if (success == EGL_FALSE) { - printf("EGLBridge: Error: eglMakeCurrent() failed: %p\n", eglGetError_p()); - } else { - printf("EGLBridge: eglMakeCurrent() succeed!\n"); - } -} bool locked = false; void pojavMakeCurrent(void* window) { diff --git a/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libOSMesa.so b/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libOSMesa.so new file mode 100755 index 000000000..f2370e569 Binary files /dev/null and b/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libOSMesa.so differ diff --git a/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libOSMesa_8.so b/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libOSMesa_8.so deleted file mode 100644 index 04da835ad..000000000 Binary files a/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libOSMesa_8.so and /dev/null differ diff --git a/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libvulkan.adr.so b/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libvulkan.adr.so new file mode 100644 index 000000000..02fd8533c Binary files /dev/null and b/app_pojavlauncher/src/main/jniLibs/arm64-v8a/libvulkan.adr.so differ diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index 593162506..3a5f307a1 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -403,5 +403,6 @@ Smoothing time Reduce jitter in exchange of latency. 0 to disable it - + Use system Vulkan driver + Force the launcher to use the system Vulkan driver instead of Turnip on Adreno GPUs. Only affects Zink. diff --git a/app_pojavlauncher/src/main/res/xml/pref_misc.xml b/app_pojavlauncher/src/main/res/xml/pref_misc.xml index 6ecce2806..b93694ab6 100644 --- a/app_pojavlauncher/src/main/res/xml/pref_misc.xml +++ b/app_pojavlauncher/src/main/res/xml/pref_misc.xml @@ -16,6 +16,11 @@ android:key="arc_capes" android:summary="@string/arc_capes_desc" android:title="@string/arc_capes_title" /> +