SuperCompressed is a library for creating 'super compressed' textures from images and transcoding those super compressed textures to common DirectX, OpenGL, and Vulkan (compressed) pixel formats.
To measure the benefits I've tested the famous Sponza scene (with textures for Physically Based Rendering by Alexandre Pestana) in my toy engine.
Method | Total Texture Size on Disk | Total Texture Size in GPU Memory |
---|---|---|
Uncompressed | 218MB | 870 MB |
SuperCompressed | 70 MB | 672 MB |
Notes:
1. SuperCompressed textures are encoded using the default encoding speed and include mipmaps
2. SuperCompressed textures use the BC7_* format, uncompressed textures use the B8G8R8A8_* format
You can install SuperCompressed via the NuGet package manager, or directly via the command line via:
dotnet add package SuperCompressed
Warning Because of a native component, SuperCompressed works only on 64bit .NET 6 or higher applications, targeting Windows 7 or higher. In general this is not a problem since you are probably also targeting a native Graphics API if you are using this library.
SuperCompressed can load any JPG/PNG/TGA/BMP/PSD/GIF image for you. Alternatively you can bring your own image data as a byte array with 1, 2, 3 or 4 color components per pixel.
using System.IO;
using SuperCompressed;
var image = Image.FromStream(File.OpenRead(filename));
// or alternatively you can provide your own image data
var image = new Image(/*byte[]*/ data, ColorComponents.RedGreenBlue, width, height);
You can encode this image using the Encoder
class. SuperCompressed can work in several different modes, which are useful for different kinds of images.
Mode.SRgb
, for regular images, such as photos and diffuse texturesMode.Linear
, for linear data, such as height maps and look-up tablesMode.Normalized
, for normalized data, mostly normal maps
There are a few things you can configure:
- Mipmap generation and the filter used to create the mipmaps. See this document for an overview of supported filters and their effects.
- Encoding quality settings.
Warning Higher quality levels are significantly slower to encode and provide only a fractionally better image quality. In most scenarios the default quality level should suffice.
Span<byte> encoded = Encoder.Instance.Encode(image, Mode.SRgb, MipMapGeneration.Default /*Kaiser*/, Quality.Default);
You can then write the data to disk using the standard .NET APIs.
When you want to transcode the data to a GPU (compressed) pixel format you can use the Transcoder
class. You should first query how many images and mipmaps are in the data block that you provided.
Note Currently the SuperCompressed Encoder supports only one image per data block. This will change in the future, which is why the Transcoder already has support for multiple images.
var transcoder = Transcoder.Instance;
var images = transcoder.GetImageCount(encoded);
if (images <= 0)
return;
var imageIndex = 0;
var levels = transcoder.GetLevelCount(encoded, 0);
if (levels <= 0)
return;
var mipMapLevel = 0;
After you have determined what image and mipmap level you want to transcode. You can do so using the Transcode
method. Check the TranscodeFormats
enum for an up to date list of supported formats.
TranscodedTextureData transcoded = Transcoder.Instance.Transcode(encoded, imageIndex, mipMapLevel, TranscodeFormats.BC7_RGBA);
Note You will have to call the
Transcode
method multiple times to get all the mipmaps, if there are more than one.
The returned instance of the TranscodedTextureData
class contains all the information you need to upload the texture to your GPU, using the appropriate method of your graphics framework. For example, using the excellent Vortice.Windows C# bindings for DirectX.
using Vortice.Direct3D11;
using Vortice.DXGI;
// ....
var description = new Texture2DDescription
{
Width = transcoded.Width,
Height = transcoded.Height,
Format = ToDirectXFormat(transcoded.Format),
MipLevels = 1,
ArraySize = 1,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.ShaderResource,
CPUAccessFlags = CpuAccessFlags.None,
MiscFlags = ResourceOptionFlags.None
};
ID3D11Texture2D texture = ID3D11Device.CreateTexture2D(description);
ID3D11DeviceContext.UpdateSubresource(transcoded.Data, texture, 0, transcoded.Pitch, 0);
After you have uploaded the texture data you should dispose the TranscodedTextureData
instance to release native memory.
encoded.Dispose();
For more examples checkout the unit tests.
SuperCompressed uses the Basis Universal GPU Texture Codec for encoding and transcoding textures.
BasisU runs in UASTC
mode with the RDO optimizations enabled. After encoding the texture is further compressed using the zstd algorithm.
All this leads to files that are frequently less than 20% of the original file size. While also fast to decompress and transcode.
Image | MipMaps | Original size | Encoded size | Compressed size |
---|---|---|---|---|
image_with_alpha.tga | Full | 65536 bytes | 22133 bytes | 15964 bytes |
image_with_alpha.tga | None | 65536 bytes | 16484 bytes | 10737 bytes |
Images are loaded using the stb-image library.
SuperCompressed builds upon the following libraries
The logo used for the NuGet package is Video Card by Satawat Anukul from NounProject.com