Add integrated Turnip driver + loader

Also adds an override so you can turn it off when you have your own, good-working system Vulkan driver
This commit is contained in:
BuildTools
2023-08-01 00:17:41 +03:00
parent 05015111a2
commit 5f64d811e3
13 changed files with 330 additions and 20 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
//
// Created by maks on 05.06.2023.
//
#include <android/dlext.h>
#include <android/log.h>
#include <string.h>
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);
}

View File

@@ -0,0 +1,165 @@
//
// Created by maks on 05.06.2023.
//
#include "nsbypass.h"
#include <dlfcn.h>
#include <android/dlext.h>
#include <android/log.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <string.h>
#include <stdio.h>
#include <linux/limits.h>
#include <errno.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <elf.h>
/* 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);
}

View File

@@ -0,0 +1,16 @@
//
// Created by maks on 05.06.2023.
//
#ifndef POJAVLAUNCHER_NSBYPASS_H
#define POJAVLAUNCHER_NSBYPASS_H
#include <stdbool.h>
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

View File

@@ -11,6 +11,8 @@
#include <EGL/egl.h>
#include <GL/osmesa.h>
#include "ctxbridges/osmesa_loader.h"
#include "driver_helper/nsbypass.h"
#ifdef GLES_TEST
#include <GLES2/gl2.h>
@@ -21,6 +23,7 @@
#include <android/rect.h>
#include <string.h>
#include <environ/environ.h>
#include <android/dlext.h>
#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) {

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -403,5 +403,6 @@
<string name="preference_gyro_damper_window_title">Smoothing time</string>
<string name="preference_gyro_damper_window_description">Reduce jitter in exchange of latency. 0 to disable it</string>
<string name="preference_vulkan_driver_system_title">Use system Vulkan driver</string>
<string name="preference_vulkan_driver_system_description">Force the launcher to use the system Vulkan driver instead of Turnip on Adreno GPUs. Only affects Zink.</string>
</resources>

View File

@@ -16,6 +16,11 @@
android:key="arc_capes"
android:summary="@string/arc_capes_desc"
android:title="@string/arc_capes_title" />
<SwitchPreference
android:defaultValue="false"
android:key="zinkPreferSystemDriver"
android:summary="@string/preference_vulkan_driver_system_description"
android:title="@string/preference_vulkan_driver_system_title"/>
</PreferenceCategory>