diff --git a/.github/scripts/validate-plugin-apks.sh b/.github/scripts/validate-plugin-apks.sh new file mode 100644 index 00000000..0f62e691 --- /dev/null +++ b/.github/scripts/validate-plugin-apks.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " >&2 + exit 64 +fi + +aapt2=$1 +plugin_apk=$2 +trace_apk=$3 + +require() { + local needle=$1 + local content=$2 + local description=$3 + if ! grep -Fq -- "$needle" <<<"$content"; then + echo "::error::Missing ${description}: ${needle}" >&2 + exit 1 + fi +} + +for apk in "$plugin_apk" "$trace_apk"; do + [[ -f "$apk" ]] || { echo "::error::APK not found: $apk" >&2; exit 1; } +done + +plugin_manifest=$("$aapt2" dump xmltree --file AndroidManifest.xml "$plugin_apk") +plugin_resources=$("$aapt2" dump resources "$plugin_apk") +plugin_resource_text=$(tr -d '"' <<<"$plugin_resources") +trace_manifest=$("$aapt2" dump xmltree --file AndroidManifest.xml "$trace_apk") +plugin_contents=$(unzip -Z1 "$plugin_apk") + +require 'top.mobilegl.plugin' "$plugin_manifest" 'plugin package name' +require 'MobileGL' "$plugin_manifest" 'plugin label' +require 'fclPlugin' "$plugin_manifest" 'legacy plugin marker' +require 'fclPlugin_V2' "$plugin_manifest" 'V2 plugin marker' +require 'LIBGL_ES=3:POJAV_RENDERER=opengles3:MOBILEGL_BACKEND_TYPE=DirectGLES' "$plugin_manifest" 'V1 DirectGLES fallback' +require 'string/config' "$plugin_resources" 'V2 renderer configuration resource' +require '{displayName:MobileGL,rendererId:opengles3' "$plugin_resource_text" 'V2 MobileGL entry and renderer ID' +require 'rendererGLPath:**|libMobileGL.so' "$plugin_resource_text" 'V2 GL library path' +require 'rendererEGLPath:**|libMobileGL.so' "$plugin_resource_text" 'V2 EGL library path' +require 'key:LIBGL_ES,value:3' "$plugin_resource_text" 'V2 fixed LIBGL_ES variable' +require 'key:MOBILEGL_BACKEND_TYPE' "$plugin_resource_text" 'V2 backend variable' +require 'defaultValue:DirectGLES' "$plugin_resource_text" 'V2 DirectGLES default' +require 'DirectVulkan' "$plugin_resource_text" 'V2 DirectVulkan option' +require 'key:MOBILEGL_DISABLE_TIMERQUERY' "$plugin_resource_text" 'V2 timer-query toggle' +require 'key:MOBILEGL_DISABLE_SUBGROUP' "$plugin_resource_text" 'V2 Vulkan subgroup toggle' +require 'key:MOBILEGL_MAGMA_R11G11B10F_FALLBACK' "$plugin_resource_text" 'V2 Magma format fallback toggle' +require 'key:MOBILEGL_MAGMA_FRAMESINFLIGHT' "$plugin_resource_text" 'V2 Magma frames-in-flight setting' +require 'key:MOBILEGL_AVOID_SAMPLER_MIPMAP_MIN_FILTER' "$plugin_resource_text" 'V2 sampler workaround toggle' +require 'key:MOBILEGL_USE_ANGLE' "$plugin_resource_text" 'V2 ANGLE toggle' + +if [[ $(grep -Fc 'fclPlugin_V2' <<<"$plugin_manifest") -ne 1 ]]; then + echo '::error::Plugin manifest must expose exactly one V2 descriptor' >&2 + exit 1 +fi + +if ! grep -Eq '^lib/[^/]+/libMobileGL\.so$' <<<"$plugin_contents"; then + echo '::error::Plugin APK does not contain libMobileGL.so' >&2 + exit 1 +fi + +require 'top.mobilegl.plugin.trace' "$trace_manifest" 'trace package name' +require 'top.mobilegl.plugin.TRACE_REPLAY' "$trace_manifest" 'trace replay action' +if grep -Fq 'fclPlugin' <<<"$trace_manifest"; then + echo '::error::Trace APK must not advertise renderer-plugin metadata' >&2 + exit 1 +fi +if grep -Fq 'android.intent.action.MAIN' <<<"$trace_manifest"; then + echo '::error::Trace APK must not expose a launcher activity' >&2 + exit 1 +fi + +echo 'Validated unified MobileGL plugin APK and isolated trace APK.' diff --git a/.github/workflows/apk.yml b/.github/workflows/apk.yml index 05e1d7e1..5ce4c82d 100644 --- a/.github/workflows/apk.yml +++ b/.github/workflows/apk.yml @@ -73,8 +73,8 @@ jobs: working-directory: 3rdparty/glslang run: python update_glslang_sources.py - - name: Build plugin APKs - run: gradle --no-daemon -p android-plugin :app:assembleEsprytPluginRelease :app:assembleMagmaPluginRelease -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)" + - name: Build plugin APK + run: gradle --no-daemon -p android-plugin :app:assemblePluginRelease -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)" env: SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} @@ -115,8 +115,8 @@ jobs: d0f4298ccc770cc801fc52e21733521646161e8a4adb3bd0052d9a1b57ee0ca8 \ 66fdc867e552192d553d59095ea2e3cef4829de65c356f1fd826027b1905972e - - name: Build retrace APKs - run: gradle --no-daemon -p android-plugin :app:assembleEsprytTraceRelease :app:assembleMagmaTraceRelease -Pmobilegl.abis=all -Pmobilegl.debuggableRelease=true -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)" + - name: Build retrace APK + run: gradle --no-daemon -p android-plugin :app:assembleTraceRelease -Pmobilegl.abis=all -Pmobilegl.debuggableRelease=true -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)" env: SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} @@ -126,6 +126,15 @@ jobs: if: always() run: ccache --show-stats + - name: Verify APK metadata and packaging + run: | + AAPT2="$(find "$ANDROID_HOME/build-tools" -name aapt2 -type f | sort -V | tail -n 1)" + plugin_apk="$(find android-plugin/app/build/outputs/apk/plugin/release -name '*.apk' ! -name '*-debug-signed.apk' -print -quit)" + trace_apk="$(find android-plugin/app/build/outputs/apk/trace/release -name '*.apk' ! -name '*-debug-signed.apk' -print -quit)" + test -n "${plugin_apk}" + test -n "${trace_apk}" + bash .github/scripts/validate-plugin-apks.sh "$AAPT2" "$plugin_apk" "$trace_apk" + - name: Verify signed APKs run: | APKSIGNER="$(find "$ANDROID_HOME/build-tools" -name apksigner -type f | sort -V | tail -n 1)" @@ -148,14 +157,14 @@ jobs: uses: actions/upload-artifact@v4 with: name: MobileGL-plugin-${{ env.date_today }}-${{ env.short_sha }} - path: android-plugin/app/build/outputs/apk/*Plugin/release/*.apk + path: android-plugin/app/build/outputs/apk/plugin/release/*.apk if-no-files-found: error - name: Upload retrace APK uses: actions/upload-artifact@v4 with: name: MobileGL-retrace-apk-${{ env.date_today }}-${{ env.short_sha }} - path: android-plugin/app/build/outputs/apk/*Trace/release/*.apk + path: android-plugin/app/build/outputs/apk/trace/release/*.apk if-no-files-found: error trace-cases: @@ -267,14 +276,8 @@ jobs: matrix: backend: - name: DirectGLES - id: espryt - apk: MobileGL-EsprytTrace-release.apk - package: top.mobilegl.plugin.espryt.trace gpu: software - name: DirectVulkan - id: magma - apk: MobileGL-MagmaTrace-release.apk - package: top.mobilegl.plugin.magma.trace gpu: lavapipe case: ${{ fromJSON(needs.trace-cases.outputs.android) }} steps: @@ -362,7 +365,7 @@ jobs: MOBILEGL_TRACE_ANGLE_VARIANT: ${{ matrix.case.name == 'minecraft-1.21.4-fabric-iris-bliss-in-world' && '90a62123d794' || 'ec889e6ea831' }} MOBILEGL_MAGMA_R11G11B10F_FALLBACK: ${{ matrix.backend.name == 'DirectVulkan' && '1' || '0' }} run: | - apk_file="$(find android-retrace-apks -name '${{ matrix.backend.apk }}' -print -quit)" + apk_file="$(find android-retrace-apks -name 'MobileGL-Trace-release.apk' -print -quit)" extra_retrace_args=() # Bliss needs the newer signed ANGLE variant plus sampler mipmap # min-filter downgrading on ANGLE llvmpipe. @@ -371,7 +374,7 @@ jobs: fi timeout "$(( ${{ matrix.case.timeout_seconds }} + 300 ))" sh android-plugin/trace-replay-ci.sh \ --apk-file "${apk_file}" \ - --package "${{ matrix.backend.package }}" \ + --package top.mobilegl.plugin.trace \ --backend "${{ matrix.backend.name }}" \ --result-root android-retrace-result \ --fixture-root android-retrace-fixture \ diff --git a/README.md b/README.md index 74f146b0..e9ec47d0 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,12 @@ MobileGL supports runtime configuration via environment variables. | Variable | Description | Allowed Values | Default | |-------------------------|--------------------------------------------------|--------------------------------------|----------------| | `MOBILEGL_BACKEND_TYPE` | Select active backend implementation at startup. | `DirectGLES`, `DirectVulkan` | `DirectGLES` | +| `MOBILEGL_DISABLE_TIMERQUERY` | Disable GPU timer-query exposure and use. | `0`, `1` | `0` | +| `MOBILEGL_USE_ANGLE` | Load ANGLE EGL/GLES libraries. | `0`, `1` | `0` | +| `MOBILEGL_DISABLE_SUBGROUP` | Disable Vulkan shader subgroup support. | `0`, `1` | `0` | +| `MOBILEGL_MAGMA_R11G11B10F_FALLBACK` | Use Magma's R11G11B10F format fallback. | `0`, `1` | `0` | +| `MOBILEGL_MAGMA_FRAMESINFLIGHT` | Set Magma frames in flight. | Integer `1`–`64` | `3` | +| `MOBILEGL_AVOID_SAMPLER_MIPMAP_MIN_FILTER` | Avoid sampler mipmap minification filters. | `0`, `1` | `0` | | `VK_ICD_FILENAMES` | Select the Vulkan ICD used by the Vulkan loader. | Path to an ICD JSON file | Loader default | ## Notice diff --git a/android-plugin/app/build.gradle b/android-plugin/app/build.gradle deleted file mode 100644 index a7ce5f14..00000000 --- a/android-plugin/app/build.gradle +++ /dev/null @@ -1,186 +0,0 @@ -plugins { - id 'com.android.application' -} - -def mobileGlEnvString = { backendType -> - [ - LIBGL_ES : '3', - POJAV_RENDERER : 'opengles3', - MOBILEGL_BACKEND_TYPE: backendType, - ].collect { key, value -> "${key}=${value}" }.join(':') -} -def signingStoreFile = file('../keystore.jks') -def signingStorePassword = System.getenv('SIGNING_STORE_PASSWORD') -def signingKeyAlias = System.getenv('SIGNING_KEY_ALIAS') -def signingKeyPassword = System.getenv('SIGNING_KEY_PASSWORD') -def releaseSigningReady = signingStoreFile.exists() - && signingStorePassword - && signingKeyAlias - && signingKeyPassword -def debuggableRelease = (findProperty('mobilegl.debuggableRelease') ?: 'false').toString().toBoolean() -def mobileGlCmakeCompilerLauncher = { - (findProperty('mobilegl.cmakeCompilerLauncher') ?: System.getenv('MOBILEGL_CMAKE_COMPILER_LAUNCHER') ?: '').toString().trim() -} -def mobileGlAbiFilters = { - def abiList = (findProperty('mobilegl.abis') ?: System.getenv('MOBILEGL_ABIS') ?: 'arm64-v8a') as String - if (abiList.equalsIgnoreCase('all')) { - return ['arm64-v8a', 'x86_64'] - } - abiList - .split(',') - .collect { it.trim() } - .findAll { !it.isEmpty() } -} - -// Calendar versioning: major = two-digit year, minor = month. The build -// identifier is the short commit hash, giving version names like 26.07.4e558ee. -def mobileGlVersionMajor = 26 -def mobileGlVersionMinor = 7 -def mobileGlGitShortHash = { - try { - def proc = ['git', 'rev-parse', '--short=7', 'HEAD'].execute(null, rootDir) - proc.waitFor() - def hash = proc.in.text.trim() - (proc.exitValue() == 0 && hash) ? hash : 'nogit' - } catch (Exception ignored) { - 'nogit' - } -}() -// Monthly revision: commits reachable from HEAD since the start of the -// version's month. Multiple releases in one month (calendar versioning ships -// several per month) each get a strictly larger versionCode; a new month -// bumps the minor and dwarfs any reset via the *10000 weight. -def mobileGlMonthlyRevision = { - try { - def since = String.format('%d-%02d-01T00:00:00', 2000 + mobileGlVersionMajor, mobileGlVersionMinor) - def proc = ['git', 'rev-list', '--count', "--since=${since}", 'HEAD'].execute(null, rootDir) - proc.waitFor() - def n = proc.in.text.trim() - (proc.exitValue() == 0 && n) ? n.toInteger() : 0 - } catch (Exception ignored) { - 0 - } -}() - -android { - namespace 'top.mobilegl.plugin' - compileSdk 34 - ndkVersion '27.3.13750724' - - defaultConfig { - minSdk 26 - targetSdk 34 - // year*1_000_000 + month*10_000 + monthly revision keeps versionCode - // strictly increasing across releases (monotonic within a month and - // across month/year rollovers), so every build is an installable - // upgrade. Max ~99_129999, well under Android's 2_100_000_000 ceiling. - versionCode mobileGlVersionMajor * 1000000 + mobileGlVersionMinor * 10000 + mobileGlMonthlyRevision - versionName String.format('%d.%02d.%s', mobileGlVersionMajor, mobileGlVersionMinor, mobileGlGitShortHash) - - ndk { - def abiFiltersOverride = mobileGlAbiFilters() - if (!abiFiltersOverride.isEmpty()) { - abiFilters(*abiFiltersOverride) - } - } - externalNativeBuild { - cmake { - def compilerLauncher = mobileGlCmakeCompilerLauncher() - if (!compilerLauncher.isEmpty()) { - arguments "-DCMAKE_C_COMPILER_LAUNCHER=${compilerLauncher}", - "-DCMAKE_CXX_COMPILER_LAUNCHER=${compilerLauncher}" - } - } - } - } - - flavorDimensions 'backend', 'profile' - - productFlavors { - espryt { - dimension 'backend' - applicationId 'top.mobilegl.plugin.espryt' - def env = mobileGlEnvString('DirectGLES') - resValue 'string', 'mobilegl_default_backend', 'DirectGLES' - manifestPlaceholders = [ - appLabel: 'MobileGL Espryt', - des : 'MobileGL Espryt', - renderer: 'MobileGL Espryt:libMobileGL.so:/libMobileGL.so', - boatEnv : env, - pojavEnv: env, - ] - } - magma { - dimension 'backend' - applicationId 'top.mobilegl.plugin.magma' - def env = mobileGlEnvString('DirectVulkan') - resValue 'string', 'mobilegl_default_backend', 'DirectVulkan' - manifestPlaceholders = [ - appLabel: 'MobileGL Magma', - des : 'MobileGL Magma', - renderer: 'MobileGL Magma:libMobileGL.so:/libMobileGL.so', - boatEnv : env, - pojavEnv: env, - ] - } - plugin { - dimension 'profile' - } - trace { - dimension 'profile' - applicationIdSuffix '.trace' - versionNameSuffix '-trace' - } - } - - if (releaseSigningReady) { - signingConfigs { - release { - storeFile signingStoreFile - storePassword signingStorePassword - keyAlias signingKeyAlias - keyPassword signingKeyPassword - } - } - } - - buildTypes { - release { - debuggable debuggableRelease - minifyEnabled false - if (releaseSigningReady) { - signingConfig signingConfigs.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 { variant -> - variant.outputs.configureEach { - outputFileName = "MobileGL-${variant.flavorName.capitalize()}-${variant.buildType.name}.apk" - } -} - -androidComponents { - onVariants(selector().withFlavor('profile', 'plugin')) { variant -> - variant.packaging.jniLibs.excludes.add('**/libtrace_replay_runner.so') - } -} - -dependencies { - implementation project(':MobileGL') -} diff --git a/android-plugin/app/build.gradle.kts b/android-plugin/app/build.gradle.kts new file mode 100644 index 00000000..e4f5aada --- /dev/null +++ b/android-plugin/app/build.gradle.kts @@ -0,0 +1,200 @@ +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 { + 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() + +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 = 7 +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 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_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" + 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" + } + } +} + +androidComponents { + onVariants(selector().withFlavor("profile" to "plugin")) { variant -> + variant.packaging.jniLibs.excludes.add("**/libtrace_replay_runner.so") + } +} + +dependencies { + implementation(project(":MobileGL")) +} diff --git a/android-plugin/app/src/main/AndroidManifest.xml b/android-plugin/app/src/main/AndroidManifest.xml index 1734dd54..faf347a9 100644 --- a/android-plugin/app/src/main/AndroidManifest.xml +++ b/android-plugin/app/src/main/AndroidManifest.xml @@ -22,6 +22,31 @@ android:exported="true" android:theme="@style/Theme.MobileGLPlugin.NoDisplay" /> + + + + + + + + + diff --git a/android-plugin/app/src/main/res/values/strings.xml b/android-plugin/app/src/main/res/values/strings.xml index d740167f..25ac0140 100644 --- a/android-plugin/app/src/main/res/values/strings.xml +++ b/android-plugin/app/src/main/res/values/strings.xml @@ -1,4 +1,12 @@ MobileGL + Rendering backend + Disable GPU timer queries + Disable Vulkan shader subgroups + Use Magma R11G11B10F fallback + Magma frames in flight (1-64) + Avoid sampler mipmap minification filters + Use ANGLE GLES libraries + DirectGLES diff --git a/android-plugin/app/src/trace/AndroidManifest.xml b/android-plugin/app/src/trace/AndroidManifest.xml index 8f04cbc4..5b38ea99 100644 --- a/android-plugin/app/src/trace/AndroidManifest.xml +++ b/android-plugin/app/src/trace/AndroidManifest.xml @@ -1,7 +1,54 @@ - + + + + + + + + + + + + + + + + + - subproject.plugins.withId('com.android.library') { - subproject.android { - defaultConfig { - ndk { - def abiFiltersOverride = mobileGlAbiFilters() - if (!abiFiltersOverride.isEmpty()) { - abiFilters(*abiFiltersOverride) - } - } - externalNativeBuild { - cmake { - def compilerLauncher = mobileGlCmakeCompilerLauncher() - if (!compilerLauncher.isEmpty()) { - arguments "-DCMAKE_C_COMPILER_LAUNCHER=${compilerLauncher}", - "-DCMAKE_CXX_COMPILER_LAUNCHER=${compilerLauncher}" - } - cppFlags "-DMOBILEGL_LOG_ACTIVE_LEVEL=${mobileGlLogActiveLevel()}" - } - } - } - } - } -} diff --git a/android-plugin/build.gradle.kts b/android-plugin/build.gradle.kts new file mode 100644 index 00000000..c351d75e --- /dev/null +++ b/android-plugin/build.gradle.kts @@ -0,0 +1,44 @@ +import com.android.build.gradle.LibraryExtension + +plugins { + id("com.android.application") version "8.6.0" apply false + id("com.android.library") version "8.6.0" apply false +} + +fun Project.mobileGlAbiFilters(): List { + 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.mobileGlLogActiveLevel(): String = + (findProperty("mobilegl.logLevel") ?: System.getenv("MOBILEGL_LOG_ACTIVE_LEVEL") ?: "MOBILEGL_LOG_LEVEL_INFO").toString() + +fun Project.mobileGlCmakeCompilerLauncher(): String = + (findProperty("mobilegl.cmakeCompilerLauncher") ?: System.getenv("MOBILEGL_CMAKE_COMPILER_LAUNCHER") ?: "").toString().trim() + +subprojects { + plugins.withId("com.android.library") { + extensions.configure { + defaultConfig { + ndk { + abiFilters += mobileGlAbiFilters() + } + externalNativeBuild { + cmake { + mobileGlCmakeCompilerLauncher().takeIf(String::isNotEmpty)?.let { compilerLauncher -> + arguments += listOf( + "-DCMAKE_C_COMPILER_LAUNCHER=$compilerLauncher", + "-DCMAKE_CXX_COMPILER_LAUNCHER=$compilerLauncher", + ) + } + cppFlags += "-DMOBILEGL_LOG_ACTIVE_LEVEL=${mobileGlLogActiveLevel()}" + } + } + } + } + } +} diff --git a/android-plugin/settings.gradle b/android-plugin/settings.gradle.kts similarity index 70% rename from android-plugin/settings.gradle rename to android-plugin/settings.gradle.kts index a3b1bb7b..c89a25aa 100644 --- a/android-plugin/settings.gradle +++ b/android-plugin/settings.gradle.kts @@ -14,7 +14,6 @@ dependencyResolutionManagement { } } -rootProject.name = 'MobileGLPlugin' -include ':app' -include ':MobileGL' -project(':MobileGL').projectDir = file('..') +rootProject.name = "MobileGLPlugin" +include(":app", ":MobileGL") +project(":MobileGL").projectDir = file("..")