Why you can’t update Android’s GPU drivers like on your PC
If you’re a PC gamer, then you’re probably on top of keeping your GPU drivers updated, whether you have an AMD or NVIDIA GPU. Updating your graphics drivers can bring better performance and improved compatibility with new games, so there’s no real reason not to update them. Plus, it’s incredibly easy to update graphics drivers on Windows, either manually through Device Manager or automatically through a program offered by your GPU vendor.
Android, on the other hand, doesn’t offer an easy way to update your phone’s or tablet’s graphics drivers. In fact, short of acquiring root access, updating the graphics drivers on your own is nigh impossible on most devices. In this week’s edition of Android Dessert Bites, I’ll be diving into how GPU drivers are distributed, how Google and silicon vendors have tried to make them easier to update, and what third-party developers are doing to get around Android’s limitations.
Thanks for signing up for The Android Edge newsletter. Every week, my Android Dessert Bites column will share the latest news about the Android platform that matters to system engineers, app developers, and power users.
The challenges of updating GPU drivers on Android
Unlike AMD and NVIDIA, Qualcomm and Arm, the makers of the two most popular lines of GPUs in Android devices, have no direct relationship with consumers. You can’t build your own smartphone like you can a PC, so you’ll never directly give Qualcomm or Arm your money to buy a GPU. Instead, Qualcomm sells its GPUs to smartphone OEMs as part of a system-on-chip (SoC), while Arm licenses its GPU designs to silicon vendors like MediaTek which in turn design SoCs using those GPUs. Qualcomm is thus one step removed from the smartphone that ends up in the hands of consumers, while Arm is two steps removed.
Having no direct relationship with consumers, these mobile GPU makers have little incentive to provide the public with access to updated graphics drivers packages. Instead, GPU drivers are only directly distributed to smartphone OEMs as part of a Board Support Package (BSP). A BSP, as I previously explained in my article on the Google Requirements Freeze program, is a package that contains the source code and prebuilt binaries needed to get Android (or other operating systems) up and running on a particular chipset platform. BSPs contain the hardware abstraction layers (HALs) and drivers that the Android OS needs to communicate with your device’s hardware, including the GPU. This BSP code is contained within the read-only vendor partition of the device, and it can only be updated by the OEM with support from the SoC vendor.
SoC vendors update BSPs for the lifetime of a chipset, which is set by the SoC vendor but can be extended through paid software support licenses. Not every BSP update will contain updated GPU drivers, but when they do, OEMs can pull them from the updated BSP and then ship them to devices through an OTA update. If you know anything about how Android updates work, though, then you’ll probably spot the problem here. Android updates are fragmented, which means that graphics drivers are, too.
First of all, graphics drivers usually aren’t SoC specific. Qualcomm’s graphics drivers, for instance, support multiple Adreno GPUs. That means the graphics drivers contained within the BSP for one chipset may be compatible with the GPU in another chipset. However, one BSP may have an updated version of those graphics drivers while others do not, potentially leaving devices on other chipsets with older versions of the drivers. The graphics drivers for the Snapdragon 8 Gen 1, for example, directly support Snapdragon 845 and later platforms, or to be more precise, any Adreno 6xx or 7XX GPU.
The next big problem is that OEMs have to merge the updated graphics drivers from the BSP into their projects, test them, and then ship them out to devices via an OTA update. It’s not even a given that your particular Android device will actually get updates from the OEM, let alone an update that includes the newer graphics drivers. This means that even devices with the same chipset could have different graphics drivers.
Finally, because most Android devices ship without superuser access available to the user, there’s no way to update the GPU drivers yourself, meaning you’re totally dependent on the OEM you bought your device from. If you attain root access, then it’s possible to manually update the graphics drivers in the vendor partition, provided you somehow get your hands on the necessary files. Usually, this means pulling drivers from the firmware of another device, which is what some tinkerers on the XDA-Developers community do. It’s how I was able to update the graphics drivers on my end-of-life Pixel 3 XL, significantly boosting the phone’s graphics performance.
Those drivers were from a device with the Snapdragon 8 Gen 1 and were made flashable on my Pixel 3 XL with its Snapdragon 845 thanks to developer bylaws, who created a shim to translate the calls between the QtiMapper API used by newer Adreno graphics drivers and the gralloc API available on older Adrenos.
It’s not the end of the world if your device doesn’t have the latest graphics drivers, but it can certainly be annoying for users and game developers. As I previously mentioned, updated graphics drivers can bring better performance, as demonstrated by the uplift in 3DMark scores on my Pixel 3 XL. More importantly, newer graphics drivers may fix bugs in how some graphics API features are implemented. It’s already a challenge for game developers to target Android because of how vastly different each GPU is when it comes to feature support. Couple that with inconsistent driver versions between devices with the same GPU, and you’ll see why Android is a challenging platform to target from a game development perspective. Developers either need to maintain a dizzying array of workarounds for driver bugs or limit device compatibility, neither of which are good options. Google is making a baseline profile so game developers can more easily determine if a device supports a set of common Vulkan capabilities, but this still doesn’t solve the fragmentation problem.
What would reduce fragmentation, however, is making graphics drivers easier to update. Google recognized this years ago, and thanks to the system-vendor separation introduced with Project Treble, they were able to ship updated graphics drivers as part of a standard Android application package. Qualcomm recognized the potential of this, so the company started marketing that its latest chipsets support updating GPU drivers through an app store. However, this ultimately did little to improve the situation.
How Google and SoC vendors tried to make graphics drivers (more) updatable
Thanks to Project Treble standardizing the interface between HALs and the OS framework, it became easier to update the former without breaking compatibility with the latter. As a result, Treble made it easier to independently update graphics drivers, removing the need to tie graphics drivers updates to overall firmware updates. The challenge, however, is figuring out exactly how to deliver those graphics driver updates outside of a standard OTA update.
The answer is the Android application package (.APK files). APKs can contain shared libraries (.so files), and they’re also the preferred delivery format of every Android app store, including Google Play. So it’s no surprise that the APK was chosen as the mechanism to deliver updated graphics drivers, even over the APEX file format introduced alongside Project Mainline. After all, if you have an APK, you can create a listing on Google Play for your deliverable graphics drivers.
From a marketing perspective, this is great. Imagine a user opening up Google Play and seeing an update for their phone’s GPU drivers. Some of them will be thinking, “cool, that’s just like my PC! Great job, X!” I’m sure that’s the reaction that Qualcomm and Arm are hoping for, and it’s why several OEMs like Xiaomi, OPPO, and Samsung started to deliver GPU driver updates through app stores.
From a security perspective, though, this delivery mechanism raises some questions. How do you ensure that only approved APKs carrying validated and tested graphics drivers are delivered to users? If any random APK could update the graphics drivers on your phone, then your phone could become inoperable from improper drivers or put at risk of security issues. So how can OEMs securely deliver graphics driver updates using an app store?
First, the OEM needs to create and sign an empty updatable driver APK. This APK should have its own unique package name if the OEM wants to publish the app on an app store. This APK should be installed on the device in the /vendor/app path under the vendor partition, which is where the preinstalled graphics drivers are located.
Next, the OEM needs to create and sign a non-empty updatable driver APK. This APK should actually contain the updated graphics drivers that are intended to be delivered, and it should have the same package name and signature as the empty updatable driver APK. Thanks to signature verification, Android will reject any app with the same package name as the driver APK if it wasn’t signed by the OEM, thus ensuring that only the OEM can update the driver APK.
Since the vendor partition is read-only, the preinstalled graphics drivers aren’t directly overwritten when the driver APK is updated (and the same is true for the empty updatable driver APK in /vendor/app). The only way to overwrite those files is to update the vendor image, which means going through the standard OTA updating process.
To get around this, Google modified Android to add support for loading the graphics drivers libraries from three different sources: an updatable production driver contained within an APK, an updatable prerelease driver contained within an APK, or the system graphics driver that’s preinstalled in the vendor partition. The production driver is intended to be shipped on consumer devices and is contained within the APK that has the package name defined in the system property ro.gfx.driver.0. The prerelease driver, meanwhile, is intended for testing the driver before it’s pushed to consumers, and it is contained within the APK that has the package name defined in the prop ro.gfx.driver.1.
Android decides which drivers to load based on a whitelist system. The driver APK itself may contain a whitelist of packages that the OEM wants to automatically opt-in to use the driver, but developers can also go to Settings > Developer options > Graphics Driver Preferences to either choose the graphics driver that all apps should use or select which graphics driver to use on a per-app basis.
To minimize the harm from a buggy driver update, though, updatable drivers are never loaded for privileged or system apps. For the same reason, SoC vendors themselves don’t push updates to graphics drivers using this scheme. Imagine if Qualcomm pushed a driver update that broke compatibility with a bunch of games on an OEM’s product; that OEM would understandably be very upset with Qualcomm, which is probably why the silicon vendor only provides prerelease driver APKs and requires the OEM to unpack, stress test, repack, sign, and then push the driver update to production themselves.
Silicon vendors could solve the problem of GPU driver fragmentation themselves if they chose to set up app store listings for their driver APKs because they already include their own empty signed prerelease driver APKs in their BSP. All OEMs would have to do is not remove the empty signed prerelease driver APK provided by the silicon vendor. However, the silicon vendors’ desire to avoid liability over shipping buggy driver updates, which is reasonable to be fair, ends up undermining the entire updatable GPU driver initiative. The only thing that’s changed is the mechanism of delivering GPU driver updates — OEMs are still responsible for actually pushing updates, which means updated drivers won’t be rolled out uniformly. Most OEMs will just default to releasing driver updates with firmware OTAs regardless, because then they only have to test one combination of driver plus firmware rather than multiple combinations. Thus, we’re back to square one.
The Skyline developers’ wild solution to GPU driver inconsistency
Unsatisfied with the inconsistency of GPU drivers on Android, two of the developers behind Skyline, an in-development, open source Nintendo Switch emulator for Android, decided to take matters into their own hands. While it’s annoying for any Android game developer to deal with the many different GPUs and their varying capabilities, it’s even more annoying for emulator developers, who are forced to implement a ton of hacky workarounds to fix compatibility issues with games they have very little control over.
For example, Skyline developer Mark ‘Pixelylon’ recently told me about one of the major driver-related limitations the team is working to overcome. According to him, a lot of Switch games use a texture compression format called BCn. While Adreno GPUs have apparently had hardware support for BCn textures for years, Qualcomm reportedly didn’t expose support for BCn in their drivers until the Snapdragon 865 era. Mark speculates that this is due to the (now expired) patents on BCn, but regardless, this posed a problem for the Skyline developers, as users with older Adreno GPUs would have compatibility issues with games that use BCn.
Fellow Skyline developer bylaws, who I mentioned previously in this article, came up with a solution. He developed a library called BCeNabler, which patches Adreno GPU drivers to expose support for BCn textures. While this did work, it wasn’t a viable long-term solution for the Skyline developers, because it was difficult to patch Qualcomm’s proprietary Adreno GPU drivers to add anything past basic features.
While brainstorming what to do next, Mark joked about replacing, instead of patching, the graphics driver that Skyline loaded. Loading a different graphics driver isn’t a ludicrous idea — I spent a good chunk of this article talking about ways to do just that. However, the Skyline developers wanted to do this without support from Google, the OEM, or the SoC vendor and without needing root access. In other words, they wanted to inject an entirely different driver than the one(s) provided by the system, and they wanted to do it without needing any special privileges.
In a stroke of genius, developer bylaws figured out how to replace the userspace driver loaded by an app, provided that userspace driver is compatible with the kernel driver, which Mark says is “generally fairly stable and doesn’t change in a breaking way too often.” Bylaws extended BCeNabler to create Adreno Tools, a rootless library for applying Adreno GPU driver modifications or replacements. The library supports loading custom GPU drivers, enabling BCn textures, and redirecting file operations to enable accessing shader dumps and modifying the driver configuration file.
The way Adreno Tools works is quite complicated, but basically, it takes advantage of the fact that Android apps don’t load the graphics driver themselves. Instead, they use the Android driver (libvulkan.so) that loads the actual driver (libvulkan.adreno.so). Adreno Tools intercepts this process by injecting hooks into system libraries and then swapping in the new driver.
With Adreno Tools, the Skyline app can load Turnip, an open source Vulkan driver for Adreno GPUs, without needing root access. This is a major breakthrough for the Android emulation scene because any project dealing with Adreno GPU driver issues can bypass those issues by loading Turnip. Turnip is developed in the open by a team of developers on the Freedreno project, which is under the Mesa project. Because of its open nature, it’s easier to talk to the developers, report bugs, or submit patches, making Turnip an attractive workaround for all the driver-related issues that emulator developers face.
Tahlreth, the developer of AetherSX2, a PlayStation 2 emulator for Android, is already taking advantage of Adreno Tools, for example. According to the developer, using Turnip fixes emulation issues otherwise seen with the Adreno 660 GPU driver, but using it does degrade performance compared to the vendor driver. Still, Tahlreth offers builds of AetherSX2 with the Turnip driver built-in for users who have emulation problems stemming from their device’s drivers. Although the developer says that these builds can’t be uploaded to Google Play, I’m told by Mark from the Skyline project that they recently received a go-ahead from Google to ship their app with Turnip. Thus, it’s likely that Tahlreth can do the same for AetherSX2, so users won’t have to dig for a special version of the emulator on the project’s website.
If you browse the communities where emulation enthusiasts congregate, then you’ll have probably heard this piece of advice before: get a device with an Adreno GPU because they’re better for emulation. This advice stems from the belief that Adreno GPUs offer better performance, have fewer driver bugs, and support more features. For many years, this seemed to be the case based on my own experience and what I’ve heard from emulator developers, but Mark tells me that Arm’s Mali GPUs have closed the gap significantly when it comes to the quality of their drivers. Qualcomm’s Adreno GPU drivers have also “really picked up” in the last 2 years, only missing support for a handful of Vulkan extensions that are supported by Turnip. Meanwhile, the driver for Samsung’s new Xclipse 920 GPU in the Galaxy S22 supports even more extensions than the latest Mali and Adreno GPU drivers, though Mark tells me the Skyline project doesn’t really need any of those additional features.
When it comes to Qualcomm GPUs, though, Mark tells me that most of the features they need are supported by the Adreno GPU hardware, so all that’s needed is a driver update to get them. Adreno Tools and Turnip make that possible, so emulator developers who are trying to deal with driver issues on Android may want to take a look at what bylaws and Mark have cooked up. Once development of PanVK, the open source Vulkan driver for Mali GPUs, picks up steam, then the developers will see if Adreno Tools can be extended to support Mali devices. There hopefully won’t be any blockers preventing that from happening, but regardless, this is an exciting project to watch unfold.
Thanks for reading this week’s edition of Android Dessert Bites. I’d like to thank both Mark and bylaws from the Skyline project for their extensive help in drafting this article.