mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
The Minecraft-shaped driver benchmark could only be run from a desktop shell against a desktop driver, which is the wrong machine: MobileGL exists to run on mobile GPUs, and nothing said what its translation costs there. This puts the same cases on an Android device, both in the plugin's POST screen and from a shell, and adds the native-driver baseline they have to be read against. The cases move into DriverBenchCases.inc so both harnesses run byte-identical bodies - the desktop program resolving entry points from one EGL provider, and DriverBenchJni.cpp calling MobileGL's frontend in-process. The JNI file binds every gl*/egl* name to MG_Impl by macro rather than by linkage: this library legitimately has the platform libEGL and libGLESv3 in its own lookup scope, and a benchmark that quietly measured the device driver instead of the translation layer would have looked like very good news. Frames are now closed with a fence wait instead of glFinish. MobileGL implements glFinish and glFlush as no-ops, so the old loop timed submit-plus-GPU on a native driver and submit-only on a MobileGL backend, and the two numbers did not describe the same work. To measure a device's own driver the cases needed to be expressible in GLES: ESSL 3.20 twins of the four shaders (chosen at runtime from GL_VERSION, since MobileGL is deliberately still fed desktop GLSL - translating it is the thing under test), a multi-draw hook that loops DrawElementsBaseVertex where the multi-draw entry point does not exist, and an EGL bootstrap that falls back from desktop GL to GLES 3. The binary cross-compiles for arm64 unchanged. BenchService hosts each run in its own process and exits afterwards. That is not caution: the backend is latched from MOBILEGL_BACKEND_TYPE at initialization, so Espryt and Magma can never share a process, and Espryt's teardown terminates the process-default EGL display, which would take the POST activity's own EGL objects with it. Running it found that Magma could not create a windowless context on Mali at all - CreateInstance required VK_EXT_headless_surface, which no mobile driver here exposes, and aborted the process. The Xlib path already probes and falls back to a hidden window for the same reason on NVIDIA; Android now probes too and hands the WSI an AImageReader's ANativeWindow, a real producer surface attached to no display whose images are never acquired. DriverPost reports the extension's absence as a WARN so the fallback is visible rather than silent. Measured on a Mali-G77 MC9 (native / Espryt / Magma, ns per operation): 5495 chunk draws 14397 / 36934 / 33763, the 26.2 per-draw uniform-range pattern 13710 / 31205 / 21252, sodium-style multi-draw 256956 / 238389 / 209527. The translation costs about 2.4x per draw here against 5-9x on the desktop, because the mobile driver's own per-call cost dwarfs it - and both backends beat the native driver on multi-draw, which it has to emulate. Desktop unit tests 421/421; the POST screen and both Run Bench buttons verified on the device.
225 lines
8.2 KiB
Kotlin
225 lines
8.2 KiB
Kotlin
import com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
|
import com.launchers_plugin.renderer.buildscript.RendererConfig
|
|
import com.launchers_plugin.renderer.buildscript.buildEnvs
|
|
import com.launchers_plugin.renderer.buildscript.buildJsonValue
|
|
import com.launchers_plugin.renderer.buildscript.legacyManifest
|
|
import com.launchers_plugin.renderer.buildscript.nativePath
|
|
import com.launchers_plugin.renderer.buildscript.renderer
|
|
|
|
buildscript {
|
|
repositories {
|
|
maven("https://jitpack.io")
|
|
}
|
|
dependencies {
|
|
classpath("com.github.ZalithLauncher.RendererPlugin-v2:dsl:1.0-alpha6")
|
|
}
|
|
}
|
|
|
|
plugins {
|
|
id("com.android.application")
|
|
}
|
|
|
|
apply(plugin = "com.launchers_plugin.renderer.dsl")
|
|
|
|
fun Project.mobileGlAbiFilters(): List<String> {
|
|
val abiList = (findProperty("mobilegl.abis") ?: System.getenv("MOBILEGL_ABIS") ?: "arm64-v8a").toString()
|
|
return if (abiList.equals("all", ignoreCase = true)) {
|
|
listOf("arm64-v8a", "x86_64")
|
|
} else {
|
|
abiList.split(',').map(String::trim).filter(String::isNotEmpty)
|
|
}
|
|
}
|
|
|
|
fun Project.mobileGlCmakeCompilerLauncher(): String =
|
|
(findProperty("mobilegl.cmakeCompilerLauncher") ?: System.getenv("MOBILEGL_CMAKE_COMPILER_LAUNCHER") ?: "").toString().trim()
|
|
|
|
// Optional application-id suffix, so a development build can sit next to an
|
|
// already-installed plugin instead of having to replace it - a differently
|
|
// signed APK cannot upgrade one in place, and uninstalling costs the user their
|
|
// plugin settings and the launcher's binding to it.
|
|
fun Project.mobileGlApplicationIdSuffix(): String =
|
|
(findProperty("mobilegl.applicationIdSuffix") ?: "").toString().trim()
|
|
|
|
fun Project.runGit(vararg arguments: String): String? = runCatching {
|
|
ProcessBuilder("git", *arguments)
|
|
.directory(rootDir)
|
|
.redirectErrorStream(true)
|
|
.start()
|
|
.let { process ->
|
|
val output = process.inputStream.bufferedReader().use { it.readText().trim() }
|
|
output.takeIf { process.waitFor() == 0 && it.isNotEmpty() }
|
|
}
|
|
}.getOrNull()
|
|
|
|
val signingStoreFile = file("../keystore.jks")
|
|
val signingStorePassword = System.getenv("SIGNING_STORE_PASSWORD")
|
|
val signingKeyAlias = System.getenv("SIGNING_KEY_ALIAS")
|
|
val signingKeyPassword = System.getenv("SIGNING_KEY_PASSWORD")
|
|
val releaseSigningReady = signingStoreFile.exists()
|
|
&& !signingStorePassword.isNullOrEmpty()
|
|
&& !signingKeyAlias.isNullOrEmpty()
|
|
&& !signingKeyPassword.isNullOrEmpty()
|
|
val debuggableRelease = (findProperty("mobilegl.debuggableRelease") ?: "false").toString().toBoolean()
|
|
|
|
val mobileGlVersionMajor = 26
|
|
val mobileGlVersionMinor = 8
|
|
val mobileGlGitShortHash = runGit("rev-parse", "--short=7", "HEAD") ?: "nogit"
|
|
val mobileGlMonthlyRevision = runGit(
|
|
"rev-list",
|
|
"--count",
|
|
"--since=${String.format("%d-%02d-01T00:00:00", 2000 + mobileGlVersionMajor, mobileGlVersionMinor)}",
|
|
"HEAD",
|
|
)?.toIntOrNull() ?: 0
|
|
val mobileGlApkSuffix = (findProperty("mobilegl.apkSuffix") ?: System.getenv("MOBILEGL_APK_SUFFIX") ?: mobileGlGitShortHash)
|
|
.toString()
|
|
.ifBlank { "nogit" }
|
|
|
|
val pluginRendererConfig = buildJsonValue {
|
|
renderer(
|
|
displayName = "MobileGL",
|
|
rendererId = "opengles3",
|
|
rendererGLPath = nativePath("libMobileGL.so"),
|
|
rendererEGLPath = nativePath("libMobileGL.so"),
|
|
dlopenLibPaths = emptyList(),
|
|
env = buildEnvs {
|
|
normal("LIBGL_ES", "3")
|
|
selectable(
|
|
key = "MOBILEGL_BACKEND_TYPE",
|
|
title = RendererConfig.MetaString("mobilegl_backend_type_title"),
|
|
items = RendererConfig.EnvItems("DirectGLES", listOf("DirectVulkan")),
|
|
)
|
|
toggleable("MOBILEGL_DISABLE_TIMERQUERY", "1", false, RendererConfig.MetaString("mobilegl_disable_timerquery_title"))
|
|
toggleable("MOBILEGL_DISABLE_SUBGROUP", "1", false, RendererConfig.MetaString("mobilegl_disable_subgroup_title"))
|
|
toggleable("MOBILEGL_MAGMA_R11G11B10F_FALLBACK", "1", false, RendererConfig.MetaString("mobilegl_magma_r11g11b10f_fallback_title"))
|
|
customizable("MOBILEGL_MAGMA_FRAMESINFLIGHT", "3", RendererConfig.MetaString("mobilegl_magma_frames_inflight_title"))
|
|
toggleable("MOBILEGL_AVOID_SAMPLER_MIPMAP_MIN_FILTER", "1", false, RendererConfig.MetaString("mobilegl_avoid_sampler_mipmap_min_filter_title"))
|
|
toggleable("MOBILEGL_COHERENT_AS_FLUSH", "1", false, RendererConfig.MetaString("mobilegl_coherent_as_flush_title"))
|
|
toggleable("MOBILEGL_RELAXED_SEMANTICS", "1", false, RendererConfig.MetaString("mobilegl_relaxed_semantics_title"))
|
|
toggleable("MOBILEGL_USE_ANGLE", "1", false, RendererConfig.MetaString("mobilegl_use_angle_title"))
|
|
},
|
|
minMCVer = null,
|
|
maxMCVer = null,
|
|
)
|
|
}
|
|
|
|
android {
|
|
namespace = "top.mobilegl.plugin"
|
|
compileSdk = 34
|
|
ndkVersion = "27.3.13750724"
|
|
|
|
defaultConfig {
|
|
applicationId = "top.mobilegl.plugin"
|
|
mobileGlApplicationIdSuffix().takeIf { it.isNotEmpty() }?.let { applicationIdSuffix = it }
|
|
minSdk = 26
|
|
targetSdk = 34
|
|
versionCode = mobileGlVersionMajor * 1_000_000 + mobileGlVersionMinor * 10_000 + mobileGlMonthlyRevision
|
|
versionName = "%d.%02d.%s".format(mobileGlVersionMajor, mobileGlVersionMinor, mobileGlGitShortHash)
|
|
resValue("string", "config", pluginRendererConfig)
|
|
|
|
manifestPlaceholders.putAll(legacyManifest {
|
|
displayName = "MobileGL"
|
|
rendererName = "MobileGL"
|
|
rendererLib = "libMobileGL.so"
|
|
eglLib = "/libMobileGL.so"
|
|
minMCVer = ""
|
|
maxMCVer = ""
|
|
boatEnv {
|
|
put("LIBGL_ES", "3")
|
|
put("POJAV_RENDERER", "opengles3")
|
|
put("MOBILEGL_BACKEND_TYPE", "DirectGLES")
|
|
}
|
|
pojavEnv {
|
|
put("LIBGL_ES", "3")
|
|
put("POJAV_RENDERER", "opengles3")
|
|
put("MOBILEGL_BACKEND_TYPE", "DirectGLES")
|
|
}
|
|
})
|
|
manifestPlaceholders["appLabel"] = "MobileGL"
|
|
|
|
ndk {
|
|
abiFilters += mobileGlAbiFilters()
|
|
}
|
|
externalNativeBuild {
|
|
cmake {
|
|
mobileGlCmakeCompilerLauncher().takeIf(String::isNotEmpty)?.let { compilerLauncher ->
|
|
arguments += listOf(
|
|
"-DCMAKE_C_COMPILER_LAUNCHER=$compilerLauncher",
|
|
"-DCMAKE_CXX_COMPILER_LAUNCHER=$compilerLauncher",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
buildFeatures {
|
|
resValues = true
|
|
}
|
|
|
|
flavorDimensions += "profile"
|
|
productFlavors {
|
|
create("plugin") {
|
|
dimension = "profile"
|
|
}
|
|
create("trace") {
|
|
dimension = "profile"
|
|
applicationIdSuffix = ".trace"
|
|
versionNameSuffix = "-trace"
|
|
}
|
|
}
|
|
|
|
if (releaseSigningReady) {
|
|
signingConfigs {
|
|
create("release") {
|
|
storeFile = signingStoreFile
|
|
storePassword = signingStorePassword
|
|
keyAlias = signingKeyAlias
|
|
keyPassword = signingKeyPassword
|
|
}
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
getByName("release") {
|
|
isDebuggable = debuggableRelease
|
|
isMinifyEnabled = false
|
|
if (releaseSigningReady) {
|
|
signingConfig = signingConfigs.getByName("release")
|
|
}
|
|
}
|
|
}
|
|
|
|
externalNativeBuild {
|
|
cmake {
|
|
path = file("src/trace/cpp/CMakeLists.txt")
|
|
version = "3.22.1"
|
|
}
|
|
}
|
|
|
|
packaging {
|
|
jniLibs {
|
|
useLegacyPackaging = true
|
|
excludes += "**/libSPIRV-Tools-shared.so"
|
|
}
|
|
}
|
|
}
|
|
|
|
android.applicationVariants.configureEach {
|
|
outputs.configureEach {
|
|
(this as ApkVariantOutputImpl).outputFileName = when (flavorName) {
|
|
"plugin" -> "MobileGL-plugin-release-$mobileGlApkSuffix.apk"
|
|
"trace" -> "MobileGL-plugin-trace-release-$mobileGlApkSuffix.apk"
|
|
else -> outputFileName
|
|
}
|
|
}
|
|
}
|
|
|
|
androidComponents {
|
|
onVariants(selector().withFlavor("profile" to "plugin")) { variant ->
|
|
variant.packaging.jniLibs.excludes.add("**/libtrace_replay_runner.so")
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation(project(":MobileGL"))
|
|
}
|