Target texture compression formats in Android App Bundles

Textures are images that can be applied to the surface of a 3D model. Textures are also used by 2D renderers to draw elements such as sprites or backgrounds. This page describes popular texture compression formats used in games and how to target them in Android App Bundles. Read About Android App Bundles and Play Asset Delivery before starting this guide.

Background

GPUs typically support a set of texture compression formats. A texture compression format (or TCF) is a file format that is optimized for GPUs. The GPU loads and renders a texture quicker, and with less memory, than if it were using an array of RGBA values in memory. This support is done at the hardware level: the GPU manufacturer embeds components into the graphic cards chip that read, decompress, and render the supported formats.

The following are common texture compression formats on modern mobile hardware:

  • ASTC: Recent format designed to supersede prior formats. More flexible than previous formats due to support for various block sizes. Using this format is a good way to optimize the size of your game.
  • ETC2: Supported by all devices that support OpenGL ES 3.0 and higher. This includes nearly all active Android mobile devices.

These formats are supported by the following approximate percentages of Android devices:

Texture compression format Percentage of Google Play devices with support
ASTC >80%
ETC2 >95%

Desktop computer GPUs running Google Play Games for PC also support this format:

  • DDS or S3TC: Sometimes called BCn, DXTC or DXTn.

Older, no longer recommended texture compression formats include:

  • ETC1: Supported on most devices. This format has no transparency support, but games can use a second texture file for the alpha component.
  • PVRTC: Popular with iOS games, and also supported on some Android devices.

ETC1 support is only a requirement for games supporting very old legacy devices or select Android TV devices which don't support OpenGL ES 3.0 and higher.

A default format

With so many available formats (with varying levels of device support), you may not know which formats to use when building your game textures. As a safeguard, the app bundle format lets you select a default texture compression format for each asset pack. If a device does not support the other specified formats, assets using this default format are installed.

Unless you are targeting very old device hardware, ETC2 is a good choice for a default format. You should use the ETC2 formats that are guaranteed to be supported in OpenGL ES 3.0. These formats are also available in the Vulkan graphics API.

The ASTC format defines a variety of compression block sizes, which allow you to selectively trade reduced image quality for greater compression. Depending on the nature of the source art material, for a given texture you may choose a smaller or larger block size to maintain acceptable visual quality.

If your game supports Google Play Games for PC and uses Vulkan, you should include S3TC textures. The S3TC formats are supported by all desktop GPUs.

Build an app bundle

Google Play uses Android App Bundles to generate and serve optimized APKs for each user's device configuration, so users download only the code and resources they need to run your game. These optimized APKs include a single set of texture assets, formatted with the optimal compression format for the device.

If your game is not in Unity, use Gradle to build an app bundle. Advanced users may want to use bundletool.

If your game is in Unity, support for app bundles with Play Asset Delivery is available in Unity 2021.3 and higher. For more information, see the Unity documentation. You can use a Unity plugin to build an app bundle with lower versions of Unity.

Use Gradle

  1. Update the version of the Android Gradle plugin in your project's build.gradle file to 4.1 or higher (for example, com.android.tools.build:gradle:4.1.0).

  2. Determine the set of device types that you want to target for your game and the texture compression formats they support (for more information on formats, see Background).

  3. Build versions of your assets for each texture compression format from the previous step. This could involve generating sprite sheets using software like TexturePacker, or running a script that converts raw assets into those with a specific format (for example, astc-encoder).

  4. Create asset packs (see Build for C++ or Java), which contain your game assets and are used by Play Asset Delivery. For example, you can create one asset pack per level or asset packs for different parts of your game.

  5. Inside your asset packs, add directories for each texture compression format that you want to support. Add supported suffixes to the texture directory names that correspond to the texture compression format used for the contained files.

    Create a directory with no suffix in its name (for example, common/src/main/assets/textures/). In this directory, place the default format of your texture assets. This default format should be supported by most devices (for example, ETC1 or ETC2). If a device does not support the other specified formats (for example, PVRTC and ASTC in the table below), the Google Play Store installs this directory instead.

    Directory before Directory after
    common asset pack:
    common/build.gradle
    common/src/main/assets/textures/...
    common asset pack:
    common/build.gradle
    common/src/main/assets/textures/...
    common/src/main/assets/textures#tcf_astc/...
    common/src/main/assets/textures#tcf_pvrtc/...
    level1 asset pack:
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1 asset pack:
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1/src/main/assets/textures#tcf_astc/...
    level1/src/main/assets/textures#tcf_pvrtc/...
    level2 asset pack:
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2 asset pack:
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2/src/main/assets/textures#tcf_astc/...
    level2/src/main/assets/textures#tcf_pvrtc/...
  6. Update your app's build.gradle file to enable the splitting of your asset packs per textures.

    // In the app build.gradle file:
    android {
        ...
        bundle {
            texture {
                enableSplit true
            }
        }
    }
    
  7. In Android Studio, select Build > Generate Signed Bundle / APK, or launch the Gradle task from the command line to generate your bundle.

Use the Google Play Unity plugin

Get the Unity plugin (or package) for Play Asset Delivery to create an app bundle with texture-targeted asset packs.

Prepare the assets

To prepare your texture assets for building an app bundle, do the following:

  1. Package your scene and assets into multiple Unity AssetBundles.

  2. Determine the set of device types that you want to target for your game and the texture compression formats they support (for more information on formats, see Background).

  3. Modify your game's build script to generate the AssetBundles multiple times, once for each texture format that you want to support. See the following example script:

    using Google.Android.AppBundle.Editor;
    using UnityEditor;
    
    public class MyBundleBuilder
    {
       [MenuItem("Assets/Build AssetBundles TCF variants")]
       public static void BuildAssetBundles()
       {
           // Describe the AssetBundles to be built:
           var assetBundlesToBuild = new []
           {
               new AssetBundleBuild
               {
                   assetBundleName = "level1-textures",
                   assetNames = new[] {"level1/character-textures", "level1/background-textures"}
               },
               new AssetBundleBuild
               {
                   assetBundleName = "level2-textures",
                   assetNames = new[] {"level2/character-textures", "level2/background-textures"}
               }
           };
    
           // Describe where to output the asset bundles and in which formats:
           var outputPath = "Assets/AssetBundles";
           var defaultTextureFormat = MobileTextureSubtarget.ETC2;
           var additionalTextureFormats = new[] { MobileTextureSubtarget.ASTC, MobileTextureSubtarget.PVRTC }
           var allowClearDirectory = true;
    
           // Generate asset bundles:
           AssetBundleBuilder.BuildAssetBundles(
               outputPath,
               assetBundlesToBuild,
               BuildAssetBundleOptions.UncompressedAssetBundle,
               defaultTextureFormat,
               additionalTextureFormats,
               allowClearDirectory);
    
           // While in this example we're using the UI to configure the
           // AssetBundles, you can use the value returned by BuildAssetBundles
           // to configure the asset packs, if you want to build the bundle
           // entirely using the scripting API.
       }
    }
    
  4. Verify that each texture asset is output in a directory with the correct suffix in its name (for example, #tcf_astc).

    Verify that a directory with no suffix in its name is output (for example, Assets/AssetBundles/). This directory contains the default format of your texture assets. This default format should be supported by most devices (for example, ETC2). If a device does not support the other specified formats (for example, ASTC in the code from the previous step), then the Google Play Store installs this directory instead.

    Assets/AssetBundles.meta
    Assets/AssetBundles/AssetBundles
    Assets/AssetBundles/AssetBundles.manifest
    Assets/AssetBundles/AssetBundles.manifest.meta
    Assets/AssetBundles/AssetBundles.meta
    Assets/AssetBundles/samplescene
    Assets/AssetBundles/samplescene.manifest
    Assets/AssetBundles/samplescene.manifest.meta
    Assets/AssetBundles/samplescene.meta
    Assets/AssetBundles/texturesbundle
    Assets/AssetBundles/texturesbundle.manifest
    Assets/AssetBundles/texturesbundle.manifest.meta
    Assets/AssetBundles/texturesbundle.meta
    Assets/AssetBundles#tcf_astc.meta
    Assets/AssetBundles#tcf_astc/AssetBundles
    Assets/AssetBundles#tcf_astc/AssetBundles.manifest
    Assets/AssetBundles#tcf_astc/AssetBundles.manifest.meta
    Assets/AssetBundles#tcf_astc/AssetBundles.meta
    Assets/AssetBundles#tcf_astc/samplescene
    Assets/AssetBundles#tcf_astc/samplescene.manifest
    Assets/AssetBundles#tcf_astc/samplescene.manifest.meta
    Assets/AssetBundles#tcf_astc/samplescene.meta
    Assets/AssetBundles#tcf_astc/texturesbundle
    Assets/AssetBundles#tcf_astc/texturesbundle.manifest
    Assets/AssetBundles#tcf_astc/texturesbundle.manifest.meta
    Assets/AssetBundles#tcf_astc/texturesbundle.meta
    
  5. Select Google > Android > Assets Delivery.

  6. Click Add Folder to add the folder containing your default asset bundles. These bundles are installed on devices that don't support the other formats you define.

    Make sure to set the Delivery mode for the AssetBundle.

    Unity AssetBundle Delivery default format

  7. Click Add Folder to add a folder containing AssetBundles built for another format (for example, ASTC). Repeat as needed.

    Make sure to set the Delivery mode for each AssetBundle.

    Unity AssetBundle Delivery ASTC format

Build

Select Google > Build Android App Bundle to launch the Unity build of your game. It also packages the AssetBundles into multiple asset packs where each AssetBundle name is converted to a single asset pack.

(Advanced) Use bundletool

For more information on bundletool, see Build an app bundle using bundletool.

To create the app bundle, do the following:

  1. Download bundletool from its GitHub repository.

  2. Determine the set of device types that you want to target for your game and the texture compression formats they support (for more information on formats, see Background).

  3. Build versions of your assets for each texture compression format from the previous step. This could involve generating sprite sheets using software like TexturePacker, or running a script that converts raw assets into those with a specific format (for example, astc-encoder).

  4. Create asset packs (see Build for C++ or Java), which contain your game assets and are used by Play Asset Delivery. For example, you can create one asset pack per level or asset packs for different parts of your game.

  5. In your different asset packs, add supported suffixes to the texture directory names that correspond to the texture compression format used for the contained files.

    Create a directory with no suffix in its name (for example, common/src/main/assets/textures/). In this directory, place the default format of your texture assets. This default format should be supported by most devices (for example, ETC1 or ETC2). If a device does not support the other specified formats (for example, PVRTC and ASTC in the table below), the Google Play Store installs this directory instead.

    Directory before Directory after
    common asset pack:
    common/build.gradle
    common/src/main/assets/textures/...
    common asset pack:
    common/build.gradle
    common/src/main/assets/textures/...
    common/src/main/assets/textures#tcf_astc/...
    common/src/main/assets/textures#tcf_pvrtc/...
    level1 asset pack:
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1 asset pack:
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1/src/main/assets/textures#tcf_astc/...
    level1/src/main/assets/textures#tcf_pvrtc/...
    level2 asset pack:
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2 asset pack:
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2/src/main/assets/textures#tcf_astc/...
    level2/src/main/assets/textures#tcf_pvrtc/...
  6. Add the TCF dimension to the app bundle metadata file (BundleConfig.json). Use TEXTURE_COMPRESSION_FORMAT for the value field:

    {
      ...
      "optimizations": {
        "splitsConfig": {
          "splitDimension": [
          ...
          {
             "value": "TEXTURE_COMPRESSION_FORMAT",
             "negate": false,
             "suffixStripping": {
               "enabled": true,
               "defaultSuffix": ""
              }
          }],
        }
      }
    }
    

    Set suffixStripping.enabled to true to remove the suffix (for example, #tcf_astc) from the directory names when generating the asset packs. This enables your game to read files from a well-known directory name (such as level1/assets/textures). Some game engines can detect the format of a file, so your game can be indifferent about the format of texture assets that it was installed with.

    suffixStripping.defaultSuffix specifies the default directory suffix when bundletool generates a standalone APK for devices running Android 5.0 (API level 21) and lower. In the example table earlier, the default version of the texture assets is installed on these devices; this is the intended behavior in most cases.

  7. Build the app bundle:

    bundletool build-bundle --config=BUILD_CONFIG.json \
      --modules=level1.zip,level2.zip,common.zip,base.zip --output=MY_BUNDLE.aab
    

Verify the contents of the app bundle

If you haven't already, download bundletool from the GitHub repository.

Verify the contents of the output app bundle by building APKs from it and inspecting them:

bundletool build-apks --output=APKS.apks --bundle=MY_BUNDLE.aab
zipinfo APKS.apks

The output should be similar to the following:

toc.pb
splits/base-master.apk
splits/base-armeabi_v7a.apk
splits/…
asset-slices/level1-astc.apk
asset-slices/level1-other_tcf.apk
asset-slices/level1-pvrtc.apk

These names indicate that the TCF targeting is properly applied. If you extract the contents of a level APK (for example, asset-slices/level1-astc.apk), you can verify that only one directory named textures is present.

Test the app bundle

Connect a device and install the applicable asset packs:

bundletool install-apks --apks=APKS.apks

This command installs only the asset packs that meet the device's specification. These specifications include ABI, screen density, language, and the most applicable texture compression format. This operation simulates what is done by the Google Play Store for your published game.

To verify that the correct asset packs were installed, do any of the following:

  • Use the bundletool extract-apks command to output the apks installed for your device into a directory and then inspect this directory.

    1. Extract the specification of your device:

      bundletool get-device-spec --output=MY_DEVICE_SPEC.json
      
    2. Run bundletool extract-apks with this device specification:

      bundletool extract-apks --apks=APKS.apks --device-spec=MY_DEVICE_SPEC.json \
          --output-dir out
      
    3. List the files in the out directory and verify that the proper asset packs are installed. Asset pack names are suffixed by the texture format name (for example, level1-astc.apk).

  • Add log statements in your game that output the texture format when loading a texture.

  • Generate a test set of textures (for example, replace a texture with a single bright color for a given format). Run the game and verify that it is present.

If your app contains on-demand or fast-follow asset packs, use the local testing solution for asset delivery.

Supported suffixes for texture directory names

Google Play understands the following suffixes used in texture directory names:

  • #tcf_astc for Adaptive Scalable Texture Compression (ASTC)
  • #tcf_atc for ATI texture compression (ATC)
  • #tcf_dxt1 for S3 DXT1 texture compression (DXT1)
  • #tcf_latc for Luminance-Alpha texture compression (LATC)
  • #tcf_paletted for generic paletted texture compression
  • #tcf_pvrtc for PowerVR texture compression (PVRTC)
  • #tcf_etc1 for Ericsson texture compression (ETC1)
  • #tcf_etc2 for Ericsson texture compression 2 (ETC2)
  • #tcf_s3tc for S3 texture compression (S3TC)
  • #tcf_3dc for ATI 3Dc texture compression (3Dc)

Google Play serving rules

Google Play inspects the OpenGL extension strings used by the device and the OpenGL version supported by the device. Google Play uses this information to determine the correct texture format to deliver to the device from the Android App Bundle.

Google Play delivers the first format, in the order listed in the following table, that is supported by the device.

If none of the texture formats in the App Bundle are supported by the device, Google Play delivers the texture formats packaged in the default format. (Unless you are targeting specific device hardware, ETC1 or ETC2 are good choices for a default format.) For information on how to package assets in the default format, see Use bundletool or Use the Google Play Unity plugin.

If assets have not been packaged in a default format, Google Play marks the app as not available for the device. In this case, users cannot download the app.

Format (designated in tcf_xxxx) Supported on devices with OpenGL extension string
astc GL_KHR_texture_compression_astc_ldr
pvrtc GL_IMG_texture_compression_pvrtc
s3tc GL_EXT_texture_compression_s3tc
dxt1 GL_EXT_texture_compression_dxt1
latc GL_EXT_texture_compression_latc
atc GL_AMD_compressed_ATC_texture
3dc GL_AMD_compressed_3DC_texture
etc2 Not applicable. The device must support OpenGL ES version 3.0 or later.
etc1 GL_OES_compressed_ETC1_RGB8_texture
paletted GL_OES_compressed_paletted_texture