Files
DroidDeck/tools/tests/test_kgsl_busy.py
The412Banner 55da0afd06 fix(hud): GPU clock and load without root on Adreno 8xx and hardware-scheduled kernels (#1981)
On phones where Android blocks the GPU's sysfs files for apps, the HUD
reads the GPU without root (#1969). On Adreno 8xx that showed no GPU
clock and only the partial load from Vulkan timing (OnePlus 13, Adreno
830, reported by iamTooShaar).

- GPU clock: the clock job used the Adreno 6xx/7xx registers. The
  register layout is now picked from the KGSL chip id: on 8xx
  CP_ALWAYS_ON_COUNTER is 0x8e7 and the RBBM cycle countable is 1.
  Unknown later generations read nothing.
- GPU load: hardware-scheduled kgsl kernels (every 8xx, and some 7xx
  such as the AYANEO Pocket FIT's) never write the memstore ringbuffer
  timestamps. When those stay 0, the reader samples the contexts' own
  memstore entries (every context that has submitted work, up to 64,
  contexts with new or running work first).
- HUD diagnostics report: a "GPU readers without root" line with the
  reason each reader works or fails, "none" instead of "null", and the
  per-core CPU % source named honestly.

Tested: iamTooShaar's OnePlus 13 shows GPU clock and load with the PR
test build (06be2c5). Pocket FIT standalone build of the final readers
against the kernel's sysfs numbers: idle 1-2 % vs 3-4 %, GPU job loop
25-29 % vs 28-30 %, clock 1050 MHz vs 1050 MHz. The review fixes after
the test build were not tested on the OnePlus 13.
2026-11-02 10:07:26 +00:00

36 lines
1.4 KiB
Python

"""Exercise the HUD's kgsl memstore busy sampling and counter layout (app/src/main/cpp/gpuclock)."""
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
REPO = Path(__file__).resolve().parents[2]
class KgslBusyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
compiler = shutil.which(os.environ.get('CC', 'cc'))
if compiler is None:
raise unittest.SkipTest('A host C compiler is required')
cls.temp = tempfile.TemporaryDirectory(prefix='droiddeck-kgsl-busy-')
cls.addClassCleanup(cls.temp.cleanup)
cls.binary = Path(cls.temp.name) / 'kgsl-busy-test'
subprocess.run([compiler, '-std=c11', '-Wall', '-Wextra', '-Werror',
'-I', str(REPO / 'app/src/main/cpp/gpuclock'),
str(REPO / 'tools/tests/kgsl_busy_harness.c'), '-o', str(cls.binary)],
check=True, capture_output=True, text=True)
def test_ringbuffer_slots_and_busy_rule(self):
result = subprocess.run([str(self.binary)], capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn('PASS: kgsl memstore busy', result.stdout)
self.assertIn('PASS: kgsl memstore contexts', result.stdout)
self.assertIn('PASS: kgsl counter layout', result.stdout)
if __name__ == '__main__':
unittest.main()