As my XNA project grows larger and larger in terms of content, I try to isolate the bottlenecks of content compilation. Because even if theoretically all content builds are incremental, everytime you play with your content pipeline assemblies, everything needs to be rebuilt… and that gets seriously annoying if your build takes more than 5 minutes.
I noticed that the built-in TextureImporter isn’t exactly fast, even for very small textures (under 256×256). Looks like it uses a lot of “legacy” Direct3D code that takes a while to initialize and spends more time doing that than processing the texture. So I tried making my own simplistic TextureImporter to handle 2D textures with standard RGBA colors, but fast.
I started with the built-in System.Drawing.Bitmap class, but it doesn’t support a very wide array of image formats. I then tried using the managed wrapper for FreeImage and Tao.DevIl, both seemed a little backwards or clunky for the small amount of work I needed it to do. Then I gave DevIL.NET a shot and it does exactly what I want : load any image, get a GDI+ Bitmap object. Perfect!
Here’s the code of my FastTextureImporter, which only supports 2D textures, only parses the first mip level, and assumes a RGBA format, which should actually be fine for most textures. It supports the DDS format, which allows much more format options, but should not be used on 3D or Cube textures, and would also lose DXT compression.
using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using Microsoft.Xna.Framework.Content.Pipeline; using Microsoft.Xna.Framework.Content.Pipeline.Graphics; using Color=Microsoft.Xna.Framework.Graphics.Color; namespace MyContentPipeline.Importers { [ContentImporter(".bmp", ".cut", ".dcx", ".dds", ".ico", ".gif", ".jpg", ".lbm", ".lif", ".mdl", ".pcd", ".pcx", ".pic", ".png", ".pnm", ".psd", ".psp", ".raw", ".sgi", ".tga", ".tif", ".wal", ".act", ".pal", DisplayName = "DevIL.NET Texture Importer", DefaultProcessor = "TextureProcessor")] public class FastTextureImporter : ContentImporter<Texture2DContent> { public override Texture2DContent Import(string filename, ContentImporterContext context) { var content = new Texture2DContent { Identity = new ContentIdentity(new FileInfo(filename).FullName, "DevIL.NET Texture Importer") }; using (var bitmap = DevIL.DevIL.LoadBitmap(filename)) { var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); int byteCount = bitmapData.Stride * bitmap.Height; var bitmapBytes = new byte[byteCount]; Marshal.Copy(bitmapData.Scan0, bitmapBytes, 0, byteCount); bitmap.UnlockBits(bitmapData); var bitmapContent = new PixelBitmapContent<Color>(bitmap.Width, bitmap.Height); bitmapContent.SetPixelData(bitmapBytes); content.Mipmaps.Add(bitmapContent); } content.Validate(); return content; } } }
I tested it with all the 2D textures in my project, and the build speed difference is really noticeable. It also accepted all my textures, although I don’t use DXT compression nor exotic formats like A8/L8, so I can’t guarantee that it works for everything.
This class should go in a ContentPipelineExtension project which has a reference to the DevIL.NET2 assembly, and the DevIL.dll file in its build path. You can get those on DevIL.NET’s website; even though the last release dates from 2007, it works great.
Oh, nice to see you eventually used DevIL :D
Btw, the last video of Fez is just… wow! I loved the trippy blur effects in dark areas.
You should probably explicitly ask for PixelFormat.Format32bppArgb when locking the bitmap bits, otherwise in the event that you load a texture in a different format, you’ll be writing that format into an array and handing it to a PixelBitmapContent that’s expecting Color (32bpp ARGB).