basisu-wrapper

Java wrapper for Basis Universal texture transcoder native native code.

License

License

Categories

Categories

CRaSH General Purpose Libraries Utility
GroupId

GroupId

com.crashinvaders.basisu
ArtifactId

ArtifactId

basisu-wrapper
Last Version

Last Version

0.1.0
Release Date

Release Date

Type

Type

jar
Description

Description

basisu-wrapper
Java wrapper for Basis Universal texture transcoder native native code.
Project URL

Project URL

http://github.com/crashinvaders/gdx-basis-universal
Source Code Management

Source Code Management

http://github.com/crashinvaders/gdx-basis-universal

Download basisu-wrapper

How to add to project

<!-- https://jarcasting.com/artifacts/com.crashinvaders.basisu/basisu-wrapper/ -->
<dependency>
    <groupId>com.crashinvaders.basisu</groupId>
    <artifactId>basisu-wrapper</artifactId>
    <version>0.1.0</version>
</dependency>
// https://jarcasting.com/artifacts/com.crashinvaders.basisu/basisu-wrapper/
implementation 'com.crashinvaders.basisu:basisu-wrapper:0.1.0'
// https://jarcasting.com/artifacts/com.crashinvaders.basisu/basisu-wrapper/
implementation ("com.crashinvaders.basisu:basisu-wrapper:0.1.0")
'com.crashinvaders.basisu:basisu-wrapper:jar:0.1.0'
<dependency org="com.crashinvaders.basisu" name="basisu-wrapper" rev="0.1.0">
  <artifact name="basisu-wrapper" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.crashinvaders.basisu', module='basisu-wrapper', version='0.1.0')
)
libraryDependencies += "com.crashinvaders.basisu" % "basisu-wrapper" % "0.1.0"
[com.crashinvaders.basisu/basisu-wrapper "0.1.0"]

Dependencies

test (2)

Group / Artifact Type Version
junit : junit jar 4.13
com.badlogicgames.gdx : gdx-jnigen-loader jar 2.0.0-SNAPSHOT

Project Modules

There are no modules declared in this project.

GDX Basis Universal

Maven Central

Cross-platform support for Binomial's Basis Universal supercompressed GPU textures.

Use the same intermediate compressed texture assets (.basis) for all the LibGDX backends and save on RAM leveraging the platforms' natively supported GPU compression.

If you've never heard of Basis Universal project or unfamiliar with the "supercompressed texture" term, this is how it works...

The problem

When using traditional image formats (like PNG, JPG, TIFF, etc), they all get decoded to plain (uncompressed) RGB/RGBA representation before supplied to the OpenGL and loaded to the RAM. This is mostly fine, but once you get to the point when you need to use lots of simultaneously loaded huge textures you may easily run out of memory (especially on mobile devices). To give a better idea, a 4096x4096 RGBA32 (8 bits per channel) texture, being loaded into the GPU, holds roughly 64MB of memory. Stack a few of those and you're pretty much screwed.

The solution(?)

To address this issue many GPU manufacturers introduced their texture compression formats that are available on their hardware. Some may help you to chop down the memory footprint with the compression ratio of impressive 8 times, if you're agreed on a small trade-off in image quality (most of the texture compression formats are lossy).

The only downside is there is no one universal texture compression format that guarantees to work on every single platform/hardware. And if you're up for the game of supplying GPU compressed textures for your game, you have to pack your distributions with a whole lot of specific types of compressed textures per device hardware support for them (to mention, what in a way Unity practices for quite a while).

The compromise

To address this issue, Binomial LLC founded their Basis Universal project. The solution is to use one intermediate compressed texture format (supercompressed) and transcode it on runtime to one of the supported texture formats by the running platform. The Basis transcoder is capable of transcoding a Basis texture to one of the dozen native GPU compressed formats and covers all the modern platforms that way. There's a little overhead price for processing the texture data, but the transcoding operation is highly optimized and, to say, only should happen once upon asset loading.

The transcoder uses a number of transcoding tables to port data across different formats. Some of them pretty bulky and thus being dropped from compilation for specific platforms (e.g. there's no reason to support mobile-only formats like PVRTC1 on desktop). So this library maintains the selection logic as well, leaving you with a simple portable compressed texture format that will transparently work everywhere.

Basis Universal is backed by Google, open-source, and now available to everyone!

Cool, how do I turn my images into Basis textures?

You can use the command-line tool or build the encoder from the sources yourself.

As of now, there's no a web-based encoder or a desktop GUI tool to convert to the Basis format. But don't be discouraged, there is a number of convenient options that will be available for that purpose soon.

Please read the "Texture format notes" and "Feature support notes" sections before encoding your textures.

Using the library

Once added as 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.

Connecting dependencies (Gradle)

The release and snapshot Maven artifacts are available on 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. declaring gdxBasisuVersion=0.1.0 in the project's root settings.gradle file).

Core module

dependencies {
    api "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion"
    api "com.crashinvaders.basisu:basisu-gdx:$gdxBasisuVersion"
}

Desktop module (LWJGL, LWJGL3, Headless backends)

dependencies {
    runtimeOnly "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-desktop"
}

Android module (Android backend)

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"
}

iOS module (RoboVM backend)

dependencies {
    runtimeOnly "com.crashinvaders.basisu:basisu-wrapper:$gdxBasisuVersion:natives-ios"
}

Web module (GWT backend)

As usual, GWT backed 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"
    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>

Example code

The library provides transparent support for Basis format textures using BasisuTextureData class, which acts very similar to LibGDX's implementation of ETC1TextureData. The only difference is LibGDX doesn't know how to load .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 BasisuTextureData(Gdx.files.internal("MyTexture.basis")));

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 textures, you can easily integrate with it as well using BasisuTextureLoader class.

// Register the texture loader for the ".basis" file extension.
assetManager.setLoader(Texture.class, ".basis", new BasisuTextureLoader(assetManager.getFileHandleResolver()));
// ...
// Post your texture assets for loading as usual.
assetManager.load("MyTexture.basis", Texture.class);
// ...
// When the asset manager has finished loading, retrieve your texture as usual.
Texture myTexture = assetManager.get("MyTexture.basis", Texture.class);

Platform limitations

Here's the list of the limitations you should be aware of when using this library (on top of regular LibGDX backend limitations).

  • [Android] Due to NDK specs, Android 4.1 (API 16) is the minimum supported version.
  • [GWT] WebAssembly is available pretty much on every modern browser (compatibility chart). Just for reference, the support is enabled by default as of Firefox 52, Chrome 57, Opera 44, and Edge 16.

Basis Universal feature support notes

Most of the essential Basis transcoder features are exposed and implemented for Java (including file validation, transcoding to all the necessary formats, and Basis file/image information lookup methods).

Transcoding from both intermediate formats (ETC1S low-medium quality and UASTC high quality) works as intended.

Texture channel layout types

There are four possible texture channel layout types:

  1. RGBA - three-color component images with full alpha channel support
  2. RGB - fully opaque three-color component images
  3. RG or XY - luminance and alpha
  4. R - luminance or alpha

The RGBA and RGB types are the general case. For these, there are the most variety of transcoder texture formats supported.

The rest two are considered as 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 texture types

Basis supports five different texture types:

  1. Regular 2D - an arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels.
  2. Regular 2D array - an array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image has the same resolution and mipmap levels.
  3. Cubemap array - an array of cubemap levels, total # of images must be divisible by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps.
  4. Video frames - an array of 2D video frames, with optional mipmaps, # frames = # images, each image has the same resolution and # of mipmap levels.
  5. volume - a 3D texture with optional mipmaps, Z dimension = # images, each image has the same resolution and # of mipmap levels.

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.

Texture format notes

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.

  • PVRTC1 requires square textures with the power of two sides.
  • BC1_RGB and BC3_RGBA require textures with sides to be multiple of four (superseded by PVRTC1 requirements).

To round up, always use square images with the power of two dimensions for Basis texture assets.

Texture format resolution strategy

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):

  • Intermediate texture channel layout (RGBA/RGB/RG/R).
  • OpenGL/hardware support for the texture format.
  • Basis transcoder support for the texture format (to save up on the native library size, some of the transcoder formats are disabled per platform).
  • Whether the intermediate image meets the target format's requirements (see Texture format notes section).
  • The target format quality loss and/or transcoding speed.

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 fallbacks to the uncompressed texture formats (RGBA8888/RGB888). Which are pretty much regular LibGDX texture formats and have guaranteed support on all the platforms.

If your case requires a different selection strategy, you can always create a custom implementation for BasisuTextureFormatSelector and use it selectively with BasisuTextureData#setTextureFormatSelector() methods or set it to be used as the default selector by updating the BasisuTextureDatastatic#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.

Native dependencies

The project is using 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.

com.crashinvaders.basisu

Crashinvaders

Indie game development team

Versions

Version
0.1.0