Android Opengl Texture Compression

Android OpenGL Texture Compression

There are mainly four texture compression types supported on Android:

  • ETC1 (Ericsson texture compression). This format is supported by all Android phones. But, it doesn't support an alpha channel, so can only be used for opaque textures.
  • PVRTC (PowerVR texture compression). Supported by devices with PowerVR GPUs (Nexus S, Kindle fire, etc.).
  • ATITC (ATI texture compression). Used in devices with Adreno GPU from Qualcomm (Nexus One, etc.).
  • S3TC (S3 texture compression). This texture compression is used in the NVIDIA chipset integrated devices (Motorola Xoom, etc.)

More detailed information here and here.

In short, if your textures don't have alpha, you can use ETC1. If they do have alpha, and you want to support all devices, you must have your textures compressed in the other three types and load them according to the device.

How to use:

  1. Compress your png files (You can use a tool like ETC-Pack, PVRTexTool, ATI Compressonator, Nvidia Texure Tools according to the type of texture) and add to your project assets.

  2. Determine which extensions are available in the device, if you're not using ETC1:

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

    String s = gl.glGetString(GL10.GL_EXTENSIONS);

    if (s.contains("GL_IMG_texture_compression_pvrtc")){
    //Use PVR compressed textures
    }else if (s.contains("GL_AMD_compressed_ATC_texture") ||
    s.contains("GL_ATI_texture_compression_atitc")){
    //Load ATI Textures
    }else if (s.contains("GL_OES_texture_compression_S3TC") ||
    s.contains("GL_EXT_texture_compression_s3tc")){
    //Use DTX Textures
    }else{
    //Handle no texture compression founded.
    }

    }
  3. Load compressed texture as raw data.

  4. Use glCompressedTexImage2D instead of glTexImage2D:

    public void onDrawFrame(GL10 gl) {

    ....

    gl.glCompressedTexImage2D(GL10.GL_TEXTURE_2D, level, internalformat, width,
    height, border, imageSize, data);

    }

Texture compression strategy for Android OpenGL ES that caters for alphas

Rather than providing alpha textures in 4 different compression formats, a better approach is to split the alpha from the images and use ETC1 for the color and possibly even the alpha part of the images. The tricky part is that you must separate the alpha from each image into separate texture files and then write a fragment shader for OpenGL ES that samples from these texture pairs using two samplers and re-combines them. The shader code would be like this:

uniform sampler2D   sampler_color;
uniform sampler2D sampler_alpha;
varying vec2 texCoord;

void main()
{
vec3 vColor = texture2D(sampler_color, texCoord);
float fAlpha = texture2D(sampler_alpha, texCoord);
gl_FragColor = vec4(vColor, fAlpha);
}

This will work on over 99% of Android devices and allow all of your alpha textures to be compressed, which not only makes them smaller, but they will load faster too.

Loading compressed textures in Android OpenglES 2.0

You want to load PVRTC 4bpp RGBA texture via glCompressedTexImage2D? You should use COMPRESSED_RGBA_PVRTC_4BPPV1_IMG instead of GL10.GL_PALETTE4_RGBA8_OES.

IMG_texture_compression_pvrtc
https://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt

I'm not sure but it seems Android GLES20 doesn't have those constants, so you need to define it.

// PowerVR Texture compression constants
public static final int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;
public static final int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01;
public static final int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
public static final int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;

And you should check glGetError after glCompressedTexImage2D calling. The value of glGetError should be GL_NO_ERROR.

What texture compression formats are available on Android-WebGL?

What texture compression formats are available on Android-WebGL?

It's up to the GPU/driver/browser on the device

You can check a particular device by using

 gl.getSupportedExtensions();

As of 2017-07-15 checking webglstats.com it claims

  • no android devices support s3tc
  • 2% of devices support pvrtc
  • 97% of devices support etc1
  • 97% of devices support etc
  • 48% of devices support atc
  • 46% of devices support astc

I'm 99% sure it's not fully accurate as I'm pretty sure the NVidia Shield supports s3tc but maybe no one using an NVidia Shield has ever visited a site that uses webglstats.com or maybe the number of users is so small it rounds down to 0%

What is my best option for choosing a texture compression format that will work on modern Android phones?

What you should arguably do is support all of etc1, etc, atc, astc. Store you assets in folders or with extensions etc

assets/etc1/image1
assets/etc1/image2
assets/astc/image1
assets/astc/image2
...

or

assets/image1.etc1
assets/image2.etc1
assets/image1.astc
assets/image2.astc
...

Then at startup query which format the user's device supports and choose the best one (for some definition of best like maybe smallest size)

Example

// in order of best compression to worst
const compressions = [
{ ext: "astc", name: "WEBGL_compressed_texture_astc" },
{ ext: "etc1", name: "WEBGL_compressed_texture_etc1" },
...
];
let imageExtension = ""; // pick a default?
for (let i = 0; i < compressions.length; ++i) {
const info = compressions[i];
const ext = gl.getExtension(info.name);
if (ext) {
imageExtension = info.ext;
break;
}
}

function loadImage(baseUrl) {
...
img.src = baseUrl + imageExtension;
...
}

Or other variations of the above where you figure out what the user's device needs and then use that.

How can I create and mipmap compressed textures for OpenGL ES 2.0 on board (Android)?

There's nothing in OpenGL or OpenGLES for generating compressed textures.

Generally, creating compressed textures is slow, and people almost always generate them offline. For runtime generated textures, people usually keep them in basic formats like 8888, 565 or 4444 because the trade-off of compression-time vs rendering efficiency is just not worth it.

That said, it's possible to find open source code for generating most compressed formats, and there's nothing stopping you from plugging that code into your game code. For ETC1 there's this (github version). For S3TC (aka DXT) there's squish (or many others).

Obtaining Comprehensive list of Android Devices support of Texture Compression Formats

There is no such list (at least not trustworthy) because

  • there are several Android devices released every day
  • there are more texture compression formats than you mentioned in your list (ETC2/EAC, ASTC, LATC, ...)
  • there are more 3D HW vendors than you mentioned in your list (ARM Mali, Intel HD Graphics, Vivante, ...)

You should never rely on relation between 3D HW vendor & particular texture compression support, instead of this
you should check for supported texture compressions run-time (after OpenGL/ES initialization) by looking for extension sub string in string returned by glGetString(GL_EXTENSIONS),
e.g "GL_EXT_texture_compression_s3tc" for S3TC, "GL_IMG_texture_compression_pvrtc" for PVRTC.

android opengl es 1.1 texture compression on the fly

with help from Arne Bergene Fossaa i get to this solution:

int size = m_TexBitmap.getRowBytes() * m_TexBitmap.getHeight();
ByteBuffer bb = ByteBuffer.allocateDirect(size); // size is good
bb.order(ByteOrder.nativeOrder());
m_TexBitmap.copyPixelsToBuffer(bb);
bb.position(0);

ETC1Texture etc1tex;
// RGB_565 is 2 bytes per pixel
//ETC1Texture etc1tex = ETC1Util.compressTexture(bb, m_TexWidth, m_TexHeight, 2, 2*m_TexWidth);

final int encodedImageSize = ETC1.getEncodedDataSize(m_TexWidth, m_TexHeight);
ByteBuffer compressedImage = ByteBuffer.allocateDirect(encodedImageSize).order(ByteOrder.nativeOrder());
// RGB_565 is 2 bytes per pixel
ETC1.encodeImage(bb, m_TexWidth, m_TexHeight, 2, 2*m_TexWidth, compressedImage);
etc1tex = new ETC1Texture(m_TexWidth, m_TexHeight, compressedImage);

//ETC1Util.loadTexture(GL10.GL_TEXTURE_2D, 0, 0, GL10.GL_RGB, GL10.GL_UNSIGNED_SHORT_5_6_5, etc1tex);
gl.glCompressedTexImage2D(GL10.GL_TEXTURE_2D, 0, ETC1.ETC1_RGB8_OES, m_TexWidth, m_TexHeight, 0, etc1tex.getData().capacity(), etc1tex.getData());

bb = null;
compressedImage = null;
etc1tex = null;

i know about the ETC1Util.compressTexture and ETC1Util.loadTexture, but they were giving corrupted textures. good thing is that i went from 100MB down to 26MB with native memory consumption. but this solution is slow as hell. and even though it is done on a separate thread with min priority, the rendering thread is totally blocked. is there a more efficent way? or do i have to create these ETC1 textures on the first run on a new device and save them to SD card for later reuse?

Android NDK. Loading ETC1 compressed texture from .PVR file

Unfortunately there doesn't seem to be much info on this out there, however there are a couple of things. http://www.brokenteapotstudios.com/android-game-development-blog/2011/05/loading-opengl-textures-in-c-and-etc1-texture-compression.html provides most of the info needed. If you want to use the header version, the header format is described here http://www.mhgames.org/2012/03/android-development-loading-etc1-textures-from-ndk/

Hopefully that's helpful :)



Related Topics



Leave a reply



Submit