Cross-platform support for Binomial's Basis Universal supercompressed GPU textures.
Use the same intermediate compressed texture assets (.ktx2
/.basis
) for all the libGDX backends and save on video memory leveraging the platforms' natively supported GPU compression.
GDX Texture Packer GUI has full support for encoding atlases as Basis Universal textures. Also, it provides a CLI interface to turn any PNG/JPG image into a KTX2/Basis texture.
You can also use the official command-line tool or build the encoder from the sources yourself.
There could be some other options (even potentially encoding in the browser), but I'm not aware of ATM, and it's worth googling.
Please read the "Texture format notes" and "Feature support notes" sections before encoding your textures.
Once added as a Maven dependency to libGDX project modules, the library is pretty easy to deal with, no global initialization calls are required in the code.
All the official libGDX backends are fully supported.
The release and snapshot Maven artifacts are available on the Maven Central repository
buildscript {
repositories {
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } // <-- optional, for snapshot versions
}
}
And then just add these records to the dependency section of your build.gradle
files.
Don't forget to set
gdxBasisuVersion
property with the correct library version (e.g. declaringgdxBasisuVersion=1.0.2
in the project's rootsettings.gradle
file).
dependencies {
api "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion"
api "com.crashinvaders.basisu:basisu-gdx:$gdxBasisuVersion"
}
dependencies {
runtimeOnly "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-desktop"
}
dependencies {
natives "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-armeabi-v7a"
natives "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-arm64-v8a"
natives "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-x86"
natives "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-x86_64"
}
It's highly recommended to use
robovm-metalangle
libGDX backend in favor of classicrobovm
, as this is the only way to unlock access to more compressed texture formats on Apple devices.
dependencies {
implementation "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-ios"
// This is highly recomended, otherwise you're stuck with only PVRTC1 textures.
implementation "com.badlogicgames.gdx:gdx-backend-robovm-metalangle:$gdxVersion"
}
As usual, the GWT module requires a bit more dance around. You need to declare an extra dependency and the sources for all the used jars.
dependencies {
implementation "com.crashinvaders.basisu:basisu-gdx-gwt:$gdxBasisuVersion"
implementation "com.crashinvaders.basisu:basisu-gdx-gwt:$gdxBasisuVersion:sources"
implementation "com.crashinvaders.basisu:basisu-gdx:$gdxBasisuVersion:sources"
implementation "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:sources"
implementation "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-web"
}
Don't forget to add a GWT module entry to your GdxDefinition.gwt.xml
file.
<module>
<inherits name='com.crashinvaders.basisu.BasisuGdxGwt'/>
</module>
The library provides transparent support for KTX2
/Basis
format textures using Ktx2TextureData
/BasisuTextureData
classes respectively. Each acts very similar to libGDX's implementation of ETC1TextureData
. The only difference is that libGDX doesn't know how to load .ktx2
/.basis
texture files out of the box, so you have to explicitly use the proper texture data class when creating a texture.
Texture myTexture = new Texture(new Ktx2TextureData(Gdx.files.internal("MyTexture.ktx2")));
From now on, it's safe to use the texture instance as usual, and it already should hold the data transcoded to the best suited native GPU compressed texture format for your platform.
If you're using AssetManager
to load game assets, you can easily integrate with it as well using BasisuTextureLoader
class.
// Register the texture loader for the ".ktx2" file extension.
assetManager.setLoader(Texture.class, ".ktx2", new Ktx2TextureLoader(assetManager.getFileHandleResolver()));
// Post your texture assets for loading as usual.
assetManager.load("MyTexture.ktx2", Texture.class);
// You can also use ktx2-based atlases.
// The Basis textures will be automatically resolved and loaded.
assetManager.load("MyAtlas.atlas", TextureAtlas.class);
// When the asset manager has finished loading, retrieve the assets as usual.
Texture myTexture = assetManager.get("MyTexture.ktx2", Texture.class);
TextureAtlas myAtlas = assetManager.get("MyAtlas.atlas", TextureAtlas.class);
You can use
gdx-texture-packer-gui
to create Basis based texture atlases.
Here's the list of the limitations you should be aware of when using this library (on top of regular libGDX backend limitations).
Most of the essential Basis transcoder features are exposed and implemented for Java (including file validation, transcoding to all the necessary formats, and KTX2/Basis file/image information lookup methods).
Transcoding from both intermediate formats (ETC1S low-medium quality and UASTC high quality) works as intended.
Mipmaps are supported, but not implemented on the libGDX integration side. The feature will be enabled in future releases.
There are four possible texture channel layout types:
The RGBA and RGB types are the general case. For these, there is the most variety of transcoder texture formats supported.
The rest two are considered niche types, and the transcoder has fewer options.
Please be aware, that at the moment the default texture format selector doesn't recognize RG and R texture types, and they will be treated as RGBA and RGB respectively. If you need support for them, provide a custom selector implementation.
Basis supports five different texture types:
Out of which support only for Regular 2D is implemented through BasisuTextureData for libGDX textures.
I'm not very familiar with 3D related formats like Cubemap array and skipped them for now. The Basis data for those is fully available from
basisu-wrapper
, only the libGDX support is missing. If you're in demand for those or may assist with the implementation, please come forward and open an issue with the details.
Simply put, when in doubt, go with .ktx2
files in favor of .basis
.
The long story. Those are simply two different texture file containers. Both are capable of containing the same Basis textures, but a little differently in terms of inner layout (like mp4
and mpv
in the world of video files). Historically .basis
was the only container, made specifically for the needs of the Basis Universal library.
Later on, Basis Universal was standardized by The Khronos Group and the new .ktx2
container came along.
If you're writing your own transcoder format selector or just wish to know which of the textures are supported on the specific platform, here's a little snippet that logs out the Basis Universal support report:
BasisuNativeLibLoader.loadIfNeeded(); // Make sure the Basis Universal natives are loaded.
Gdx.app.log(TAG, BasisuGdxUtils.reportAvailableTranscoderFormats(BasisuTextureFormat.ETC1S));
Gdx.app.log(TAG, BasisuGdxUtils.reportAvailableTranscoderFormats(BasisuTextureFormat.UASTC4x4));
Besides that, the library prints a bunch of useful information (like texture transcoding operations) to debug log. Check it on your device.
Basis Universal texture transcoder supports a bunch of very different GPU-compressed texture formats. Some of them impose very important limitations and cannot be used (cannot be transcoded to on runtime) unless all the requirements are met.
To have the widest possible native format support, it's highly recommended to encode intermediate Basis images that comply with ALL of these specifics.
To round up, always use square images with the power of two dimensions for Basis texture assets.
Basis textures can be easily transcoded to many other texture formats. This is great, but another challenge here is to transcode to the format that is most appropriate for the current runtime platform.
Here are all the criteria we should respect in making such a decision (the most important ones at the top):
The default texture format selector logic is implemented based on these. That way it should always pick the best available option. In case there are none of the texture formats are passing the check, the selector falls back to the uncompressed texture formats (RGBA8888/RGB888). Which are regular libGDX texture formats and have guaranteed support on all the platforms.
If you require a different selection strategy, you can always create a custom implementation for BasisuTextureFormatSelector
and use it selectively with BasisuTextureData#setTextureFormatSelector()
/Ktx2TextureData#setTextureFormatSelector()
methods or set it to be used as the default selector by updating the BasisuGdxUtils#defaultFormatSelector
static field.
Please be aware, that at the moment the default texture format selector doesn't recognize RG and R texture types and they will be treated as RGBA and RGB respectively. If you need support for them, provide a custom selector implementation.
The project uses Basis Universal C/C++ code and JNI wrappers to connect to libGDX cross-platform code. basisu-wrapper module provides a pure Java (no dependencies) abstraction layer over the native libs.
Read more about the module and the native library building notes on the module's page.