Moving Lua Scripts to C++


#1

What we want to accomplish with Thrive is monumental, and to do this, we need to increase the FPS as much as we can now so that we can add CPU-heavy features in the future without too much problem. Right now, I am running at 35 fps on my laptop, and we haven’t even implemented an AI and toxins and compound clouds and countless others important features which will surely drop the framerate even more. So let’s get Thrive up to 60 fps, that sounds like a good benchmark.

Okay, so Lua scripts. They’re quick to type up, easy to modify, take no time to recompile. Even better—anyone can change them, even someone lacking extensive programming training (we already have two mods from non-programmers). However, all of this comes with one huge BUT. Lua scripts are days slow. Their greatest strength—no need to compile them—is also their greatest weakness. While I was doing the compound clouds, I realized that the game starts to significantly lag when there are more than a few microbes in the screen. Imagine my surprise when I realized that all the microbe computing (AI, spawning, processes, etc…) is done at run-time in the Lua scripts.

Here are some statistics to show what I mean:
With the one player microbe on the screen the MicrobeSystem:update() function takes under 5 ms to complete. With 3-5 microbes, the times was 10-15 ms, and in multiple cases, I got over 20 ms for the MicrobeSystem update function alone. This is all with the game running at under 35 fps (28 milliseconds per frame), so all in all, the microbe update function is around 5% of the total frame rate. Okay, I’ll admit, I was expecting more when I started writing this post, but it adds up.

Anyway, I propose moving all of the components and systems from the Lua scripts to C++.


Game engine and moving the core from lua
#2

I did some profiling on Linux and found out that:

  • Disabling sound system results in effectively doubled FPS. I suspect that the reason is the Lua scripts creating too many sound emitters instead of reusing old ones.
  • During rendering a lot of the time is spend rendering the GUI where the culprit is AlpaHitWindow::renderingEndedHandler. I’m unsure if we really need that. It seems jut so inefficient (according to my limited understanding) to read the alpha channel of a texture after each rendering pass for each GUI widget. Instead of reading that data on start up, for example.
  • SystemWrapper::update() which is called through Lua does take a long time to complete.

So the real performance issue might not be Lua, but I still support ditching it.
Though, maybe some other scripting language could give better performance. AngelScript should have faster script to C++ calls which could help (haven’t actually done any benchmarking, it’s just what I have read about the differences between AngelScript and Lua), I’m just unsure about how good the JIT is for that (at least I got it running for some basic examples). And AngelScript is quite close to C++ so the real benefit might be quite minimal, if we add a way to quickly tweak some settings during runtime making a scripting language less useful.


#3

I can confirm that the sound system takes up quite a bit of on the overal fps. The reason I wanted to move stuff from Lua to C++ is because you can compile C++ files, which makes them a lot faster. I don’t think it’s worth switching to AngelScript, because it will require us to rewrite a lot of luabind code.

Also, fantastic job on getting everything to compile on Linux!


#4

You think it might be worth looking into LuaJIT?


#5

As I’ve been going trough the code and chatting with other programmers in the team, we all agreed on a fact that Lua is used in too many core elements of the game engine. This brings us to a possiblity of changing the game engine and moving stuff out of Lua to C++.
Let’s leave the technical stuff on side for a moment and focus more on the architecture of the engine.

This brings us to a question, what should be moved to C++ and what should be left in Lua. What I would imagine is that Lua should be used only for mod-specific stuff and by that I mean perhaps UI architecture, adding content like more entities ( more on that later ), more assets and stuff like that. Everything else should be made in C++. This would require some model of what is modder allowed to change and what he should have no idea about.

What I imagine is that modding allows people to make more content but never ( or in a very limited way ) more game logic. Everything like rendering or actully any game system should never be exposed to Lua / modder. I have already spend some time on this topic for my projects ( even though 2D, but same design can be applied in here ) and a good way of doing this is having hash tables of game content exposed to Lua so modder can add/modify/remove stuff out of this table. All entities are defined in this table as a table of components and accessed by ID.

To give you an example, let’s say we have some Car that has specific assets like sounds, textures, and so on ( everything only as a list of IDs ) and also has some AI ( the AI can be defined somewhere in a table and accessed by ID ). This way modder can add more cars, change its assets and so on. But the modder is never allowed to modify how to car is rendered, how it’s collision detection works and so on.

To summarize this:
All systems moved to C++ ( that includes events, sounds, input and so on ) and moddable stuff will be as a table with bunch of flags that can be modified but are managed in the Engine.

What do you think?


#6

I don’t think creating a bunch of hashtables with moddable parameters will be enough – I’d like a system where development can continue in Lua, with a backing engine that decreases the associated performance costs.

For example, if we created an API where, for example, some Lua code defines the structure/behaviour of a particular System, or a particular GameState, but the actual frame-by-frame computation is wholly done in C++, then that would be good.


Admin note: topic on Lua and C++ architecture was merged here, and this thread’s scope increased to a general architecture discussion. Having two threads was just going to muddle things.