AGI Components with Insight3D Alpha 2008 r8
Batching

For raw rendering speed, use the least amount of primitives to represent as many objects as possible.

Topic Description
Triangle Mesh Combine AgGxPrimitiveTriangleMesh objects that have the same color. When using indexed triangle lists as input, the triangles do not need to be adjacent. See the Triangle Mesh Overview to learn about indexed triangle lists.
Point Batch Instead of creating several AgGxPrimitivePointBatch objects, each with a single point, create one AgGxPrimitivePointBatch with many points.
Text Batch Use one AgGxPrimitiveTextBatch object to render several strings. Although in many cases, rendering thousands of strings with a small number of objects will be as efficient as rendering with just one, and is likely to provide better culling.
Polyline Instead of creating several AgGxPrimitivePolyline objects with two points each, create one AgGxPrimitivePolyline with several points. Also, instead of creating several AgGxPrimitivePolyline objects, using PolylineTypeLineStrip, create one AgGxPrimitivePolyline using PolylineTypeLines. This trades memory for better batching.
Triangle Mesh

To illustrate combing triangle mesh primitives that share the same color, the following example renders 5 degree extents across the entire globe using 2,592 triangle mesh primitives.

121.38 fps is reasonable performance; however if we can decrease the number of primitives, we can improve performance even though the number of pixels rendered remains the same. By reducing the number of primitives from 2,592 to 72, the frame rate increases to 994.48.

This batching relies on primitives sharing the same color so it is limitied by the number of unique colors used - 72 in this case. The following class can be used to combine triangle mesh primitives.

CopyC#
class TriangleMeshBatch
{
    public TriangleMeshBatch(Color color)
    {
        _color = color;
        _vertices = new List<double>();
        _normals = new List<double>();
        _indices = new List<int>();
    }

    /// <summary>
    /// Call this one or more times to add triangles to the mesh.
    /// </summary>
    public void Append(IAgGxTriangulator triangles)
    {
        double[] vertices = triangles.Vertices as double[];
        double[] normals = triangles.Normals as double[];
        int[] indices = triangles.Indices as int[];

        int offset = _vertices.Count / 3;

        _vertices.AddRange(vertices);
        _normals.AddRange(normals);

        _indices.Capacity = Math.Max(_indices.Capacity, _indices.Capacity + indices.Length);
        for (int i = 0; i < indices.Length; ++i)
        {
            _indices.Add(indices[i] + offset);
        }
    }

    /// <summary>
    /// Call this once after all calls to Append().
    /// </summary>
    public IAgGxPrimitiveTriangleMesh Commit(IAgGxCentralBody centralBody, IAgGxPrimitiveManager primitives)
    {
        double[] vertices = new double[_vertices.Count];
        double[] normals = new double[_normals.Count];
        int[] indices = new int[_indices.Count];

        _vertices.CopyTo(vertices);
        _vertices.Clear();
        _vertices.TrimExcess();

        _normals.CopyTo(normals);
        _normals.Clear();
        _normals.TrimExcess();

        _indices.CopyTo(indices);
        _indices.Clear();
        _indices.TrimExcess();

        Array aryVertices = vertices;
        Array aryNormals = normals;
        Array aryIndices = indices;

        IAgGxPrimitiveTriangleMesh mesh = new AgGxPrimitiveTriangleMesh();
        mesh.Initialize(centralBody,
            AgGxReferenceFrame.ReferenceFrameFixed,
            AgGxVertexUpdate.VertexUpdateNone,
            ref aryVertices,
            ref aryNormals,
            ref aryIndices);

        mesh.SetRGBA(_color.R, _color.G, _color.B, 255);
        primitives.Add(mesh);
        return mesh;
    }

    private Color _color;
    private List<double> _vertices;
    private List<double> _normals;
    private List<int> _indices;
}

To use this class, first create an instance, specifying the color of the mesh. Then call Append one or more times to add triangles to the mesh. The triangulator objects do not need to be adjacent to each other. Finally, call Commit to create the primitive. An example that combines two extents is shown below.

CopyC#
IAgGxTriangulatorSurfaceExtent extent1 = new AgGxTriangulatorSurfaceExtent();
IAgGxTriangulatorSurfaceExtent extent2 = new AgGxTriangulatorSurfaceExtent();

extent1.Initialize(earth, 0, 0, Trig.DegreesToRadians(1), Trig.DegreesToRadians(1));
extent2.Initialize(earth, Trig.DegreesToRadians(2), 0, Trig.DegreesToRadians(3), Trig.DegreesToRadians(1));

TriangleMeshBatch batch = new TriangleMeshBatch(Color.Green);
batch.Append(extent1);
batch.Append(extent2);
IAgGxPrimitiveTriangleMesh mesh = batch.Commit(earth, sceneManager.Primitives);
Point Batch

For best performance, use large point batches. If a point batch is created for each point in the STK Area Targets for the United States, 13,691 primitives are created. The frame rate is 46.84 fps as shown below.

Instead, if one point batch is created for each area target, only 134 primitives are created and the frame rate jumps to 1,559.88.

The frame rate can be improved to 1,886.59 if only one point batch is used to represent every point.

In order to combine point batches, it may be necessary to use InitializeWithColors or InitializeCartographicWithColors if the orginial point batches have different colors. These methods allow each point in a point batch to have a unique color.

Text Batch

To demonstrate AgGxPrimitiveTextBatch performance, the following example renders a string for each point in the STK Area Targets for the United States. 13,691 strings, with a total of 192,073 characters, are rendered.

The strings were rendered using a various number of text batches, as shown in the table. Peak performance is achieved when the number of strings (or more precisely characters) in each batch is large. Rendering with 1-1,000 batches yields the best frame rate, rendering with a single batch per string yields a poor frame rate.

Number of text batches fps
1-1,000 164
1,500 137
2,000 101
5,000 38
13,691 (one batch per string) 14

Since 1,000 batches performed as well as 1 batch, it is recommended to use 1,000 batches to achieve the best view-dependent performance. See the object culling section for the reason why. Keep in mind that you might want to scale the number of batches back when other primitives are in the scene.

Text batches provide a great degree of flexibility to allow grouping several strings into one batch. Using AgGxPrimitiveTextBatchOptionalParameters, each string can have a unique color, horizontal origin, vertical origin, pixel offset, and eye offset. The main constraint is each string in a batch must share the same AgGxFont.

Polyline

It is straightforward and beneficial to combine several polylines that use PolylineTypeLines into one polyline. It is also possible to combine several polylines that use PolylineTypeLineStrip into a single primitive that uses PolylineTypeLines by duplicating vertices. To illustrate this, the following example renders the boundary for each state using a separate primitive at 1,508.04 fps.

If per-vertex color is used and the type is changed from line strips to individual lines, one polyline primitive can render the boundary for every state and improve the frame rate to 1,915.39 fps.

This uses a significant amount more memory – more then double the previous method. Be cautious with this technique because it may hurt performance when limited GPU memory is available. If too much memory is used, it will not all fit into GPU memory and will cause more bus traffic.