add support for virgl

This commit is contained in:
ShirosakiMio 2024-08-20 12:11:37 +08:00
parent 5d3acf9804
commit 568f42015e
6 changed files with 271 additions and 20 deletions

View File

@ -66,6 +66,7 @@ LOCAL_SRC_FILES := \
pojav/ctxbridges/swap_interval_no_egl.c \
pojav/environ/environ.c \
pojav/input_bridge_v3.c \
pojav/virgl/virgl.c \
driver_helper/nsbypass.c
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)

View File

@ -5,6 +5,8 @@
#include <stdlib.h>
#include <dlfcn.h>
#include "osmesa_loader.h"
#include "pojav/environ/environ.h"
#include "pojav/virgl/virgl.h"
GLboolean (*OSMesaMakeCurrent_p) (OSMesaContext ctx, void *buffer, GLenum type,
GLsizei width, GLsizei height);
@ -20,14 +22,15 @@ 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 ||
asprintf(&alt_path, "%s/libOSMesa.so.8", getenv("POJAV_NATIVEDIR")) == -1) {
char *fmt = "%s/libOSMesa_8.so";
if (pojav_environ->config_renderer == RENDERER_VIRGL) {
fmt = "%s/libOSMesa_81.so";
}
if (asprintf(&main_path, fmt, getenv("POJAV_NATIVEDIR")) == -1) {
abort();
}
void* dl_handle = NULL;
dl_handle = dlopen(alt_path, RTLD_GLOBAL);
if(dl_handle == NULL) dl_handle = dlopen(main_path, RTLD_GLOBAL);
dl_handle = dlopen(main_path, RTLD_GLOBAL);
if(dl_handle == NULL) abort();
OSMesaMakeCurrent_p = dlsym(dl_handle, "OSMesaMakeCurrent");
OSMesaGetCurrentContext_p = dlsym(dl_handle,"OSMesaGetCurrentContext");

View File

@ -1,3 +1,4 @@
#include "pojav/egl_bridge.h"
#include <jni.h>
#include <assert.h>
#include <dlfcn.h>
@ -37,22 +38,12 @@
// This means that you are forced to have this function/variable for ABI compatibility
#define ABI_COMPAT __attribute__((unused))
struct PotatoBridge {
/* EGLContext */ void* eglContext;
/* EGLDisplay */ void* eglDisplay;
/* EGLSurface */ void* eglSurface;
/*
void* eglSurfaceRead;
void* eglSurfaceDraw;
*/
};
EGLConfig config;
struct PotatoBridge potatoBridge;
#include "ctxbridges/egl_loader.h"
#include "ctxbridges/osmesa_loader.h"
#include "pojav/virgl/virgl.h"
#define RENDERER_GL4ES 1
#define RENDERER_VK_ZINK 2
@ -94,6 +85,9 @@ Java_net_kdt_pojavlaunch_utils_JREUtils_releaseBridgeWindow(ABI_COMPAT JNIEnv *e
}
EXTERNAL_API void* pojavGetCurrentContext() {
if (pojav_environ->config_renderer == RENDERER_VIRGL) {
return virglGetCurrentContext();
}
return br_get_current();
}
@ -197,7 +191,12 @@ int pojavInitOpenGL() {
// NOTE: Override for now.
const char *renderer = getenv("POJAV_RENDERER");
if (strncmp("opengles", renderer, 8) == 0) {
if (strcmp(renderer, "opengles3_virgl") == 0) {
pojav_environ->config_renderer = RENDERER_VIRGL;
loadSymbolsVirGL();
virglInit();
return 0;
} else if (strncmp("opengles", renderer, 8) == 0) {
pojav_environ->config_renderer = RENDERER_GL4ES;
set_gl_bridge_tbl();
} else if (strcmp(renderer, "vulkan_zink") == 0) {
@ -240,17 +239,28 @@ EXTERNAL_API void pojavSetWindowHint(int hint, int value) {
}
EXTERNAL_API void pojavSwapBuffers() {
br_swap_buffers();
if (pojav_environ->config_renderer == RENDERER_VIRGL) {
virglSwapBuffers();
} else {
br_swap_buffers();
}
}
EXTERNAL_API void pojavMakeCurrent(void* window) {
br_make_current((basic_render_window_t*)window);
if (pojav_environ->config_renderer == RENDERER_VIRGL)
{
virglMakeCurrent(window);
} else {
br_make_current((basic_render_window_t*)window);
}
}
EXTERNAL_API void* pojavCreateContext(void* contextSrc) {
if (pojav_environ->config_renderer == RENDERER_VULKAN) {
return (void *) pojav_environ->pojavWindow;
} else if (pojav_environ->config_renderer == RENDERER_VIRGL) {
return virglCreateContext(contextSrc);
}
return br_init_context((basic_render_window_t*)contextSrc);
}
@ -266,6 +276,10 @@ Java_org_lwjgl_vulkan_VK_getVulkanDriverHandle(ABI_COMPAT JNIEnv *env, ABI_COMPA
}
EXTERNAL_API void pojavSwapInterval(int interval) {
br_swap_interval(interval);
if (pojav_environ->config_renderer == RENDERER_VIRGL) {
virglSwapInterval(interval);
} else {
br_swap_interval(interval);
}
}

View File

@ -0,0 +1,23 @@
//
// Created by mio on 2024/8/20.
//
#ifndef FOLD_CRAFT_LAUNCHER_EGL_BRIDGE_H
#define FOLD_CRAFT_LAUNCHER_EGL_BRIDGE_H
#include <EGL/egl.h>
struct PotatoBridge {
/* EGLContext */ void* eglContext;
/* EGLDisplay */ void* eglDisplay;
/* EGLSurface */ void* eglSurface;
/*
void* eglSurfaceRead;
void* eglSurfaceDraw;
*/
};
extern EGLConfig config;
extern struct PotatoBridge potatoBridge;
#endif //FOLD_CRAFT_LAUNCHER_EGL_BRIDGE_H

View File

@ -0,0 +1,192 @@
//
// Created by mio on 2024/8/20.
//
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <dlfcn.h>
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include "virgl.h"
#include "pojav/environ/environ.h"
#include "pojav/ctxbridges/osm_bridge.h"
#include "pojav/egl_bridge.h"
#include "pojav/ctxbridges/egl_loader.h"
#define RENDERER_VIRGL 3
int (*vtest_main_p)(int argc, char **argv);
void (*vtest_swap_buffers_p)(void);
void *virglGetCurrentContext() {
return (void *) OSMesaGetCurrentContext_p();
}
void virglSwapBuffers() {
glFinish_p();
vtest_swap_buffers_p();
}
void virglMakeCurrent(void *window) {
printf("OSMDroid: making current\n");
OSMesaMakeCurrent_p((OSMesaContext) window,
setbuffer,
GL_UNSIGNED_BYTE,
pojav_environ->savedWidth,
pojav_environ->savedHeight);
printf("OSMDroid: vendor: %s\n", glGetString_p(GL_VENDOR));
printf("OSMDroid: renderer: %s\n", glGetString_p(GL_RENDERER));
glClear_p(GL_COLOR_BUFFER_BIT);
glClearColor_p(0.4f, 0.4f, 0.4f, 1.0f);
// Trigger a texture creation, which then set VIRGL_TEXTURE_ID
int pixelsArr[4];
glReadPixels_p(0, 0, 1, 1, GL_RGB, GL_INT, &pixelsArr);
virglSwapBuffers();
}
void *virglCreateContext(void *contextSrc) {
printf("OSMDroid: generating context\n");
void *ctx = OSMesaCreateContext_p(OSMESA_RGBA, contextSrc);
printf("OSMDroid: context=%p\n", ctx);
return ctx;
}
void loadSymbolsVirGL() {
dlsym_OSMesa();
dlsym_EGL();
char *fileName = calloc(1, 1024);
sprintf(fileName, "%s/libvirgl_test_server.so", getenv("POJAV_NATIVEDIR"));
void *handle = dlopen(fileName, RTLD_LAZY);
printf("VirGL: libvirgl_test_server = %p\n", handle);
if (!handle) {
printf("VirGL: %s\n", dlerror());
}
vtest_main_p = dlsym(handle, "vtest_main");
vtest_swap_buffers_p = dlsym(handle, "vtest_swap_buffers");
free(fileName);
}
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");
}
if (pojav_environ->config_renderer == RENDERER_VIRGL) {
printf("VirGL: vtest_main = %p\n", vtest_main_p);
printf("VirGL: Calling VTest server's main function\n");
vtest_main_p(3, (const char *[]) {"vtest", "--no-loop-or-fork", "--use-gles", NULL, NULL});
}
}
void virglSwapInterval(int interval) {
eglSwapInterval_p(potatoBridge.eglDisplay, interval);
}
int virglInit() {
if (potatoBridge.eglDisplay == NULL || potatoBridge.eglDisplay == EGL_NO_DISPLAY) {
potatoBridge.eglDisplay = eglGetDisplay_p(EGL_DEFAULT_DISPLAY);
if (potatoBridge.eglDisplay == EGL_NO_DISPLAY) {
printf("EGLBridge: Error eglGetDefaultDisplay() failed: %p\n", eglGetError_p());
return 0;
}
}
printf("EGLBridge: Initializing\n");
// printf("EGLBridge: ANativeWindow pointer = %p\n", pojav_environ->pojavWindow);
//(*env)->ThrowNew(env,(*env)->FindClass(env,"java/lang/Exception"),"Trace exception");
if (!eglInitialize_p(potatoBridge.eglDisplay, NULL, NULL)) {
printf("EGLBridge: Error eglInitialize() failed: %s\n", eglGetError_p());
return 0;
}
static const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
// Minecraft required on initial 24
EGL_DEPTH_SIZE, 24,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
EGLint num_configs;
EGLint vid;
if (!eglChooseConfig_p(potatoBridge.eglDisplay, attribs, &config, 1, &num_configs)) {
printf("EGLBridge: Error couldn't get an EGL visual config: %s\n", eglGetError_p());
return 0;
}
assert(config);
assert(num_configs > 0);
if (!eglGetConfigAttrib_p(potatoBridge.eglDisplay, config, EGL_NATIVE_VISUAL_ID, &vid)) {
printf("EGLBridge: Error eglGetConfigAttrib() failed: %s\n", eglGetError_p());
return 0;
}
ANativeWindow_setBuffersGeometry(pojav_environ->pojavWindow, 0, 0, vid);
eglBindAPI_p(EGL_OPENGL_ES_API);
potatoBridge.eglSurface = eglCreateWindowSurface_p(potatoBridge.eglDisplay, config,
pojav_environ->pojavWindow, NULL);
if (!potatoBridge.eglSurface) {
printf("EGLBridge: Error eglCreateWindowSurface failed: %p\n", eglGetError_p());
//(*env)->ThrowNew(env,(*env)->FindClass(env,"java/lang/Exception"),"Trace exception");
return 0;
}
// sanity checks
{
EGLint val;
assert(eglGetConfigAttrib_p(potatoBridge.eglDisplay, config, EGL_SURFACE_TYPE, &val));
assert(val & EGL_WINDOW_BIT);
}
printf("EGLBridge: Initialized!\n");
printf("EGLBridge: ThreadID=%d\n", gettid());
printf("EGLBridge: EGLDisplay=%p, EGLSurface=%p\n",
/* window==0 ? EGL_NO_CONTEXT : */
potatoBridge.eglDisplay,
potatoBridge.eglSurface
);
// Init EGL context and vtest server
const EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
EGLContext *ctx = eglCreateContext_p(potatoBridge.eglDisplay, config, NULL, ctx_attribs);
printf("VirGL: created EGL context %p\n", ctx);
pthread_t t;
pthread_create(&t, NULL, egl_make_current, (void *) ctx);
usleep(100 * 1000); // need enough time for the server to init
if (OSMesaCreateContext_p == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "测试", "null");
printf("OSMDroid: %s\n", dlerror());
}
return 0;
}

View File

@ -0,0 +1,18 @@
//
// Created by mio on 2024/8/20.
//
#ifndef FOLD_CRAFT_LAUNCHER_VIRGL_H
#define FOLD_CRAFT_LAUNCHER_VIRGL_H
#define RENDERER_VIRGL 3
void* virglGetCurrentContext();
void loadSymbolsVirGL();
int virglInit();
void virglSwapBuffers();
void virglMakeCurrent(void* window);
void* virglCreateContext(void* contextSrc);
void virglSwapInterval(int interval);
#endif //FOLD_CRAFT_LAUNCHER_VIRGL_H