My game lets users create custom content so loading a large number of textures quickly is important. I was wondering if it was possible to load dds files since they already have dxt info stored in them and it would be much faster than loading a jpg or png.
I’ve found a lot of good help on the Unity forums, so it is my turn to provide an answer.
DDS (DirectDraw Surface) files can be loaded in Unity at runtime using the Texture2D LoadRawTextureData() method. On our project at NOAA, we are loading hundreds of 4000 x 2000 jpg images and the time required to load each texture and compress it was roughly 600 ms (milliseconds). Too slow!
I found that the time required to load the DDS version of the same image (with DXT1 compression) is roughly 3 ms! Amazingly fast. So how can you get your images into DDS format, and how can you load them in Unity?
To save your images into DDS format, you can use Photoshop with the Nvidia texture tools plugin (Texture Tools Exporter | NVIDIA Developer), or you can use a free tool like GIMP (with its DDS plugin–Google Code Archive - Long-term storage for Google Code Project Hosting.). I used GIMP 2.8 with the plugin. Load your jpg/png image(s) into one of these tools, and then export it as a DDS file. Choose DXT1 compression if your image doesn’t have an alpha channel (e.g., JPG images) or DXT5 compression if your images do have alpha (e.g., PNG).
To load your DDS file in Unity, you’ll need to read the DDS header which is described in detail here: Microsoft DirectX 9.0 SDK
Basically, a DDS file has a 128 byte header which contains some useful information like the width and height of your image. You need to exclude the header bytes, however, when you pass the bytes to the LoadRawTextureData method (which was added in Unity 4.3 but sadly has not yet been documented). Here’s my code for loading a DDS file into a Texture2D within a class named TextureLoader:
public static Texture2D LoadTextureDXT(byte[] ddsBytes, TextureFormat textureFormat)
{
if (textureFormat != TextureFormat.DXT1 && textureFormat != TextureFormat.DXT5)
throw new Exception("Invalid TextureFormat. Only DXT1 and DXT5 formats are supported by this method.");
byte ddsSizeCheck = ddsBytes[4];
if (ddsSizeCheck != 124)
throw new Exception("Invalid DDS DXTn texture. Unable to read"); //this header byte should be 124 for DDS image files
int height = ddsBytes[13] * 256 + ddsBytes[12];
int width = ddsBytes[17] * 256 + ddsBytes[16];
int DDS_HEADER_SIZE = 128;
byte[] dxtBytes = new byte[ddsBytes.Length - DDS_HEADER_SIZE];
Buffer.BlockCopy(ddsBytes, DDS_HEADER_SIZE, dxtBytes, 0, ddsBytes.Length - DDS_HEADER_SIZE);
Texture2D texture = new Texture2D(width, height, textureFormat, false);
texture.LoadRawTextureData(dxtBytes);
texture.Apply();
return (texture);
}
So you just need to read the bytes from a DDS file and then pass them to this C# method. Here’s some simple code to do that:
`
byte bytes = System.IO.File.ReadAllBytes(ddsFilePath);
Texture2D myTexture = TextureLoader.LoadTextureDXT(bytes, TextureFormat.DXT1);
`
You should see 100x speedup in loading images into Texture2Ds. I hope this post helps someone.
In case anyone else stumbles on this, you can also detect what compression algorithm the DDS file is using by checking bytes in the header file.
ddsBytes[84], ddsBytes[85], ddsBytes[86], ddsBytes[87]
Each one of these bytes contains the ASCII values of a 4 letter code.
DXT1 and DXT5 are the most relevant in the case of Unity.
You can also use Unity’s crunch to process on the command line