Creating Efficient Models, a Coder's Point of View

By taylor.

There is a growing problem of large and detailed models, which do look great, but their use causes big slowdowns and excessive memory usage. There have been conversations about this stuff before but most of those were between Lightspeed and myself when he was making all of the new texture map for old models. Similar conversations have taken place over effects but most of those too were more private than public. In a Mantis bug about mipmaps and LODs, VA asked numerous questions about how to do some the things that I was hinting at for reducing memory usage. Considering he is making a lot of the really nice models, the fact that he asked those questions made me a happy man. There isn't going to be one solution to all of this but at some point it's the model makers that are going to have to take blame for the ever increasing memory usage and slower render times. As coders we can only do so much until our fixes no longer do enough to control the problems. At that point there gets to be more pressure inside the ranks to impose certain limits on effects and texture sizes. I don't think that's a good thing for anyone but it's up to the makers of the content to do the right thing, impose some creative controls on themselves, and provide the rest of the help needed to keep the main game and mods working as well as they really could.

I'll go over the basic problems we have first and then get to questions/solutions:

  1. Too many bitmaps are being used.

    There is currently a limit of 4,750 bitmaps that can be loaded at one time. Not all of those will actually be in memory at any given moment but all of them need to be available for use constantly. That number may appear high but a typical mission with MediaVPs in use will easily use over 3,000 of the available slots. When it goes over the max is when we have the texture corruption and various other rendering issues that we used to have really bad. 3,500 used to be the limit for retail and after considerable work on changing how bmpman works a new limit of 4,750 was put into place. THAT LIMIT WILL NOT BE INCREASED AGAIN. The limit was calculated (by myself) based on the code changes to bmpman, the largest texture map using mission available at the time, plus an extra buffer factor for the future. In theory, if the effect and model producers followed certain rules (which almost no one knows at this point), then it would actually be possible to drop that limit to a lower level given the changes to bmpman.

  2. Textures are too large and detailed.

    While this may not seem like a real problem, I guarantee you that 2048x2048 model textures serve almost no purpose in any game. 99% of the time you just can't see that much detail, especially in the FS2 engine, but you have this massive texture stuck in memory anyway. Now I'm not about to say what size textures should be since that will always depend on the model or effect but this is where the self control comes into play. You should always try to use the smallest texture map possible to not only improve rendering speed but decrease memory usage. Just because a compressed DDS texture allows you twice the map size for the same memory usage does not mean you should actually do that. Remember that this is a game not an art show. There is a ton of content that needs to be loaded and rendered, and you have to remember that any model is nothing but a very small part of a very big game. Mods get a bit more play with that statement than anyone making upgrades for standard ships but mod makers probably know this already since they must take the mod as a whole into account when doing anything.

  3. Inefficient use of technology.

    Stay away from TGA textures unless you really need them. Reuse textures from other models when possible rather than including a copy with a different filename. Use compressed DDS whenever possible and make sure that mipmaps are generated with it. Include IBX files with all models that get released. Keep animation frame counts to a minimum. The list goes on but you get the idea. There is lots of advice for making things work more efficiently so just pay attention to that and apply as many of those things as possible to whatever you are making. Too many people don't.

Now to those questions:

  1. What does FS do with LOD textures?

    Ie, does it load them all into memory and switch when needed, or does it only load them when it needs to switch?

    When a model is loaded all of the textures used by that model are also loaded. Switching LODs would be painfully slow unless all of the needed content was already in memory, plus there will be many times that the same basic model is going to be on the screen many times using various LODs.

  2. Do different formats of textures affect performance/memory taken up? And if so, by how much?

    A DXT5 texture will take at most 1/4th of the memory of a TGA texture of the same size. A DXT1 texture will take between 1/8th and 1/6th of the memory of that TGA texture. If you feel the need to use TGA then make sure to use 24-bit TGA if you don't need an alpha channel. All DXT1, DXT3, and DXT5 textures are used as is, there is no additional processing on the images before they get shown on the screen. All other textures require some amount of work before they can be shown and so are inherently slower. Those compressed textures come at a price or course, quality. That loss of quality will have to be considered by whoever is making the images. As a general rule you can determine memory usage like this: (width * height * bytes_per_pixel). An 8-bit file is 1-byte per pixel, a 16-bit file is 2-bytes, a 24-bit file is 3-bytes, and a 32-bit file is 4-bytes. So, a 1024x1024 32-bit TGA needs 4meg of RAM (1024 * 1024 * 4). A PCX image is 8-bit but used as 16-bit in the game so you always use "2" for the bytes_per_pixel when looking at PCX. JPG images are always 3-bytes and use the same formula for size calculation. The DXT? formats do not use that formula, when loaded in memory they will use the same amount as the size of the file (ie. a 1meg file will take 1meg of RAM).

  3. Approximately what would be the 'perfect LOD' consist of?

    The fewer textures the better. Lots of polys can usually be handled pretty well but you don't want to be wasteful with that. If you are really close to something then more polys is great. The further away you are then you'll tend to get more models in view so if you aren't dropping enough polys going from LOD0 to LOD1 to LOD2 then it hurts overall performance. You don't want it to look like crap of course but if LOD1 looks exactly the same as LOD0 then you are probably doing something wrong, or you are really, really good at what you do. Todays graphics cards are very fast at rendering the basic model but it's the texturing always tends to slow things down. Use few textures, try to keep the overall dimensions of those textures as small as possible without losing too much detail, use DXT? textures with mipmaps, try to use those same textures on as many LODs as possible. If you can get away with using the LOD0 texture on LOD1 then do so, if it has mipmaps then you get faster rendering, better quality, and a significant reduction in memory usage. Use smaller textures for subsequent LODs if you aren't reusing the same texture, if LOD0 is 1024x1024 and you use a different texture for LOD1 then that LOD1 texture shouldn't still be 1024x1024. Remember that LOD2 and beyond are used when the model is getting far away so keep making smaller and smaller textures for those (or reuse the same texture, with mipmaps, from earlier LODs). If using animated glowmaps then drop the number of frames as well as the dimensions for different LODs, by LOD2 you usually can't see the animation anyway. And when I say to try and use the same texture for LOD0 that was used for LOD1 I don't mean one really big texture but the same texture. If it's mipmaped then it will work much better than a different texture. Doing it this way can't always be done but it should be whenever possible.

  4. Is it more or less efficient to have a really big ship use 2 big 2048 res maps as opposed to say, 8 smaller ones (256), and is the vastly superior visual quality worth the cost in terms of RAM usage?

    The fewer textures the better. Try not to waste the texture, nearly every part of the texture image should apply to some part of the model. If you've got a bunch of black space in the texture then you didn't map it very well. As mention in the above responses you have to try and control the want of quality with the need for usability. Not only is the visual quality usually not worth the RAM and general slowdown of a 2048x2048 texture but since the texture will have to get resized 99% of the time it's on the screen the visual quality will actually decreased in when use. Use the fewest and the smallest textures that you can in order to get the required detail across to the gamer. This will mean sacrificing something but when it comes down to sacrificing some detail in order to keep the game playable I would think that no hesitation would come from ditching that extra detail. Remember than each model is just a piece of the game and needs to be created knowing that the more detail that's included in the model's textures is a price that the game as a whole has to pay. What you can get away with will always differ based on the model but that's why this is a consideration for the model makers and those that create textures. Oh and never, EVER use a texture larger than 2048x2048. That's the largest size that most cards can use and anything larger will get resized to that automatically but will also seriously hurt rendering speed, memory, and image quality.

  5. Are nameplates fine as they are or are they costing us precious frames per second/megs of RAM?

    They should be fine. Those are extra of course but generally add enough to the game experience to justify them being there. Just remember to use PCX or DXT5 for those so you don't waste unnecessary memory on TGA versions. It's quite likely that nameplates will be automatically generated at some point down the road since that's possible with render-to-texture and future font code. That's not going to happen tomorrow or anything but is something to look forward to.

  6. Big one: Is the cost of storing even efficient LODs in the RAM worth it?

    (Especially on the highest detail settings, where the lods don't appear to be used anyway?) Could it be more efficient RAM-wise to keep using the highest detail model, but with smaller textures?

    LODs do get used always but the distance at which they change will vary when using the various detail settings. If you don't LOD efficiently (ie. you don't drop enough model detail to really justify LOD drop) then that can hurt efficiently. Properly using DDS textures with mipmaps will greatly help speed though. Even on the same LOD the mipmap levels will automatically change to help speed rendering and reduce visual quality loss from resized textures. Use fewer/smaller textures and let that be how you save RAM. As already mentioned, most modern video cards to fully capable of rendering very detailed models without skipping a beat. The OpenGL code especially has been optimized to deal with complex models effectively and there is still room to improve the code in the future. It's just the textures that cause the problems. For some like the hi-poly Triton for example, I use the highest detail version but with lower quality textures (even if I just resize the hi-quality ones myself). It still looks great and renders fast but I come out way ahead on memory saving. There is a comment I added to bmpman.h when I rewrote how it handles most images. This comment is something of a flame since I was pretty pissed about having to deal with this problem because of the artists. I used as an example the Terran Mara (before the hi-poly one) as an example of the texture abuse that was going on. The Terran Mara was using 117 bitmaps, taking into account the glow maps where each frame is considered one texture by the game.

  7. And while I'm here, might it be possible to dynamically resize textures based on the in-game detail settings?

    Before they're loaded into RAM in the first place that is? Ie, if they have it at 50% detail, all the textures above say, 512 res are halved in size. At 80%, they get 80% of the full textures etc. Basically it puts the efficiency cleanly in the end users control. If they have trouble running at full, they can just bite the bullet and tone down the settings, that way those who've paid for powerful rigs can get the best graphics, and those with less powerful rigs can get as much as they can.

    This is already controlled by the "3-D Hardware Textures" slider in the detail options in game. There are two problems with this though, it doesn't have any affect on compressed DDS textures, and it doesn't save any system memory. That option only does anything to help rendering speed. There has been something else proposed recently though. WMCoolmon proposed different filenames so that high, medium, and low quality textures could be always be available and a new setting would choose what gets loaded by default. I rather like the idea of different directories myself, use controlled by an existing in-game detail setting. For the directory option you would have "data/maps_lo" for low quality maps, "data/maps" would be for medium quality, and "data/maps_hi" would be for high quality maps. "data/maps" would always be a fallback if the other directories didn't have the file. Both proposals are possible, we just have to decided which is better for the modders to use (obviously with their input).

  8. And something that wasn't asked but an old feature that I wish more people would use, special models for the hud target box. The ships.tbl has an entry for a special model to use for the hud target box, this model is always rendered at full detail. Using a much lower poly model for this would seriously help rendering speeds when you have something targeted. Also be sure to use as small of textures as possible since that model won't ever be rendered very large. That's extra work so it's not something that I would want to force on anyone. It does help rendering speeds though and it would allow something like TBP to use quick rendering wireframe models for the targetbox since wireframe hi-poly models are chaotic to look at and very slow to draw.

    In summary:
  • Use LODs effectively

    Try to use the same textures on more than one LOD, but be sure so include mipmaps with the texture. This will reduce memory usage and rendering time. Make sure that there is enough geometry difference between LODs to help rendering. Always test in game when deciding how much geometry you can drop, but drop as much as you can. LOD changes are based on distance and detail levels. The "Model Detail" slider in the options screen can set these levels, it takes effect instantly and you can adjust the slider during a mission (by pressing F2 and going to the Details tab) to see the various effects.

  • Use smaller textures

    Try to keep textures at or under 1024x1024 if at all possible. Going with 1024x512, 512x512, or 512x256 should take care of most models without sacrificing much, if any, detail. Include mipmaps so those textures are used effectively. The graphics API will scale up textures if need be and you should let it rather than trying to make the texture as large as possible. Use smaller glow and spec maps whenever you can, those tend to not need as much detail as the main texture.

  • Use a special hud targetbox model if you can

    This won't save any memory, it will actually use more. But it can greatly increase rendering speed which would negate any impact the small amount of extra memory might take. It would also allow effective use of wireframe views in the targetbox since they are much slower to draw with complex models.

  • Use DDS textures when at all possible

    There are compressed (DXT1, DXT3, DXT5) and uncompressed DDS images available so use compressed when you can and go with uncompressed otherwise. Compressed images are both faster to process and use and consume at least 1/4th of the memory as uncompressed versions but do decrease quality to some degree. Always generate mipmaps for your DDS images.

  • Always use power-of-2 textures

    Textures are much faster to use if they are power-of-2. Power-of-2 textures are some combination in width or height of these dimensions: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048.

  • Never use a texture over 2048 for either width or height

    Most video cards have a max texture size of 2048x2048 and if you use something like a 4096x2048 texture then it will get resized automatically so that the video card can use it. The quality will get decreased, it will be slower to use, and it will still take up more memory than you will get out of it. In other words, it offer no positive benefit what so ever.

  • Try to reuse other people textures when possible

    Things like cockpit graphics, glow points, and even some base textures may be shareable between models. Rather than create a copy of the same image with a different filename just use the same filename. This lets other models use the same texture which saves memory.

  • Stay away from animated glow maps when at all possible

    They look cool but every frame of that animation counts as another texture that your model uses. If you feel the need to use an animation then try to use an EFF with compressed DDS frames when at all possible. Keep the number of frames used for your animation to a minimum. 25 frames for a glow map would be considered high, not average. Try not to use animated glows for anything less than LOD1, you probably couldn't see it anyway, go with static glows instead. Reusing the LOD0 glowmap (with mipmaps when using EFF/DDS) for at least LOD1 is also a good option.

  • Always try to include the IBX file(s) when you distribute a model

    This isn't required of course but it would save everyone who uses your model from having to do generate it themselves. There have been bugs in the model code which caused some IBX files to get generated wrong but those problems have since been fixed. Even where there were bugs, the format of IBX files has not changed and will not change, an IBX created today will still work the same in a build from early last year.

  • Remember that one model is just one model, a small piece of the game

    There are many other models and textures and effects that need to get loaded every mission. The more detail and the larger the textures used for one model the more it hurts the game as a whole. Every model needs to be designed with the knowledge that it's only one piece of the experience and compromises may have to be made to enhance the experience rather than the model.

For more information, see the original thread on HLP.