Texture Compression Techniques and Performance in WebGL and Mobile Games
Texture compression dramatically reduces memory and CPU decode costs while allowing GPUs to read data directly, making formats such as ETC1/2, ASTC, PVRTC and S3TC essential for WebGL and mobile game graphics, though developers must manage lossy quality, size constraints, and platform support.
Background: Texture compression reduces memory and CPU decode time, and is GPU‑friendly, which is crucial for game development on mobile devices where memory is limited.
Traditional image formats (PNG, JPEG) require CPU decoding and store data uncompressed (e.g., a 1024×1024 JPEG occupies ~4‑5 MB). Compressed texture formats store data in fixed‑size blocks that can be read directly by the GPU; the same image using ASTC_4x4 occupies ~1.3 MB, saving more than 70 % memory.
Compressed textures also support random access, improving rendering efficiency.
In WebGL, texture compression is accessed via extensions such as WEBGL_compressed_texture_etc , WEBGL_compressed_texture_astc , and WEBGL_compressed_texture_pvrtc . Major game engines like pixi.js handle the details, allowing developers to use compressed textures like ordinary images.
Limitations include lossy compression, size constraints (e.g., power‑of‑two dimensions for some formats), larger file size than JPEG, and varying platform support.
Common compressed texture formats:
ETC1/ETC2 – widely supported on Android; ETC2 adds alpha support.
ASTC – high quality, flexible block sizes, not limited to power‑of‑two.
PVRTC – dominant on iOS, requires square power‑of‑two textures.
S3TC – primarily used on desktop.
KTX (Khronos Texture) is a container format that stores compressed texture data together with metadata, usable across OpenGL, OpenGL ES, and Vulkan.
Example KTX loader in JavaScript:
// KhronosTextureContainer
constructor(arrayBuffer, facesExpected, baseOffset = 0) {
this.arrayBuffer = arrayBuffer;
this.baseOffset = baseOffset;
// Test identifier
const identifier = new Uint8Array(this.arrayBuffer, this.baseOffset, 12);
if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 ||
identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 ||
identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D ||
identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
return;
}
const dataSize = Uint32Array.BYTES_PER_ELEMENT;
const headerDataView = new DataView(this.arrayBuffer, this.baseOffset + 12, 13 * dataSize);
const endianness = headerDataView.getUint32(0, true);
const littleEndian = endianness === 0x04030201;
this.glType = headerDataView.getUint32(1 * dataSize, littleEndian);
// ... other header fields ...
}Performance measurements on Pixel 4 and iPhone 11 Pro Max using pixi.js show:
Compressed textures can reduce memory usage by over 50 % compared to uncompressed images.
File size may increase, leading to longer download times.
GPU upload time for compressed textures is negligible.
In mini‑programs, WebGL extensions are mapped to corresponding OpenGL ES extensions (e.g., WEBGL_compressed_texture_astc → GL_KHR_texture_compression_astc_ldr ), enabling straightforward use of compressed textures.
Conclusion: Texture compression is essential for modern mobile graphics, offering significant memory and GPU upload benefits while requiring careful format selection per platform.
DaTaobao Tech
Official account of DaTaobao Technology
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.