From 5128bbd45307095dc10b33bb686dd961d359cb08 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 28 Sep 2026 12:28:45 -0400 Subject: [PATCH] mangoapp: stat sources for more Snapdragon generations KGSL's files and thermal zone names differ between Snapdragon generations. Each value now takes the first source that exists, in the order GameNative and the Winlator forks read them: GPU load from gpu_busy_percentage or devfreq/gpu_load, GPU clock from devfreq/cur_freq or gpuclk, GPU temperature from KGSL's temp, devfreq/temp or a zone whose sensor name has "gpu". CPU zones are any whose name has "cpu" and not "gpu" (cpuss-0, cpu0-silver-usr, apc1-cpu0-usr, ...), matched without regard to case. --- .../launcher/runtime/LinuxRuntime.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java b/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java index b04548b00..ddec26da0 100644 --- a/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java +++ b/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java @@ -249,30 +249,32 @@ public final class LinuxRuntime { /** * Valve's mangoapp (Deck mode's performance overlay) reads an Adreno GPU's load, clock and * temperatures from where they are on Valve's own hardware; every Adreno under Android keeps - * them in KGSL's sysfs, readable by the app. Only the GPU temperature has no KGSL file on older - * kernels, and thermal zones are numbered per device, so a zone is found by its sensor's name, - * which differs between Snapdragon generations. A value that cannot be found is left alone. + * them in KGSL's sysfs, readable by the app, though which files a kernel has differs between + * Snapdragon generations - so each value takes the first source that exists, the order other + * Android PC emulators (GameNative, Winlator forks) read them in. A value found nowhere is left + * alone. */ private static void bindAdrenoStats(List cmd) { String kgsl = "/sys/class/kgsl/kgsl-3d0/"; - String gpuTemp = new File(kgsl + "temp").canRead() ? kgsl + "temp" - : thermalZone("gpuss-0", "gpuss-0-usr", "gpu0-usr", "gpu-usr", "gpu"); + String gpuTemp = firstReadable(kgsl + "temp", kgsl + "devfreq/temp", thermalZone("gpu")); String[][] stats = { - {kgsl + "gpu_busy_percentage", "/sys/kernel/debug/dri/0/perf_now"}, - {kgsl + "devfreq/cur_freq", "/sys/devices/platform/soc@0/3d00000.gpu/devfreq/3d00000.gpu/cur_freq"}, + {firstReadable(kgsl + "gpu_busy_percentage", kgsl + "devfreq/gpu_load"), "/sys/kernel/debug/dri/0/perf_now"}, + {firstReadable(kgsl + "devfreq/cur_freq", kgsl + "gpuclk"), + "/sys/devices/platform/soc@0/3d00000.gpu/devfreq/3d00000.gpu/cur_freq"}, {gpuTemp, "/sys/class/thermal/thermal_zone28/temp"}, {gpuTemp, "/sys/class/thermal/thermal_zone26/temp"}, - {thermalZone("ddr", "ddr-usr", "ddr-thermal"), "/sys/class/thermal/thermal_zone22/temp"}, + {thermalZone("ddr"), "/sys/class/thermal/thermal_zone22/temp"}, }; for (String[] stat : stats) { - if (stat[0] != null && new File(stat[0]).canRead()) bind(cmd, stat[0] + ":" + stat[1]); + if (stat[0] != null) bind(cmd, stat[0] + ":" + stat[1]); } } /** * mangoapp's CPU temperature is the mean of the thermal zones named cpuN-thermal or * cpuN-top-thermal, as mainline kernels name them; Android kernels name the same sensors - * cpuss-0, cpu-1-0 and the like, so those zones are shown under the mainline name. + * cpuss-0, cpu-1-0, cpu0-silver-usr, apc1-cpu0-usr and the like, so those zones are shown + * under the mainline name. */ private static void bindCpuTemps(List cmd, File root) { File name = new File(root, "etc/bannerlator/cpu-thermal-type"); @@ -283,29 +285,37 @@ public final class LinuxRuntime { } for (java.util.Map.Entry zone : thermalZones().entrySet()) { String type = zone.getKey(); - if (type.startsWith("cpu") && !type.matches("cpu\\d-(top-)?thermal")) { + if (type.contains("cpu") && !type.contains("gpu") && !type.matches("cpu\\d-(top-)?thermal")) { bind(cmd, name.getPath() + ":" + new File(zone.getValue(), "type").getPath()); } } } - /** The temp file of the first thermal zone whose sensor has one of these names, in that order. */ - private static String thermalZone(String... types) { - java.util.Map zones = thermalZones(); - for (String type : types) { - if (zones.containsKey(type)) return new File(zones.get(type), "temp").getPath(); + private static String firstReadable(String... paths) { + for (String path : paths) { + if (path != null && new File(path).canRead()) return path; } return null; } - /** The device's thermal zones by their sensor's name. */ + /** The temp file of the thermal zone for a sensor: the one named so, else the first whose name has it. */ + private static String thermalZone(String sensor) { + java.util.Map zones = thermalZones(); + File zone = zones.get(sensor); + for (java.util.Map.Entry e : new java.util.TreeMap<>(zones).entrySet()) { + if (zone == null && e.getKey().contains(sensor)) zone = e.getValue(); + } + return zone != null ? new File(zone, "temp").getPath() : null; + } + + /** The device's thermal zones by their sensor's name, in lower case. */ private static java.util.Map thermalZones() { java.util.Map byType = new java.util.HashMap<>(); File[] zones = new File("/sys/class/thermal").listFiles((dir, n) -> n.startsWith("thermal_zone")); if (zones == null) return byType; for (File zone : zones) { try (java.io.BufferedReader r = new java.io.BufferedReader(new java.io.FileReader(new File(zone, "type")))) { - byType.putIfAbsent(String.valueOf(r.readLine()).trim(), zone); + byType.putIfAbsent(String.valueOf(r.readLine()).trim().toLowerCase(java.util.Locale.ROOT), zone); } catch (IOException ignored) { } }