When targeting modern iOS and Android devices in Unity, ASTC is often the first texture format people reach for. It supports RGB and RGBA, offers block sizes from 4x4 through 12x12, and gives you a practical way to trade image quality for memory and bandwidth.
But "use ASTC" is not a complete production strategy.
You still need answers to questions such as:
- Which iPhones and Android devices can actually use ASTC?
- Does Android support depend on the OS version, OpenGL ES, Vulkan, or the GPU?
- Is ASTC 6x6 unfriendly to GPUs because 6 is not a power of two?
- Is it reasonable to use 6x6 for every texture?
- What happens when Unity loads ASTC on unsupported hardware?
- How is a PNG or JPEG file size related to runtime texture memory?
This article focuses on those production decisions rather than treating ASTC as simply "the newer mobile compression format." The terminology and Unity settings are based mainly on Unity 6.4 documentation as of July 2026. If a later Unity version changes the UI or delivery behavior, use the documentation for that version.
The short version is:
- On Apple hardware, A8 is the hardware boundary for LDR ASTC, but that is not the same thing as the minimum device you can ship to with a current Unity project.
- For Android phones and tablets distributed through Google Play, a robust default is an AAB with ETC2 as the default texture format and ASTC as an additional format through Texture Compression Targeting.
- ASTC 6x6 is not a problem just because 6 is not a power of two. It is a standard hardware texture format with 128-bit blocks.
- Do not force every texture to 6x6. A useful starting point is 6x6 for general albedo/sprites, 4x4-5x5 for text, SDFs, important normals and high-detail UI, and 8x8 for low-frequency backgrounds when visual testing allows it.
If you only want the actionable settings, skip to A practical project policy near the end. If you are specifically interested in why 6x6 works well on GPUs, read Is ASTC 6x6 a problem because it is not power-of-two?
First, separate three different meanings of "image format"
Texture discussions often mix three layers:
- Source files: PNG, JPEG, PSD, TIFF, EXR. Their file size does not directly determine GPU memory. A 1024x1024 texture expanded to RGBA32 is about 4 MiB without mipmaps even if its PNG is tiny.
- Build/delivery containers: APK, AAB, AssetBundle, Addressables bundles, LZ4, LZMA. These affect download size, storage, and loading, but do not change the GPU footprint of a texture once uploaded.
- GPU texture formats: ASTC, ETC2, PVRTC, BCn, RGBA32. These are designed for random-access sampling by the texture hardware and are the focus of this article.
Keeping the layers separate prevents mistakes such as "a smaller PNG means less VRAM" or "LZ4-compressing an AssetBundle reduces texture memory."
What ASTC changes
ASTC (Adaptive Scalable Texture Compression) covers RGB/RGBA, linear/sRGB, multiple bit rates, LDR and supported HDR paths, and a range of square and non-square block footprints.
The important point is that ASTC 4x4, 6x6, and 8x8 are not encoder quality presets. They specify how many texels are represented by one 128-bit compressed block.
ASTC block size and bit rate
Every 2D ASTC block is 128 bits, or 16 bytes. Increasing the number of texels represented by that block lowers the amount of information available per texel and therefore lowers the memory footprint.
The theoretical bit rate is:
bits per pixel = 128 / (block width * block height)
A useful subset of Unity's options looks like this:
| ASTC | Texels per block | Bit rate | 1024x1024, no mips | Typical starting use |
|---|---|---|---|---|
| 4x4 | 16 | 8.00 bpp | 1.00 MiB | High-quality UI, text, visible detail |
| 5x5 | 25 | 5.12 bpp | ~657 KiB | High-quality general textures |
| 6x6 | 36 | 3.56 bpp | ~457 KiB | General quality/memory balance |
| 8x8 | 64 | 2.00 bpp | 256 KiB | Backgrounds and low-frequency imagery |
| 10x10 | 100 | 1.28 bpp | ~166 KiB | Aggressive size reduction |
| 12x12 | 144 | 0.89 bpp | ~116 KiB | Very aggressive, specialized use |
Because dimensions such as 1024 are not divisible by every ASTC block size, the real base-level size uses rounded-up block counts:
ceil(width / blockWidth)
* ceil(height / blockHeight)
* 16 bytes
For a sufficiently large 2D texture, a complete mip chain is often roughly 4/3 the base level. Small mip levels introduce extra rounding because every level still needs at least one compression block.
In this article, MipMap refers to the Unity setting, Mip to an individual level, and "mip chain" to the whole sequence.
Is ASTC 6x6 a problem because it is not power-of-two?
No. The fact that 6 is not a power of two is not, by itself, a disadvantage.
ASTC deliberately defines many non-power-of-two footprints: 5x4, 5x5, 6x5, 6x6, 8x5, 8x6, 10x5, 10x6, 10x8, 12x10, and others.
The old advice that "GPUs prefer powers of two" contains historical truth, but it refers to a different level of the problem.
Texture dimensions and compression block dimensions are different things
Older GPUs and APIs often restricted the width and height of the whole texture to power-of-two values such as 256, 512, or 1024. NPOT textures sometimes had restrictions around mipmaps or repeat modes.
The 6 in ASTC 6x6 is not the texture width. It is the texel footprint represented by one compressed block.
If a GPU exposes ASTC 6x6 support, its texture hardware is designed to address and decode that footprint. You do not need to avoid it because you imagine a general-purpose integer divide-by-six happening in shader code.
Conceptually, a texel at (x, y) belongs to a block like this:
blockX = floor(x / 6)
blockY = floor(y / 6)
localX = x mod 6
localY = y mod 6
That pseudocode describes the mapping, not the internal circuit implementation. The key layout property is that the compressed block itself is always 16 bytes. Once the block index is known, the storage address is a fixed-stride calculation:
blockAddress = baseAddress
+ (blockY * blocksPerRow + blockX) * 16
So ASTC 6x6 is not a raw 6x6 pixel array with an awkward six-byte alignment. Thirty-six texels are encoded into a 16-byte block. In memory, the compressed data is still a grid of fixed-size blocks.
Does 4x4 have exactly the same performance as 6x6?
That is a different question, and the answer is not guaranteed to be yes.
Different GPU generations can have different texture-unit throughput, cache behavior, anisotropic filtering costs, and decoder implementations. You should not assume every ASTC footprint has identical throughput on every GPU.
But in production, the more important trade-off is usually:
- 6x6 uses substantially less texture memory than 4x4.
- Less compressed data can reduce external memory bandwidth pressure.
- The same cache capacity can cover more texels.
- Image quality is lower because the same 128 bits describe more texels.
So choose between 4x4 and 6x6 based on quality, memory, bandwidth, and measurements, not because one block width happens to be a power of two.
Can a device support ASTC 4x4 but not 6x6?
That is not the normal way ASTC LDR capability is modeled. For example, when Vulkan's textureCompressionASTC_LDR feature is enabled, the standard LDR ASTC block formats are part of the supported feature set; 6x6 is not a separate optional extension.
Unity still lets you query specific formats such as TextureFormat.ASTC_6x6, which is useful for diagnostics, custom compressed data, and driver investigation. Think of the hardware capability as ASTC LDR support, while Unity's diagnostic API can inspect individual formats.
What about texture edges and NPOT sizes?
A 1024x1024 texture does not divide evenly into 6x6 blocks. It needs 171 blocks in each dimension, with the edge blocks covering a partially valid area.
That is expected behavior. Block compression rounds block counts up.
The overhead matters more for tiny textures. A 7x7 texture compressed as ASTC 6x6 needs 2x2 blocks, or 64 bytes. Its effective bit rate is about 10.45 bpp, far above the 3.56 bpp headline number for large images.
For many tiny textures, consider:
- Sprite Atlas or Texture Atlas packing
- R8, RG16, or even RGBA32 for very small data where appropriate
- grouping by loading and batching behavior, not just per-texture format
- disabling mipmaps for UI that does not need them
ASTC supporting NPOT textures does not mean every tiny texture is storage-efficient on its own.
ASTC support on iOS
Apple hardware is easier to reason about than the Android ecosystem. The hardware boundary for LDR ASTC is the A8 generation.
A8 is the hardware boundary for LDR ASTC. It is not the minimum shippable device list for a Unity 6.4 project.
Unity 6.4's Player requirements specify iOS/iPadOS 15 or newer and A8 SoC or newer, and the project must also satisfy its Deployment Target, Xcode version, SDK dependencies, and current App Store requirements.
A rough hardware view is:
| Apple GPU generation | Example SoC | LDR ASTC | Note |
|---|---|---|---|
| Apple1 | A7 | No | Early Metal generation |
| Apple2+ | A8+ | Yes | LDR ASTC available |
| Apple6+ | A13+ | HDR ASTC | Treat separately from LDR |
The iPhone 6 and 6 Plus used A8, while the iPhone 5s used A7. Those are SoC-generation examples, not current Unity 6.4 deployment examples. The iPhone 6 family cannot satisfy an iOS 15+ requirement.
The practical rule is to separate:
- hardware capability — whether the SoC can sample ASTC, and
- shipping eligibility — whether the OS, Unity, Xcode, SDKs, and App Store rules still allow that device into your supported set.
If every device you actually ship to supports LDR ASTC, using ASTC as the standard iOS texture format is a natural choice.
ASTC support on Android
Android cannot be summarized as "Android version X or newer supports ASTC." The actual capability depends primarily on the GPU and driver.
The Android Developers documentation gives rough Google Play ecosystem support figures of more than 80% for ASTC and more than 95% for ETC2. Those numbers do not predict your own audience, but they illustrate the important point: ASTC is mainstream, yet an ASTC-only Android strategy can still exclude a meaningful device segment.
Unity 6.4 documentation lists examples of GPU generations commonly associated with ASTC support, including:
- Qualcomm Adreno 4xx and later / Snapdragon 415 and later
- Arm Mali T624 and later
- NVIDIA Tegra K1 and later
- PowerVR GX6250 and later
Treat those as useful guidance, not a substitute for the actual API and driver capability on the device.
OpenGL ES 3.0 does not imply ASTC
ETC2 is a standard requirement in OpenGL ES 3.0. ASTC LDR under OpenGL ES 3.0/3.1 is typically exposed through extensions such as GL_KHR_texture_compression_astc_ldr, so the API version alone is insufficient.
OpenGL ES 3.2 is different: the specification includes the 2D LDR ASTC formats in its required compressed texture formats. That still does not imply HDR ASTC, and it does not tell you what Unity imported, which graphics API Unity actually selected, or which asset slice Google Play delivered.
A safer summary is:
- OpenGL ES 3.0 -> ETC2 can be expected.
- OpenGL ES 3.0 -> ASTC is not guaranteed.
- OpenGL ES 3.1 -> many modern devices support ASTC, but do not infer it from the version alone.
- OpenGL ES 3.2 -> 2D LDR ASTC is required by the specification.
- In Unity -> verify the active graphics environment and the relevant format support on real devices.
Vulkan support does not imply ASTC either
Vulkan exposes a textureCompressionASTC_LDR feature. A device being able to run Vulkan does not mean that feature must be enabled.
The correct mental model is "Vulkan device plus ASTC feature," not "Vulkan equals ASTC."
What happens when Unity hits unsupported ASTC?
Unity can fall back by decompressing an unsupported texture into an uncompressed format. Unity 6.4's Android documentation describes ASTC falling back to RGBA32 on unsupported hardware.
That can keep the app running, but it is not free:
- CPU work during loading
- longer first-use or scene-transition stalls
- transient memory pressure
- much higher texture memory usage
- higher memory bandwidth
- increased risk of out-of-memory termination on constrained devices
ASTC 6x6 is about 3.56 bpp. RGBA32 is 32 bpp: roughly nine times as much data.
For a 2048x2048 texture with no mipmaps, ASTC 6x6 is roughly 1.78 MiB after block rounding, while RGBA32 is 16 MiB. With a full mip chain, you are roughly around 2.4 MiB versus 21.3 MiB.
One texture may be survivable. Dozens of characters, backgrounds, UI atlases, and effects can turn that fallback into a serious memory problem.
Treat runtime fallback as a last safety net, not a normal production path.
Use Texture Compression Targeting on Google Play
This section is about Android phones and tablets. If you also target Google Play Games for PC from the same delivery setup, review S3TC requirements separately instead of assuming ASTC + ETC2 covers that environment.
With an Android App Bundle, Google Play can deliver different texture compression variants to different devices. In Unity 6.4, you configure multiple entries in Texture Compression Formats.
A robust order for broad mobile distribution is:
1. ETC2 // default
2. ASTC // additional optimized format
Why put ETC2 first if you prefer ASTC? The first entry is the default fallback, not the universally preferred delivery format. Google Play can still deliver the ASTC variant to ASTC-capable devices while ETC2 covers the safer fallback path. Each device downloads one matching variant, reducing the need for RGBA32 runtime expansion.
ETC2 is not visually equivalent to ASTC 6x6, so QA the ETC2 slice independently for alpha-heavy sprites, gradients, normals, and packed masks. Multiple variants also increase import/build time and CI storage, and per-texture Platform Overrides should be audited because they can defeat the global policy.
Remote Addressables and custom AssetBundles are a separate problem
Google Play's Texture Compression Targeting does not automatically rewrite arbitrary bundles hosted on your own CDN.
If you maintain ASTC and ETC2 variants for remote Addressables or custom AssetBundles, treat them as separate content lines. Include the format in identifiers such as:
- Addressables Profile
RemoteLoadPath- catalog
- bundle name
- URL
- cache key
Do not publish different binary contents behind the same URL and hope the cache sorts it out. Keep Content Update flows format-specific as well.
Direct APK distribution
Texture Compression Targeting assumes AAB delivery. Without AAB, Unity 6.4 uses only the first entry in Texture Compression Formats for the APK. If ETC2 is first and ASTC second, the APK is ETC2; an ASTC APK needs ASTC first in a separate build.
For enterprise distribution, alternative stores, MDM, or testing APKs, choose a common ETC2 build, separate ASTC/ETC2 APKs, format-specific bundles, or an explicitly ASTC-only hardware requirement. ASTC-only APKs are reasonable in controlled device fleets but risky for broad consumer distribution.
Is ASTC 6x6 a good default?
Often, yes—as a starting point, not as a rule.
ASTC 6x6 is about 3.56 bpp, roughly 44% of ASTC 4x4's bit rate and around one ninth of RGBA32. That makes it a practical baseline for many game textures.
Consider 4x4 or 5x5 for
- small text embedded in UI
- thin-line icons
- high-contrast edges
- faces, eyes, and other highly scrutinized character areas
- complex gradients mixed with fine detail
- important normal maps
- packed masks whose numeric precision matters
- SDF/font atlases
- low-resolution textures shown very large on screen
6x6 is often a good starting point for
- general character and environment albedo
- typical sprites
- VFX color textures
- UI backgrounds and panels
- general RGBA sprites
ASTC does not double in size because a texture has alpha; RGB and RGBA use the same block footprint. But complex color plus complex alpha still has to fit into the same 128 bits, so artifacts can become more visible.
Consider 8x8 or larger for
- blurred backgrounds
- skies, clouds, smoke, and other low-frequency imagery
- distant assets shown small on screen
- particles where some loss of noise/detail is acceptable
- downloadable content where size pressure is severe
At 8x8, 10x10, and 12x12, inspect edge smearing, block artifacts, gradients, and alpha transitions carefully. "Background" is not enough justification by itself if the camera can zoom in.
Starting presets by texture role
| Role | Starting point | What to inspect |
|---|---|---|
| General albedo / sprite | ASTC 6x6 | Faces, silhouettes, alpha edges |
| Hero character / high-quality UI | ASTC 4x4-5x5 | Thin lines, eyes, text, gradients |
| UI background / large panel | ASTC 6x6-8x8 | Banding, rounded corners, translucency |
| Distant or blurred background | ASTC 8x8 | Block artifacts when camera approaches |
| Normal map | ASTC 4x4-6x6 | Lighting distortion; ensure sRGB is off |
| Packed ORM-like mask | ASTC 4x4-6x6 | Channel errors and threshold behavior |
| Font / SDF | ASTC 4x4 or compare uncompressed | Small-size readability and edges |
| Very small texture | Prefer atlasing | Block rounding and wasted mips |
These are comparison starting points, not universal answers.
Treat normal maps and packed masks more carefully than color
Compression errors in color textures can be visually hidden. Errors in normals and masks can turn into lighting artifacts or threshold changes.
For normal maps, disable sRGB and use Unity's normal-map import path. Prefer 4x4 or 5x5 when close-up lighting, specular response, or fine normal detail matters; 6x6 can still be sufficient for distant or low-detail surfaces.
For packed metallic/occlusion/roughness/smoothness masks, remember that channels carry numeric meaning. If a shader branches around a threshold such as 0.5, compression error can change behavior even when the packed image still looks acceptable. Visualize important channels independently during QA.
Do not confuse ASTC LDR and ASTC HDR
The ASTC used for normal PNG/JPEG-derived game textures is primarily LDR ASTC. HDR ASTC has separate hardware requirements.
On Apple hardware, LDR ASTC starts with the A8-era Apple2 GPU family, while HDR ASTC starts later with the A13-era Apple6 family. On Android, HDR support likewise needs its own OpenGL ES extension or Vulkan capability.
This implication is unsafe:
The device supports ASTC 6x6
therefore
it must support HDR ASTC 6x6
Check LDR and HDR separately. HDR fallback can be even more expensive if Unity has to expand into a format such as RGBA Half.
Texture compression can affect runtime performance
ASTC is not only about package size. On mobile GPUs, external DRAM traffic is expensive in performance and power. Sampling compressed textures can reduce resident texture memory, memory traffic, and cache pressure.
Moving from ASTC 4x4 (8 bpp) to 6x6 (~3.56 bpp) cuts the compressed data footprint to about 44%, but that does not imply a similar FPS gain. If the frame is CPU-, shader-, fill-rate-, or synchronization-bound, texture compression may barely change frame time.
Measure texture memory, bandwidth when available, loading hitches, sustained thermals, and download/install size—not only FPS.
Encoder time is different from runtime decode
Higher-quality ASTC encoding can increase import and build time because the encoder searches harder for a good 128-bit representation. Runtime devices are not recompressing textures every frame; the GPU samples already-compressed blocks.
A useful split is:
- Block size -> memory footprint and quality
- Encoder quality -> build time and final compression quality
- Runtime sampling -> GPU texture hardware
- Unsupported fallback -> possible CPU work and uncompressed memory growth
Using faster encoding during development and higher-quality encoding for release is reasonable, but final visual QA should use release-equivalent settings.
Runtime diagnostics in Unity
SystemInfo.SupportsTextureFormat can tell you whether the current device/graphics environment supports a specific TextureFormat.
using UnityEngine;
public static class TextureFormatDiagnostics
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
private static void LogSupport()
{
bool astc4x4 = SystemInfo.SupportsTextureFormat(TextureFormat.ASTC_4x4);
bool astc6x6 = SystemInfo.SupportsTextureFormat(TextureFormat.ASTC_6x6);
bool astc8x8 = SystemInfo.SupportsTextureFormat(TextureFormat.ASTC_8x8);
bool etc2Rgba = SystemInfo.SupportsTextureFormat(TextureFormat.ETC2_RGBA8);
Debug.Log(
$"GPU={SystemInfo.graphicsDeviceName}, " +
$"API={SystemInfo.graphicsDeviceType}, " +
$"ASTC4x4={astc4x4}, " +
$"ASTC6x6={astc6x6}, " +
$"ASTC8x8={astc8x8}, " +
$"ETC2_RGBA8={etc2Rgba}");
}
#endif
}
Keep this as development/QA diagnostics rather than permanent release logging.
Also understand what it proves. It tells you about device capability; it does not prove that a specific loaded asset is currently resident as ASTC.
For actual asset verification, combine it with:
- the delivered Google Play slice
Texture2D.formattexture.graphicsFormat- Unity Profiler / Memory Profiler
When you need to distinguish sRGB vs linear, LDR vs HDR, or specific usages, use SystemInfo.IsFormatSupported(GraphicsFormat, GraphicsFormatUsage) as the more precise API.
And note that decoding a PNG at runtime with something like LoadImage does not magically create an ASTC-resident texture. If you need GPU-compressed runtime content, you must ship platform-appropriate compressed data and load it using the correct format.
What to verify on real devices
Do not finish ASTC validation in the Editor preview. Test representative iOS hardware, an ASTC Android path, and an ETC2/fallback Android path.
- Delivered variant: for AABs, confirm which slice reached the device with bundletool, Play Console testing, logs, or distinguishable test assets.
- Runtime memory: use Unity Profiler/Memory Profiler and platform tools. If a texture expected to be ASTC 6x6 occupies RGBA32-like memory, investigate fallback or import settings.
- Loading hitches: compare controlled builds so format expansion, bundle loading, shader warm-up, and image decoding are not confused with one another.
- Image quality: test at real resolution, render scale, mip settings, camera distance, UI scale, and post-processing. Validate the ETC2 slice separately from the ASTC slice.
A practical project policy
iOS
- Treat A8 as the LDR ASTC hardware boundary, not the current minimum shipping device.
- In Unity 6.4, also account for iOS/iPadOS 15+, Deployment Target, Xcode/SDK, and App Store requirements.
- If the actual device set supports ASTC, start general textures at 6x6, important UI/hero assets at 4x4-5x5, and compare 8x8 for low-frequency backgrounds.
- Treat HDR ASTC as a separate capability.
Android / Google Play
- For phones/tablets, prefer AAB + Texture Compression Targeting with ETC2 as default and ASTC as an additional format.
- Verify both ASTC and ETC2 slices and QA their image quality separately.
- If Google Play Games for PC is also in scope, investigate S3TC separately.
- Do not rely on RGBA32 runtime expansion as a normal shipping path.
Content production
Use 6x6 as an initial preset, not a rule. Manage role-based settings with presets or an AssetPostprocessor, treat normals/masks/SDFs/tiny textures separately, and perform final QA with release-equivalent encoding while checking memory, loading, and image quality together.
Conclusion
ASTC is one of the most practical standard texture formats for modern mobile GPUs, but the production answer is not "set everything to ASTC 6x6."
On iOS, A8 is the LDR ASTC hardware boundary, while the actual shipping device set is determined by the intersection of Unity's Player requirements, your Deployment Target, Xcode/SDK constraints, and App Store requirements. On Android, support is fundamentally a GPU/driver capability question, and a portion of the Google Play ecosystem still needs a non-ASTC path.
That is why ETC2 as the safe default plus ASTC as an additional Google Play Texture Compression Targeting format is a strong general Android policy.
And ASTC 6x6 being non-power-of-two is not something to fear. Six-by-six describes the texel footprint represented by a 128-bit fixed-size compressed block. The real decision is the trade-off between quality, GPU memory, bandwidth, supported devices, and fallback cost.
Start with 6x6 for general textures, move important assets toward 4x4-5x5, test 8x8 on low-frequency content, and validate both the delivered format and the real memory/quality result on devices. That gives you the benefits of ASTC without turning format support or aggressive compression into a hidden production risk.
References
- Unity 6.4 Manual: Choose a GPU texture format by platform
- Unity 6.4 Manual: System requirements
- Unity 6.4 Manual: Google Play delivery requirements
- Unity 6.4 Scripting API: SystemInfo.SupportsTextureFormat
- Unity 6.4 Scripting API: SystemInfo.IsFormatSupported
- Unity 6.4 Scripting API: TextureImporterFormat
- Android Developers: Target texture compression formats in Android App Bundles
- Apple: Metal Feature Set Tables
- Khronos: OpenGL ES 3.2 Specification
- Khronos Vulkan Specification: Features
- Khronos Vulkan: VK_EXT_texture_compression_astc_hdr
- Khronos: KHR_texture_compression_astc_hdr
- Arm ASTC Encoder: Format overview
- Arm: Adaptive Scalable Texture Compression User Guide
Top comments (0)