A KernelSU / APatch module that swaps the Android GPU driver (Vulkan / EGL) at runtime using bind mounts — no OverlayFS, no Magic Mount, no boot-time root required.
Built for late-loaded KernelSU where module system directories are never magic-mounted, but works with any root solution (GKI, LKM, APatch, Magisk).
┌─────────────────────────────────────────────────────────┐
│ Driver .zip files (Adreno-Tools-Drivers format) │
│ dropped into /sdcard/Download │
│ │ │
│ ▼ │
│ WebUI: pick a driver → engine extracts, maps, │
│ bind-mounts over /vendor paths │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ ▼ ▼ ▼ │
│ init (pid 1) zygote64 / zygote surfaceflinger │
│ └──── mount namespaces ────────────────────┘ │
│ │ │
│ ▼ │
│ Every app forked from zygote afterwards │
│ resolves the custom driver files │
└─────────────────────────────────────────────────────────┘
The vendor partition is never written — dm-verity/AVB stay intact, and a reboot restores everything to stock. Rollback is one tap.
- Driver-zip management — drop any Adreno-Tools-Drivers
package (Turnip or Qualcomm) into
/sdcard/Download, pick it from the WebUI. Swap drivers without reflashing. - Global mode — every app started after apply gets the custom driver
- Per-app mode — only listed packages get it (banking, camera, etc. stay stock)
- Auto-detection — finds every Vulkan loader path (legacy hw module,
ICD manifests, any
vulkan.*.soin sphal search dirs) - SELinux-safe — files are relabeled to the stock vendor label;
bundled
sepolicy.ruleas fallback; never sets the device permissive - Self-diagnosing —
doctorcommand checks the entire chain: mounts, labels, permissions, dependencies, mapped libs, avc denials - Non-blocking WebUI — driver selection returns in ~2s; staging and mounting run in background with live progress feedback
./build.sh # produces live_gpu_driver-<ver>.zip
adb push live_gpu_driver-*.zip /sdcard/
Install from KernelSU / APatch manager → Modules → Install from storage.
- Get a driver — download from
Adreno-Tools-Drivers
(Turnip or Qualcomm) and push to
/sdcard/Download/ - Open the WebUI — manager → module → WebUI button
- Tap "Use" on the driver you want
- Force-stop + relaunch your apps to pick up the new driver
- After every reboot — WebUI → Apply (or the Action button)
adb shell su -c 'getprop ro.hardware.egl; getprop ro.hardware.vulkan'
adb shell su -c 'ls /vendor/lib64/hw/ /vendor/lib64/egl/ | grep -iE "vulkan|egl|adreno|mali|pvr"'
The engine auto-detects all Vulkan loader targets. For EGL/GLES, it
auto-maps over every existing egl/libEGL_* entry regardless of vendor
tag.
| Driver type | System-wide | Notes |
|---|---|---|
| Mesa Turnip | ✅ works | Best compatibility; targets stock kgsl across generations |
| Qualcomm blob (any generation) | ✅ works | Auto-fixes file permissions and SELinux labels; verified with Quest 3 (Adreno 740) and Ray-Ban Display (v863.1) not*-only packages on 8 Gen 2 |
| Qualcomm blob (missing dependencies) | Some packages ship without all required libs (e.g. hexlp); doctor reports unresolvable dependencies automatically |
The engine auto-fixes the two issues that previously broke system-wide
Qualcomm blob loading: restrictive file permissions (zip-stored 0600 →
corrected to 0644) and wrong SELinux labels (vendor_file →
same_process_hal_file). Both are verified on-device.
In the WebUI, switch to Per-app and add packages (typed or picked from installed apps). A small watcher catches app starts via logcat and mounts the driver into each app process's own mount namespace only.
# Full diagnostic (mounts, labels, deps, mapped libs, avc, selftest)
adb shell su -c 'sh /data/adb/modules/live_gpu_driver/bin/webui_ctl.sh doctor'
# Status (JSON)
adb shell su -c 'sh /data/adb/modules/live_gpu_driver/bin/webui_ctl.sh status'
# Manual apply (same as Action button)
adb shell su -c 'sh /data/adb/modules/live_gpu_driver/action.sh'
# Restore stock
adb shell su -c 'sh /data/adb/modules/live_gpu_driver/action.sh unmount'The test suites run on Linux in isolated user+mount namespaces (no device needed, host untouched):
test/run_tests.sh # global mode (40 assertions)
test/run_perapp_tests.sh # per-app watcher (20 assertions)
test/run_webui_tests.sh # WebUI backend + driver zips (74 assertions)All state files are written atomically; concurrent engine invocations are safe (regression-tested by polling status at 5 Hz during an async apply).
- Kernel-side GPU driver (kgsl/msm_drm) can't be swapped live — the userspace blob must be compatible with your kernel's kgsl.
- Mounts are in-memory — that's what makes rollback instant. Re-apply after each reboot.
- Staging cost: a payload file with a new name in a directory
forces a merged copy of that whole directory. New names are therefore
placed in
/vendor/lib64/egl(in the linker's sphal search path) rather than/vendor/lib64, which keeps staging at ~43 MB instead of ~1 GB. Replacing a stock lib needs no staging (per-file bind).doctorreports the staged size; the engine warns above 256 MB. - Never delete the module directory (or
.staging*) while a driver is active — the mounts point at those files. Usedriver-clear,action.sh unmount, or reboot first. - Running apps keep the old driver until restarted (mount namespaces are copied at fork time).
- surfaceflinger keeps the old driver until restart (
RESTART_SF=1in config for immediate restart with a screen flicker). - Some Qualcomm packages ship incomplete dependency sets (e.g. missing
hexlp). The dependency checker reports unresolvable libraries; use a package that ships everything, or Turnip.
MIT — see LICENSE