We are planning a new voxel project, the time to choose tech has arrived. I like open-source software, so even though I knew a bit of Unity, I started exploring the Godot engine. Despite voxel games being commonly perceived as “simple” by gamers and the general public, voxel operations are quite computationally heavy. In this post, I’m going present you with data from two CPU-oriented tasks which are prevalent in procedural voxel games.

My code is not advanced, I tried to be as idiomatic and as consistent between languages as possible. No optimizations are made (e.g. using while instead of for construct for an iteration over given interval with a control variable in GDScript). I would like to note that I am no expert in neither of those languages – I started learning C++ just a month ago.

Both benchmarks only measure time for a specific task, startup time is not taken into account. All measurements are done in release mode (aka “exported” or “build”). Both benchmarks run on chunk radius of 7 with chunk size 32 (all side lengths are the same). I have chosen these values because their runs are fairly short (not longer than a few minutes) and because 32-chunk size performed quite well in my previous voxel experiments.

Terrain data generation

The first task is the generation of terrain data. This is usually implemented as a process of filling three dimensional arrays with integer values (block identifiers). The task is mainly about “language” performance, or rather about compiler, interpreter or virtual machine performance/optimizations. I would consider this particular benchmark semi-synthetic. The filling of chunks with data (tight loops, creation of arrays and structures for holding these arrays, function/method calls, passing data) is very close to operations a real game has to implement. On the other hand, the way of determining block values (ids) is rather synthetic (uses sine and cosine functions).

Since this benchmark mostly tests raw performance, not the engine itself, C# performance for both, Unity and Godot, is comparable. Godot is slightly faster, my guess would be this is caused by Unity using different version of Mono.

Engine, Language Seconds Chunks per Second
Unity 2018.3.0f2 (C#) 10.44 323.28
Godot 3.0.6 stable (GDScript) 124.34 27.14
Godot 3.1 beta 3 (C#) 10.29 327.99
Godot 3.1 beta 4 (GDNative + C++) 1.39 2428.06
Godot 3.1 stable (GDScript) 129.24 26.11
Godot 3.1 stable (C#) 10.34 326.40
Godot 3.1 stable (GDNative + C++) 1.39 2428.06

GDScript is very slow (by 11 times slower than C#), but that was, after all, expected since it is a dynamic language therefore not quite suitable for performance heavy code. This might change a bit after the typed version and its optimizations are implemented, but I don’t expect GDScript to be on par with C# or C++ (if no large/frequent costly communication takes place, which in case of C# is quite a problem; it does not happen in this benchmark).

I have to admit, I was really surprised by the performance difference of C# and C++. I was expecting C++ to fare better than C#, but not by so much, C++ is more than by 6 times faster compared to C#.

Mesh generation

Our second benchmark focuses on generating meshes from the terrain data obtained in the first step (measurements do not include time spent on terrain data generation, only on the “meshing”). I decided to not implement this task in GDScript on account of its not particularly encouraging results in the previous benchmark.

This test dealt with the creation of chunk meshes using means provided by respective engines. I have visually verified the correctness of resulting meshes.

Engine, Language Seconds Chunks per Second
Unity 2018.3.0f2 (C#) 27.93 120.84
Godot 3.1 beta 3 (C#) 30.40 111.02
Godot 3.1 beta 4 (GDNative + C++) 16.14 209.11
Godot 3.1 stable (C#) 30.51 110.62
Godot 3.1 stable (GDNative + C++) 16.02 210.67

We can see that C# versions are again comparable while Godot is in this case a bit slower, approximately by 9%. I think this is a great result considering that Unity has much more funding, and 3D capabilities of Godot are very young.

C++ wins in this benchmark, being by 90% faster than C# with Godot.

Conclusion

At this time, I am leaning towards C++ for our next project. It is harder to set up and use, but the performance gain looks rather significant and, from what I read, it appears interoperability is also better compared to C#.

Disclaimer

I am not trying to dismiss GDScript (or C#) as being too slow or unusable, nor do I say it should be avoided. Remember to always choose appropriate tools for your task. Most games don’t need this kind of performance, because they rely to a greater extent (or entirely) on the engine to do the heavy lifting.

There are significant down-sides to C++. In its current state, it lacks in the documentation department, is hard to set up, doesn’t support automatic compilation (out-of-box), is harder to debug, and engine’s Ref class exhibits unexpected behavior. As a language, it requires much more knowledge, effort and time from the programmer, since memory management is your responsibility.

In a similar vein, C# also has its down-sides. Passing data is rather costly (marshaling), introduces garbage collection (so potential frame drops) and documentation is sometimes confusing or missing.

I plan on trying to use GDScript for computationally undemanding tasks, like describing world generation parameters, GUI, defining properties and behavior of blocks and entities. Since we would like to have modding support for the game and Godot supports loading scenes and scripts at runtime, I hope that we are going to be able to make use of GDScript for this purpose as well.