From 20185ab7e988f25ed443d8eb09329d83fa2eddb1 Mon Sep 17 00:00:00 2001 From: The412Banner Date: Mon, 12 Oct 2026 19:21:10 -0400 Subject: [PATCH] Auto: the one-binary PanVK kbase build for Mali v9-v13 on Android 15+ The412Banner/droiddeck-nonadreno-drivers adds panvk-mali-kbase: one PanVK build for G57/G68/G77/G78, G610/G710, G615/G715, G720/G725 and G925, whose display half needs Android 15. On Android 15+ Auto prefers it for every Mali it covers; below 15, and whenever its release is not listed, Auto falls back to the GPU generation's own pair. G52/G76 keep the Job Manager build; a G715/G725/G925 below Android 15, or any Mali no pair covers, draws through the bridge. Every pair stays selectable by hand. --- .../com/droiddeck/launcher/DriverMenus.kt | 2 +- .../com/droiddeck/launcher/gpu/DriverPairs.kt | 33 ++++++++++++++++--- .../com/droiddeck/launcher/gpu/GpuInfo.kt | 22 ++++++++++--- .../droiddeck/launcher/gpu/DriverPairsTest.kt | 30 ++++++++++++++--- 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/DriverMenus.kt b/app/src/main/java/com/droiddeck/launcher/DriverMenus.kt index 8fb037a17..a70f07b4f 100644 --- a/app/src/main/java/com/droiddeck/launcher/DriverMenus.kt +++ b/app/src/main/java/com/droiddeck/launcher/DriverMenus.kt @@ -165,7 +165,7 @@ internal class DriverMenus(private val activity: Activity, private val ui: Handl } if (!force && autoCheckedThisProcess) return autoCheckedThisProcess = true - if (gpu.family == GpuInfo.Family.MALI_OTHER) { + if (gpu.family.mali && DriverPairs.recommendedKey(gpu, emptyList()) == null) { useMaliBridge() autoStatus = activity.getString(R.string.drivers_mali_bridge_auto) refreshDrivers() diff --git a/app/src/main/java/com/droiddeck/launcher/gpu/DriverPairs.kt b/app/src/main/java/com/droiddeck/launcher/gpu/DriverPairs.kt index 62d61d310..f272407c2 100644 --- a/app/src/main/java/com/droiddeck/launcher/gpu/DriverPairs.kt +++ b/app/src/main/java/com/droiddeck/launcher/gpu/DriverPairs.kt @@ -34,6 +34,9 @@ object DriverPairs { const val NAD_MALI_G710 = "nad:panvk-mali-g710" const val NAD_MALI_G615 = "nad:panvk-mali-g615" const val NAD_MALI_G720 = "nad:panvk-mali-g720" + /** One PanVK kbase binary for Mali v9-v13; its display half needs Android 15. */ + const val NAD_MALI_KBASE = "nad:panvk-mali-kbase" + const val MALI_KBASE_MIN_SDK = 35 /** "PanVK-Mali-G720" (the release asset's id) to its pair key, "nad:panvk-mali-g720". */ fun nonAdreno(id: String) = "$NON_ADRENO:" + id.lowercase() /** True for a non-Adreno pair's key, or a bundle installed from one ("PanVK-Mali-G720"). */ @@ -48,6 +51,8 @@ object DriverPairs { NAD_MALI_G710 -> "PanVK · Mali-G610/G710" to setOf(Family.MALI_G610_G710) NAD_MALI_G615 -> "PanVK · Mali-G615" to setOf(Family.MALI_G615) NAD_MALI_G720 -> "PanVK · Mali-G720" to setOf(Family.MALI_G720) + NAD_MALI_KBASE -> "PanVK kbase · Mali G57-G925" to + setOf(Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720, Family.MALI_LATEST) else -> key.removePrefix("$NON_ADRENO:") to emptySet() } @@ -71,6 +76,8 @@ object DriverPairs { fun suits(gpu: GpuInfo) = when { // The system-driver family covers PowerVR and Xclipse; RADV is only for the Xclipse. key == NAD_RADV_XCLIPSE -> gpu.family == Family.POWERVR && gpu.name.contains("Xclipse", ignoreCase = true) + // One binary for Mali v9-v13, not the older G52/G76 in the Job Manager family. + key == NAD_MALI_KBASE -> gpu.family.mali && GpuInfo.maliKbaseCovered(gpu.name) else -> gpu.family in families || (gpu.family == Family.ADRENO_UNKNOWN && (Family.A7XX in families || Family.A8XX in families)) } @@ -121,14 +128,11 @@ object DriverPairs { * release is listed, the OneUI build for an 8 Gen 2 under One UI, the patched test builds for * the 710/720/722, and upstream Banners-Turnip for the rest. Null when nothing fits (not Adreno). */ - fun recommendedKey(gpu: GpuInfo, pairs: List): String? = when (gpu.family) { + fun recommendedKey(gpu: GpuInfo, pairs: List, sdk: Int = android.os.Build.VERSION.SDK_INT): String? = when (gpu.family) { // PowerVR and Xclipse keep the system driver (RADV stays a manual choice); a Mali no open // build covers draws through the Vulkan bridge instead (DriverMenus.ensureAuto). Family.NOT_ADRENO, Family.POWERVR, Family.MALI_OTHER -> null - Family.MALI_JM -> NAD_MALI_JM - Family.MALI_G610_G710 -> NAD_MALI_G710 - Family.MALI_G615 -> NAD_MALI_G615 - Family.MALI_G720 -> NAD_MALI_G720 + Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720, Family.MALI_LATEST -> maliKey(gpu, pairs, sdk) Family.A8XX -> if (gpu.model == 840) newestDdTurnip(pairs) ?: WN_BALANCED else WN_BALANCED Family.A7XX_LOW -> BANNER_710 Family.A7XX -> if (gpu.oneUi8Gen2) BANNER_ONEUI else BANNER @@ -137,6 +141,25 @@ object DriverPairs { Family.ADRENO_UNKNOWN -> if (android.os.Build.VERSION.SDK_INT >= 36) WN_BALANCED else BANNER } + /** + * A Mali's pair: the one-binary kbase build on Android 15+ for every Mali it covers, else the + * build for the GPU's generation. A preferred pair the check did not find falls back to the + * generation's own, and a G715/G725/G925 without the kbase build gets none (the bridge). + */ + private fun maliKey(gpu: GpuInfo, pairs: List, sdk: Int): String? { + val family = when (gpu.family) { + Family.MALI_JM -> NAD_MALI_JM + Family.MALI_G610_G710 -> NAD_MALI_G710 + Family.MALI_G615 -> NAD_MALI_G615 + Family.MALI_G720 -> NAD_MALI_G720 + else -> null + } + val kbase = NAD_MALI_KBASE.takeIf { sdk >= MALI_KBASE_MIN_SDK && GpuInfo.maliKbaseCovered(gpu.name) } + if (pairs.isEmpty()) return kbase ?: family + fun listed(key: String?) = key?.takeIf { k -> pairs.any { it.key == k && it.complete } } + return listed(kbase) ?: listed(family) ?: kbase ?: family + } + /** The complete DD-Turnip pair with the highest version, or null when none is listed. */ fun newestDdTurnip(pairs: List): String? = pairs .filter { it.key.startsWith("$DD_TURNIP:") && it.complete } diff --git a/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt b/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt index 347db8854..a17b8a39e 100644 --- a/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt +++ b/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt @@ -41,7 +41,9 @@ data class GpuInfo( MALI_G610_G710("Mali-G610/G710"), MALI_G615("Mali-G615"), MALI_G720("Mali-G720"), - /** A Mali no open driver build covers yet (G925...): the Vulkan bridge is its only path. */ + /** Mali-G715/G725/G925: only the one-binary PanVK kbase build covers them (Android 15+). */ + MALI_LATEST("Mali-G715/G725/G925"), + /** A Mali no open driver build covers: the Vulkan bridge is its only path. */ MALI_OTHER("Mali"), NOT_ADRENO("Not an Adreno GPU"); @@ -69,7 +71,7 @@ data class GpuInfo( Support.UNTESTED -> when (family) { Family.POWERVR -> "System PowerVR driver: compatibility varies by device" Family.MALI_OTHER -> "Experimental: Mali through the Vulkan bridge" - Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720 -> "Experimental: open Mali driver (test builds)" + Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720, Family.MALI_LATEST -> "Experimental: open Mali driver (test builds)" Family.A7XX_LOW -> "Experimental: its drivers are test builds" else -> "Outside tested hardware (Adreno 650, 725 and newer): it may not run" } @@ -82,7 +84,7 @@ data class GpuInfo( Support.UNTESTED -> when (family) { Family.POWERVR -> R.string.gpuinfo_powervr Family.A7XX_LOW -> R.string.gpuinfo_experimental - Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720, Family.MALI_OTHER -> R.string.gpuinfo_experimental + Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720, Family.MALI_LATEST, Family.MALI_OTHER -> R.string.gpuinfo_experimental else -> R.string.gpuinfo_below_tested } Support.UNSUPPORTED -> R.string.gpuinfo_unsupported @@ -159,15 +161,25 @@ data class GpuInfo( ?.substringBefore(" ") ?: "" + private fun maliNumber(name: String): Int? = + Regex("""G(\d{2,3})""", RegexOption.IGNORE_CASE).find(name)?.groupValues?.get(1)?.toIntOrNull() + + /** + * The Malis the one-binary PanVK kbase build covers (arch v9-v13): G57/G68/G77/G78, + * G610/G710, G615/G715, G720/G725 and G925. Not the older Bifrost G52/G76. + */ + fun maliKbaseCovered(name: String): Boolean = + maliNumber(name) in setOf(57, 68, 77, 78, 610, 710, 615, 715, 720, 725, 925) + /** The PanVK generation a Mali name belongs to; MALI_OTHER when none of the builds covers it. */ internal fun maliFamily(name: String): Family { - val number = Regex("""G(\d{2,3})""", RegexOption.IGNORE_CASE).find(name)?.groupValues?.get(1)?.toIntOrNull() - ?: return Family.MALI_OTHER + val number = maliNumber(name) ?: return Family.MALI_OTHER return when (number) { 31, 51, 52, 57, 68, 71, 72, 76, 77, 78 -> Family.MALI_JM 610, 710 -> Family.MALI_G610_G710 615 -> Family.MALI_G615 720 -> Family.MALI_G720 + 715, 725, 925 -> Family.MALI_LATEST else -> Family.MALI_OTHER } } diff --git a/app/src/test/java/com/droiddeck/launcher/gpu/DriverPairsTest.kt b/app/src/test/java/com/droiddeck/launcher/gpu/DriverPairsTest.kt index f3750e48c..8c4695272 100644 --- a/app/src/test/java/com/droiddeck/launcher/gpu/DriverPairsTest.kt +++ b/app/src/test/java/com/droiddeck/launcher/gpu/DriverPairsTest.kt @@ -149,14 +149,34 @@ class DriverPairsTest { @Test fun autoPicksTheOpenDriverForEachMaliAndNothingForTheRest() { fun mali(name: String) = GpuInfo(name, 0, GpuInfo.familyOf(false, 0, mali = name), "", false) - assertEquals(DriverPairs.NAD_MALI_JM, DriverPairs.recommendedKey(mali("Mali-G57"), emptyList())) - assertEquals(DriverPairs.NAD_MALI_G710, DriverPairs.recommendedKey(mali("Mali-G710"), emptyList())) - assertEquals(DriverPairs.NAD_MALI_G615, DriverPairs.recommendedKey(mali("Mali-G615"), emptyList())) - assertEquals(DriverPairs.NAD_MALI_G720, DriverPairs.recommendedKey(mali("Mali-G720"), emptyList())) - assertNull("a Mali no build covers uses the bridge", DriverPairs.recommendedKey(mali("Mali-G925"), emptyList())) + // Android 14: the per-generation builds; the newest Malis have none and use the bridge. + assertEquals(DriverPairs.NAD_MALI_JM, DriverPairs.recommendedKey(mali("Mali-G57"), emptyList(), sdk = 34)) + assertEquals(DriverPairs.NAD_MALI_G710, DriverPairs.recommendedKey(mali("Mali-G710"), emptyList(), sdk = 34)) + assertEquals(DriverPairs.NAD_MALI_G615, DriverPairs.recommendedKey(mali("Mali-G615"), emptyList(), sdk = 34)) + assertEquals(DriverPairs.NAD_MALI_G720, DriverPairs.recommendedKey(mali("Mali-G720"), emptyList(), sdk = 34)) + assertNull(DriverPairs.recommendedKey(mali("Mali-G925-Immortalis MC12"), emptyList(), sdk = 34)) + // Android 15+: the one-binary kbase build for every Mali it covers, not the Bifrost G52/G76. + assertEquals(DriverPairs.NAD_MALI_KBASE, DriverPairs.recommendedKey(mali("Mali-G57"), emptyList(), sdk = 35)) + assertEquals(DriverPairs.NAD_MALI_KBASE, DriverPairs.recommendedKey(mali("Mali-G720"), emptyList(), sdk = 36)) + assertEquals(DriverPairs.NAD_MALI_KBASE, DriverPairs.recommendedKey(mali("Mali-G925-Immortalis MC12"), emptyList(), sdk = 36)) + assertEquals(DriverPairs.NAD_MALI_JM, DriverPairs.recommendedKey(mali("Mali-G52 MC2"), emptyList(), sdk = 36)) + assertNull("a Mali no build covers uses the bridge", DriverPairs.recommendedKey(mali("Mali-T880"), emptyList(), sdk = 36)) assertEquals(DriverPairs.BANNER, DriverPairs.recommendedKey(gpu(740), emptyList())) } + @Test + fun aMissingKbaseReleaseFallsBackToTheGenerationsPair() { + val g720 = GpuInfo("Mali-G720", 0, Family.MALI_G720, "", false) + fun bundle(id: String) = TurnipReleases.Asset("Non-Adreno", id.lowercase(), "$id-Pair.zip", "https://example/$id", 9_000_000, + false, "label", "0".repeat(64), DriverPairs.nonAdreno(id), bundle = true) + val onlyG720 = DriverPairs.from(TurnipReleases.Check(listOf(bundle("PanVK-Mali-G720")), emptyList(), emptyList(), 0L)) + assertEquals(DriverPairs.NAD_MALI_G720, DriverPairs.recommendedKey(g720, onlyG720, sdk = 36)) + val both = DriverPairs.from(TurnipReleases.Check(listOf(bundle("PanVK-Mali-G720"), bundle("PanVK-Mali-kbase")), emptyList(), emptyList(), 0L)) + assertEquals(DriverPairs.NAD_MALI_KBASE, DriverPairs.recommendedKey(g720, both, sdk = 36)) + assertTrue(both.single { it.key == DriverPairs.NAD_MALI_KBASE }.suits(g720)) + assertFalse(both.single { it.key == DriverPairs.NAD_MALI_KBASE }.suits(GpuInfo("Mali-G52", 0, Family.MALI_JM, "", false))) + } + @Test fun nonAdrenoPairsComeFromTheirBundlesAndSuitOnlyTheirGpu() { val pairs = DriverPairs.from(TurnipReleases.Check(