Language: English | Українська
Per-app HWUI renderer control (Vulkan / OpenGL) for rooted Android devices. Pick the global renderer once, then override it for individual apps.
Author: EXLOUD
GitHub: https://github.com/EXLOUD
Render Switcher lets you choose which HWUI rendering backend Android uses: Vulkan (skiavk) or OpenGL (skiagl).
- A global default is applied at boot for every app.
- Per-app overrides let you run selected apps on the other renderer, without touching the rest of the system.
Everything can be managed from the built-in WebUI or from the skiactl command line tool.
- Root: Magisk, KernelSU or APatch
- Zygisk: enabled (Magisk built-in Zygisk) or a Zygisk implementation such as Zygisk Next / ReZygisk / NeoZygisk (KernelSU, APatch)
- Architecture: arm64-v8a, armeabi-v7a, x86, x86_64
- Open your root manager (Magisk / KernelSU / APatch)
- Install
Render_Switcher_v1.0.0.zipas a module - Make sure Zygisk is enabled
- Reboot
- Open the module's WebUI and add the apps you want to override
| Layer | Role |
|---|---|
| Global default | post-fs-data sets debug.hwui.renderer early in boot (default: skiavk) |
| Per-app | The Zygisk module patches the renderer properties in the app's private copy-on-write property page(s) right after specialize. The shared system property area is never modified, and no PLT hooks are used. |
Only packages listed in targets.conf are touched. Apps that are not targets are left alone and produce no log lines.
- Dashboard: global renderer switch, system options and a log of recent events
- Packages: searchable list of apps (User / System / All) with a renderer picker per app: Default, Vulkan or OpenGL
- English and Ukrainian interface
- System option Always GPU screen composition (
persist.skia.force_gpu)
Open it from your root manager's module list.
/data/adb/modules/render_switcher/bin/skiactl status| Command | Description |
|---|---|
skiactl status |
Show global renderer, paths and targets |
skiactl renderer get |
Print the global renderer |
skiactl renderer set <skiavk|skiagl> |
Set the global renderer |
skiactl target list |
List per-app targets |
skiactl target add <pkg> <renderer> |
Add an app override |
skiactl target set <pkg> <renderer> |
Change an app's renderer |
skiactl target get <pkg> |
Show an app's renderer and state |
skiactl target enable|disable <pkg> |
Toggle an override without removing it |
skiactl target remove <pkg> |
Remove an app override |
skiactl zygisk |
Check that Zygisk is active (ok / missing) |
skiactl logs |
Show module logs |
skiactl packages [user|system|all] |
List installed packages |
Example:
skiactl target add com.example.game skiagl
skiactl renderer set skiavkThe target app is force-stopped automatically, so the new renderer is used on its next launch.
Data is stored outside the module folder and survives module updates:
/data/adb/render_switcher/
├── config/
│ ├── targets.conf # package=renderer
│ └── settings.conf # GLOBAL_RENDERER=skiavk
├── state/
├── locks/
└── render_switcher.log
targets.conf format:
com.example.game=skiagl
com.example.app=skiavk
Allowed renderers: skiavk | skiagl.
📄 module.prop
📄 customize.sh # Installer
📄 post-fs-data.sh # Early boot: global renderer
📄 service.sh # Late boot
📄 uninstall.sh # Full cleanup
📂 bin/skiactl # CLI
📂 common/ # Shell libraries
📂 scripts/boot.sh # Boot logic
📂 config/ # Default configuration
📂 webroot/ # WebUI
📂 zygisk/ # Native module sources + build scripts
📂 docs/
📂 tests/ # Mock environment + tests
- Bootloop guard: if the device fails to finish booting several times in a row, the module disables enforcement automatically instead of keeping you stuck.
- Uninstall: removing the module deletes
/data/adb/render_switcher(targets, settings, logs) and resets the module's runtime properties. - Manual disable: create a
disablefile in the module folder, or turn the module off in your root manager.
- Use at your own risk. Not every app or GPU driver behaves well on both renderers, so switch one app at a time.
- Changing an override takes effect when the app is restarted.
- A reboot is recommended after installing or updating the module.
Per-app override is not applied
- Make sure Zygisk is enabled:
skiactl zygiskshould printok - Check that the app is listed and enabled:
skiactl target get <pkg> - Restart the app, then check
skiactl logs
Global renderer is wrong after boot
- Run
skiactl renderer getand checkskiactl logs - If the bootloop guard has triggered, enforcement is disabled until the module is reinstalled
WebUI shows "WebUI bridge unavailable"
- Open the WebUI from KernelSU, APatch or Magisk's module screen, not from a regular browser
This is the full source tree. The release ZIP additionally contains the compiled zygisk/<abi>.so libraries, which are not included here.
Requirements: Android NDK r25+, CMake 3.18+ (Linux / macOS / WSL / Windows)
cd zygisk
export ANDROID_NDK_HOME=$HOME/Android/Sdk/ndk/27.0.12077973 # your NDK path
./build.sh # all ABIs
./build.sh arm64-v8a armeabi-v7a # selected ABIs onlyOn Windows use build.bat. The build writes zygisk/arm64-v8a.so, armeabi-v7a.so, x86.so and x86_64.so.
Packaging: zip the module root (module.prop must be at the top level of the archive) and install it in your root manager. For a release build you can strip the libraries first (strip --strip-unneeded) and drop tests/, scripts/check.sh and the zygisk/ sources/build scripts from the archive.
Details: zygisk/BUILD.md, docs/RENDERER_ISOLATION_V1_0_0.md, docs/ARCHITECTURE.md.
Checks (host or device):
sh scripts/check.sh # static checks
sh tests/test_targets.sh # targets manager tests with mocked environment- GitHub: https://github.com/EXLOUD
Warning: This module changes system graphics properties. Make sure you understand the consequences before use.