Skip to content

Commit 07aacbe

Browse files
committed
[Feat|Chore] (MobileGlues|MGGLInfoGetter): Implement MGGLInfoGetter & MGGLInfoDialogBuilder. Update MobileGlues. Bump version to 1.3.3
1 parent d9e6cdc commit 07aacbe

15 files changed

Lines changed: 479 additions & 23 deletions

app/build.gradle.kts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import com.android.build.api.dsl.Packaging
2+
13
plugins {
24
alias(libs.plugins.android.application)
35
alias(libs.plugins.kotlin.android)
@@ -13,8 +15,8 @@ android {
1315
applicationId = "com.fcl.plugin.mobileglues"
1416
minSdk = 26
1517
targetSdk = 36
16-
versionCode = 1320
17-
versionName = "1.3.2"
18+
versionCode = 1330
19+
versionName = "1.3.3"
1820

1921
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2022
}
@@ -49,7 +51,7 @@ android {
4951

5052
manifestPlaceholders["boatEnv"] = mutableMapOf<String,String>().apply {
5153
put("LIBGL_ES", "3")
52-
put("DLOPEN", "libspirv-cross-c-shared.so,libshaderconv.so")
54+
put("DLOPEN", "libspirv-cross-c-shared.so")
5355
}.run {
5456
var env = ""
5557
forEach { (key, value) ->
@@ -59,7 +61,7 @@ android {
5961
}
6062
manifestPlaceholders["pojavEnv"] = mutableMapOf<String,String>().apply {
6163
put("LIBGL_ES", "3")
62-
put("DLOPEN", "libspirv-cross-c-shared.so,libshaderconv.so")
64+
put("DLOPEN", "libspirv-cross-c-shared.so")
6365
put("POJAV_RENDERER", "opengles3")
6466
put("POJAVEXEC_EGL", "libmobileglues.so")
6567
put("LIBGL_EGL", "libmobileglues.so")
@@ -73,6 +75,12 @@ android {
7375
}
7476
}
7577

78+
externalNativeBuild {
79+
cmake {
80+
path = file("src/main/cpp/CMakeLists.txt")
81+
}
82+
}
83+
7684
compileOptions {
7785
sourceCompatibility = JavaVersion.VERSION_11
7886
targetCompatibility = JavaVersion.VERSION_11

app/src/main/cpp/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project("mobileglues_info_getter")
3+
4+
add_library(mobileglues_info_getter SHARED mobileglues_info_getter.cpp)
5+
6+
find_library(log-lib log)
7+
find_library(android-lib android)
8+
9+
set(PLATFORM_LIBS ${log-lib} dl ${android-lib})
10+
11+
target_include_directories(mobileglues_info_getter PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
12+
target_link_libraries(mobileglues_info_getter ${PLATFORM_LIBS})

app/src/main/cpp/MG/extensions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#pragma once
2+
3+
#define GL_BACKEND_GETTER_MG 0x0401
4+
#define GL_SETTINGS_MG 0x0402
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#include "mobileglues_info_getter.h"
2+
3+
static void* mg_handle = nullptr;
4+
5+
static void* try_load_symbol(void* handle, const char* name) {
6+
void* s = dlsym(handle, name);
7+
if (!s) {
8+
LOGE("Error: dlsym %s failed: %s", name, dlerror());
9+
}
10+
return s;
11+
}
12+
13+
static bool load_mobile_symbols() {
14+
if (mg_handle) return true;
15+
mg_handle = dlopen("libmobileglues.so", RTLD_NOW | RTLD_LOCAL);
16+
if (!mg_handle) {
17+
LOGE("Error: dlopen libmobileglues.so failed: %s", dlerror());
18+
return false;
19+
}
20+
21+
p_eglGetDisplay = (PFN_eglGetDisplay) try_load_symbol(mg_handle, "eglGetDisplay");
22+
p_eglInitialize = (PFN_eglInitialize) try_load_symbol(mg_handle, "eglInitialize");
23+
p_eglChooseConfig = (PFN_eglChooseConfig) try_load_symbol(mg_handle, "eglChooseConfig");
24+
p_eglCreatePbufferSurface = (PFN_eglCreatePbufferSurface) try_load_symbol(mg_handle, "eglCreatePbufferSurface");
25+
p_eglCreateContext = (PFN_eglCreateContext) try_load_symbol(mg_handle, "eglCreateContext");
26+
p_eglMakeCurrent = (PFN_eglMakeCurrent) try_load_symbol(mg_handle, "eglMakeCurrent");
27+
p_eglDestroySurface = (PFN_eglDestroySurface) try_load_symbol(mg_handle, "eglDestroySurface");
28+
p_eglDestroyContext = (PFN_eglDestroyContext) try_load_symbol(mg_handle, "eglDestroyContext");
29+
p_eglTerminate = (PFN_eglTerminate) try_load_symbol(mg_handle, "eglTerminate");
30+
31+
p_glGetIntegerv = (PFN_glGetIntegerv) try_load_symbol(mg_handle, "glGetIntegerv");
32+
p_glGetString = (PFN_glGetString) try_load_symbol(mg_handle, "glGetString");
33+
p_glGetStringi = (PFN_glGetStringi) try_load_symbol(mg_handle, "glGetStringi");
34+
35+
if (!p_glGetStringi) {
36+
typedef void* (*PFN_eglGetProcAddress)(const char*);
37+
auto p_eglGetProcAddress = (PFN_eglGetProcAddress) try_load_symbol(mg_handle, "eglGetProcAddress");
38+
if (p_eglGetProcAddress) {
39+
p_glGetStringi = (PFN_glGetStringi) p_eglGetProcAddress("glGetStringi");
40+
}
41+
}
42+
43+
if (!p_eglGetDisplay || !p_eglInitialize || !p_eglChooseConfig || !p_eglCreatePbufferSurface || !p_eglCreateContext || !p_eglMakeCurrent || !p_glGetString || !p_glGetIntegerv) {
44+
LOGE("Error: some required symbols are missing");
45+
return false;
46+
}
47+
48+
return true;
49+
}
50+
51+
static std::string create_context_and_query() {
52+
if (!load_mobile_symbols()) return "Error: failed to load libmobileglues symbols";
53+
54+
EGLDisplay display = p_eglGetDisplay(EGL_DEFAULT_DISPLAY);
55+
if (display == EGL_NO_DISPLAY) return "Error: eglGetDisplay returned EGL_NO_DISPLAY";
56+
57+
EGLint major = 0, minor = 0;
58+
if (!p_eglInitialize(display, &major, &minor)) {
59+
return "Error: eglInitialize failed";
60+
}
61+
62+
EGLint configAttribs[] = {
63+
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
64+
EGL_RED_SIZE, 8,
65+
EGL_GREEN_SIZE, 8,
66+
EGL_BLUE_SIZE, 8,
67+
EGL_ALPHA_SIZE, 8,
68+
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
69+
EGL_NONE
70+
};
71+
EGLConfig config;
72+
EGLint numConfigs = 0;
73+
if (!p_eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) || numConfigs <= 0) {
74+
p_eglTerminate(display);
75+
return "Error: eglChooseConfig failed";
76+
}
77+
78+
EGLint pbufAttribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
79+
EGLSurface surface = p_eglCreatePbufferSurface(display, config, pbufAttribs);
80+
if (surface == EGL_NO_SURFACE) {
81+
p_eglTerminate(display);
82+
return "Error: eglCreatePbufferSurface failed";
83+
}
84+
85+
EGLContext context = EGL_NO_CONTEXT;
86+
EGLint ctxAttribs3[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
87+
EGLint ctxAttribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
88+
89+
context = p_eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttribs3);
90+
if (context == EGL_NO_CONTEXT) {
91+
LOGI("Error: ES3 context failed, try ES2");
92+
context = p_eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttribs2);
93+
}
94+
95+
if (context == EGL_NO_CONTEXT) {
96+
p_eglDestroySurface(display, surface);
97+
p_eglTerminate(display);
98+
return "Error: eglCreateContext failed";
99+
}
100+
101+
if (!p_eglMakeCurrent(display, surface, surface, context)) {
102+
p_eglDestroyContext(display, context);
103+
p_eglDestroySurface(display, surface);
104+
p_eglTerminate(display);
105+
return "Error: eglMakeCurrent failed";
106+
}
107+
108+
std::ostringstream out;
109+
110+
std::ostringstream out_exts;
111+
112+
GLint numExt = 0;
113+
p_glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
114+
if (numExt > 0 && p_glGetStringi) {
115+
out_exts << "Extensions (" << numExt << "):\n";
116+
for (GLint i = 0; i < numExt; ++i) {
117+
const GLubyte* ext = p_glGetStringi(GL_EXTENSIONS, (GLuint)i);
118+
if (ext) {
119+
const char* extStr = reinterpret_cast<const char*>(ext);
120+
if (strcmp(extStr, "GL_MG_mobileglues") == 0) g_MGQueryCapability.HasMobileGluesExt = true;
121+
if (strcmp(extStr, "GL_MG_backend_string_getter_access") == 0) g_MGQueryCapability.BackendStringGetterAccess = true;
122+
if (strcmp(extStr, "GL_MG_settings_string_dump") == 0) g_MGQueryCapability.SettingsStringDump = true;
123+
out_exts << " " << reinterpret_cast<const char*>(ext) << "\n";
124+
}
125+
}
126+
} else {
127+
const GLubyte* exts = p_glGetString(GL_EXTENSIONS);
128+
out_exts << "Extensions: " << (exts ? reinterpret_cast<const char*>(exts) : "NULL") << "\n";
129+
if (exts) {
130+
const char* extStr = reinterpret_cast<const char*>(exts);
131+
if (strstr(extStr, " GL_MG_mobileglues ")) g_MGQueryCapability.HasMobileGluesExt = true;
132+
if (strstr(extStr, " GL_MG_backend_string_getter_access ")) g_MGQueryCapability.BackendStringGetterAccess = true;
133+
if (strstr(extStr, " GL_MG_settings_string_dump ")) g_MGQueryCapability.SettingsStringDump = true;
134+
}
135+
}
136+
137+
out << "Is MobileGlues (>=1.3.3): " << (g_MGQueryCapability.HasMobileGluesExt ? "Yes\n" : "No\n");
138+
139+
const GLubyte* renderer = p_glGetString(GL_RENDERER);
140+
const GLubyte* version = p_glGetString(GL_VERSION);
141+
const GLubyte* vendor = p_glGetString(GL_VENDOR);
142+
const GLubyte* shading = p_glGetString(GL_SHADING_LANGUAGE_VERSION);
143+
144+
out << "\nOpenGL Frontend\n====\n";
145+
out << "Renderer: " << (renderer ? reinterpret_cast<const char*>(renderer) : "NULL") << "\n";
146+
out << "Version: " << (version ? reinterpret_cast<const char*>(version) : "NULL") << "\n";
147+
out << "Vendor: " << (vendor ? reinterpret_cast<const char*>(vendor) : "NULL") << "\n";
148+
out << "Shading language version: " << (shading ? reinterpret_cast<const char*>(shading) : "NULL") << "\n";
149+
out << out_exts.str();
150+
151+
out << "\nOpenGL ES Backend\n====\n";
152+
if (g_MGQueryCapability.BackendStringGetterAccess) {
153+
const GLubyte* backendRenderer = p_glGetString(GL_RENDERER + GL_BACKEND_GETTER_MG);
154+
const GLubyte* backendVersion = p_glGetString(GL_VERSION + GL_BACKEND_GETTER_MG);
155+
const GLubyte* backendVendor = p_glGetString(GL_VENDOR + GL_BACKEND_GETTER_MG);
156+
const GLubyte* backendShading = p_glGetString(GL_SHADING_LANGUAGE_VERSION + GL_BACKEND_GETTER_MG);
157+
out << "Renderer: " << (backendRenderer ? reinterpret_cast<const char*>(backendRenderer) : "NULL") << "\n";
158+
out << "Version: " << (backendVersion ? reinterpret_cast<const char*>(backendVersion) : "NULL") << "\n";
159+
out << "Vendor: " << (backendVendor ? reinterpret_cast<const char*>(backendVendor) : "NULL") << "\n";
160+
out << "Shading language version: " << (backendShading ? reinterpret_cast<const char*>(backendShading) : "NULL") << "\n";
161+
162+
GLint backendNumExt = 0;
163+
p_glGetIntegerv(GL_NUM_EXTENSIONS + GL_BACKEND_GETTER_MG, &backendNumExt);
164+
if (backendNumExt > 0 && p_glGetStringi) {
165+
out << "Extensions (" << backendNumExt << "):\n";
166+
for (GLint i = 0; i < backendNumExt; ++i) {
167+
const GLubyte* ext = p_glGetStringi(GL_EXTENSIONS + GL_BACKEND_GETTER_MG, (GLuint)i);
168+
out << " " << reinterpret_cast<const char*>(ext) << "\n";
169+
}
170+
} else {
171+
const GLubyte* exts = p_glGetString(GL_EXTENSIONS + GL_BACKEND_GETTER_MG);
172+
out << "Extensions: " << (exts ? reinterpret_cast<const char*>(exts) : "NULL") << "\n";
173+
}
174+
} else {
175+
out << "Not Compatible. GL_MG_backend_string_getter_access is not supported in current context.\n";
176+
}
177+
178+
out << "\nSettings\n====\n";
179+
if (g_MGQueryCapability.SettingsStringDump) {
180+
const GLubyte* settingsStr = p_glGetString(GL_SETTINGS_MG);
181+
out << reinterpret_cast<const char*>(settingsStr) << "\n";
182+
} else {
183+
out << "Not Compatible. GL_MG_settings_string_dump is not supported in current context.\n";
184+
}
185+
186+
p_eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
187+
p_eglDestroySurface(display, surface);
188+
p_eglDestroyContext(display, context);
189+
p_eglTerminate(display);
190+
191+
dlclose(mg_handle);
192+
mg_handle = nullptr;
193+
194+
return out.str();
195+
}
196+
197+
extern "C"
198+
JNIEXPORT jstring JNICALL
199+
Java_com_fcl_plugin_mobileglues_MGGLInfoDialogBuilder_getMobileGluesGLInfo(JNIEnv *env,
200+
jobject thiz) {
201+
std::string res = create_context_and_query();
202+
LOGI("MobileGlues GL Info: \n%s", res.c_str());
203+
return env->NewStringUTF(res.c_str());
204+
}
205+
206+
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
207+
(void)vm;
208+
(void)reserved;
209+
LOGI("JNI_OnLoad called");
210+
return JNI_VERSION_1_6;
211+
}
212+
213+
extern "C"
214+
JNIEXPORT jint JNICALL
215+
Java_com_fcl_plugin_mobileglues_MGGLInfoDialogBuilder_setenv(JNIEnv *env, jobject thiz, jstring key,
216+
jstring value, jint overwrite) {
217+
const char* k = env->GetStringUTFChars(key, nullptr);
218+
const char* v = env->GetStringUTFChars(value, nullptr);
219+
int ret = setenv(k, v, overwrite);
220+
env->ReleaseStringUTFChars(key, k);
221+
env->ReleaseStringUTFChars(value, v);
222+
return ret;
223+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#pragma once
2+
#include <jni.h>
3+
#include <string>
4+
#include <vector>
5+
#include <sstream>
6+
#include <dlfcn.h>
7+
#include <android/log.h>
8+
#include <EGL/egl.h>
9+
#include <GLES3/gl3.h>
10+
#include "MG/extensions.h"
11+
12+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MobileGluesInfoGetter", __VA_ARGS__)
13+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MobileGluesInfoGetter", __VA_ARGS__)
14+
15+
typedef EGLDisplay (*PFN_eglGetDisplay)(EGLNativeDisplayType);
16+
typedef EGLBoolean (*PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*);
17+
typedef EGLBoolean (*PFN_eglChooseConfig)(EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
18+
typedef EGLSurface (*PFN_eglCreatePbufferSurface)(EGLDisplay, EGLConfig, const EGLint*);
19+
typedef EGLContext (*PFN_eglCreateContext)(EGLDisplay, EGLConfig, EGLContext, const EGLint*);
20+
typedef EGLBoolean (*PFN_eglMakeCurrent)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
21+
typedef EGLBoolean (*PFN_eglDestroySurface)(EGLDisplay, EGLSurface);
22+
typedef EGLBoolean (*PFN_eglDestroyContext)(EGLDisplay, EGLContext);
23+
typedef EGLBoolean (*PFN_eglTerminate)(EGLDisplay);
24+
typedef void (*PFN_glGetIntegerv)(GLenum, GLint*);
25+
typedef const GLubyte* (*PFN_glGetString)(GLenum);
26+
typedef const GLubyte* (*PFN_glGetStringi)(GLenum, GLuint);
27+
typedef void (*PFN_glGetError)();
28+
29+
static PFN_eglGetDisplay p_eglGetDisplay = nullptr;
30+
static PFN_eglInitialize p_eglInitialize = nullptr;
31+
static PFN_eglChooseConfig p_eglChooseConfig = nullptr;
32+
static PFN_eglCreatePbufferSurface p_eglCreatePbufferSurface = nullptr;
33+
static PFN_eglCreateContext p_eglCreateContext = nullptr;
34+
static PFN_eglMakeCurrent p_eglMakeCurrent = nullptr;
35+
static PFN_eglDestroySurface p_eglDestroySurface = nullptr;
36+
static PFN_eglDestroyContext p_eglDestroyContext = nullptr;
37+
static PFN_eglTerminate p_eglTerminate = nullptr;
38+
static PFN_glGetIntegerv p_glGetIntegerv = nullptr;
39+
static PFN_glGetString p_glGetString = nullptr;
40+
static PFN_glGetStringi p_glGetStringi = nullptr;
41+
42+
struct MGQueryCapability {
43+
bool HasMobileGluesExt = false; // GL_MG_mobileglues
44+
bool BackendStringGetterAccess = false; // GL_MG_backend_string_getter_access
45+
bool SettingsStringDump = false; // GL_MG_settings_string_dump
46+
}g_MGQueryCapability;

app/src/main/java/com/fcl/plugin/mobileglues/AppInfoDialogBuilder.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ import android.content.Intent
66
import android.view.LayoutInflater
77
import android.widget.TextView
88
import androidx.core.net.toUri
9+
import com.fcl.plugin.mobileglues.settings.MGConfig
10+
import com.google.android.material.button.MaterialButton
911
import com.google.android.material.dialog.MaterialAlertDialogBuilder
1012

1113
@SuppressLint("InflateParams")
12-
class AppInfoDialogBuilder(context: Context) : MaterialAlertDialogBuilder(context) {
14+
class AppInfoDialogBuilder(private val context: Context, private val config: MGConfig?) : MaterialAlertDialogBuilder(context) {
1315

14-
init {
16+
fun showDialog() {
1517
val view = LayoutInflater.from(context).inflate(R.layout.dialog_app_info, null, false)
1618
view.findViewById<TextView>(R.id.info_version).text = BuildConfig.VERSION_NAME
1719

1820
setTitle(R.string.dialog_info)
1921
setView(view)
22+
2023
setNeutralButton(R.string.dialog_positive, null)
2124
setNegativeButton(R.string.dialog_sponsor) { _, _ ->
2225
context.startActivity(
@@ -34,5 +37,19 @@ class AppInfoDialogBuilder(context: Context) : MaterialAlertDialogBuilder(contex
3437
)
3538
)
3639
}
40+
41+
val dialog = this.create()
42+
43+
dialog.setOnShowListener {
44+
val glInfoButton = dialog.findViewById<MaterialButton>(R.id.button_gl_info)
45+
glInfoButton?.setOnClickListener {
46+
val mgGLInfoDialog = MGGLInfoDialogBuilder(context, config)
47+
mgGLInfoDialog.show()
48+
mgGLInfoDialog.fillMobileGluesInfo()
49+
dialog.dismiss()
50+
}
51+
}
52+
53+
dialog.show()
3754
}
3855
}

0 commit comments

Comments
 (0)