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.
Answer by NOAA_jeff
May 14, 2014 at 03:13 PM
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 (https://developer.nvidia.com/nvidia-texture-tools-adobe-photoshop), or you can use a free tool like GIMP (with its DDS plugin--https://code.google.com/p/gimp-dds/). 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: http://doc.51windows.net/Directx9_SDK/?url=/directx9_sdk/graphics/reference/DDSFileReference/ddsfileformat.htm
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;
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 * 256 + ddsBytes;
int width = ddsBytes * 256 + ddsBytes;
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);
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.
Very thorough answer :)
I have been attempting to use your method, but keep getting this error. Did you run into this at all?
UnityException: LoadRawTextureData: not enough data provided (will result in overread).
Utility.LoadTexture (System.Byte ddsBytes) (at Assets/Scripts/Utilities/Utility.cs:134)
The file is definitely a DDS file. It loads the header and accurately obtains the width and height.
I found out my above error was caused by trying to LoadRawTextureData into a Texture that I had mipmaps set to true. My DDS file had no mipmaps, so it was unable to deal with that.
Setting mipmaps to false when initializing my texture fixed the problem.
That's a subtle error - glad you figured it out!
The docs for LoadRawTextureData are nonexistent, so a lot of what we've figured out is by trial and error. I've been wary to try anything fancy, like loading into a cubemap or a texture with mipmaps. Good to know that it appears to support mipmaps.
It would be really nice for debugging to have the reverse method, e.g. ReadRawTextureData, which would return a byte stream so we know what Unity is expecting..
Glad you figured that out, Tylo. You'll also see that error message if your texture.format is mismatched. For example if texture.format=TextureFormat.ARGB32 (PNG) and yet your source DDS file is 24 bit (JPG with no alpha).
Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.
The best place to ask and answer questions about development with Unity.
To help users navigate the site we have posted a site navigation guide.
If you are a new user to Unity Answers, check out our FAQ for more information.
Make sure to check out our Knowledge Base for commonly asked Unity questions.
If you are a moderator, see our Moderator Guidelines page.
We are making improvements to UA, see the list of changes.
Answers and Comments
19 People are following this question.
Importing and configuring Normal Maps at runtime ?
Raycast project to texture at runtime
Substance Dynamic Texture Size
Grid background in runtime - with missing lines?
Apply a new texture at runtime to slowly cover the transform