diff --git a/app/src/main/runtime/display/XServerDisplayActivity.java b/app/src/main/runtime/display/XServerDisplayActivity.java index c054b7a38..5c902dd87 100644 --- a/app/src/main/runtime/display/XServerDisplayActivity.java +++ b/app/src/main/runtime/display/XServerDisplayActivity.java @@ -440,7 +440,7 @@ private boolean isAnyControllerConnected() { private final ArrayList taskManagerAccum = new ArrayList<>(); private boolean taskManagerCpuExpanded = false; private boolean taskManagerPaneVisible = false; - private short[] cachedMaxClockSpeeds; + private CPUStatus.CpuSample prevTaskCpuSample; private boolean drawerEdgeGesturePossible = false; private float drawerEdgeGestureStartX = 0f; private float drawerEdgeGestureStartY = 0f; @@ -4965,6 +4965,7 @@ private void stopTaskManagerPolling() { if (winHandler != null) winHandler.setOnGetProcessInfoListener(null); taskManagerAccum.clear(); taskManagerCpuExpanded = false; + prevTaskCpuSample = null; if (drawerStateHolder != null) { drawerStateHolder.setTaskManagerState(new TaskManagerPaneState( new ArrayList<>(), 0, 0, new ArrayList<>(), 0, "")); @@ -5011,35 +5012,33 @@ private void handleTaskManagerProcessInfo(int index, int numProcesses, ProcessIn private void pushTaskManagerSystemStats() { if (drawerStateHolder == null) return; - short[] clockSpeeds = CPUStatus.getCurrentClockSpeeds(); - if (cachedMaxClockSpeeds == null || cachedMaxClockSpeeds.length != clockSpeeds.length) { - short[] maxes = new short[clockSpeeds.length]; - for (int i = 0; i < clockSpeeds.length; i++) maxes[i] = CPUStatus.getMaxClockSpeed(i); - cachedMaxClockSpeeds = maxes; - } - - int totalClock = 0; - short maxClock = 0; - for (int i = 0; i < clockSpeeds.length; i++) { - totalClock += clockSpeeds[i]; - if (cachedMaxClockSpeeds[i] > maxClock) maxClock = cachedMaxClockSpeeds[i]; - } + CPUStatus.CpuSample cpuSample = CPUStatus.readCpuSample(); + int coreCount; int cpuPercent = 0; - if (clockSpeeds.length > 0 && maxClock > 0) { - int avg = totalClock / clockSpeeds.length; - cpuPercent = (int) (((float) avg / maxClock) * 100.0f); - } - - ArrayList corePercents; - if (taskManagerCpuExpanded) { - corePercents = new ArrayList<>(clockSpeeds.length); - for (int i = 0; i < clockSpeeds.length; i++) { - short maxFor = cachedMaxClockSpeeds[i]; - int corePercent = maxFor > 0 ? (int) (((float) clockSpeeds[i] / maxFor) * 100.0f) : 0; - corePercents.add(corePercent); + ArrayList corePercents = new ArrayList<>(); + if (cpuSample != null) { + coreCount = cpuSample.coreCount(); + if (prevTaskCpuSample != null) { + int agg = cpuSample.percentSince(prevTaskCpuSample); + if (agg >= 0) cpuPercent = agg; + if (taskManagerCpuExpanded) { + for (int i = 0; i < coreCount; i++) { + corePercents.add(cpuSample.corePercentSince(prevTaskCpuSample, i)); + } + } } + prevTaskCpuSample = cpuSample; } else { - corePercents = new ArrayList<>(); + prevTaskCpuSample = null; + short[] clocks = CPUStatus.getCurrentClockSpeeds(); + coreCount = clocks != null ? clocks.length : 0; + int agg = CPUStatus.getClockFreqLoadPercent(); + cpuPercent = agg >= 0 ? agg : 0; + if (taskManagerCpuExpanded) { + for (int i = 0; i < coreCount; i++) { + corePercents.add(CPUStatus.getClockFreqCorePercent(i)); + } + } } ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); @@ -5053,7 +5052,7 @@ private void pushTaskManagerSystemStats() { drawerStateHolder.setTaskManagerState(new TaskManagerPaneState( current.getProcesses(), cpuPercent, - clockSpeeds.length, + coreCount, corePercents, memPercent, memDetail)); diff --git a/app/src/main/runtime/display/ui/FrameRating.java b/app/src/main/runtime/display/ui/FrameRating.java index 1e0c90740..8b185a203 100644 --- a/app/src/main/runtime/display/ui/FrameRating.java +++ b/app/src/main/runtime/display/ui/FrameRating.java @@ -95,6 +95,8 @@ public class FrameRating extends LinearLayout implements Runnable { private boolean canReadGpu; private Context context; private int cpuFailCount; + private CPUStatus.CpuSample prevCpuSample; + private boolean cpuWarmedUp; private volatile int cpuPercent; private volatile int cpuTemp; private volatile int cpuSensorTemp; @@ -163,6 +165,7 @@ public void setFrameObserver(FrameObserver observer) { private static final long FALLBACK_SUPPRESSION_NS = 2000000000L; private static final long FPS_CALC_INTERVAL_NS = 1000000000L; private static final long HUD_REFRESH_MS = 1000L; + private static final long CPU_WARMUP_POLL_MS = 500L; private static final long MIN_FRAME_INTERVAL_NS = 1000000L; private static final int MAX_FRAME_SAMPLES = 1024; @@ -210,6 +213,8 @@ public FrameRating( this.enableTemp = true; this.enableRenderer = true; this.cpuPercent = -1; + this.prevCpuSample = null; + this.cpuWarmedUp = false; this.gpuLoad = -1; this.batteryWatts = -1.0f; this.cpuTemp = -1; @@ -299,7 +304,8 @@ public void run() { if (isStatsRunning) { calculateStats(); if (statsHandler != null) { - statsHandler.postDelayed(this, 1000L); + long next = (prevCpuSample != null && !cpuWarmedUp) ? CPU_WARMUP_POLL_MS : 1000L; + statsHandler.postDelayed(this, next); } } } @@ -311,6 +317,8 @@ private void startStatsUpdate() { return; } this.isStatsRunning = true; + this.prevCpuSample = null; + this.cpuWarmedUp = false; this.statsThread = new HandlerThread("HardwareStatsThread"); this.statsThread.start(); this.statsHandler = new Handler(this.statsThread.getLooper()); @@ -1304,7 +1312,8 @@ private int calculateGPULoad() throws Exception { File[] gpuFiles = { new File("/sys/class/kgsl/kgsl-3d0/gpu_busy_percentage"), new File("/sys/class/kgsl/kgsl-3d0/devfreq/gpu_load"), - new File("/sys/class/misc/mali0/device/utilisation") + new File("/sys/class/misc/mali0/device/utilisation"), + new File("/sys/kernel/gpu/gpu_busy") }; for (File f : gpuFiles) { @@ -1319,6 +1328,24 @@ private int calculateGPULoad() throws Exception { } } + File mtkMali = new File("/proc/mtk_mali/utilization"); + if (mtkMali.exists() && mtkMali.canRead()) { + try (BufferedReader reader = new BufferedReader(new FileReader(mtkMali))) { + String line; + while ((line = reader.readLine()) != null) { + int idx = line.indexOf("ACTIVE="); + if (idx >= 0) { + int start = idx + 7; + int end = line.indexOf(' ', start); + String num = (end > start ? line.substring(start, end) : line.substring(start)) + .replaceAll("[^0-9]", ""); + if (!num.isEmpty()) return Integer.parseInt(num); + } + } + } catch (Exception ignored) { + } + } + File gpubusy = new File("/sys/class/kgsl/kgsl-3d0/gpubusy"); if (gpubusy.exists() && gpubusy.canRead()) { try (BufferedReader reader = new BufferedReader(new FileReader(gpubusy))) { @@ -1328,7 +1355,7 @@ private int calculateGPULoad() throws Exception { if (parts.length >= 2) { long busy = Long.parseLong(parts[0]); long total = Long.parseLong(parts[1]); - if (total != 0) return (int) ((100 * busy) / total); + return total != 0 ? (int) ((100 * busy) / total) : 0; } } } catch (Exception ignored) { @@ -1358,17 +1385,21 @@ private void calculateStats() { } if (this.enableCpu && this.canReadCpu) { try { - short[] clocks = CPUStatus.getCurrentClockSpeeds(); - if (clocks != null && clocks.length > 0) { - long cur = 0; - long max = 0; - for (int i = 0; i < clocks.length; i++) { - cur += clocks[i]; - max += CPUStatus.getMaxClockSpeed(i); - } - if (max > 0) { - this.cpuPercent = (int) ((cur * 100) / max); - this.cpuFailCount = 0; + CPUStatus.CpuSample sample = CPUStatus.readCpuSample(); + int pct = -1; + if (sample != null) { + if (this.prevCpuSample != null) pct = sample.percentSince(this.prevCpuSample); + this.prevCpuSample = sample; + } else { + pct = CPUStatus.getClockFreqLoadPercent(); + } + if (pct >= 0) { + this.cpuPercent = pct; + this.cpuFailCount = 0; + if (!this.cpuWarmedUp) { + this.cpuWarmedUp = true; + Handler refresh = this.uiRefreshHandler; + if (refresh != null) refresh.post(this); } } } catch (Exception e) { diff --git a/app/src/main/runtime/system/CPUStatus.java b/app/src/main/runtime/system/CPUStatus.java index 05cde0788..3bec623d0 100644 --- a/app/src/main/runtime/system/CPUStatus.java +++ b/app/src/main/runtime/system/CPUStatus.java @@ -1,7 +1,9 @@ package com.winlator.cmod.runtime.system; import com.winlator.cmod.shared.io.FileUtils; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import java.util.ArrayList; import java.util.Collections; import java.util.Locale; @@ -85,4 +87,125 @@ private static int rankCpuZone(String type) { if (type.contains("big") || type.contains("little")) return 8; return -1; } + + public static final class CpuSample { + private final long aggBusy; + private final long aggIdle; + private final long[] coreBusy; + private final long[] coreIdle; + + private CpuSample(long aggBusy, long aggIdle, long[] coreBusy, long[] coreIdle) { + this.aggBusy = aggBusy; + this.aggIdle = aggIdle; + this.coreBusy = coreBusy; + this.coreIdle = coreIdle; + } + + public int coreCount() { + return coreBusy.length; + } + + public int percentSince(CpuSample prev) { + if (prev == null) return -1; + return percent(aggBusy - prev.aggBusy, aggIdle - prev.aggIdle); + } + + public int corePercentSince(CpuSample prev, int core) { + if (prev == null || core < 0 || core >= coreBusy.length || core >= prev.coreBusy.length) + return 0; + return percent(coreBusy[core] - prev.coreBusy[core], coreIdle[core] - prev.coreIdle[core]); + } + + private static int percent(long busyDelta, long idleDelta) { + long total = busyDelta + idleDelta; + if (total <= 0) return 0; + long p = (100 * busyDelta) / total; + if (p < 0) return 0; + if (p > 100) return 100; + return (int) p; + } + } + + public static CpuSample readCpuSample() { + int numProcessors = Runtime.getRuntime().availableProcessors(); + long[] coreBusy = new long[numProcessors]; + long[] coreIdle = new long[numProcessors]; + long aggBusy = 0; + long aggIdle = 0; + try (BufferedReader reader = new BufferedReader(new FileReader("/proc/stat"))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("cpu")) break; + String[] f = line.trim().split("\\s+"); + if (f.length < 5) continue; + long user = parseTick(f, 1); + long nice = parseTick(f, 2); + long system = parseTick(f, 3); + long idle = parseTick(f, 4); + long iowait = parseTick(f, 5); + long irq = parseTick(f, 6); + long softirq = parseTick(f, 7); + long steal = parseTick(f, 8); + long idleAll = idle + iowait; + long busy = user + nice + system + irq + softirq + steal; + if (f[0].equals("cpu")) { + aggBusy = busy; + aggIdle = idleAll; + } else { + int idx = parseCoreIndex(f[0]); + if (idx >= 0 && idx < numProcessors) { + coreBusy[idx] = busy; + coreIdle[idx] = idleAll; + } + } + } + } catch (Exception e) { + return null; + } + return new CpuSample(aggBusy, aggIdle, coreBusy, coreIdle); + } + + private static long parseTick(String[] fields, int index) { + if (index >= fields.length) return 0; + try { + return Long.parseLong(fields[index]); + } catch (NumberFormatException e) { + return 0; + } + } + + private static int parseCoreIndex(String label) { + try { + return Integer.parseInt(label.substring(3)); + } catch (Exception e) { + return -1; + } + } + + public static int getClockFreqLoadPercent() { + short[] clocks = getCurrentClockSpeeds(); + if (clocks == null || clocks.length == 0) return -1; + long cur = 0; + long max = 0; + for (int i = 0; i < clocks.length; i++) { + cur += clocks[i]; + max += getMaxClockSpeed(i); + } + if (max <= 0) return -1; + return clampPercent((int) ((cur * 100) / max)); + } + + public static int getClockFreqCorePercent(int core) { + short[] clocks = getCurrentClockSpeeds(); + if (clocks == null || core < 0 || core >= clocks.length) return 0; + int max = getMaxClockSpeed(core); + if (max <= 0) return 0; + return clampPercent((int) (((float) clocks[core] / max) * 100.0f)); + } + + private static int clampPercent(int p) { + if (p < 0) return 0; + if (p > 100) return 100; + return p; + } }