World Wide Guide | Knowledge BankKukushkin's Notebook | Design Fundamentals


Keeping frame rates high

With increasing scenery complexity, the number of computations FS5 has to do for completing each frame increases, thus decreasing the frame rate. This is inevitable. However, there are many tricks that allow reducing the amount of computations FS5 has to do for each frame. Some of them result in a small decrease of the display quality, others have no visual effects at all.

The view window has a certain number of pixels. Without any visual and non-flat synth scenery in sight, each pixel is written into the display memory ("drawn") only once per frame - either when the sky or the ground texture is drawn. This results in a very high frame rate.

With increasing complexity of the scenery, some graphics primitives become covered by others. Pixels in covered areas are overwritten more than once, and the time needed to draw each frame increases. The more primitives cover each other, the more pixels are over-written more than once and the lower is the frame rate.

For most 3-D objects, primitives covering each other are inevitable. Ground polygons and synth tiles, however, can often be adjusted so that no overlapping would occur.

For example, if a runway is drawn like this:

1. Underlying synth tile (textured)
2. Ground polygon(s) (textured)
3. Green polygon defining the airport area (textured?)
4. Runway (textured)

each pixel on the runway is drawn 4 times a frame, each pixel on runway markings even 5 times.

Covered synth tiles should be made transparent

The first step in optimizing the ground is changing the type of fully covered synth tiles to transparent. Sometimes a significant part, but not all of a synth tile is covered. In such cases, ground polygons covering it can be extended or new polygons added in order to cover the tile completely thus allowing to make it transparent. The main difficulty while defining transparent tiles is to ensure a full coverage by ground polygons. Here, rounding errors in polygon drawing instructions and limited visibility ranges of RefPoints are main sources of problems.

Rounding errors sometimes cause polygons to be displayed in slightly different locations from those specified in drawing instructions. They normally appear only when adjacent polygons are drawn using different RefPoints with different scale factors. This difference does not exceed a few pixels, so normally it does not create any problems. However, if these polygons are supposed to cover a transparent synth tile, rounding errors would cause a part of it to be uncovered, which can cause many unpleasant visual effects. For this reason, such polygons often have to be programmed so that they slightly overlap.

The visibility range of a RefPoint is limited to 32767 RefPoint units. The maximum visibility for synth tiles is about 65 km. For this reason, the minimum scale factor for polygons covering the transparent tile should be 2, assuming the RefPoint is located in the center of the tile. Unlike a regular 3-D view, the background of the map view is erased before drawing each frame, so transparent tiles there normally do not create problems. Besides the visibility range of the RefPoint, the visibility range of the Area() block should also be set to a sufficient value. Because the necessary visibility range exceeds 40km, only Area(B)-blocks or combinations of Area(5) and Area(8) for different detail versions of ground polygons come in question.

It may be useful to replace transparent tiles with some distinctive tiles, like desert, during the design process. Then, uncovered tiles could easily be seen.

Eliminate overlapping ground polygons

The second step is to eliminate overlapping ground polygons. It is very difficult and not worth the effort to eliminate them completely, but there are many evident cases where a big performance gain can be obtained with little effort. In the example with the airport above, the polygon (2) can easily be split into parts so that the part covered by the polygon (3) would not have to be drawn. Splitting the polygon (3) in order not to draw its part covered by the runway is much more difficult, but sometimes worth the effort because the frame rate during the final approach is especially important. Splitting it further in order not to draw its parts covered by taxiways, buildings etc. is very difficult and not worth the effort, especially considering that drawing many parts instead of one polygon would make the code much longer.

There is no explicit rule, to what extent overlapping polygons should be eliminated. In general, a good optimization is needed only in frame rate-critical areas, such as runways. In other parts of the scenery, only big overlapped polygons should be optimized and only if the optimization can be done without extraordinary efforts.

Details of the scenery can only be seen from a close distance. Distant parts of the scenery can be drawn with much less detail almost without reducing the visual quality. Because simpler drawing routines normally require less processing time, the frame rate would increase. Drawing routines with different detail levels can also be used for reducing the memory consumption by the scenery and are discussed in "How to stay within limits".

Reduce the visibility where appropriate

Many small objects, like runway signs etc, cannot be seen from a big distance. The RefPoint() instruction has a V1= [FSASM:Range] parameter that can be used to reduce the visibility of such an object. Units for V1 are METERS, and not RefPoint units. If the distance from the viewpoint to the RefPoint would exceed the limit specified in V1, a jump to the label specified in the RefPoint command is performed. Normally, this causes object drawing instructions to be skipped over. If V1 is set to 0 (default value), no visibility test using V1 is performed.

Note that for objects with a visibility range more than a few miles, the range parameter in the Area()-block can be used instead. The advantage of using the range parameter for visibility tests is that invisible Area()-blocks do not occupy memory. However, visibility tests in Area()blocks are less precise due to the hysteresis effect described in "How to stay within limits".

Normally, RefPoints() located in front of as well as behind the view window are processed. However, objects located far enough behind the view window cannot be seen, so executing their drawing instructions would only consume processing time without any visual effect. The RefPoint() instruction has a special visibility parameter, V2 [FSASM:Var1], that allows defining the visibility for objects with RefPoints behind the view window. V2 defines the maximum distance to the view window _plane_ for such an object to be seen. The unit here is not 1 meter, but 1/sqrt(2) meters. V2 does not pay attention to the vertical component of this distance. V2 should normally be set to the maximum horizontal distance between the surface of the object and its RefPoint. For example, for a square object with the RefPoint in the middle, V2 should be set to half of its diagonal length. For a square with a side of 100 meters, this would mean V2= 100 [meters/sqrt(2)]. If V2 is set to 0 (default value), no visibility test using V2 is performed.

Failed visibility tests in RefPoints used for sorting PerspectiveCalled objects cause PerspectiveCall() to discard their addresses. For example, a sequence

PerspectiveCall( :P )
...
:P
Perspective
RefPoint( :Less_detail ... V1= 4000 ) ; Are we close enough?
... ; Yes - draw a detailed version of the object
:Less_detail ; No - draw a less detailed version... or not?
RefPoint( ... )
...

would not work as could be expected. If the visibility test in the first RefPoint would fail, this routine would be ignored and not called among other 3-D objects. Thus, a less detailed version would never be drawn.

Pay attention to texture mapping commands

A close attention must be paid to texture mapping instructions, because they normally consume the most processing time. Texture mapping in "flat" polygons and TexPolys is implemented using very different algorithms. The drawing time of a "flat" textured polygon mainly depends on  its size on the screen and almost doesn't depend on its scale factor, orientation etc. unlike that, the drawing time of a TexPoly strongly depends from the amount of texture pixels contained in it, even if the polygon is so small that most pixels are skipped over. The actual size of the polygon on the screen is much less important. For this reason, TexPolys containing very few texture pixels are sometimes drawn faster than similar "flat" textured surfaces. However, in most cases TexPolys are much slower.

Buildings drawn by the Building() command vary the amount of texture pixels used to draw their sides depending from the distance to the viewpoint. This significantly reduces the drawing time for buildings that are far away. The same trick can be done by custom scenery objects as well. By reducing the resolution of the texture, the drawing time of distant TexPolys can be significantly reduced.

From a big distance, objects appear very small and the difference between textured and non-textured surfaces can hardly be recognized. The flickering of texture pixels sometimes causes textured objects to appear even worse. The object drawing routine can check the distance and display a non-textured version of the object if it is distant enough. This could improve both the frame rate and the appearance of the object.

While optimization can significantly improve the frame rate, the overall decrease of the frame rate is inevitable with increasing visual complexity of the scenery. Nothing can be done about that, but it is possible to allow the user to determine the scenery complexity and so the frame rate.

Scenery complexity options

The user can affect the scenery complexity by changing different settings for display preferences and scenery complexity. FS5 automatically interprets many settings, like image quality or ground scenery shadows. Other settings must be interpreted by the scenery itself. In particular, a good scenery should react to different 'textured ...' settings and display non-textured polygons when asked to.

The scenery density setting is supposed to determine the number of visible objects. An object can be programmed to be visible only at a certain scenery density setting by testing the scenery density setting (variable 0346, FSASM:vSceneryComplexity) before drawing it:

IfVarRange( :Do_not_draw 0346 3 4 ) ; Between dense and very dense
PerspectiveCall( :Draw )
...
:Do_not_draw
...

Note that the test is done _before_ executing a PerspectiveCall() instruction. The limit on the total amount of PerspectiveCall()s decreases with decreasing scenery density, so it is important to PerspectiveCall() only routines that will really be used. Similarly, the scenery density for 2-D objects should be tested before executing the RefPoint() instruction in order to reduce the processing time for objects not drawn.

Different objects should be visible at different minimum scenery density settings. This would allow more precise adjustments of their number with the scenery density setting.

The frame rate is especially important during the landing. For this reason, "luxury" things like taxiway centerlines and -lights, airport signs, runway markings besides those drawn by the RunwayData instruction and other installations visible during the landing should disappear at low scenery complexity settings.

Each dynamic scenery object has its minimum detail level too. Similarly to static objects, minimum detail levels for different objects should be different in order to allow more precise adjustments of their number.


Last updated 11 October 1996 by Gene Kraybill.