Switch to the Czech version

Tuesday, 17 January 2012

Alecraft :: optimizations

Funny thing, this "optimization". The project development looks kind of like this:
  1. A great idea for a new feature comes up.
  2. I start adding the feature, the FPS drops down rapidly. It becomes unplayable.
  3. This sends me out on a crusade to optimize the code, FPS returns to the original playable range.
  4. I go back to the first step having enhanced the code with no real FPS loss.
To make this article a more technical one, lets take a look at some examples I encountered and would like to share:

Large cube world
Situation: the world is built from thousands of cubes.
Problem: this means thousands of triangles to be rendered. Most of them are not even visible.
Solution: render only the visible polygons. It is quite an obvious answer, but the real question is - how do I know which of them are visible?
Part of the problem is solved by jME3 (the game engine). It knows which objects are inside the camera viewing frustum and it ignores the rest.
The second part (which has to be handled by the game itself) is how to handle large amount of cube blocks, which block each other from the view. For example: if there is a large mountain, all you can see is its surface. No need to show what is inside the mountain. You have to mine it to get there first. And this is how it has to be done here as well.
My solution was to make each cube block "look around", find if it is completely surrounded by other cube blocks and if so, it would just sit there, not rendering a thing. This can be extended to render only the sides of the cube which are not hidden by a neighbour-cube. In the end this results in a nice "wrapper" of every compact mass of blocks.

Mesh batching
Situation: the scene consists of lots of small objects - cubes, trees, ...
Problem: graphics cards hate to deal with a lot of different objects. They are all right with rendering tons of triangles, but they all have to be of the same type.
Solution: batch the objects together, join the meshes into a few large objects.
This helps quite a lot, the graphics card can easily render large scenes when the meshes are joined together. On the other hand, if we join everything together it is not possible to skip the rendering of parts of the scene which are not visible. This again brings FPS drops. It is also not possible to selectively hide only parts of the scene.
My solution was to partition the scene by levels (layers of tiles based on the height) and then subdivide them into a grid. This gives me a smaller number of larger objects while allowing me to work only with the visible portion of the scene.
Again jME3 was a great help here, it comes with a scene node optimizer. I had to alter it slightly, because it changes the original meshes, which is not suitable for me. I maintain the original meshes (and do not render them), every "back-end" part (tile, etc.) changes the original node data. These are then optimized (copied and altered), put to the scene and rendered. After another change they are simply discarded and replaced by a newer optimized version.
The only remaining question is to find the perfect granularity of the scene. But this can be easily tweaked by changing a single number setting.

Changing mesh batches
Situation: the world is a changing environment, every now and then there is a new patch of grass, someone digs out a piece of dirt, ...
Problem: mesh batching is a complicated and time-consuming process and cannot be done every time something changes.
Solution: optimize only the nearest visible parts of the scene.
This one occurred to me when I finished some batching optimizations and turned the growing-grass feature back on. The game dropped to 0-1 FPS. No wonder - it had to recreate the meshes for most of the scene almost every single frame.
The first thing I tried was introducing the "how long has this region been dirty?" information. Using this I managed to lower the number of mesh rebuilding, because there was a timeout before every rebuild. The problem was this resulted in a really slowly changing environment, player had to wait a few moments for the scene to adapt to any changes.
What helped even more was when the rebuilding process started ignoring distant regions. If it cannot be seen clearly by the player it does not need to be updated (at all). I also added a condition which disqualified regions outside of the camera view frustum. Again, if it is not currently visible, just ignore it.
In the end the result is that any changes to the environment are handled only if the player can see them, otherwise it is pointless.

No comments:

Post a Comment