Lua scripting in OpenMW

Everything about development and the OpenMW source code.
User avatar
psi29a
Posts: 5357
Joined: 29 Sep 2011, 10:13
Location: Belgium
Gitlab profile: https://gitlab.com/psi29a/
Contact:

Re: Lua scripting in OpenMW

Post by psi29a »

AnyOldName3 wrote: 30 Oct 2020, 13:12 If anything, header-only libraries are easier to have as dependencies than compiled ones. You're not going to get linker issues if you don't need to link with anything, and you just need to make sure CMake knows where the headers are, whether that's in the extern directory, a build/deps directory, or in the system include directory.

Problem solved... sol2/3 is just a another dep to download, we make sure our build system has access to it in include path, done.
https://github.com/ThePhD/sol2/tags

There is no need to pull it into /extern
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

psi29a wrote: 30 Oct 2020, 13:51 Problem solved... sol2/3 is just a another dep to download, we make sure our build system has access to it in include path, done.
https://github.com/ThePhD/sol2/tags

There is no need to pull it into /extern
The problem is not only in CI builds. It also complicates setting up a development workflow.

Now on ubuntu it is enough to run:
sudo apt install <deps> && git clone https://gitlab.com/OpenMW/openmw && mkdir openmw/build && cd openmw/build && cmake .. && make

sol2/3 doesn't provide a DEB (or some other) package. If we depend on it, setting up the workflow would include downloading sources manually and adjusting paths.

But it was only the first point. Other reasons to use custom solution:
  • Custom solution can better suit OpenMW needs since I develop it exactly for this. For example my prototype already applies sandboxing.
  • Lua C API is quite nice and working with it directly is not complicated.
  • My implementation is very small, and it is easy to support.
  • As NullCascade said, sol2/3 increases binary size a lot. I've checked it:

    The same example as in my prototype, but using sol3. Produces binary that is 11 times bigger than in my solution.
    Spoiler: Show
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

Did some benchmarking. It turned out that sol3 has much better performance. Still don't understand how does it work (28K lines of template magic :shock: ), but it is twice faster.
Most probably I'll give up with the idea of custom lua wrapper, but it was worth to try.
User avatar
psi29a
Posts: 5357
Joined: 29 Sep 2011, 10:13
Location: Belgium
Gitlab profile: https://gitlab.com/psi29a/
Contact:

Re: Lua scripting in OpenMW

Post by psi29a »

I do believe that we can have cmake download and inject the headers for us... that should simplify things quite a bit.

https://cmake.org/cmake/help/git-master ... oject.html

Example:
https://stackoverflow.com/a/21223763
User avatar
AnyOldName3
Posts: 2668
Joined: 26 Nov 2015, 03:25

Re: Lua scripting in OpenMW

Post by AnyOldName3 »

Look at what Foal's done for OpenXR. I convinced him to set it up so CMake grabs it as he kind of wanted a submodules-but-good approach without the complications of having stuff in extern.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

Multithreading and Lua scripting

Let's discuss the question "do we need a separate evaluation thread for lua scripting".

Currently scripting (mwscript) works in the same thread as rendering and in most cases doesn't take a lot of time.
Why would we want to run lua in a separate thread?
  1. Lua scripting will presumably be used by mods very extensively and in the future can become a bottleneck.
  2. We plan to dehardcode game mechanics and move some code from C++ to local Lua scripts. Lua is slower, so if we run it in the main thread, it will cause visible performance loss.
  3. In multiplayer mode all communication with the server will likely work in the same thread as local lua scripts. For the best performance it should be separate from the rendering thread.
The main drawback is difficulties with synchronization and concurrent access to the data.

Synchronization and access to the data

Even read access to the world state is not thread safe. For example there will be a problem if one thread reads a value from some std::vector exactly at the same time when another thread adds a new element to the vector, because the vector can decide to reallocate its internal storage and move all elements to the new one.

Locking concurrent access by critical sections doesn't look reasonable here, because it would happen too often and would kill all the benefits of multithreading.

Instead we can create a separate data holder "ScriptingWorld". Once per frame the main thread will send a message with actual actor positions and other fast-changing information. Scripting thread will receive the message, update ScriptingWorld, run scripts, and send the changed data to the main thread. Some data that is not needed for physics and for rendering, can live in the ScriptingWorld only.
It adds an additional overhead of synchronizing the states of World and ScriptingWorld, but it likely will be insignificant in comparison with benefits of handling scripts and dehardcoded mechanics outside of the main rendering thread.

Frame rate and scripting rate

In the basic approach “scripting rate” is the same as “frame rate”. Scripts work in a separate thread, but since we synchronize it with the main thread, frames are the same. However we can consider other variants.

Let’s imagine that some mod uses a really slow Lua local script. How will it affect the game? There are several ways to handle such situations:
  • Block the rendering thread until all scripts are processed. In this case the slow script drops frame rate and makes the game less playable.
  • Interrupt scripts that work too long. It’s unacceptable because it can break some essential functionality.
  • Don’t wait for scripts to finish, and start rendering a new frame. I.e. skip one update of the ScriptingWorld. If the script is constantly slow, we will have one scripting frame per every 2 (or even more) rendering frames. In this case “scripting rate” can be different from “frame rate” and some scripted events may happen with a minor delay, but the game will remain playable. This approach seems to be the most reasonable.


* * *

Overall I don't have a definite opinion what would be the best decision about multithreading for scripts. Any comments are welcome.
User avatar
AnyOldName3
Posts: 2668
Joined: 26 Nov 2015, 03:25

Re: Lua scripting in OpenMW

Post by AnyOldName3 »

It might be better for us to make modders deal with it. If you're writing a resource-hungry mod, maybe it should be your responsibility to spin the heavy lifting off into its own worker thread. That way, no one pays the complexity cost unless they're actually causing problems.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

AnyOldName3 wrote: 07 Nov 2020, 23:06 It might be better for us to make modders deal with it. If you're writing a resource-hungry mod, maybe it should be your responsibility to spin the heavy lifting off into its own worker thread. That way, no one pays the complexity cost unless they're actually causing problems.
Lua doesn't support threads. Async logic can be implemented with coroutines, but it's not real threads.

It is definitely simpler to leave mechanic and rendering mixed, but I am not sure what is better.

If somebody decides to create a completely new game using OpenMW as an engine, scripting is the only instrument to define game mechanics. Interesting new mechanics can require a lot of code and resources. It is quite unfortunate if game creators have to choose "better graphics or smart AI" while every player has a multicore CPU. In strategic perspective it can be reasonable to move scripting to a separate thread.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

ptmikheev wrote: 07 Nov 2020, 23:52 Lua doesn't support threads. Async logic can be implemented with coroutines, but it's not real threads.
Well, there is a library that adds multithreading to Lua, but anyway, a worker thread is not a solution.

In a general case we will have a bunch of scripts, each of them is too small to be moved to an own thread (that's also not clear how to do due to concurrent access to the common data), but together it may be more expensive than rendering.
User avatar
AnyOldName3
Posts: 2668
Joined: 26 Nov 2015, 03:25

Re: Lua scripting in OpenMW

Post by AnyOldName3 »

Could we have something like a bunch of scripts that need to be run at some point during the frame, and the main thread sticks them in a queue of waiting scripts that get picked up by a pool of worker threads, then once it needs the results of things, it can either deal with what the worker threads have already processed, or pick a script from the front of the queue to run itself if no results are ready yet. This architecture would still leave the main thread in control, if OpenMW was run on a machine with as many cores as scripts it could scale, and we can reuse the worker threads for other async tasks like preloading for the part of the frame where we've got no pending scripts yet.

I sort of anticipate that we're going to have a bunch of work that absolutely needs doing before any scripts can be run and a bunch that needs doing after all scripts have finished and then a bunch of work that all arrives at once and needs to be finished as soon as possible, so we'll want a system that copes well with that.
Post Reply