Improve XVR performance

From VRwiki
Jump to: navigation, search

As GPUs become faster, it becomes more and more important to group geometry into large batches. This translates into fewer draw calls and reduced overhead, allowing the GPU to achieve higher performance. This page collects some suggestions on how to obtain this in XVR.

Background

When you draw an object with the function CVmObj::Draw() XVR sets the OpenGL state to obtain the right placement and the right look of the mesh.

For every draw call XVR uses the roto-translation matrix to move the objects. If an object is fixed and it always stays in the same place, this work is useless. This problem becomes more apparent if you have a large number of objects, because you send a lot of useless positions to the OpenGL driver.

A similar problem arises for material appearance: OpenGL commands to set it (colour, texture...) are issued for every object drawn.

Tips

Exporting stationary objects as a single mesh

To avoid the repositioning of objects at every frame, you can export all stationary objects in the correct position as a single mesh. In that case you will send only one rototranslation matrix to OpenGL. If the objects share the same material, the gain is even better.

Using CVmNewMesh::Draw() instead of CVmObj::Draw()

If you know in advance that the object will never move, you may want to avoid sending even that matrix by calling CVmNewMesh::draw() instead of CVmObj::draw: this is because the CVmNewMesh class doesn't maintain any information on the pose of an object and will always draw the mesh at its origin. Use CVmObj only if your object is able to move.

Reducing the number of different materials

You can improve material handling even further. A lot of materials differ only because of their textures. You can merge several texture into a single one by using utility programs such as Texture Atlas from nVidia.

Merging the textures and modifying the UV coordinates of your mesh accordingly, you can draw more objects without changing the state for the material. If you use this technique you will have problems with tiled textures, but you can overcome them with a fragment shader that manages the tiling correctly; this is particularly useful with lightmaps.

Example

A slow program

var myObjects;
function onInit(){
   var treePositions;
   treePositions = readPositionFromFile(); // read the position of the three from a text file
   myObjects = array(100);
   var myMesh = CVmNewMesh("tree.aam");   // mesh of a tree
   for(var i = 0; i < len(myObjects); ++i){
      myObjects[i] = CVmObj(myMesh);
      myObjects[i].setPosition(treePositions[i]);
   }
}
 
function onFrame(){
   forech(var o in myObjects)
       o.draw();
}

A fast program

var myMesh;
function onInit(){
   myMesh = CVmNewMesh("forest.aam");   // mesh with a lot of tree positioned in the right place
}
 
function onFrame(){
   myMesh.draw();
}

Unfortunately you can't optimize the drawing of dynamics object the same way, but limiting the use of classes such as CVmObj and CVmCharacter to a minimum will help. As a rule of thumb, you shouldn't have more then 2000-3000 dynamic objects in your scene, or your fps will start to suffer.

Summary

To summarize:

  • If an object never moves, use the CVmNewMesh class to draw it
  • Merge objects sharing the same material
  • If the materials differ only by their texture, use a tool such as nVidia's Texture Atlas to merge them
  • Limit the use of dynamic objects