This article describes how to optimize the performance of scenery and airplanes that use OBJs. OBJs are often the most expensive part of an airplane or scenery pack, so careful construction of OBJs can help.
Identifying Performance Limitations
There are three typical scenarios for object performance tuning:
- You have a small number of objects with a lot of detail in each one. Typically you will be constrained on “detail” – that is, the total VRAM used by all objects, total number of vertices, and sometimes attributes. A typical scenario might be an airplane, where there are only 20 objects, but each one uses a huge texture, a huge normal map, and has a ton of vertices.
- You have a moderate number of objects, each with a moderate amount of geometry. But do to the repetition, the total vertex count is very high. This is a case where the “leverage” of repeating an object causes problems. A typical case might be a jetway that has a lot of detail and is placed into an airport scenery package 200 times.
- You have a huge number of objects; pure object count is the limiting factor, even though the objects themselves are quite trivial. This can happen when objects are used to build cities, e.g. you have 10,000 building objects in a small area.
Before you can optimize your airplane or scenery, you must identify which scenario you are in. The best way to do this is to reduce the ‘cost’ of your scenery/airplane and watch performance change.
- Reduce the size of textures in photoshop.
- Temporarily remove parts of a model to reduce vertex count.
- Remove some objects from the scenery pack.
When you find a crude technique that improves framerate, you know where your costs are.
If you have a few objects and are still having performance problems, there are a few things to look out for:
First, be aware of overall vertex count. X-Plane can handle fairly large models, but at some point you will run out of vertex budget. Use LOD to simplify meshes when viewed from far away. Two notes on LOD use for vertex count:
- Don’t use LOD to save a small number of vertices. For example, LOD to reduce from 100 to 50 vertices is not a win for detail-bound models. But if your airplane is 500,000 vertices when viewed up close, LOD might be a real win.
- You get maximum performance when an object is not drawn at all because the maximum LOD distance is set low. Separate details that can be eliminated entirely into their own objects, then set their LOD very low to avoid them. The very act of drawing an OBJ is expensive, even if it contains one OBJ, so you save setup costs in this case.
The total amount of texture used by a model or scenery can start to crowd out VRAM. Here LOD is important too. If an object is not drawn at all because we are farther away than its highest LOD, then its texture won’t have to sit in VRAM. But this only works if the texture is not used by any objects.
- Organize your textures so that all details are in one texture, then use LOD to try to eliminate details when viewed far away.
Example: if you are building an airport, put all of your trucks, cones, and other small objects into one texture. Keep the terminals separate. The terminals will be visible from 10 miles away, but if all of the details have low LOD, the detail texture may not be used at all when on final approach, cutting down VRAM use.
Be aware that the cockpit object’s texture is never compressed or reduced in size, so use this texture sparingly; use it only for details that must be crisp to make the plane flyable. If the user is running at a low texture setting, there is a reason for it!
Optimizing Leveraged Geometry
When you have a moderate number of high-cost objects, the total geometry can start to add up. For example, 200 jetways with 10,000 vertices each will add two million vertices to the scene. It’s likely that all of the jetways are visible when the user flies over the airport; two million vertices is a lot, particularly when the rest of the world must be drawn too.
When working on an object that will be placed many times and has a lot of detail, use LOD carefully to avoid high vertex count. For example, it’s fine to have a high detail jetway with 10,000 vertices as long as we have another LOD with only 1000 vertices and a reasonable transition distance. At any one time, most jetways won’t be nearby and will take only 1000 vertices (for 200,000 vertices total) with maybe one jetway using 10,000 vertices. That’s almost a 10x savings in vertex count.
But be aware: there is a cost to having lots of LODs, so generally you should only use two or three LODs.
Optimizing Total Object Count
When placing a large number of objects, the object count becomes the most important factor. Most users will have machines that can draw between 3000 and 8000 objects, per frame, depending on rendering settings, hardware, and drivers. When building cities, it’s pretty easy to exceed these numbers.
The most effective way to cut object count is to make the farthest LOD of your objects be not too far away. When an object is beyond its maximum LOD distance, it is not drawn at all, reducing object count. X-Plane is very very fast at eliminating objects that are beyond their LOD distance. For example, X-Plane can easily draw an OBJ for every taxiway light (over 10,000 objects at KORD) because the LOD distance is only 500m on each light . Thus of those 10,000 objects, almost all of them are eliminated rapidly.
When object count gets large, small mistakes in the OBJs themselves start to add up. A single attribute in an OBJ can add one batch and make the OBJ twice as slow on the CPU to draw. For an airplane cockpit this is no problem, but if the OBJ is repeated 5000 times then we’ve added 5000 batches. When building an object that will be placed many times:
- Do not use attributes if you can possibly avoid it.
- Keep LOD low when possible.
- If possible, use only triangles; avoid lines and animation.
OBJ File Format Performance
This section contains some notes regarding the creation of fast OBJ files. This is only of interest if you are working on an export script that creates the OBJ file itself – that is, it deals with how to best organize an OBJ file for maximum speed.
Let’s start with the ‘cost’ of everything. Basically, the rough list is (most expensive changes come first: these are the things you do NOT want to interleave):
- Changing primitive type (tris vs lines vs lights vs smoke – always put lights last!).
- Changing to and from the cockpit texture (try to avoid doing this more than once!!)
- Changing “shader” (this includes: poly OS, ATTR_light_level — Material changes)
- “Cheap” attributes (ATTR_hard/ATTR_hard_deck, ATTR_draw_disable). Basically these control which ‘physical’ mesh a tri is part of are the cheapest attributes and lowest priority to optimize.
In other words, you never want to do anything to make your OBJ bigger to optimize out ATTR_hard, because the sim is good at ATTR_hard, but it is very slow at changing to the cockpit texture, so avoid doing that more than once. Within these buckets, there really isn’t much difference – that is, poly_os and blend mode both change shaders, so they have nearly the same cost.
You can avoid ‘small’ batches of two sided by duplicating the geometry and reversing the normal – if an entire OBJ is two sided, ATTR_two_sided _might_ be best, but if a model has only a few two-sided geometry, duplicate the mesh; it can be worth it to create up to 100+ extra triangles to avoid a shader change attribute.
The cost of anim transforms is of course partly in the animation, but part is in the stoppage of drawing. So given a HUGE pile of animation commands, it’s good to have them all in one group. That’s why the XPlane2Blender exporter performs well even though it duplicates the animations (to avoid attribute state) – the result is typically a set of animations, and they’re cheaper in groups.
One other note: if you put the word DEBUG on its own line at the end of your OBJ, X-Plane will output the internal command structure, which is what X-Plane will actually run. It will be different from what you output – some commands are optimized out, and some OBJ commands become multiple X-Plane commands. Generally, shorter output in that log is better, and fewer shader changes are really important. Note that the list contains aesthetic ideas too – e.g. poly_os is before non-poly_os because otherwise the poly_os geometry will look bad!
In the end, the fast path is an OBJ with _no_ internal shader changes; this case is fast no matter what the sort order (since there are no shader changes to sort) but if a user includes shader changes, there is going to be a speed hit.